dcp-worker 3.2.10 → 3.2.12-b

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/bin/dcp-worker CHANGED
@@ -12,15 +12,30 @@ const process = require('process');
12
12
  const os = require('os');
13
13
  const fs = require('fs');
14
14
 
15
- const DEFAULT_CORES = os.cpus().length - 1;
15
+ // Set to true to try to understand unhandled rejection.
16
+ const ANALYZE_UNHANDELD_REJECTION = false;
17
+
18
+ const TOTAL_CPU_VCORES = os.cpus().length;
19
+ const DEFAULT_CORES = TOTAL_CPU_VCORES - 1;
16
20
  var worker, dcpConfig;
17
21
 
22
+ const EXIT_CLEAN = 0; // normal exit, no error
23
+ const EXIT_SIGQUIT = 2; // received SIGQUIT
24
+ const EXIT_ERROR_STOPPING = 3; // failed to stop worker, no other error
25
+ const EXIT_TIMED_OUT = 4; // failed to exit after requesting graceful exit
26
+ const EXIT_UNHANDLED = 5; // unhandled rejection
27
+
28
+
29
+ process.on('unhandledRejection', (error) => {
30
+ console.error('Unhandled rejection:', error);
31
+ });
32
+
18
33
  async function main () {
19
- process.on('unhandledRejection', unhandledRejectionHandler);
34
+ if (!ANALYZE_UNHANDELD_REJECTION)
35
+ process.on('unhandledRejection', unhandledRejectionHandler);
20
36
 
21
37
  await require('dcp-client').init(require(process.env.DCP_CONFIG || '../etc/dcp-config'));
22
38
  dcpConfig = require('dcp/dcp-config');
23
- require('../lib/check-scheduler-version').check();
24
39
 
25
40
  const cliArgs = require('dcp/cli')
26
41
  .base('Standalone NodeJS DCP Worker')
@@ -38,15 +53,17 @@ async function main () {
38
53
  verbose: {
39
54
  alias: 'v',
40
55
  describe: 'Enable verbose output',
41
- type: 'boolean',
56
+ type: 'count',
42
57
  default: false,
58
+ group: 'Output options',
43
59
  },
44
60
  outputMode: {
45
- alias: 'o',
61
+ alias: ['o', 'output'],
46
62
  describe: 'Set the output mode',
47
63
  type: 'string',
48
64
  default: 'detect',
49
- choices: ['detect', 'console', 'dashboard'],
65
+ choices: ['detect', 'console', 'dashboard', 'event-log', 'syslog', 'logfile'],
66
+ group: 'Output options',
50
67
  },
51
68
  hostname: {
52
69
  alias: 'H',
@@ -71,130 +88,220 @@ async function main () {
71
88
  describe: 'Restrict worker to a specific job (use N times for N jobs)',
72
89
  type: 'array',
73
90
  },
74
- 'leave-public-group': {
75
- type: 'boolean',
76
- hidden: true,
77
- default: false
78
- },
79
- 'join': {
91
+
92
+ join: {
80
93
  alias: 'g',
81
- describe: 'Join compute group',
94
+ describe: 'Join compute group; the format is "joinKey,joinSecret" or "joinKey,eh1-joinHash"',
82
95
  type: 'array'
83
96
  },
84
- 'join-keystore': {
97
+ joinKeystore: {
85
98
  hidden: true,
86
99
  /* future */
87
100
  },
88
- 'allowed-origins': {
101
+
102
+ leavePublicGroup: {
103
+ type: 'boolean',
104
+ describe: 'Do not fetch slices from public compute group',
105
+ default: false,
106
+ },
107
+ publicGroupFallback: {
108
+ describe: 'If set, worker will prefer private groups but fall back on the public group if no preferred work is available',
109
+ type: 'boolean',
110
+ default: false,
111
+ },
112
+
113
+ identityKey: {
114
+ describe: 'Identity key, in hex format',
115
+ type: 'string',
116
+ group: 'Identity options',
117
+ },
118
+ identityKeystore: {
119
+ describe: 'Identity keystore, in json format',
120
+ type: 'string',
121
+ group: 'Identity options',
122
+ },
123
+
124
+ reportInterval: {
125
+ describe: 'If set, output a status summary every [interval] seconds in console output mode',
126
+ type: 'number',
127
+ group: 'Output options',
128
+ },
129
+ eventDebug: {
130
+ hide: true,
131
+ describe: 'If set, dump all sandbox and worker events',
132
+ },
133
+
134
+ logfile: {
135
+ describe: 'Path to log file (if --output=file)',
136
+ type: 'string',
137
+ group: 'Log File output options',
138
+ },
139
+ syslogAddress: {
140
+ describe: 'Address of rsyslog server (if --output=syslog)',
141
+ type: 'string',
142
+ group: 'Syslog output options',
143
+ },
144
+ syslogTransport: {
145
+ describe: 'Transport to connect to rsyslog daemon (if --output=syslog)',
146
+ type: 'string',
147
+ choices: ['udp','tcp'],
148
+ group: 'Syslog output options',
149
+ },
150
+ syslogPort: {
151
+ describe: 'UDP/TCP port of rsyslog server',
152
+ type: 'number',
153
+ group: 'Syslog output options',
154
+ },
155
+
156
+ allowedOrigins: {
89
157
  alias: 'a',
90
- describe: 'origins where the worker is allowed to fetch input data from',
158
+ describe: 'modify the \'any\' allow origins of dcpConfig',
91
159
  type: 'array'
92
160
  },
161
+
162
+ replPort: {
163
+ describe: 'If set, open a REPL on specified TCP port',
164
+ type: 'number',
165
+ default: undefined,
166
+ },
167
+ watchdogInterval: {
168
+ alias: 'W',
169
+ describe: 'Number of milliseconds between watchdog cycles',
170
+ type: 'number',
171
+ hidden: true,
172
+ },
173
+ dumpConfig: {
174
+ describe: 'If set, dump the configuration and exit',
175
+ type: 'boolean',
176
+ hidden: true,
177
+ }
93
178
  })
94
179
  .strict()
95
180
  .wrap(process.stdout.columns || 80)
96
181
  .argv;
97
182
 
98
- await startWorking(cliArgs);
183
+ if (cliArgs.dumpConfig)
184
+ {
185
+ console.debug(JSON.stringify(require('dcp/dcp-config'), null, 2));
186
+ process.exit(1);
187
+ }
188
+
189
+ return startWorking(cliArgs);
99
190
  }
100
191
 
101
192
  // Preserve console.error, the dashboard replaces it with a custom logger
102
193
  const logError = console.error;
103
194
  main()
104
- .then(() => process.exit(0))
195
+ .then(exitcode => process.exit(exitcode || 0))
105
196
  .catch(e => {
106
197
  logError("Script failed:");
107
198
  logError(e);
108
199
  process.exit(1);
109
200
  });
110
201
 
202
+ // Imperfect, but handles CG { joinKey, joinHash }.
203
+ function isHash(b) {
204
+ return b && b.length === 68 && b.startsWith('eh1-');
205
+ }
206
+
111
207
  async function startWorking(cliArgs) {
208
+ //console.log('cliArgs', cliArgs);
112
209
  const wallet = require('dcp/wallet');
113
210
  const DCPWorker = require('dcp/worker').Worker;
114
211
  const { startWorkerLogger } = require('../lib/startWorkerLogger');
115
- const identityKeystore = await wallet.getId();
116
- /** @type {string[]} */
117
- var dcpWorkerOptions;
118
- var paymentAddress;
119
- var sawOptions = {
212
+ const sawOptions = {
120
213
  hostname: cliArgs.hostname,
121
214
  port: cliArgs.port
122
215
  };
123
216
 
124
- if (cliArgs.paymentAddress) {
217
+ let paymentAddress;
218
+ if (cliArgs.paymentAddress)
125
219
  paymentAddress = new wallet.Address(cliArgs.paymentAddress);
126
- } else {
220
+ else
127
221
  paymentAddress = (await wallet.get()).address;
128
- }
129
222
 
130
- dcpWorkerOptions = {
223
+
224
+ // Different ways to get the identity:
225
+ let identityKeystore = false;
226
+
227
+ if (cliArgs.identityKey)
228
+ identityKeystore = await new wallet.IdKeystore(cliArgs.identityKey, '');
229
+ else if (cliArgs.identityKeystore)
230
+ identityKeystore = await new wallet.IdKeystore(JSON.parse(cliArgs.identityKeystore), '');
231
+ else
232
+ identityKeystore = await wallet.getId();
233
+
234
+ // Set the provided identity as the wallet's default
235
+ await wallet.addId(identityKeystore);
236
+
237
+
238
+ if (typeof dcpConfig.worker.unhandledRejectionCleanupTimeout !== 'undefined')
239
+ unhandledRejectionHandler.timeout = dcpConfig.worker.unhandledRejectionCleanupTimeout;
240
+
241
+ // Leave the public compute group, if desired
242
+ if (cliArgs.leavePublicGroup || cliArgs.publicGroupFallback)
243
+ dcpConfig.worker.leavePublicGroup = true;
244
+
245
+
246
+ // The exitGuard will hold an "exit" method, and a Promise to await for
247
+ // the exit code passed to exitGuard.exit()
248
+ let exitcode = EXIT_CLEAN;
249
+ const exitGuard = {
250
+ promise: Promise.resolve(0), // will be overwritten when worker starts
251
+ exit(code) { process.exit(code||exitcode||0) }, // will be overwritten when worker starts
252
+ };
253
+ process.on('SIGQUIT', () => {
254
+ exitcode = EXIT_SIGQUIT;
255
+ cliArgs.verbose >= 1 && console.info(`240: Caught SIGQUIT; exiting worker with exitcode ${exitcode}`);
256
+ exitGuard.exit(exitcode);
257
+ });
258
+
259
+
260
+ /** @type {string[]} */
261
+ const dcpWorkerOptions = {
131
262
  paymentAddress,
263
+ identity: identityKeystore,
132
264
  maxWorkingSandboxes: cliArgs.cores,
133
- priorityOnly: cliArgs.priorityOnly,
265
+ cores: { cpu: TOTAL_CPU_VCORES, gpu: undefined }, /** XXXpfr @todo: Figure out how many gpus. */
266
+ targetLoad: { cpu: 1.0, gpu: 1.0 }, /** Use 100%: XXXpfr @todo Allow command-line override. */
134
267
  sandboxOptions: {
135
268
  SandboxConstructor: require('dcp-client/lib/standaloneWorker').workerFactory(sawOptions)
136
269
  },
137
270
  computeGroups: [], /* public group is implied */
138
- leavePublicGroup: cliArgs.leavePublicGroup
271
+ leavePublicGroup: cliArgs.leavePublicGroup || dcpConfig.worker.leavePublicGroup,
139
272
  };
140
273
 
141
274
  /* cliArgs.join is the list of compute groups to join */
142
275
  if (cliArgs.join && cliArgs.join.length)
143
276
  {
144
277
  dcpWorkerOptions.computeGroups = cliArgs.join
145
- .map ((el) => { var [a,b] = el.split(','); return { joinKey: a, joinSecret: b }}) /* Map cliArgs.join to give us [{ joinKey, joinSecret }...] */
146
- .filter((el) => el.joinKey) /* Filter out entries with no joinKey */
147
- ;
278
+ .map((el) => {
279
+ /* Map cliArgs.join to give us [{ joinKey, joinSecret/joinHash }...] */
280
+ const [a, b] = el.split(',');
281
+ return isHash(b) ? { joinKey: a, joinHash: b } : { joinKey: a, joinSecret: b };
282
+ })
283
+ .filter((el) => el.joinKey); /* Filter out entries with no joinKey */
284
+ //console.log(dcpWorkerOptions.computeGroups);
148
285
  }
149
286
 
150
- if (typeof dcpConfig.worker.unhandledRejectionCleanupTimeout !== 'undefined')
151
- unhandledRejectionHandler.timeout = dcpConfig.worker.unhandledRejectionCleanupTimeout;
152
-
153
- if (cliArgs.jobId) {
287
+ if (cliArgs.jobId)
288
+ {
154
289
  dcpWorkerOptions.jobAddresses = cliArgs.jobId;
290
+ dcpWorkerOptions.priorityOnly = true;
155
291
  }
156
- if (cliArgs.allowedOrigins) {
157
- dcpWorkerOptions.allowedOrigins = cliArgs.allowedOrigins;
158
- }
292
+ if (cliArgs.allowedOrigins)
293
+ dcpConfig.worker.allowOrigins.any = cliArgs.allowedOrigins;
294
+ if (cliArgs.watchdogInterval)
295
+ dcpWorkerOptions.watchdogInterval = cliArgs.watchdogInterval;
159
296
 
160
- worker = new DCPWorker(dcpWorkerOptions);
161
-
162
- if (process.env.TEST_HARNESS) {
163
- const { bindToTestHarness } = require('../lib/bindToTestHarness');
164
- bindToTestHarness(worker);
165
- }
166
297
 
167
- /* Delay log messages so that they appear in TUI window */
168
- setImmediate(() => {
169
- console.log(` * Starting DCP Worker`);
170
- console.log(` . Configured for scheduler ${dcpConfig.scheduler.location}`);
171
- console.log(` . Bank is ${dcpConfig.bank.location}`);
172
- console.log(` . Earned funds will be deposited in account ${paymentAddress}`);
173
- console.log(` . Identity is ${identityKeystore.address}`);
174
-
175
- function qty(amount, singular, plural) /* XXX i18n */
176
- {
177
- if (Array.isArray(amount))
178
- amount = amount.length;
179
- if (!plural)
180
- plural = singular + 's';
181
- if (!amount)
182
- return plural;
183
- if (amount == 1)
184
- return singular;
185
- return plural;
186
- }
298
+ worker = new DCPWorker(dcpWorkerOptions);
187
299
 
188
- if (dcpWorkerOptions.jobAddresses)
189
- console.log(` * Processing only ${qty(dcpWorkerOptions.jobAddresses, 'job')}`, dcpWorkerOptions.jobAddresses.join(', '));
190
- if (dcpWorkerOptions.computeGroups.length)
191
- console.log(` * Joining compute ${qty(dcpWorkerOptions.computeGroups, 'group')}`, dcpWorkerOptions.computeGroups.map(el => el.joinKey).join(', '));
192
- if (dcpWorkerOptions.leavePublicGroup)
193
- console.log(' * Leaving the public compute group');
194
- console.log(' . ready');
195
- });
196
-
197
300
  /**
301
+ * NOTE: In Supervisor2 this function is a NOOP.
302
+ * When (and if) we stop using Supevisor1, delete this reference to setDefaultIdentityKeystore
303
+ * and delete the corresponding fucntion from Supervisor2.
304
+ *
198
305
  * startWorkerLogger needs to be called before the worker is started so that
199
306
  * it can attach event listeners before the events fire, else UI events for
200
307
  * things such as progress will never get attached.
@@ -207,15 +314,208 @@ async function startWorking(cliArgs) {
207
314
  * implementation of the worker that should be addressed in Supervisor 2
208
315
  */
209
316
  await worker.supervisor.setDefaultIdentityKeystore();
317
+
318
+
319
+ if (cliArgs.eventDebug)
320
+ {
321
+ worker.debug = true;
322
+ worker.supervisor.debug = true;
323
+ }
324
+
325
+
326
+ // if the worker stops internally (eg. schedmsg stop), then exit without
327
+ // changing the saved exitcode
328
+ worker.on('stop', () => {
329
+ exitGuard.exit();
330
+ });
331
+
332
+
210
333
  startWorkerLogger(worker, {
334
+ exitGuard,
211
335
  verbose: cliArgs.verbose,
212
336
  outputMode: cliArgs.outputMode,
337
+
338
+ logfile: cliArgs.logfile,
339
+
340
+ syslogAddress: cliArgs.syslogAddress,
341
+ syslogTransport: cliArgs.syslogTransport,
342
+ syslogPort: cliArgs.syslogPort,
343
+ });
344
+
345
+ require('../lib/remote-console').init(cliArgs.replPort, {
346
+ help: {
347
+ report: 'Print a worker status & slice report',
348
+ kill: 'Kill the worker',
349
+ },
350
+ commands: {
351
+ report: printReport,
352
+ kill: exitcode => exitGuard.exit(exitcode),
353
+ },
354
+ });
355
+ require('../lib/remote-console').setMainEval(function mainEval() { return eval(arguments[0]) });
356
+
357
+ console.log(` * Starting DCP Worker`);
358
+ console.log(` . Configured for scheduler ${dcpConfig.scheduler.location}`);
359
+ console.log(` . Bank is ${dcpConfig.bank.location}`);
360
+ console.log(` . Earned funds will be deposited in account ${paymentAddress}`);
361
+ console.log(` . Identity is ${identityKeystore.address}`);
362
+
363
+ function qty(amount, singular, plural) /* XXX i18n */
364
+ {
365
+ if (Array.isArray(amount))
366
+ amount = amount.length;
367
+ if (!plural)
368
+ plural = singular + 's';
369
+ if (!amount)
370
+ return plural;
371
+ if (amount == 1)
372
+ return singular;
373
+ return plural;
374
+ }
375
+
376
+ if (dcpWorkerOptions.jobAddresses)
377
+ console.log(` * Processing only ${qty(dcpWorkerOptions.jobAddresses, 'job')}`, dcpWorkerOptions.jobAddresses.join(', '));
378
+ if (dcpWorkerOptions.computeGroups.length)
379
+ console.log(` * Joining compute ${qty(dcpWorkerOptions.computeGroups, 'group')}`, dcpWorkerOptions.computeGroups.map(el => el.joinKey).join(', '));
380
+ if (dcpWorkerOptions.publicGroupFallback)
381
+ console.log(' * Falling back on public group when preferred groups have no work');
382
+ else if (dcpWorkerOptions.leavePublicGroup)
383
+ console.log(' * Leaving the public compute group');
384
+ if (cliArgs.verbose)
385
+ console.log(` + Verbosity level: ${cliArgs.verbose}`);
386
+ if (cliArgs.eventDebug)
387
+ console.log(' + Event debug on');
388
+ console.log(' . output mode: ' + cliArgs.output);
389
+
390
+ require('../lib/check-scheduler-version').check();
391
+
392
+ console.log(' . ready');
393
+
394
+
395
+ /** print the slice report via console.log */
396
+ function printReport()
397
+ {
398
+ console.log(sliceReport());
399
+ }
400
+
401
+ /** retrieve a slice report screen */
402
+ function sliceReport()
403
+ {
404
+ const sup = worker.supervisor;
405
+ let report = '';
406
+
407
+ report += ('='.repeat(78)) + '\n';
408
+
409
+ const sbStates = {
410
+ WORKING: 0,
411
+ ASSIGNED: 0,
412
+ READY: 0,
413
+ TERMINATED: 0,
414
+ };
415
+ const stateNames = {
416
+ WORKING: 'Working',
417
+ ASSIGNED: 'Assigned',
418
+ READY: 'Ready',
419
+ TERMINATED: 'Terminated',
420
+ };
421
+ sup.sandboxes.forEach(sb => {
422
+ const { state } = sb;
423
+ if (!sbStates[state])
424
+ sbStates[state] = 0;
425
+ sbStates[state]++;
426
+ });
427
+
428
+ report += (Date()) + '\n';
429
+ report += ('Sandboxes:') + '\n';
430
+ Object.keys(sbStates).forEach(state => {
431
+ const stateName = stateNames[state] || state;
432
+ report += (` ${(stateName + ':').padEnd(12)} ${sbStates[state]}`) + '\n';
433
+ })
434
+ report += (` * ALL: ${sup.sandboxes.length}`) + '\n';
435
+
436
+ report += ('Progress:') + '\n';
437
+ sup.workingSandboxes.forEach(sb => {
438
+ const jobName = sb.job && sb.job.public && sb.job.public.name || `idek (${sb.jobAddress})`;
439
+ let el = Date.now() - sb.sliceStartTime;
440
+ const t = el < 1000000
441
+ ? toInterval(el)
442
+ : 'new';
443
+
444
+ el = sb.progressReports && sb.progressReports.last
445
+ ? Date.now() - (sb.sliceStartTime + sb.progressReports.last.timestamp)
446
+ : 0;
447
+ const pct = (typeof sb.progress) === 'number'
448
+ ? `${Number(sb.progress).toFixed(0).padStart(2)}%`
449
+ : 'ind';
450
+ const stale = (el < 2000) ? '' : `(stale: ${toInterval(el)})`;
451
+
452
+ report += (` ${String(sb.id).padStart(4)}: ${sb.jobAddress} ${jobName.padEnd(34)} `+ `${t} ${pct} ${stale}`.padStart(13)) + '\n';
453
+ });
454
+
455
+ report += ('Slices:') + '\n';
456
+ report += (` working: ${sup.allocatedSlices.length}`) + '\n';
457
+ report += (` queued: ${sup.queuedSlices.length}`) + '\n';
458
+
459
+ report += ('='.repeat(78)) + '\n';
460
+
461
+ return report;
462
+ }
463
+
464
+ /**
465
+ * Convert a timespan in ms to a human-readable interval in minutes and seconds
466
+ *
467
+ * @param {number} el Milliseconds to convert
468
+ * @return {string} Timespan formatted as `m:ss`
469
+ */
470
+ function toInterval(el)
471
+ {
472
+ const m = Math.floor((el / 1000) / 60).toString(10);
473
+ const s = Math.floor((el / 1000) % 60).toString(10).padStart(2, '0');
474
+ return `${m}:${s}`;
475
+ }
476
+
477
+ if (parseFloat(cliArgs.reportInterval))
478
+ {
479
+ if (cliArgs.outputMode !== 'dashboard')
480
+ setInterval(printReport, parseFloat(cliArgs.reportInterval) * 1000);
481
+ else
482
+ console.log('Ignoring --reportInterval in dashboard output mode');
483
+ }
484
+
485
+
486
+ // Set the exit guard - this method can be called by signal and exception
487
+ // handlers
488
+ exitGuard.promise = new Promise(resolve => {
489
+ exitGuard.exit = resolve;
213
490
  });
214
491
 
492
+
215
493
  await worker.start();
216
- await new Promise(resolve => process.on('SIGQUIT', resolve));
217
- console.log('\n*** caught SIGQUIT; exiting...\n');
218
- await worker.stop(true);
494
+
495
+ exitcode = await exitGuard.promise;
496
+
497
+ cliArgs.verbose >= 1 && console.log(`418: exit guard called with ${exitcode}`);
498
+
499
+ const exitTimeAllowed = 30; // seconds to allow for worker to stop gracefully
500
+ const forceExitTimeout = setTimeout(() => {
501
+ console.error(`396: Worker failed to exit within ${exitTimeAllowed} seconds; terminating forcibly.`);
502
+ process.exit(exitcode || EXIT_TIMED_OUT)
503
+ }, exitTimeAllowed * 1000);
504
+
505
+ await worker.stop(true)
506
+ .catch(error => {
507
+ if (error.message.includes('Already stopped'))
508
+ return;
509
+ console.error('255: Unexpected error stopping worker:',
510
+ error.code
511
+ ? `${error.code}: ${error.message}`
512
+ : error.message);
513
+ exitcode = exitcode || EXIT_ERROR_STOPPING;
514
+ });
515
+
516
+ clearTimeout(forceExitTimeout);
517
+
518
+ return exitcode;
219
519
  }
220
520
 
221
521
  /**
@@ -234,7 +534,7 @@ async function unhandledRejectionHandler (error) {
234
534
 
235
535
  try {
236
536
  let log = dcpConfig && dcpConfig.worker && dcpConfig.worker.unhandledRejectionLog;
237
- log = process.env.DCP_WORKER_UNHANDLED_REJECTION_LOG;
537
+ if (!log) log = process.env.DCP_WORKER_UNHANDLED_REJECTION_LOG;
238
538
  if (log) {
239
539
  fs.appendFileSync(process.env.DCP_WORKER_UNHANDLED_REJECTION_LOG,
240
540
  `${Date.now()}: ${error.message}\n${error.stack}\n\n`);
@@ -247,6 +547,7 @@ async function unhandledRejectionHandler (error) {
247
547
  if (screen) {
248
548
  screen.log(error.message + '\n' + error.stack);
249
549
  screen.destroy();
550
+ logError(error.message + '\n' + error.stack);
250
551
  } else {
251
552
  console.error('Unhandled rejection - preparing to exit:', error.message);
252
553
  }
@@ -260,7 +561,7 @@ async function unhandledRejectionHandler (error) {
260
561
  } catch(e) {
261
562
  console.error(error);
262
563
  }
263
- process.exit(exitCode || 3);
564
+ process.exit(exitCode || EXIT_UNHANDLED);
264
565
  }
265
566
  setTimeout(bail, 1000 * unhandledRejectionHandler.timeout);
266
567
 
@@ -270,6 +571,6 @@ async function unhandledRejectionHandler (error) {
270
571
  console.log('Error during worker.stop:', e);
271
572
  }
272
573
 
273
- setImmediate(() => bail(33));
574
+ setImmediate(() => bail(EXIT_UNHANDLED));
274
575
  };
275
576
  unhandledRejectionHandler.timeout = 5;
package/etc/dcp-config.js CHANGED
@@ -30,6 +30,14 @@ const dcpConfig =
30
30
  },
31
31
  reloadBehaviour: 'process.exit(12)',
32
32
  },
33
+ worker:
34
+ {
35
+ sandbox: {
36
+ progressTimeout: 30 * 1000,
37
+ },
38
+ leavePublicGroup: false,
39
+ allowConsoleAccess: false,
40
+ },
33
41
  }
34
42
 
35
43
  if (!dcpConfig.evaluator.location)
@@ -0,0 +1,107 @@
1
+ /**
2
+ * @file remote-console.js
3
+ * DCP Service Worker support for a remote console, accessible via telnet.
4
+ *
5
+ * * SECURITY NOTICE *
6
+ *
7
+ * This feature is turned off by default and should not be turned on except for
8
+ * troubleshooting, as any attacker on the network will have the same OS-level
9
+ * priviledges as the DCP Service Worker.
10
+ *
11
+ * To enable this feature, create a file in ../etc name enabled-debug-console. That
12
+ * file should contain a number which identities the port number this service will
13
+ * listen on.
14
+ *
15
+ * The port file could also contain the string "false", which is just an explicit
16
+ * way of turning this feature off.
17
+ *
18
+ * A history file will also be created in the etc directory, assuming the service
19
+ * worker has permission to write there.
20
+ *
21
+ * @author Wes Garland, wes@kingsds.network
22
+ * @date Dec 2021
23
+ */
24
+ 'use strict';
25
+
26
+ const path = require('path');
27
+ const fs = require('fs');
28
+ var dcpConfig;
29
+ var mainEval;
30
+ var ci;
31
+
32
+ function daemonEval()
33
+ {
34
+ try {
35
+ if (typeof dcpConfig === 'undefined')
36
+ dcpConfig = require('dcp/dcp-config');
37
+ }
38
+ catch(e){};
39
+
40
+ if (mainEval)
41
+ return mainEval(arguments[0]);
42
+ return eval(arguments[0]);
43
+ }
44
+
45
+ function callbackTelnet(port, client, registry) {
46
+ console.log(' ! telnetd - listening on port', port);
47
+ }
48
+
49
+ /**
50
+ * Function to let this module know about eval within the context of the main function in
51
+ * the service worker.
52
+ */
53
+ exports.setMainEval = function removeConsole$$setMainEval()
54
+ {
55
+ mainEval = arguments[0];
56
+ }
57
+
58
+ exports.init = function remoteConsole$$init(port, ...commands)
59
+ {
60
+ try
61
+ {
62
+ let historyFilename;
63
+
64
+ if (!port)
65
+ {
66
+ /* invoked before main */
67
+ const edcFilename = path.resolve(__dirname, '..', 'etc', 'enable-debug-console');
68
+ if (!fs.existsSync(edcFilename))
69
+ return;
70
+
71
+ port = JSON.parse(fs.readFileSync(edcFilename, 'ascii').trim());
72
+ if (!port)
73
+ return;
74
+ historyFilename = edcFilename + '.history';
75
+ }
76
+ else
77
+ {
78
+ historyFilename = path.resolve(__dirname, '..', 'etc', `telnetd-repl-port-${port}.history`);
79
+ }
80
+
81
+ if (port)
82
+ {
83
+ console.warn(`*** Enabling telnet daemon on port ${port} (security risk) ***`);
84
+ ci = require('telnet-console').start({
85
+ port,
86
+ callbackTelnet,
87
+ eval: daemonEval,
88
+ histfile: historyFilename,
89
+ }, ...commands);
90
+ exports.init = () => {
91
+ throw new Error('remote console has already been initialized');
92
+ };
93
+ }
94
+ }
95
+ catch(e)
96
+ {
97
+ console.warn(' ! Failed to enable telnet daemon:', e.message);
98
+ }
99
+ }
100
+
101
+ exports.reintercept = function remoteConsole$$reintercept()
102
+ {
103
+ if (typeof ci === 'undefined')
104
+ return; /* no interception => no reinterception */
105
+
106
+ ci.reintercept();
107
+ }
@@ -22,14 +22,36 @@ function getLogger({ outputMode='detect' }) {
22
22
  }
23
23
  }
24
24
 
25
- switch (outputMode) {
26
- case 'console':
27
- return require('./worker-loggers/console');
28
- case 'dashboard':
29
- return require('./worker-loggers/dashboard');
30
- default:
31
- throw new Error(`Unknown outputMode "${outputMode}"`);
25
+ try
26
+ {
27
+ const om = require('path').basename(outputMode);
28
+ return require('./worker-loggers/' + om);
32
29
  }
30
+ catch (error)
31
+ {
32
+ console.error(`032: Failed to load worker logger "${outputMode}":`, error);
33
+ throw new Error(`Unknown outputMode "${outputMode}"`);
34
+ }
35
+ }
36
+
37
+ const workerEvents = {
38
+ fetchStart: 'onFetchingSlices',
39
+ fetchEnd: 'onFetchedSlices',
40
+ fetchError: 'onFetchSlicesFailed',
41
+ submit: 'onSubmit',
42
+ submitError: 'onSubmitError',
43
+ payment: 'onPayment',
44
+ error: 'onError',
45
+ warning: 'onWarning',
46
+ }
47
+ const supervisorEvents = {
48
+ submittingResult: 'onSubmitStart',
49
+ }
50
+ const sandboxEvents = {
51
+ start: 'sandbox$onSliceStart',
52
+ sliceProgress: 'sandbox$onSliceProgress',
53
+ sliceFinish: 'sandbox$onSliceFinish',
54
+ terminated: 'sandbox$onWorkerStop',
33
55
  }
34
56
 
35
57
  Object.assign(exports, {
@@ -40,28 +62,35 @@ Object.assign(exports, {
40
62
  *
41
63
  * @param {Worker} worker
42
64
  * @param {object} options
43
- * @param {boolean} options.verbose
65
+ * @param {number} options.verbose
44
66
  * @param {boolean} options.outputMode - which logger to use (default='detect')
45
67
  */
46
68
  startWorkerLogger(worker, options={}) {
47
69
  const logger = getLogger(options);
48
70
  logger.init(worker, options);
49
71
 
50
- worker.on('fetchStart', logger.onFetchingSlices.bind(logger));
51
- worker.on('fetch', logger.onFetchedSlices.bind(logger));
52
- worker.on('fetchError', logger.onFetchSlicesFailed.bind(logger));
72
+ for (const [ev, handler] of Object.entries(workerEvents))
73
+ {
74
+ if (typeof logger[handler] === 'function')
75
+ worker.on(ev, logger[handler].bind(logger));
76
+ }
77
+ for (const [ev, handler] of Object.entries(supervisorEvents))
78
+ {
79
+ if (typeof logger[handler] === 'function')
80
+ worker.supervisor.on(ev, logger[handler].bind(logger));
81
+ }
82
+
53
83
  worker.on('sandbox', (sandbox) => {
54
84
  /**
55
85
  * logger.onSandboxStart can return a data object that will be provided to
56
86
  * the other sandbox event handlers
57
87
  */
58
88
  const data = logger.onSandboxReady(sandbox) || {};
59
- sandbox.on('sliceStart', logger.sandbox$onSliceStart.bind(logger, sandbox, data));
60
- sandbox.on('sliceProgress', logger.sandbox$onSliceProgress.bind(logger, sandbox, data));
61
- sandbox.on('sliceFinish', logger.sandbox$onSliceFinish.bind(logger, sandbox, data));
62
- sandbox.on('workerStop', logger.sandbox$onWorkerStop.bind(logger, sandbox, data));
89
+ for (const [ev, handler] of Object.entries(sandboxEvents))
90
+ {
91
+ if (typeof logger[handler] === 'function')
92
+ sandbox.on(ev, logger[handler].bind(logger, sandbox, data));
93
+ }
63
94
  });
64
-
65
- worker.on('payment', logger.onPayment.bind(logger));
66
95
  }
67
96
  });
@@ -5,6 +5,8 @@
5
5
  * @property {function} onFetchingSlices
6
6
  * @property {function} onFetchedSlices
7
7
  * @property {function} onFetchSlicesFailed
8
+ * @property {function} onError
9
+ * @property {function} onWarning
8
10
  * @property {SandboxCallback} sandbox$onSliceStart
9
11
  * @property {SandboxCallback} sandbox$onSliceProgress
10
12
  * @property {SandboxCallback} sandbox$onSliceFinish
@@ -14,24 +14,23 @@ const consoleLogger = {
14
14
  init(worker, options) {
15
15
  this.worker = worker;
16
16
  this.supervisor = worker.supervisor;
17
- this.options = options;
17
+ this.options = Object.assign({}, options);
18
18
  },
19
19
 
20
20
  onSandboxReady(sandbox) {
21
- const zeroes = '000';
22
- const shortId = (zeroes + sandbox.id.toString(16).toUpperCase()).slice(-zeroes.length);
21
+ const shortId = sandbox.id.toString(10).padStart(3);
23
22
 
24
23
  const sandboxData = {
25
24
  shortId,
26
25
  };
27
26
 
28
- console.log(` ~ [Sandbox 0x${sandboxData.shortId}] Initialized`);
27
+ console.log(` * Sandbox ${sandboxData.shortId}: Initialized`);
29
28
 
30
29
  return sandboxData;
31
30
  },
32
31
 
33
32
  sandbox$onSliceStart(sandbox, sandboxData, slice) {
34
- console.log(` ~ [Sandbox 0x${sandboxData.shortId}] Slice Started - "${sandbox.public.name}"`);
33
+ console.log(` * Sandbox ${sandboxData.shortId}: Slice Started: ${sandbox.jobAddress} ${sandbox.public.name}`);
35
34
  },
36
35
 
37
36
  sandbox$onSliceProgress(sandbox, sandboxData, ev) {
@@ -39,38 +38,66 @@ const consoleLogger = {
39
38
  },
40
39
 
41
40
  sandbox$onSliceFinish(sandbox, sandboxData, ev) {
42
- console.log(` ~ [Sandbox 0x${sandboxData.shortId}] Slice Completed - "${sandbox.public.name}"`);
41
+ console.log(` * Sandbox ${sandboxData.shortId}: Slice Completed: ${sandbox.jobAddress} ${sandbox.public.name}`);
43
42
  },
44
43
 
45
44
  sandbox$onWorkerStop(sandbox, sandboxData, _event) {
46
- const job = sandbox.job ? sandbox.job.address : sandbox.job;
47
- console.log(
48
- ` ~ [Sandbox 0x${sandboxData.shortId}] Terminated - Job: ${job}`,
49
- );
45
+ const job = sandbox.public ? `${sandbox.jobAddress} ${sandbox.public.name}` : sandbox.job;
46
+ console.log(` * Sandbox ${sandboxData.shortId}: Terminated: ${job}`);
50
47
  },
51
48
 
52
49
  onPayment({ payment }) {
53
50
  try {
54
51
  payment = parseFloat(payment);
55
52
  } catch (e) {
56
- console.error("Failed to parse payment float:", payment);
53
+ console.error(" ! Failed to parse payment:", payment);
57
54
  return;
58
55
  }
59
56
 
60
- console.log(`DCC Credit: ${payment}`);
57
+ if (payment > 0) console.log(` * DCC Credit: ${payment.toFixed(3)}`);
61
58
  },
62
59
 
63
60
  onFetchingSlices() {
64
- this.options.verbose && console.log("Fetching slices...");
61
+ this.options.verbose && console.log(" * Fetching slices...");
65
62
  },
66
63
 
67
64
  onFetchedSlices(ev) {
68
- this.options.verbose && console.log("Finished fetching slices", ev);
65
+ this.options.verbose && console.log(" * Fetched", ev, 'slices');
69
66
  },
70
67
 
71
68
  onFetchSlicesFailed(ev) {
72
- this.options.verbose && console.log("Failed to fetch slices", ev);
69
+ console.log(" ! Failed to fetch slices:", ev);
73
70
  },
71
+
72
+ onSubmitStart() {
73
+ this.options.verbose >= 2 && console.log(" * Submitting results...");
74
+ },
75
+
76
+ onSubmit() {
77
+ this.options.verbose >= 2 && console.log(" * Submitted");
78
+ },
79
+
80
+ onSubmitError(ev) {
81
+ console.log(" ! Failed to submit results:", ev);
82
+ },
83
+
84
+ onError(ev) {
85
+ this.options.verbose && console.error(" ! Worker error:", ev);
86
+ },
87
+
88
+ onWarning(ev) {
89
+ this.options.verbose >= 2 && console.warn(" ! Worker warning:", ev);
90
+ },
91
+
92
+ onError(ev)
93
+ {
94
+ this.options.verbose && console.error("Worker Error:", ev);
95
+ },
96
+
97
+ onWarning(ev)
98
+ {
99
+ this.options.verbose && console.warn("Worker Warning:", ev);
100
+ }
74
101
  };
75
102
 
76
103
  Object.assign(exports, consoleLogger);
@@ -25,8 +25,9 @@ const SLICE_FETCH_STATUS = {
25
25
 
26
26
  /** @type {WorkerLogger} */
27
27
  const dashboardLogger = {
28
- init(worker) {
28
+ init(worker, options) {
29
29
  this.worker = worker;
30
+ this.options = options;
30
31
  this.supervisor = worker.supervisor;
31
32
  this.totalDCCs = 0;
32
33
  this.sliceFetchStatus = SLICE_FETCH_STATUS.IDLE;
@@ -72,7 +73,11 @@ const dashboardLogger = {
72
73
  setInterval(() => this.screen.render(), 1000);
73
74
 
74
75
  this.screen.key(['escape', 'C-c'], () => {
75
- process.exit(0);
76
+ console.log('076: Exit requested...');
77
+ if (typeof options.exitGuard?.exit === 'function')
78
+ options.exitGuard.exit(0);
79
+ else
80
+ process.exit(0);
76
81
  });
77
82
  },
78
83
 
@@ -168,6 +173,14 @@ const dashboardLogger = {
168
173
  this.sliceFetchStatus = SLICE_FETCH_STATUS.NO_WORK;
169
174
  this.updateWorkerInfo();
170
175
  },
176
+
177
+ onError(error) {
178
+ this.options.verbose && console.error('Unexpected error from worker:', error);
179
+ },
180
+
181
+ onWarning(error) {
182
+ this.options.verbose >= 2 && console.warn('Unexpected warning from worker:', error);
183
+ },
171
184
  };
172
185
 
173
186
  Object.assign(exports, dashboardLogger);
@@ -0,0 +1,88 @@
1
+ /**
2
+ * @file worker-loggers/event-log.js
3
+ * @author Eddie Roosenmaallen <eddie@kingsds.network>
4
+ * @date August 2022
5
+ *
6
+ * This logger module emits events to the Windows event-log, writing all
7
+ * console logs to it. Most worker events are passed through to be handled
8
+ * by the console logger.
9
+ *
10
+ * @TODO: This could likely be improved by handling worker events directly
11
+ * and emitting events to the event log more deliberately than just
12
+ * redirecting the base console output. ~ER20220831
13
+ */
14
+
15
+ require('./common-types');
16
+ const consoleLogger = require('./console');
17
+
18
+ const { EventLog } = require('node-eventlog');
19
+
20
+ // Copy the original global console object's properties onto a backup
21
+ const _console = Object.assign({}, console);
22
+
23
+
24
+ const eventlogLogger = {
25
+ /**
26
+ * Initialize the eventlog worker logger
27
+ *
28
+ * @param {Worker} worker DCP Worker object to log
29
+ * @param {object} options Options for logger behaviour (passed
30
+ * through to consoleLogger)
31
+ */
32
+ init(worker, options) {
33
+ consoleLogger.init(worker, options);
34
+
35
+ this._processName = require('path').basename(process.mainModule.filename || process.argv0);
36
+ const source = options.source || this._processName || 'dcp-worker';
37
+ // _console.log(`036: creating new EventLog(${source}) client...`);
38
+ this._eventLog = new EventLog(source);
39
+
40
+ ['debug','error','info','log','warn'].forEach(level => {
41
+ console[level] = (...args) => this._log(level, ...args);
42
+ });
43
+ },
44
+
45
+ _log(level, ...items) {
46
+ const strBuilder = [`${this._processName}[${process.pid}]:`];
47
+
48
+ items.forEach(i => {
49
+ try {
50
+ switch (typeof i) {
51
+ case 'object':
52
+ strBuilder.push(JSON.stringify(i));
53
+ break;
54
+ default:
55
+ strBuilder.push(String(i));
56
+ }
57
+ }
58
+ catch (e) {
59
+ if (e instanceof TypeError) {
60
+ strBuilder.push('[encoding error: ' + e.message + ']');
61
+ }
62
+ }
63
+ });
64
+
65
+ // Use the string log-level to look up the severity number:
66
+ let severity = {
67
+ error: 'error',
68
+ warn: 'warn',
69
+ log: 'info',
70
+ info: 'info',
71
+ debug: 'info',
72
+ }[level];
73
+
74
+ // _console.debug(`074: about to actually log a line:`, strBuilder, severity);
75
+ return this._eventLog.log(strBuilder.join(' '), severity).catch(error => {
76
+ if (error)
77
+ _console.error('255: Unexpected error writing to event log:', error);
78
+ });
79
+ }
80
+ };
81
+
82
+ // necessary to keep `this` pointing at the correct thing when we call
83
+ for (const [prop, value] of Object.entries(consoleLogger))
84
+ {
85
+ if (typeof value === 'function')
86
+ exports[prop] = value.bind(consoleLogger);
87
+ }
88
+ Object.assign(exports, eventlogLogger);
@@ -0,0 +1,125 @@
1
+ /**
2
+ * @file worker-loggers/logfile.js
3
+ * @author Eddie Roosenmaallen <eddie@kingsds.network>
4
+ * @date August 2022
5
+ *
6
+ * This logger module maintains a log file, writing all console logs to it.
7
+ * Most worker events are passed through to the console logger.
8
+ */
9
+
10
+ require('./common-types');
11
+ const consoleLogger = require('./console');
12
+
13
+ // Copy the original global console object's properties onto a backup
14
+ const _console = Object.assign({}, console);
15
+
16
+ const logfileLogger = {
17
+ /**
18
+ * Initialize the logfile worker logger
19
+ *
20
+ * @param {Worker} worker DCP Worker object to log
21
+ * @param {object} options Options for logger behaviour
22
+ * @param {string} options.filepath Path to worker file; default: ./dcp-worker.log
23
+ * @param {boolean} options.truncate If true, logfile will be cleared at worker startup
24
+ */
25
+ init(worker, options) {
26
+ consoleLogger.init(worker, options);
27
+
28
+ this._filepath = options.filepath || './dcp-worker.log';
29
+ this.options = options;
30
+
31
+ options.verbose >= 3 && console.debug('050: constructing LogfileConsole', this._filepath, options);
32
+
33
+ this._getLogFile();
34
+
35
+ // on SIGHUP, close the output stream and open a new one
36
+ process.on('SIGHUP', () => {
37
+ this._getLogFile(true);
38
+ });
39
+
40
+ ['debug','error','info','log','warn'].forEach(level => {
41
+ console[level] = (...args) => this._log(level, ...args);
42
+ });
43
+ },
44
+
45
+ /**
46
+ * Return a handle to the WritableStream for this logger, creating one if
47
+ * necessary.
48
+ *
49
+ * @param {boolean} forceNew If truthy, close any existing stream and open
50
+ * a new one
51
+ * @return {fs.WriteStream}
52
+ */
53
+ _getLogFile(forceNew = false) {
54
+ const fs = require('fs');
55
+
56
+ if (this._file)
57
+ {
58
+ if (!forceNew)
59
+ return this._file;
60
+
61
+ try
62
+ {
63
+ this._file.end();
64
+ }
65
+ catch (err)
66
+ {
67
+ console.error('061: failed to close old log file:', err);
68
+ }
69
+ this._file = false;
70
+ }
71
+
72
+ const options = {
73
+ flags: this.options.truncate ? 'w' : 'a', // NYI: cli --truncate
74
+ }
75
+
76
+ const file = this._file = fs.createWriteStream(this._filepath, options);
77
+
78
+ // On error, close & recreate the log file
79
+ file.on('error', err => {
80
+ _console.error('082: console-patch::LogFileConsole write error:', err);
81
+
82
+ this._getLogFile(true);
83
+ });
84
+
85
+ return file;
86
+ },
87
+
88
+ /** Write a log line to the output file. Items will be converted to strings as
89
+ * possible and concatenated after the log-level and timestamp, then written to the
90
+ * current outputStream.
91
+ * @param {string} level Log level.
92
+ * @param {any} ...items Items to log.
93
+ */
94
+ _log(level = 'none', ...items) {
95
+ const strBuilder = [
96
+ (new Date()).toISOString(),
97
+ level,
98
+ ];
99
+
100
+ items.forEach(i => {
101
+ try {
102
+ switch (typeof i) {
103
+ case 'object':
104
+ strBuilder.push(JSON.stringify(i));
105
+ default:
106
+ strBuilder.push(String(i));
107
+ }
108
+ }
109
+ catch (e) {
110
+ if (e instanceof TypeError) {
111
+ strBuilder.push('[encoding error: ' + e.message + ']');
112
+ }
113
+ }
114
+ });
115
+
116
+ try {
117
+ this._file.write(strBuilder.join(' ') + '\n');
118
+ }
119
+ catch (error) {
120
+ _console.error('131: Unexpected error writing to log file:', error);
121
+ }
122
+ },
123
+ };
124
+
125
+ Object.assign(exports, consoleLogger, logfileLogger);
@@ -0,0 +1,91 @@
1
+ /**
2
+ * @file worker-loggers/syslog.js
3
+ * @author Eddie Roosenmaallen <eddie@kingsds.network>
4
+ * @date August 2022
5
+ *
6
+ * This logger module emits log lines to a remote syslogd, writing all
7
+ * console logs to it. Most worker events are passed through to be handled
8
+ * by the console logger.
9
+ *
10
+ * @TODO: This could likely be improved by handling worker events directly
11
+ * and emitting events to the event log more deliberately than just
12
+ * redirecting the base console output. ~ER20220831
13
+ */
14
+
15
+ require('./common-types');
16
+ const consoleLogger = require('./console');
17
+
18
+ const syslog = require('syslog-client');
19
+
20
+ // Copy the original global console object's properties onto a backup
21
+ const _console = Object.assign({}, console);
22
+
23
+
24
+ const eventlogLogger = {
25
+ /**
26
+ * Initialize the syslog worker logger
27
+ *
28
+ * @param {Worker} worker DCP Worker object to log
29
+ * @param {object} options Options for logger behaviour (passed
30
+ * through to consoleLogger)
31
+ */
32
+ init(worker, options) {
33
+ consoleLogger.init(worker, options);
34
+
35
+ this.options = Object.assign({}, options);
36
+ const syslogOptions = {
37
+ transport: options.syslogTransport || 'udp', // tcp, udp, unix, tls
38
+ port: options.syslogPort || 514,
39
+ }
40
+
41
+ this._syslog = syslog.createClient(options.syslogAddress || '127.0.0.1', options);
42
+ this._processName = require('path').basename(process.mainModule.filename || process.argv0);
43
+
44
+ ['debug','error','info','log','warn'].forEach(level => {
45
+ console[level] = (...args) => this._log(level, ...args);
46
+ });
47
+ },
48
+
49
+ _log(level, ...items) {
50
+ const strBuilder = [`${this._processName}[${process.pid}]:`];
51
+
52
+ items.forEach(i => {
53
+ try {
54
+ switch (typeof i) {
55
+ case 'object':
56
+ strBuilder.push(JSON.stringify(i));
57
+ default:
58
+ strBuilder.push(String(i));
59
+ }
60
+ }
61
+ catch (e) {
62
+ if (e instanceof TypeError) {
63
+ strBuilder.push('[encoding error: ' + e.message + ']');
64
+ }
65
+ }
66
+ });
67
+
68
+ // Use the string log-level to look up the severity number:
69
+ let severity = {
70
+ error: syslog.Severity.Error,
71
+ warn: syslog.Severity.Warning,
72
+ log: syslog.Severity.Notice,
73
+ info: syslog.Severity.Informational,
74
+ debug: syslog.Severity.Debug,
75
+ }[level];
76
+
77
+ this._syslog.log(strBuilder.join(' '), {
78
+ severity,
79
+ }, error => {
80
+ if (error)
81
+ _console.error('168: Unexpected error writing to syslog:', error);
82
+ });
83
+ }
84
+ };
85
+
86
+ for (const [prop, value] of Object.entries(consoleLogger))
87
+ {
88
+ if (typeof value === 'function')
89
+ exports[prop] = value.bind(consoleLogger);
90
+ }
91
+ Object.assign(exports, eventlogLogger);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dcp-worker",
3
- "version": "3.2.10",
3
+ "version": "3.2.12-b",
4
4
  "description": "JavaScript portion of DCP Workers for Node.js",
5
5
  "main": "bin/dcp-worker",
6
6
  "keywords": [
@@ -36,7 +36,7 @@
36
36
  "blessed-contrib": "^4.11.0",
37
37
  "bravojs": "^1.0.15",
38
38
  "chalk": "^4.1.1",
39
- "dcp-client": "^4.2.10",
39
+ "dcp-client": "^4.2.12-a",
40
40
  "kvin": "^1.2.7",
41
41
  "semver": "^7.3.5"
42
42
  },
@@ -1,55 +0,0 @@
1
- /**
2
- * @file bindToTestHarness.js
3
- * @author Ryan Rossiter, ryan@kingsds.network
4
- * @date May 2020
5
- */
6
-
7
- function setObjProps(obj, source) {
8
- for (let p in source) {
9
- if (typeof source[p] === 'object') setObjProps(obj[p], source[p]);
10
- else obj[p] = source[p];
11
- }
12
- }
13
-
14
- exports.bindToTestHarness = function (worker) {
15
- worker.on('start', () => {
16
- process.send({
17
- request: 'Process Started'
18
- });
19
- });
20
-
21
- worker.on('fetch', () => {
22
- process.send({
23
- request: 'fetchedSlices'
24
- });
25
- });
26
-
27
- worker.on('sandbox', (sandbox) => {
28
- sandbox.on('sliceStart', () => {
29
- process.send({
30
- request: 'sliceStart'
31
- });
32
- });
33
- });
34
-
35
- worker.supervisor.once('capabilitiesCalculated', (originalCaps) => {
36
- let reportedCaps = originalCaps;
37
-
38
- Object.defineProperty(worker.supervisor, 'capabilities', {
39
- get: () => reportedCaps,
40
- set: v => reportedCaps = v,
41
- });
42
-
43
- process.on('message', ({ request, data }) => {
44
- const [namespace, method] = request.split('::');
45
- if (namespace !== 'dcpWorker') return
46
- if (method === 'setCaps') {
47
- // Deep copy the original caps
48
- reportedCaps = JSON.parse(JSON.stringify(originalCaps));
49
- setObjProps(reportedCaps, data);
50
- }
51
- else if (method === 'resetCaps') reportedCaps = originalCaps;
52
- // else if (method === 'reportCaps') console.error(reportedCaps);
53
- });
54
- });
55
- }