@certik/skynet 0.7.17 → 0.8.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/deploy.js CHANGED
@@ -1,27 +1,37 @@
1
1
  const fs = require("fs/promises");
2
+ const fso = require("fs");
2
3
  const execa = require("execa");
3
- const path = require("path");
4
4
  const meow = require("meow");
5
5
  const chalk = require("chalk");
6
6
  const which = require("which");
7
7
  const { getSelectorFlags, getSelectorDesc } = require("./selector");
8
8
  const { getEnvOrThrow } = require("./env");
9
+ const { getBinaryName, detectSkynetDirectory } = require("./cli");
10
+
11
+ const INTERVAL_ALIASES = {
12
+ "@secondly": "*/1 * * * * * *",
13
+ "@minutely": "0 * * * * * *",
14
+ "@hourly": "0 0 * * * * *",
15
+ "@daily": "0 0 0 * * * *",
16
+ "@weekly": "0 0 0 * * 0 *",
17
+ };
9
18
 
10
- function buildEnvSection(additionalEnv, isProduction) {
11
- const defaultEnv = {
12
- SKYNET_AWS_ACCESS_KEY_ID: getEnvOrThrow("SKYNET_AWS_ACCESS_KEY_ID"),
13
- SKYNET_AWS_SECRET_ACCESS_KEY: getEnvOrThrow("SKYNET_AWS_SECRET_ACCESS_KEY"),
14
- SKYNET_AWS_REGION: process.env.SKYNET_AWS_REGION || "us-east-1",
15
- SKYNET_ENVIRONMENT: isProduction ? "prd" : "dev"
16
- };
17
-
18
- const obj = { ...defaultEnv, ...additionalEnv };
19
-
20
- return Object.keys(obj)
21
- .map(key => `${key} = "${obj[key]}"`)
19
+ function buildEnvTemplate(additionalEnv, isProduction) {
20
+ return Object.keys(additionalEnv)
21
+ .map((key) => {
22
+ return `${key} = "${additionalEnv[key] || getEnvironmentVariableValue(key, isProduction)}"`;
23
+ })
22
24
  .join("\n ");
23
25
  }
24
26
 
27
+ function getEnvironmentVariableValue(name, isProduction) {
28
+ if (isProduction) {
29
+ return `{{key "${name}"}}`;
30
+ } else {
31
+ return process.env[name];
32
+ }
33
+ }
34
+
25
35
  const genConfig = ({
26
36
  jobName,
27
37
  workingDirectory,
@@ -31,13 +41,12 @@ const genConfig = ({
31
41
  cpu,
32
42
  mem,
33
43
  additionalEnv = [],
34
- dc = "us-east-1",
35
- isProduction
44
+ region = "us-east-1",
45
+ isProduction,
36
46
  }) => `
37
47
  job "${jobName}" {
38
- datacenters = ["${dc}"]
48
+ datacenters = ["${region}"]
39
49
 
40
- # Run this job as a "batch" type
41
50
  type = "batch"
42
51
 
43
52
  ${
@@ -66,9 +75,7 @@ job "${jobName}" {
66
75
  unlimited = false
67
76
  }
68
77
 
69
- ${
70
- isProduction
71
- ? `task "log-shipper" {
78
+ task "log-shipper" {
72
79
  driver = "raw_exec"
73
80
 
74
81
  config {
@@ -79,16 +86,13 @@ job "${jobName}" {
79
86
  ]
80
87
  }
81
88
 
82
- # It is possible to set environment variables which will be
83
- # available to the task when it runs.
84
89
  env {
85
- ${buildEnvSection({}, isProduction)}
90
+ SKYNET_ENVIRONMENT="${isProduction ? "prd" : "dev"}"
91
+ AWS_REGION="${region}"
86
92
  }
87
93
 
88
94
  kill_timeout = "120s"
89
95
 
90
- # Specify the maximum resources required to run the task,
91
- # include CPU and memory.
92
96
  resources {
93
97
  cpu = 100 # MHz
94
98
  memory = 100 # MB
@@ -98,11 +102,9 @@ job "${jobName}" {
98
102
  attempts = 3
99
103
  mode = "fail"
100
104
  }
101
- }`
102
- : ""
103
105
  }
104
106
 
105
- task "indexer" {
107
+ task "main" {
106
108
  driver = "raw_exec"
107
109
 
108
110
  config {
@@ -113,10 +115,21 @@ job "${jobName}" {
113
115
  ]
114
116
  }
115
117
 
118
+ template {
119
+ change_mode = "restart"
120
+ destination = "secrets/context.env"
121
+ env = true
122
+
123
+ data = <<EOH
124
+ ${buildEnvTemplate(additionalEnv, isProduction)}
125
+ EOH
126
+ }
127
+
116
128
  # It is possible to set environment variables which will be
117
129
  # available to the task when it runs.
118
130
  env {
119
- ${buildEnvSection(additionalEnv, isProduction)}
131
+ SKYNET_ENVIRONMENT="${isProduction ? "prd" : "dev"}"
132
+ AWS_REGION="${region}"
120
133
  }
121
134
 
122
135
  # Specify the maximum resources required to run the task,
@@ -152,10 +165,10 @@ job "${jobName}" {
152
165
  }
153
166
  }`;
154
167
 
155
- function getJobName(name, selectorFlags, mode = null, periodic = false) {
168
+ function getJobName(name, selectorFlags, mode = null) {
156
169
  const selectorNamePart = Object.keys(selectorFlags)
157
170
  .sort()
158
- .map(name => `${name}:${selectorFlags[name]}`)
171
+ .map((name) => selectorFlags[name])
159
172
  .join("-");
160
173
 
161
174
  let jobName = name;
@@ -168,14 +181,10 @@ function getJobName(name, selectorFlags, mode = null, periodic = false) {
168
181
  jobName += `-${selectorNamePart}`;
169
182
  }
170
183
 
171
- if (periodic) {
172
- jobName += `-periodic`;
173
- }
174
-
175
184
  return jobName;
176
185
  }
177
186
 
178
- function getNomadAddr(isProduction) {
187
+ async function getNomadAddr(isProduction) {
179
188
  let nomadAddr;
180
189
 
181
190
  if (isProduction) {
@@ -183,18 +192,16 @@ function getNomadAddr(isProduction) {
183
192
 
184
193
  nomadAddr = getEnvOrThrow("SKYNET_NOMAD_PRODUCTION_ADDR");
185
194
  } else {
195
+ const skynetDir = detectSkynetDirectory();
196
+
197
+ if (!fso.existsSync("/tmp/skynet")) {
198
+ await execa("ln", ["-s", skynetDir, "/tmp/skynet"]);
199
+ }
200
+
201
+ console.log("Deploy locally, please start nomad server in a separate terminal");
202
+ console.log(`You can start nomad server by running ${chalk.inverse(`${skynetDir}/infra-nomad/dev/start.sh`)}`);
186
203
  console.log(
187
- "Deploy locally, please start nomad server in a separate terminal"
188
- );
189
- console.log(
190
- `You can start nomad server by running ${chalk.inverse(
191
- "cd <skynet-repo-root> && ln -s . /opt/certik/skynet && infra-nomad/dev/start.sh"
192
- )}`
193
- );
194
- console.log(
195
- `Then you can visit ${chalk.underline(
196
- "http://localhost:4646/ui/jobs"
197
- )} to check submitted dev jobs.\n`
204
+ `Then you can visit ${chalk.underline("http://localhost:4646/ui/jobs")} to check submitted dev jobs.\n`
198
205
  );
199
206
 
200
207
  nomadAddr = "http://127.0.0.1:4646";
@@ -203,31 +210,27 @@ function getNomadAddr(isProduction) {
203
210
  return nomadAddr;
204
211
  }
205
212
 
206
- async function runNomadJob(jobName, nomadJobDefinition, isStop, isProduction) {
207
- let nomadPath;
208
-
213
+ async function getNomadPath() {
209
214
  try {
210
- nomadPath = await which("nomad");
215
+ return await which("nomad");
211
216
  } catch (missingNomad) {
212
217
  console.log(
213
- `Deploy requires ${chalk.bold(
214
- "nomad"
215
- )} binary, please follow ${chalk.underline(
218
+ `Deploy requires ${chalk.bold("nomad")} binary, please follow ${chalk.underline(
216
219
  "https://learn.hashicorp.com/tutorials/nomad/get-started-install"
217
220
  )} for installation`
218
221
  );
219
222
 
220
223
  throw new Error("missing nomad binary");
221
224
  }
225
+ }
222
226
 
223
- const nomadAddr = getNomadAddr(isProduction);
224
-
227
+ async function runNomadJob(nomadPath, nomadAddr, jobName, nomadJobDefinition, isStop) {
225
228
  try {
226
229
  if (isStop) {
227
230
  const nomad = execa(nomadPath, ["job", "stop", jobName], {
228
231
  env: {
229
- NOMAD_ADDR: nomadAddr
230
- }
232
+ NOMAD_ADDR: nomadAddr,
233
+ },
231
234
  });
232
235
  nomad.stdout.pipe(process.stdout);
233
236
  await nomad;
@@ -240,8 +243,8 @@ async function runNomadJob(jobName, nomadJobDefinition, isStop, isProduction) {
240
243
 
241
244
  const nomad = execa(nomadPath, ["job", "run", jobFileName], {
242
245
  env: {
243
- NOMAD_ADDR: nomadAddr
244
- }
246
+ NOMAD_ADDR: nomadAddr,
247
+ },
245
248
  });
246
249
  nomad.stdout.pipe(process.stdout);
247
250
  await nomad;
@@ -249,10 +252,12 @@ async function runNomadJob(jobName, nomadJobDefinition, isStop, isProduction) {
249
252
  console.log(chalk.green(`deployed ${jobName}`));
250
253
  }
251
254
  } catch (nomadExecErr) {
255
+ console.log("Nomad Execution Error:");
256
+ console.log(nomadExecErr.message);
257
+ console.log("");
258
+
252
259
  console.log(
253
- `Failed to run ${chalk.bold(
254
- "nomad"
255
- )} commands, please ensure nomad server is accessible at ${chalk.bold(
260
+ `Failed to run ${chalk.bold("nomad")} commands, please ensure nomad server is accessible at ${chalk.bold(
256
261
  nomadAddr
257
262
  )}`
258
263
  );
@@ -262,106 +267,118 @@ async function runNomadJob(jobName, nomadJobDefinition, isStop, isProduction) {
262
267
  }
263
268
 
264
269
  function createModeDeploy({
270
+ binaryName,
265
271
  name,
266
272
  workingDirectory,
267
273
  bin = "bin/indexer",
268
274
  selector = {},
269
275
  env = {},
276
+ region = "us-east-1",
277
+ check,
270
278
  deltaSchedule,
271
279
  validateSchedule,
272
280
  deltaCpu,
273
281
  deltaMem,
274
282
  rebuildCpu,
275
- rebuildMem
283
+ rebuildMem,
284
+ validateCpu,
285
+ validateMem,
276
286
  }) {
277
- async function deployMode({
278
- mode,
279
- from,
280
- to,
281
- stop,
282
- production,
283
- verbose,
284
- ...selectorFlags
285
- }) {
287
+ async function deployMode({ mode, from, to, stop, production, verbose, ...selectorFlags }) {
286
288
  if (mode === "delta") {
287
289
  // delta mode will ignore from/to flags
288
290
  from = 0;
289
291
  to = 0;
290
292
  }
291
293
 
292
- const isPeriodic =
293
- from === 0 && to === 0 && ["delta", "validate"].includes(mode);
294
+ const isPeriodic = from === 0 && to === 0 && ["delta", "validate"].includes(mode);
294
295
 
295
- const jobName = getJobName(name, selectorFlags, mode, isPeriodic);
296
+ const jobName = getJobName(name, selectorFlags, mode);
296
297
 
297
298
  const selectorCmdPart = Object.keys(selectorFlags)
298
299
  .sort()
299
- .map(name => `--${name} ${selectorFlags[name]}`)
300
+ .map((name) => `--${name} ${selectorFlags[name]}`)
300
301
  .join(" ");
301
- let cmd = `${bin} --mode ${mode} ${selectorCmdPart}`;
302
+ let args = `--mode ${mode} ${selectorCmdPart}`;
302
303
 
303
- if (from > 0) {
304
- cmd += ` --from ${from}`;
304
+ if (verbose) {
305
+ args += ` --verbose`;
305
306
  }
306
307
 
307
- if (to > 0) {
308
- cmd += ` --to ${to}`;
308
+ let rangeArgs = "";
309
+
310
+ if (from > 0) {
311
+ rangeArgs += ` --from ${from}`;
309
312
  }
310
313
 
311
- if (verbose) {
312
- cmd += ` --verbose`;
314
+ if (to > 0) {
315
+ rangeArgs += ` --to ${to}`;
313
316
  }
314
317
 
315
318
  const modeResouces = {
316
319
  rebuild: { cpu: rebuildCpu, mem: rebuildMem },
317
320
  "resume-rebuild": { cpu: rebuildCpu, mem: rebuildMem },
318
- validate: { cpu: rebuildCpu, mem: rebuildMem },
319
- delta: { cpu: deltaCpu, mem: deltaMem }
321
+ validate: { cpu: validateCpu || rebuildCpu, mem: validateMem || rebuildMem },
322
+ delta: { cpu: deltaCpu, mem: deltaMem },
320
323
  };
321
324
 
322
325
  // by default use delta cpu/mem settings
323
326
  const { cpu, mem } = modeResouces[mode] || modeResouces.delta;
324
327
 
325
- const intervalAliases = {
326
- "@secondly": "*/1 * * * * * *",
327
- "@minutely": "0 * * * * * *",
328
- "@hourly": "0 0 * * * * *",
329
- "@daily": "0 0 0 * * * *",
330
- "@weekly": "0 0 0 * * 0 *"
331
- };
332
-
333
- const deltaCron =
334
- typeof deltaSchedule === "function"
335
- ? deltaSchedule(jobName)
336
- : deltaSchedule;
328
+ const deltaCron = typeof deltaSchedule === "function" ? deltaSchedule(jobName) : deltaSchedule;
337
329
 
338
- const validateCron =
339
- typeof validateSchedule === "function"
340
- ? validateSchedule(jobName)
341
- : validateSchedule;
330
+ const validateCron = typeof validateSchedule === "function" ? validateSchedule(jobName) : validateSchedule;
342
331
 
343
332
  const modeIntervals = {
344
- delta: intervalAliases[deltaCron] || deltaCron,
345
- validate: intervalAliases[validateCron] || validateCron
333
+ delta: INTERVAL_ALIASES[deltaCron] || deltaCron,
334
+ validate: INTERVAL_ALIASES[validateCron] || validateCron,
346
335
  };
347
336
 
348
- const nomadJobDefinition = genConfig({
337
+ const mainJobDefinition = genConfig({
349
338
  jobName,
350
339
  cron: isPeriodic && modeIntervals[mode],
351
340
  workingDirectory,
352
341
  additionalEnv: env,
353
- cmd,
342
+ region,
343
+ cmd: `${bin} ${args} ${rangeArgs}`,
354
344
  cpu,
355
345
  mem,
356
- isProduction: production
346
+ isProduction: production,
357
347
  });
358
348
 
359
- await runNomadJob(jobName, nomadJobDefinition, stop, production);
349
+ const nomadPath = await getNomadPath();
350
+ const nomadAddr = await getNomadAddr(production);
351
+
352
+ await runNomadJob(nomadPath, nomadAddr, jobName, mainJobDefinition, stop);
353
+
354
+ if (check && check.bin) {
355
+ console.log("");
356
+
357
+ const monitorJobName = `${jobName}-monitor`;
358
+ const monitorJobDefinition = genConfig({
359
+ jobName: monitorJobName,
360
+ cron: INTERVAL_ALIASES[check.schedule] || check.schedule,
361
+ workingDirectory,
362
+ additionalEnv: {
363
+ ...env,
364
+ SKYNET_SLACK_TOKEN: null,
365
+ SKYNET_NOMAD_URL: null,
366
+ },
367
+ region,
368
+ cmd: `${check.bin} ${args}`,
369
+ cpu: check.cpu || 100,
370
+ mem: check.mem || 100,
371
+ isProduction: production,
372
+ });
373
+
374
+ await runNomadJob(nomadPath, nomadAddr, monitorJobName, monitorJobDefinition, stop);
375
+ }
360
376
  }
361
377
 
362
378
  function deploy() {
363
- const binaryNameParts = process.argv[1].split(path.sep);
364
- const binaryName = binaryNameParts[binaryNameParts.length - 1];
379
+ if (!binaryName) {
380
+ binaryName = getBinaryName();
381
+ }
365
382
 
366
383
  const cli = meow(
367
384
  `
@@ -389,36 +406,36 @@ ${getSelectorDesc(selector)}
389
406
  ...getSelectorFlags(selector),
390
407
  mode: {
391
408
  type: "string",
392
- default: "delta"
409
+ default: "delta",
393
410
  },
394
411
  from: {
395
412
  alias: "since",
396
413
  type: "number",
397
- default: 0
414
+ default: 0,
398
415
  },
399
416
  to: {
400
417
  alias: "until",
401
418
  type: "number",
402
- default: 0
419
+ default: 0,
403
420
  },
404
421
  verbose: {
405
422
  type: "boolean",
406
- default: false
423
+ default: false,
407
424
  },
408
425
  production: {
409
426
  alias: "prd",
410
427
  type: "boolean",
411
- default: false
428
+ default: false,
412
429
  },
413
430
  stop: {
414
431
  type: "boolean",
415
- default: false
416
- }
417
- }
432
+ default: false,
433
+ },
434
+ },
418
435
  }
419
436
  );
420
437
 
421
- deployMode(cli.flags).catch(err => {
438
+ deployMode(cli.flags).catch((err) => {
422
439
  console.error(err);
423
440
  process.exit(1);
424
441
  });
@@ -428,54 +445,81 @@ ${getSelectorDesc(selector)}
428
445
  }
429
446
 
430
447
  function createDeploy({
448
+ binaryName,
431
449
  name,
432
450
  workingDirectory,
433
451
  bin = "bin/indexer",
434
452
  selector = {},
453
+ region = "us-east-1",
435
454
  env = {},
455
+ check,
436
456
  schedule,
437
457
  restart,
438
458
  cpu,
439
- mem
459
+ mem,
440
460
  }) {
441
- async function deployModeless({
442
- production,
443
- stop,
444
- verbose,
445
- ...selectorFlags
446
- }) {
447
- const jobName = getJobName(name, selectorFlags, null, !!schedule);
461
+ async function deployModeless({ production, stop, verbose, ...selectorFlags }) {
462
+ const jobName = getJobName(name, selectorFlags, null);
448
463
 
449
464
  const selectorCmdPart = Object.keys(selectorFlags)
450
465
  .sort()
451
- .map(name => `--${name} ${selectorFlags[name]}`)
466
+ .map((name) => `--${name} ${selectorFlags[name]}`)
452
467
  .join(" ");
453
- let cmd = `${bin} ${selectorCmdPart}`;
468
+ let args = `${selectorCmdPart}`;
454
469
 
455
470
  if (verbose) {
456
- cmd += ` --verbose`;
471
+ args += ` --verbose`;
457
472
  }
458
473
 
459
474
  const cron = typeof schedule === "function" ? schedule(jobName) : schedule;
460
475
 
461
476
  const nomadJobDefinition = genConfig({
462
477
  jobName,
463
- cron,
478
+ cron: INTERVAL_ALIASES[cron] || cron,
464
479
  restart,
465
480
  workingDirectory,
466
481
  additionalEnv: env,
467
- cmd,
482
+ region,
483
+ cmd: `${bin} ${args}`,
468
484
  cpu,
469
485
  mem,
470
- isProduction: production
486
+ check,
487
+ isProduction: production,
471
488
  });
472
489
 
473
- await runNomadJob(jobName, nomadJobDefinition, stop, production);
490
+ const nomadPath = await getNomadPath();
491
+ const nomadAddr = await getNomadAddr(production);
492
+
493
+ await runNomadJob(nomadPath, nomadAddr, jobName, nomadJobDefinition, stop);
494
+
495
+ if (check && check.bin) {
496
+ console.log("");
497
+
498
+ const monitorJobName = `${jobName}-monitor`;
499
+ const monitorJobDefinition = genConfig({
500
+ jobName: monitorJobName,
501
+ cron: INTERVAL_ALIASES[check.schedule] || check.schedule,
502
+ workingDirectory,
503
+ additionalEnv: {
504
+ ...env,
505
+ SKYNET_SLACK_TOKEN: null,
506
+ SKYNET_NOMAD_URL: null,
507
+ },
508
+ region,
509
+ cmd: `${check.bin} ${args}`,
510
+ cpu: check.cpu || 100,
511
+ mem: check.mem || 100,
512
+ isProduction: production,
513
+ });
514
+
515
+ await runNomadJob(nomadPath, nomadAddr, monitorJobName, monitorJobDefinition, stop);
516
+ }
474
517
  }
475
518
 
476
519
  function deploy() {
477
- const binaryNameParts = process.argv[1].split(path.sep);
478
- const binaryName = binaryNameParts[binaryNameParts.length - 1];
520
+ if (!binaryName) {
521
+ binaryName = getBinaryName();
522
+ }
479
523
 
480
524
  const cli = meow(
481
525
  `
@@ -495,22 +539,22 @@ ${getSelectorDesc(selector)}
495
539
  ...getSelectorFlags(selector),
496
540
  verbose: {
497
541
  type: "boolean",
498
- default: false
542
+ default: false,
499
543
  },
500
544
  production: {
501
545
  alias: "prd",
502
546
  type: "boolean",
503
- default: false
547
+ default: false,
504
548
  },
505
549
  stop: {
506
550
  type: "boolean",
507
- default: false
508
- }
509
- }
551
+ default: false,
552
+ },
553
+ },
510
554
  }
511
555
  );
512
556
 
513
- deployModeless(cli.flags).catch(err => {
557
+ deployModeless(cli.flags).catch((err) => {
514
558
  console.error(err);
515
559
  process.exit(1);
516
560
  });
@@ -520,6 +564,7 @@ ${getSelectorDesc(selector)}
520
564
  }
521
565
 
522
566
  module.exports = {
567
+ getJobName,
523
568
  createModeDeploy,
524
- createDeploy
569
+ createDeploy,
525
570
  };
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+
3
+ // this file is executable and is for kafka producer testing
4
+ // a few test commands to try on
5
+ // $ examples/consumer run --protocol bsc --verbose
6
+ // $ examples/consumer run --protocol eth
7
+ // $ examples/consumer check --protocol eth
8
+ // $ examples/consumer deploy --protocol eth
9
+ // $ examples/consumer --help
10
+
11
+ const { consumer, SENSITIVE_VALUE, ERROR_LEVEL } = require("../app");
12
+
13
+ async function consume({ protocol, messages, verbose }) {
14
+ console.log("consume called with", protocol, messages, verbose);
15
+ }
16
+
17
+ async function check({ protocol, state, verbose }) {
18
+ console.log("check called with", protocol, state, verbose);
19
+
20
+ const errors = [];
21
+
22
+ errors.push({ type: ERROR_LEVEL.CRITICAL, message: "processed height lagged behind" });
23
+
24
+ return errors;
25
+ }
26
+
27
+ const app = consumer({
28
+ name: "LibSkynetExampleConsumer",
29
+ selector: { protocol: { type: "string" } },
30
+
31
+ env: {
32
+ SKYNET_KAFKA_SERVER: SENSITIVE_VALUE,
33
+ SKYNET_KAFKA_USERNAME: SENSITIVE_VALUE,
34
+ SKYNET_KAFKA_PASSWORD: SENSITIVE_VALUE,
35
+ },
36
+
37
+ consume: {
38
+ func: consume,
39
+ topic: ({ protocol }) => `lib-skynet-test-kafka-${protocol}-dev`,
40
+ maxRetry: 1,
41
+
42
+ cpu: 600,
43
+ mem: 200,
44
+ },
45
+
46
+ check: {
47
+ func: check,
48
+ schedule: "@minutely",
49
+ slackChannel: "skynet-notifications-local-dev",
50
+ },
51
+ });
52
+
53
+ app();