dcp-worker 3.3.19 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/bin/dcp-evaluator-manager +7 -2
- package/bin/dcp-evaluator-start +1 -2
- package/bin/dcp-task-probe +75 -55
- package/bin/dcp-worker +107 -272
- package/{etc/dcp-worker-config.js → examples/dcp-config.js} +11 -9
- package/lib/dashboard-tui.js +5 -5
- package/lib/worker-loggers/console.js +3 -2
- package/lib/worker-loggers/dashboard.js +2 -1
- package/package.json +3 -4
- package/etc/dcp-worker-config.js.md5 +0 -2
- package/npm-hooks/prepack +0 -21
package/README.md
CHANGED
|
@@ -32,7 +32,7 @@ This document was last updated Sep 12, 2024.
|
|
|
32
32
|
| bin/dcp-worker | Program which requests work from the scheduler and coordinates its evaluation in a secure environment called dcp-evaluator-v8
|
|
33
33
|
| bin/dcp-evaluator-start | Program which launches dcp-evaluator-v8
|
|
34
34
|
| bin/dcp-evaluator-manager | Program which which manages the launching of dcp-evaluator-v8; e.g. based on system load, terminal activity, screensaver activity, etc.
|
|
35
|
-
|
|
|
35
|
+
| examples/dcp-config.js | Default configuration for dcp-worker, copy to /etc/dcp/overrides/dcp-worker to override sections
|
|
36
36
|
|
|
37
37
|
### Related Packages
|
|
38
38
|
- dcp-evaluator-v8, our isolated JS/WASM/WebGPU evaluation environment, ships separately. Installing dcp-worker with your system's package manager will automatically install dcp-evaluator-v8 as a dependency.
|
|
@@ -527,6 +527,12 @@ async function main()
|
|
|
527
527
|
const parser = new getopt.BasicParser('h(help)P:(prefix)d:(disable-monitor)l:(max-load)r:(rate)Li:ap:s:(signal)T:(loadavg-type)f:(pidfile)', process.argv);
|
|
528
528
|
var option;
|
|
529
529
|
|
|
530
|
+
if (dcpConfig.evaluator.listen)
|
|
531
|
+
{
|
|
532
|
+
daemonConfig.net.hostname = dcpConfig.evaluator.listen.hostname;
|
|
533
|
+
daemonConfig.net.port = dcpConfig.evaluator.listen.port;
|
|
534
|
+
}
|
|
535
|
+
|
|
530
536
|
while ((option = parser.getopt()) !== undefined)
|
|
531
537
|
{
|
|
532
538
|
switch (option.option)
|
|
@@ -655,9 +661,8 @@ async function main()
|
|
|
655
661
|
|
|
656
662
|
/* Initialize dcp-client to use only local resources before launching the main function */
|
|
657
663
|
require('dcp-client').init({
|
|
658
|
-
|
|
664
|
+
programName: 'dcp-worker',
|
|
659
665
|
parseArgv: false,
|
|
660
|
-
configName: process.env.DCP_CONFIG || '../etc/dcp-worker-config', /* => .js or .json */
|
|
661
666
|
dcpConfig: {
|
|
662
667
|
scheduler: { configLocation: false },
|
|
663
668
|
bundle: { location: false },
|
package/bin/dcp-evaluator-start
CHANGED
|
@@ -274,8 +274,7 @@ function main() {
|
|
|
274
274
|
|
|
275
275
|
/* Initialize dcp-client to use only local resources before launching the main function */
|
|
276
276
|
require('dcp-client').init({
|
|
277
|
-
|
|
278
|
-
configName: process.env.DCP_CONFIG || '../etc/dcp-worker-config',
|
|
277
|
+
programName: 'dcp-worker',
|
|
279
278
|
dcpConfig: {
|
|
280
279
|
scheduler: { configLocation: false },
|
|
281
280
|
bundle: { location: false },
|
package/bin/dcp-task-probe
CHANGED
|
@@ -276,7 +276,7 @@ function cpuCommaGpu(str)
|
|
|
276
276
|
* - dumpConfig
|
|
277
277
|
* - workerId
|
|
278
278
|
*/
|
|
279
|
-
async function processOptions()
|
|
279
|
+
async function processOptions(workerConfig)
|
|
280
280
|
{
|
|
281
281
|
const opts = { verbose: 0 };
|
|
282
282
|
const parser = new getopt.BasicParser('h(help)C(dumpConfig)S:(set)M:(merge)a:(allowedOrigins)'
|
|
@@ -319,35 +319,35 @@ async function processOptions()
|
|
|
319
319
|
break;
|
|
320
320
|
|
|
321
321
|
case 'a':
|
|
322
|
-
|
|
322
|
+
workerConfig.allowOrigins.any.push(opthnd.optarg);
|
|
323
323
|
break;
|
|
324
324
|
|
|
325
325
|
case 'c':
|
|
326
|
-
Object.assign(
|
|
326
|
+
Object.assign(workerConfig.cores, cpuCommaGpu(opthnd.optarg));
|
|
327
327
|
break;
|
|
328
328
|
|
|
329
329
|
case 'u':
|
|
330
|
-
Object.assign(
|
|
330
|
+
Object.assign(workerConfig.utilization, cpuCommaGpu(opthnd.optarg));
|
|
331
331
|
break;
|
|
332
332
|
|
|
333
333
|
case 'm':
|
|
334
|
-
|
|
334
|
+
workerConfig.maxSandboxes = Number(opthnd.optarg);
|
|
335
335
|
break;
|
|
336
336
|
|
|
337
337
|
case 'J':
|
|
338
|
-
|
|
338
|
+
workerConfig.jobAddresses = (workerConfig.jobAddresses || []).concat(opthnd.optarg);
|
|
339
339
|
break;
|
|
340
340
|
|
|
341
341
|
case 'j': /** @todo integrate with upcoming scheduler changes to opaqueId /wg dec 2024 */
|
|
342
|
-
|
|
342
|
+
workerConfig.jobs = (workerConfig.jobs || []).concat(opthnd.optarg);
|
|
343
343
|
break;
|
|
344
344
|
|
|
345
345
|
case 'G':
|
|
346
|
-
|
|
346
|
+
workerConfig.leavePublicGroup = true;
|
|
347
347
|
break;
|
|
348
348
|
|
|
349
349
|
case 'H':
|
|
350
|
-
dcpConfig.evaluator.location.hostname = opthnd.
|
|
350
|
+
dcpConfig.evaluator.location.hostname = opthnd.optarg;
|
|
351
351
|
break;
|
|
352
352
|
|
|
353
353
|
case 'p':
|
|
@@ -360,9 +360,9 @@ async function processOptions()
|
|
|
360
360
|
const joinHash = joinSecret;
|
|
361
361
|
|
|
362
362
|
if (isHash(joinHash))
|
|
363
|
-
|
|
363
|
+
workerConfig.computeGroups.push({ joinKey, joinHash });
|
|
364
364
|
else
|
|
365
|
-
|
|
365
|
+
workerConfig.computeGroups.push({ joinKey, joinSecret });
|
|
366
366
|
break;
|
|
367
367
|
}
|
|
368
368
|
|
|
@@ -380,7 +380,7 @@ async function processOptions()
|
|
|
380
380
|
{
|
|
381
381
|
/* merge mode: objects are merged at the property level, except for arrays, which are concatenated */
|
|
382
382
|
if (confNode === dcpConfig.worker && (prop === 'worktimes' || prop === 'capabilities'))
|
|
383
|
-
await probeEvaluator(); /* ensure we have something to merge into */
|
|
383
|
+
await probeEvaluator(dcpConfig.worker); /* ensure we have something to merge into */
|
|
384
384
|
switch(typeof confNode[prop])
|
|
385
385
|
{
|
|
386
386
|
case 'object':
|
|
@@ -408,8 +408,8 @@ async function processOptions()
|
|
|
408
408
|
{
|
|
409
409
|
const wallet = require('dcp/wallet');
|
|
410
410
|
const snapshot = readWorkerSnapshot();
|
|
411
|
-
|
|
412
|
-
|
|
411
|
+
workerConfig = snapshot.workerConfig;
|
|
412
|
+
workerConfig.paymentAddress = new wallet.Address(workerConfig.paymentAddress);
|
|
413
413
|
}
|
|
414
414
|
}
|
|
415
415
|
}
|
|
@@ -422,23 +422,35 @@ async function processOptions()
|
|
|
422
422
|
* as late as possible during the configuration phase so that the user has the option to specify these
|
|
423
423
|
* completely on a system with no evaluator.
|
|
424
424
|
*/
|
|
425
|
-
async function probeEvaluator()
|
|
425
|
+
async function probeEvaluator(workerConfig)
|
|
426
426
|
{
|
|
427
|
+
var evaluatorInfo;
|
|
427
428
|
if (probeEvaluator.probed)
|
|
428
429
|
return;
|
|
429
430
|
probeEvaluator.probed = true;
|
|
430
|
-
const probeTimer = setTimeout(() => panic('unable to probe evaluator'), 66610e3); /* Need to keep main from exiting */
|
|
431
431
|
|
|
432
|
-
if (!
|
|
432
|
+
if (!workerConfig.capabilities || !workerConfig.worktimes)
|
|
433
433
|
{
|
|
434
|
+
const probeTimer = setTimeout(() => panic('unable to probe evaluator'), 66610e3); /* Need to keep main from exiting */
|
|
434
435
|
const { workerFactory } = require('dcp-client/lib/standaloneWorker');
|
|
435
436
|
debug('dcp-task-probe:evaluator')('Probing evaluator at', dcpConfig.evaluator.location);
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
437
|
+
try
|
|
438
|
+
{
|
|
439
|
+
evaluatorInfo = await require('dcp/worker').probeEvaluator(workerFactory(dcpConfig.evaluator.location));
|
|
440
|
+
}
|
|
441
|
+
catch(error)
|
|
442
|
+
{
|
|
443
|
+
console.warn(`Warning: unable to probe evaluator at ${dcpConfig.evaluator.location} (${error.message + (error.code ? ' ' + error.code : '')})`);
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
finally
|
|
447
|
+
{
|
|
448
|
+
clearTimeout(probeTimer);
|
|
449
|
+
}
|
|
440
450
|
|
|
441
|
-
|
|
451
|
+
workerConfig.capabilities ||= evaluatorInfo.capabilities;
|
|
452
|
+
workerConfig.worktimes ||= evaluatorInfo.worktimes;
|
|
453
|
+
}
|
|
442
454
|
}
|
|
443
455
|
|
|
444
456
|
/**
|
|
@@ -470,12 +482,11 @@ function readWorkerSnapshot()
|
|
|
470
482
|
*/
|
|
471
483
|
function makeComputeGroupsRequest(tdConn, dummyWorker)
|
|
472
484
|
{
|
|
473
|
-
const
|
|
474
|
-
const options = Object.assign({}, dcpConfig.worker, { computeGroups });
|
|
485
|
+
const options = Object.assign({}, dummyWorker.config);
|
|
475
486
|
const taskDistributor = { connection: tdConn };
|
|
476
487
|
|
|
477
488
|
dummyWorker.badSupervisorBackdoor.dcp4.generateWorkerComputeGroups.apply({ taskDistributor, options });
|
|
478
|
-
if (!
|
|
489
|
+
if (!dummyWorker.config.leavePublicGroup && !dummyWorker.config.leaveGlobalGroup)
|
|
479
490
|
options.computeGroups.unshift({ joinKey: 'public' });
|
|
480
491
|
return options.computeGroups;
|
|
481
492
|
}
|
|
@@ -485,17 +496,14 @@ function makeComputeGroupsRequest(tdConn, dummyWorker)
|
|
|
485
496
|
* Request a task from the task distributor
|
|
486
497
|
*
|
|
487
498
|
* @todo unify with supervisor /wg dec 2024
|
|
488
|
-
* @param {Keystore} identity identity of the requesting entity
|
|
489
499
|
* @param {Object} opts command-line options to dcp-task-probe
|
|
490
500
|
* .replay truey to use request from worker snapshot instead of a generated request
|
|
491
501
|
* .workerId workerId for request
|
|
492
502
|
*/
|
|
493
|
-
async function requestTask(
|
|
503
|
+
async function requestTask(dummyWorker, opts)
|
|
494
504
|
{
|
|
495
505
|
const protocol = require('dcp/protocol');
|
|
496
|
-
const worker = require('dcp/worker');
|
|
497
506
|
const tdConn = new protocol.Connection(dcpConfig.scheduler.services.taskDistributor);
|
|
498
|
-
const dummyWorker = new worker.Worker(identity, dcpConfig.worker); /* side effect: initializes dcpConfig.worker */
|
|
499
507
|
var request;
|
|
500
508
|
|
|
501
509
|
if (opts.replay)
|
|
@@ -504,29 +512,29 @@ async function requestTask(identity, opts)
|
|
|
504
512
|
request = {
|
|
505
513
|
supervisor: dummyWorker.supervisorVersion,
|
|
506
514
|
targetLoad: {
|
|
507
|
-
cpu: Math.min(
|
|
508
|
-
gpu: Math.min(
|
|
509
|
-
longSlices:
|
|
515
|
+
cpu: Math.min(dummyWorker.config.maxSandboxes, dummyWorker.config.cores.cpu * dummyWorker.config.utilization.cpu) /* cpuCoreSpace */,
|
|
516
|
+
gpu: Math.min(dummyWorker.config.maxSandboxes, dummyWorker.config.cores.gpu * dummyWorker.config.utilization.gpu) /* this.maxWorkingGPUs */,
|
|
517
|
+
longSlices: dummyWorker.config.maxSandboxes /* Math.floor(cpuCoreSpace) */,
|
|
510
518
|
},
|
|
511
519
|
coreStats: { /** @todo why are we specifying these? /wg dec 2024 */
|
|
512
|
-
worker: dummyWorker.
|
|
520
|
+
worker: dummyWorker.id,
|
|
513
521
|
lCores: require('os').cpus().length,
|
|
514
522
|
pCores: require('physical-cpu-count'),
|
|
515
|
-
sandbox:
|
|
523
|
+
sandbox: dummyWorker.config.maxSandboxes,
|
|
516
524
|
},
|
|
517
525
|
jobQuanta: [ 1 ]/* this.quanta.calculateJobQuanta() */,
|
|
518
|
-
capabilities:
|
|
519
|
-
paymentAddress:
|
|
520
|
-
jobAddresses:
|
|
526
|
+
capabilities: dummyWorker.config.capabilities,
|
|
527
|
+
paymentAddress: dummyWorker.config.paymentAddress,
|
|
528
|
+
jobAddresses: dummyWorker.config.jobAddresses || [],
|
|
521
529
|
workerComputeGroups: makeComputeGroupsRequest(tdConn, dummyWorker),
|
|
522
|
-
minimumWage:
|
|
530
|
+
minimumWage: dummyWorker.config.minimumWage,
|
|
523
531
|
soteriaJobs: [],
|
|
524
532
|
overdueSlices: [],
|
|
525
533
|
previouslyWorkedJobs: [], /* discrete jobs */
|
|
526
534
|
rejectedJobs: [],
|
|
527
535
|
unresolvedJobs: [],
|
|
528
536
|
fetchState: 1, /* normal fetch */
|
|
529
|
-
worktimes:
|
|
537
|
+
worktimes: dummyWorker.config.worktimes,
|
|
530
538
|
};
|
|
531
539
|
|
|
532
540
|
debug('dcp-task-probe:connect')('Connecting to task distributor at', tdConn.targetDescriptor.location.href);
|
|
@@ -542,7 +550,8 @@ async function requestTask(identity, opts)
|
|
|
542
550
|
if (!response.success)
|
|
543
551
|
panic('request failed:', response.payload);
|
|
544
552
|
|
|
545
|
-
|
|
553
|
+
debugger;
|
|
554
|
+
return { response, workerId: request.coreStats?.worker, workerConfig: dummyWorker.config };
|
|
546
555
|
}
|
|
547
556
|
|
|
548
557
|
async function returnTask(taskResponsePayload)
|
|
@@ -596,26 +605,35 @@ function fmtMs(ms)
|
|
|
596
605
|
/** Main program entry point */
|
|
597
606
|
async function main()
|
|
598
607
|
{
|
|
599
|
-
const wallet
|
|
600
|
-
const identity =
|
|
601
|
-
|
|
608
|
+
const wallet = require('dcp/wallet');
|
|
609
|
+
const identity = require('dcp/identity');
|
|
610
|
+
|
|
611
|
+
const initialWorkerConfig = {
|
|
612
|
+
allowOrigins: {
|
|
613
|
+
any: [],
|
|
614
|
+
}
|
|
615
|
+
};
|
|
616
|
+
const opts = await processOptions(initialWorkerConfig);
|
|
617
|
+
await identity.login('$login');
|
|
602
618
|
|
|
603
|
-
if (
|
|
604
|
-
|
|
619
|
+
if (typeof initialWorkerConfig.paymentAddress === 'string')
|
|
620
|
+
initialWorkerConfig.paymentAddress = new wallet.Address(initialWorkerConfig.paymentAddress);
|
|
621
|
+
else if (!initialWorkerConfig.paymentAddress)
|
|
622
|
+
initialWorkerConfig.paymentAddress = (await wallet.get({ oAuth: false })).address;
|
|
605
623
|
|
|
606
624
|
if (!opts.replay)
|
|
607
|
-
await probeEvaluator();
|
|
625
|
+
await probeEvaluator(initialWorkerConfig);
|
|
626
|
+
const dummyWorker = new (require('dcp/worker').DistributiveWorker)(initialWorkerConfig, function FakeSandboxConstructor(){});
|
|
608
627
|
|
|
609
628
|
if (opts.dumpConfig)
|
|
610
629
|
{
|
|
611
|
-
/* work around the cores getter/setter
|
|
612
|
-
const dump = Object.assign({},
|
|
613
|
-
dump.cores = { cpu: dcpConfig.worker.cores.cpu, gpu: dcpConfig.worker.cores.gpu };
|
|
630
|
+
/* work around the cores getter/setter */
|
|
631
|
+
const dump = Object.assign({}, dummyWorker.config);
|
|
614
632
|
console.log(dump);
|
|
615
633
|
process.exit(0);
|
|
616
634
|
}
|
|
617
635
|
|
|
618
|
-
const { workerId, response } = await requestTask(
|
|
636
|
+
const { workerId, response, workerConfig } = await requestTask(dummyWorker, opts);
|
|
619
637
|
const task = response.payload?.body?.task;
|
|
620
638
|
if (task)
|
|
621
639
|
await returnTask(response.payload);
|
|
@@ -623,7 +641,10 @@ async function main()
|
|
|
623
641
|
console.log('Task Distributor: ', dcpConfig.scheduler.services.taskDistributor.location.href);
|
|
624
642
|
console.log('Result Submitter: ', dcpConfig.scheduler.services.resultSubmitter.location.href);
|
|
625
643
|
console.log('Worker Id: ', workerId);
|
|
626
|
-
console.log('
|
|
644
|
+
console.log('Max Sandboxes: ', workerConfig.maxSandboxes);
|
|
645
|
+
console.log('Cores: ', Object.assign({}, workerConfig.cores));
|
|
646
|
+
console.log('Utilization: ', workerConfig.utilization);
|
|
647
|
+
console.log('DCP Address: ', String(identity.get().address));
|
|
627
648
|
|
|
628
649
|
if (!task)
|
|
629
650
|
{
|
|
@@ -647,8 +668,8 @@ async function main()
|
|
|
647
668
|
fmtMs(Object.keys(task).map(jobId => newJobs[jobId].metrics.sliceCPUTime).reduce((a, b) => a+b, 0)) + ',',
|
|
648
669
|
fmtMs(Object.keys(task).map(jobId => newJobs[jobId].metrics.sliceGPUTime).reduce((a, b) => a+b, 0)));
|
|
649
670
|
console.log('CPU,GPU Time/core: ',
|
|
650
|
-
fmtMs(Object.keys(task).map(jobId => newJobs[jobId].metrics.sliceCPUTime).reduce((a, b) => a+b, 0) /
|
|
651
|
-
fmtMs(Object.keys(task).map(jobId => newJobs[jobId].metrics.sliceGPUTime).reduce((a, b) => a+b, 0) /
|
|
671
|
+
fmtMs(Object.keys(task).map(jobId => newJobs[jobId].metrics.sliceCPUTime).reduce((a, b) => a+b, 0) / workerConfig.cores.cpu) + ',',
|
|
672
|
+
fmtMs(Object.keys(task).map(jobId => newJobs[jobId].metrics.sliceGPUTime).reduce((a, b) => a+b, 0) / workerConfig.cores.gpu));
|
|
652
673
|
console.log('Task Duration: ', schedulerConfig.targetTaskDuration + 's');
|
|
653
674
|
console.log('Compute Groups: ', Object.keys(computeGroupJobs).length);
|
|
654
675
|
|
|
@@ -690,7 +711,6 @@ async function main()
|
|
|
690
711
|
|
|
691
712
|
/* Initialize dcp-client to use only local resources before launching the main function */
|
|
692
713
|
require('dcp-client').init({
|
|
693
|
-
|
|
714
|
+
programName: 'dcp-worker',
|
|
694
715
|
parseArgv: false,
|
|
695
|
-
configName: process.env.DCP_CONFIG || '../etc/dcp-worker-config', /* => .js or .json */
|
|
696
716
|
}).then(main);
|
package/bin/dcp-worker
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#! /usr/bin/env node
|
|
2
|
+
|
|
2
3
|
/**
|
|
3
4
|
* @file dcp-worker.js
|
|
4
5
|
* Standalone NodeJS DCP Worker
|
|
@@ -18,14 +19,11 @@ var worker;
|
|
|
18
19
|
const process = require('process');
|
|
19
20
|
const fs = require('fs');
|
|
20
21
|
const path = require('path');
|
|
21
|
-
const crypto = require('crypto');
|
|
22
|
-
const chalk = require('chalk');
|
|
23
22
|
const telnetd = require('../lib/remote-console');
|
|
24
23
|
|
|
25
24
|
const { slicesFetched, debugging, displayMaxDiagInfo } = require('../lib/utils');
|
|
26
|
-
const { a$sleep } = require('dcp/utils');
|
|
25
|
+
const { a$sleep, leafMergeInto } = require('dcp/utils');
|
|
27
26
|
|
|
28
|
-
const configName = process.env.DCP_CONFIG || '../etc/dcp-worker-config';
|
|
29
27
|
const EXIT_UNHANDLED = 5;
|
|
30
28
|
const systemStateInfo = globalThis.systemStateInfo = {};
|
|
31
29
|
|
|
@@ -45,15 +43,7 @@ const replHelpers = {
|
|
|
45
43
|
},
|
|
46
44
|
};
|
|
47
45
|
telnetd.init(replHelpers);
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* regexps of GPUs we cannot use. Properties match device descriptors. More regexps can be added via
|
|
51
|
-
* dcpConfig.bannedGPUs.
|
|
52
|
-
*/
|
|
53
|
-
const bannedGPUs =
|
|
54
|
-
{
|
|
55
|
-
device: /llvmpipe/,
|
|
56
|
-
}
|
|
46
|
+
require('dcp-client').init().then(main).catch(handleUnhandled);
|
|
57
47
|
|
|
58
48
|
/**
|
|
59
49
|
* Output startup banner message. Currently a wrapper for console.log, future plans are to make it
|
|
@@ -66,12 +56,6 @@ function bannerLog()
|
|
|
66
56
|
}
|
|
67
57
|
bannerLog.cumulative = [];
|
|
68
58
|
|
|
69
|
-
/* Initialize dcp-client with local config defaults and run the main function. DCP_CONFIG_COOKIE becomes dcpConfig.cookie.
|
|
70
|
-
* And dcpConfig is defined as a side effect of initializing dcp-client.
|
|
71
|
-
*/
|
|
72
|
-
process.env.DCP_CONFIG_COOKIE = (Math.random().toString(16)).slice(2) + '-' + process.pid + '-' + Date.now();
|
|
73
|
-
require('dcp-client').init({ configName }).then(main).catch(handleUnhandled);
|
|
74
|
-
|
|
75
59
|
function parseCliArgs()
|
|
76
60
|
{
|
|
77
61
|
var defaultPidFileName;
|
|
@@ -85,10 +69,6 @@ function parseCliArgs()
|
|
|
85
69
|
describe: 'The address to deposit funds into, will use the default bank keystore if not provided.',
|
|
86
70
|
type: 'string',
|
|
87
71
|
},
|
|
88
|
-
defaultPaymentAddressToDCP: {
|
|
89
|
-
describe: 'If this option is set and no other payment address is provided, send payment for work completed to the DCP Community account',
|
|
90
|
-
type: 'boolean',
|
|
91
|
-
},
|
|
92
72
|
cores: {
|
|
93
73
|
alias: 'c',
|
|
94
74
|
describe: 'Number of CPU and GPU cores to work with: the format is 7,1 (or just 7) for 7 CPU cores and 1 GPU',
|
|
@@ -185,7 +165,7 @@ function parseCliArgs()
|
|
|
185
165
|
|
|
186
166
|
showConfig: {
|
|
187
167
|
hide: false,
|
|
188
|
-
describe: 'Show
|
|
168
|
+
describe: 'Show worker config',
|
|
189
169
|
type: 'string',
|
|
190
170
|
},
|
|
191
171
|
|
|
@@ -256,20 +236,6 @@ function isHash(b) {
|
|
|
256
236
|
return b && b.length === 68 && b.startsWith('eh1-');
|
|
257
237
|
}
|
|
258
238
|
|
|
259
|
-
/**
|
|
260
|
-
* Add one or more configuration objects into a target via leaf-merging.
|
|
261
|
-
*/
|
|
262
|
-
function addConfig(target, ...objs)
|
|
263
|
-
{
|
|
264
|
-
const { leafMerge } = require('dcp/utils');
|
|
265
|
-
var tmp = target;
|
|
266
|
-
|
|
267
|
-
for (let obj of objs)
|
|
268
|
-
tmp = leafMerge(tmp, obj);
|
|
269
|
-
|
|
270
|
-
Object.assign(target, tmp);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
239
|
/**
|
|
274
240
|
* Replacement for process.exit() that tries to increase the probability
|
|
275
241
|
* that remote log messages will make it out over the network.
|
|
@@ -289,17 +255,28 @@ function processExit()
|
|
|
289
255
|
*/
|
|
290
256
|
async function main()
|
|
291
257
|
{
|
|
292
|
-
const wallet
|
|
293
|
-
const
|
|
258
|
+
const wallet = require('dcp/wallet');
|
|
259
|
+
const identity = require('dcp/identity');
|
|
260
|
+
const { DistributiveWorker } = require('dcp/worker');
|
|
261
|
+
var workerConfig = {};
|
|
294
262
|
const cliArgs = parseCliArgs();
|
|
263
|
+
telnetd.setMainEval(function mainEval() { return eval(arguments[0]) }); // eslint-disable-line no-eval
|
|
264
|
+
require('../lib/startWorkerLogger').init(cliArgs); /* Start remote logger as early as possible */
|
|
265
|
+
|
|
266
|
+
/* Process any identity overrides and then establish our identity. */
|
|
267
|
+
identity.interactive = false;
|
|
268
|
+
if (cliArgs.identityKey || cliArgs.identityKeystore)
|
|
269
|
+
await identity.set(cliArgs.identityKey || cliArgs.identityKeystore);
|
|
270
|
+
await identity.login('$login');
|
|
271
|
+
|
|
272
|
+
/* Eagerly create a worker object and mutate its config property to reflect argv before starting it. */
|
|
295
273
|
const sawOptions = {
|
|
296
274
|
hostname: cliArgs.hostname,
|
|
297
275
|
port: cliArgs.port
|
|
298
276
|
};
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
require('
|
|
302
|
-
verifyDefaultConfigIntegrity(); /* Bail before TUI & as early as possible if bad conf */
|
|
277
|
+
if (typeof dcpConfig.worker.cleanupTimeout !== 'number')
|
|
278
|
+
dcpConfig.worker.cleanupTimeout = 60;
|
|
279
|
+
const SandboxConstructor = require('dcp-client/lib/standaloneWorker').workerFactory(sawOptions);
|
|
303
280
|
|
|
304
281
|
process.on('SIGINT', handleSigDeath);
|
|
305
282
|
process.on('SIGTERM', handleSigDeath);
|
|
@@ -307,74 +284,26 @@ async function main()
|
|
|
307
284
|
process.on('unhandledRejection', handleUnhandled);
|
|
308
285
|
process.on('uncaughtException', handleUnhandled);
|
|
309
286
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|| dcpConfig.worker.paymentAddress
|
|
317
|
-
|| (await wallet.get(getOpts).catch(error => {
|
|
318
|
-
// if flag is set and no other address is provided, use the DCP Community Account,
|
|
319
|
-
// which is 0x079DAC0612C710ab4e975dAb7171C7e4beF78c5a at time of writing
|
|
320
|
-
if ((error.code === 'ENOENT') && cliArgs.defaultPaymentAddressToDCP)
|
|
321
|
-
{
|
|
322
|
-
console.warn('Warning: Defaulting payment to DCP Community Account');
|
|
323
|
-
return {address:'0x079DAC0612C710ab4e975dAb7171C7e4beF78c5a'};
|
|
324
|
-
}
|
|
325
|
-
throw error;
|
|
326
|
-
})).address;
|
|
327
|
-
if (typeof paymentAddress === 'string')
|
|
328
|
-
paymentAddress = new wallet.Address(paymentAddress);
|
|
287
|
+
if (cliArgs.paymentAddress)
|
|
288
|
+
workerConfig.paymentAddress = new wallet.Address(cliArgs.paymentAddress);
|
|
289
|
+
else if (typeof workerConfig.paymentAddress === 'string')
|
|
290
|
+
workerConfig.paymentAddress = new wallet.Address(workerConfig.paymentAddress);
|
|
291
|
+
else if (!workerConfig.paymentAddress)
|
|
292
|
+
workerConfig.paymentAddress = (await wallet.get({ oAuth: false })).address;
|
|
329
293
|
|
|
330
294
|
if (cliArgs.pidFile)
|
|
331
295
|
require('../lib/pidfile').write(cliArgs.pidFile);
|
|
332
296
|
|
|
333
|
-
/* Figure out the worker's identity and put that keystore in the wallet */
|
|
334
|
-
let identityKeystore = false;
|
|
335
|
-
if (cliArgs.identityKey)
|
|
336
|
-
identityKeystore = await new wallet.IdKeystore(cliArgs.identityKey, '');
|
|
337
|
-
else if (cliArgs.identityKeystore)
|
|
338
|
-
identityKeystore = await new wallet.IdKeystore(JSON.parse(cliArgs.identityKeystore), '');
|
|
339
|
-
else
|
|
340
|
-
identityKeystore = await wallet.getId();
|
|
341
|
-
await wallet.addId(identityKeystore);
|
|
342
|
-
|
|
343
|
-
/* Build the worker options, which are largely given by dcpConfig.worker. We use a reference for
|
|
344
|
-
* dcpConfig.worker rather than copying it, so that runtime modifications to the worker configuration
|
|
345
|
-
* in memory take effect immediately.
|
|
346
|
-
*
|
|
347
|
-
* forceOptions override any setting in dcpConfig; this can be used for settings calculated above
|
|
348
|
-
* which were derived from dcpConfig in the first place. defaultOptions are overrideable by the usual
|
|
349
|
-
* dcpConfig mechanisms, but since they are dynamic (or non-user-facing) they don't come from the
|
|
350
|
-
* etc/dcp-worker-config.js file that ships with the work.
|
|
351
|
-
*
|
|
352
|
-
* It is important to never disable leavePublicGroup as a side effect of any other operation, or
|
|
353
|
-
* slight configuration errors could have large security impacts.
|
|
354
|
-
z */
|
|
355
|
-
const dcpWorkerOptions = dcpConfig.worker;
|
|
356
|
-
const forceOptions = {
|
|
357
|
-
paymentAddress,
|
|
358
|
-
};
|
|
359
|
-
const defaultOptions = {
|
|
360
|
-
sandboxOptions: {
|
|
361
|
-
SandboxConstructor: require('dcp-client/lib/standaloneWorker').workerFactory(sawOptions)
|
|
362
|
-
},
|
|
363
|
-
};
|
|
364
|
-
|
|
365
297
|
if (cliArgs.leavePublicGroup !== undefined)
|
|
366
|
-
|
|
298
|
+
workerConfig.leavePublicGroup = mkBool(cliArgs.leavePublicGroup);
|
|
367
299
|
if (cliArgs.publicGroupFallback !== undefined)
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
addConfig(dcpWorkerOptions, defaultOptions, dcpConfig.worker, forceOptions);
|
|
371
|
-
processCoresAndMaxSandboxes(dcpWorkerOptions, cliArgs);
|
|
300
|
+
workerConfig.publicGroupFallback = mkBool(cliArgs.publicGroupFallback);
|
|
372
301
|
|
|
373
302
|
/* Support magic value used by Windows screensaver configuration /wg June 2023 */
|
|
374
|
-
if (
|
|
303
|
+
if (workerConfig.leavePublicGroup === 'fallback')
|
|
375
304
|
{
|
|
376
|
-
|
|
377
|
-
|
|
305
|
+
workerConfig.publicGroupFallback = true;
|
|
306
|
+
workerConfig.leavePublicGroup = undefined;
|
|
378
307
|
}
|
|
379
308
|
|
|
380
309
|
/* cliArgs.join is the list of compute groups to join */
|
|
@@ -388,32 +317,21 @@ z */
|
|
|
388
317
|
})
|
|
389
318
|
.filter((el) => el.joinKey); /* Filter out entries with no joinKey */
|
|
390
319
|
|
|
391
|
-
|
|
320
|
+
leafMergeInto(workerConfig.computeGroups, cliComputeGroups);
|
|
392
321
|
}
|
|
393
322
|
|
|
394
323
|
if (cliArgs.jobId)
|
|
395
324
|
{
|
|
396
|
-
if (
|
|
397
|
-
|
|
398
|
-
|
|
325
|
+
if (!workerConfig.jobAddresses)
|
|
326
|
+
workerConfig.jobAddresses = [];
|
|
327
|
+
workerConfig.jobAddresses.push(...cliArgs.jobId);
|
|
399
328
|
}
|
|
400
329
|
|
|
401
330
|
if (cliArgs.allowedOrigins)
|
|
402
|
-
|
|
403
|
-
if (!dcpWorkerOptions.allowOrigins)
|
|
404
|
-
dcpWorkerOptions.allowOrigins = {};
|
|
405
|
-
if (!dcpWorkerOptions.allowOrigins.any)
|
|
406
|
-
dcpWorkerOptions.allowOrigins.any = [];
|
|
407
|
-
dcpWorkerOptions.allowOrigins.any.push(...cliArgs.allowedOrigins);
|
|
408
|
-
}
|
|
409
|
-
if (cliArgs.watchdogInterval)
|
|
410
|
-
dcpWorkerOptions.watchdogInterval = cliArgs.watchdogInterval;
|
|
331
|
+
workerConfig.allowOrigins.any.push(...cliArgs.allowedOrigins);
|
|
411
332
|
|
|
412
|
-
if (cliArgs.
|
|
413
|
-
|
|
414
|
-
console.log(eval('dcpConfig.' + cliArgs.showConfig));
|
|
415
|
-
process.exit();
|
|
416
|
-
}
|
|
333
|
+
if (cliArgs.watchdogInterval)
|
|
334
|
+
workerConfig.watchdogInterval = cliArgs.watchdogInterval;
|
|
417
335
|
|
|
418
336
|
if (cliArgs.dumpConfig)
|
|
419
337
|
{
|
|
@@ -421,34 +339,31 @@ z */
|
|
|
421
339
|
process.exit();
|
|
422
340
|
}
|
|
423
341
|
|
|
424
|
-
|
|
342
|
+
worker = new DistributiveWorker(workerConfig, SandboxConstructor);
|
|
343
|
+
workerConfig = null; /* bug-finder */
|
|
344
|
+
processCoresAndMaxSandboxes(worker.config, cliArgs);
|
|
345
|
+
|
|
346
|
+
if (cliArgs.showConfig)
|
|
425
347
|
{
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
if (!bannedGPUs[prop])
|
|
429
|
-
bannedGPUs[prop] = dcpConfig.worker?.bannedGPUs[prop];
|
|
430
|
-
else
|
|
431
|
-
bannedGPUs[prop] = new RegExp(`(${dcpConfig.worker?.bannedGPUs[prop].source})|(${bannedGPUs[prop].source})`);
|
|
432
|
-
}
|
|
348
|
+
console.log(eval('dcpConfig.' + cliArgs.showConfig)); // eslint-disable-line no-eval
|
|
349
|
+
process.exit();
|
|
433
350
|
}
|
|
434
351
|
|
|
435
|
-
|
|
436
|
-
bannerLog(` * Starting DCP Worker ${nascentWorker.workerId}`);
|
|
352
|
+
bannerLog(` * Starting Distributive Worker ${worker.id}`);
|
|
437
353
|
bannerLog(` . Using evaluator at ${dcpConfig.evaluator.location}`);
|
|
438
354
|
bannerLog(` . Configured for scheduler ${dcpConfig.scheduler.location}`);
|
|
439
355
|
bannerLog(` . Bank is ${dcpConfig.bank.location}`);
|
|
440
|
-
bannerLog(` . Earned funds will be deposited in account ${
|
|
441
|
-
bannerLog(` . Identity is ${
|
|
356
|
+
bannerLog(` . Earned funds will be deposited in account ${worker.config.paymentAddress}`);
|
|
357
|
+
bannerLog(` . Identity is ${identity.get()?.address}`);
|
|
442
358
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
359
|
+
worker.on('warning', (...payload) => console.warn (...payload));
|
|
360
|
+
worker.on('stop', () => { console.log('Worker is stopping') });
|
|
361
|
+
worker.on('end', () => { logClosing('log', 'Worker has stopped') });
|
|
362
|
+
worker.on('job', job => console.log(` . Job: ${job.name} ${job.address.slice(0,8)} ${job.description || ''} ${job.link || ''}`));
|
|
447
363
|
|
|
448
364
|
// Display clean diagnostic when not debugging and env var
|
|
449
365
|
// DCP_SUPERVISOR_DEBUG_DISPLAY_MAX_INFO isn't set.
|
|
450
|
-
|
|
451
|
-
|
|
366
|
+
worker.on('error', (error) => {
|
|
452
367
|
if (displayMaxDiagInfo())
|
|
453
368
|
console.error(error);
|
|
454
369
|
else
|
|
@@ -462,36 +377,36 @@ z */
|
|
|
462
377
|
console.error(`Error: ${error.message} ${location}`);
|
|
463
378
|
}
|
|
464
379
|
});
|
|
465
|
-
require('../lib/default-ui-events').hook(
|
|
380
|
+
require('../lib/default-ui-events').hook(worker, cliArgs);
|
|
466
381
|
|
|
467
382
|
if (cliArgs.outputMode === 'dashboard')
|
|
468
|
-
require('../lib/dashboard-tui').init(
|
|
383
|
+
require('../lib/dashboard-tui').init(worker, cliArgs, bannerLog.cumulative);
|
|
469
384
|
|
|
470
385
|
/* Let incorrect event-loop references keep us alive when linked with a debug library, but
|
|
471
386
|
* exit quickly/accurately for production code even when the library isn't perfect.
|
|
472
387
|
*/
|
|
473
388
|
if (require('dcp/build').config.build !== 'debug')
|
|
474
|
-
|
|
389
|
+
worker.on('end', processExit);
|
|
475
390
|
else
|
|
476
|
-
|
|
391
|
+
worker.on('end', () => setTimeout(processExit, worker.config.cleanupTimeout * 1e3).unref());
|
|
477
392
|
|
|
478
|
-
if (
|
|
393
|
+
if (worker.config.publicGroupFallback)
|
|
479
394
|
{
|
|
480
|
-
if (
|
|
395
|
+
if (worker.config.leavePublicGroup)
|
|
481
396
|
console.warn(' ! Global Group fallback has been requested, but the global group is blocked by local configuration');
|
|
482
397
|
else
|
|
483
398
|
{
|
|
484
399
|
/* Enable global group fallback - this currently works by enabling or disabling the global group
|
|
485
400
|
* on the next fetch based on whether or not the most recent fetch was an empty task or not.
|
|
486
401
|
*/
|
|
487
|
-
|
|
402
|
+
worker.on('fetch', fetchEventHandler);
|
|
488
403
|
|
|
489
404
|
function fetchEventHandler(ev)
|
|
490
405
|
{
|
|
491
406
|
if (ev instanceof Error)
|
|
492
407
|
console.error('Error fetching task:', ev);
|
|
493
408
|
else
|
|
494
|
-
|
|
409
|
+
worker.config.leavePublicGroup = Boolean(slicesFetched(ev) > 0);
|
|
495
410
|
}
|
|
496
411
|
}
|
|
497
412
|
}
|
|
@@ -510,25 +425,26 @@ z */
|
|
|
510
425
|
return plural;
|
|
511
426
|
}
|
|
512
427
|
|
|
513
|
-
if (
|
|
514
|
-
bannerLog(` * Processing only ${qty(
|
|
515
|
-
if (
|
|
516
|
-
bannerLog(` . Joining compute ${qty(
|
|
517
|
-
if (
|
|
428
|
+
if (worker.config.jobAddresses?.length > 0)
|
|
429
|
+
bannerLog(` * Processing only ${qty(worker.config.jobAddresses, 'job')} ` + worker.config.jobAddresses.join(', '));
|
|
430
|
+
if (worker.config.computeGroups && Object.keys(worker.config.computeGroups).length > 0)
|
|
431
|
+
bannerLog(` . Joining compute ${qty(worker.config.computeGroups, 'group')} ` + worker.config.computeGroups.map(el => el.joinKey).join(', '));
|
|
432
|
+
if (worker.config.publicGroupFallback)
|
|
518
433
|
bannerLog(' . Falling back on global group when preferred groups have no work');
|
|
519
|
-
if (
|
|
434
|
+
if (worker.config.leavePublicGroup)
|
|
520
435
|
bannerLog(' . Leaving the global compute group');
|
|
521
|
-
|
|
522
|
-
bannerLog(` .
|
|
523
|
-
bannerLog(` .
|
|
524
|
-
bannerLog(` .
|
|
436
|
+
|
|
437
|
+
bannerLog(` . Configured Cores: { cpu: ${worker.config.cores.cpu}, gpu: ${worker.config.cores.gpu} }`);
|
|
438
|
+
bannerLog(` . Utilization: { cpu: ${worker.config.utilization.cpu}, gpu: ${worker.config.utilization.gpu} }`);
|
|
439
|
+
bannerLog(` . Effective Cores: { cpu: ${worker.config.utilization.cpu * worker.config.cores.cpu}, gpu: ${worker.config.utilization.gpu * worker.config.cores.gpu} }`);
|
|
440
|
+
bannerLog(` . Maximum Sandboxes: ${worker.config.maxSandboxes}`);
|
|
525
441
|
if (cliArgs.verbose)
|
|
526
442
|
bannerLog(` + Verbosity level: ${cliArgs.verbose}`);
|
|
527
443
|
if (telnetd.hasOwnProperty('port'))
|
|
528
444
|
bannerLog(` ! telnetd listening on port ${telnetd.port}`);
|
|
529
445
|
|
|
530
446
|
let workerInfo;
|
|
531
|
-
console.throb('log',
|
|
447
|
+
console.throb('log', ` ! Waiting for dcp-evaluator on ${sawOptions.hostname}:${sawOptions.port} to start...`);
|
|
532
448
|
for (let i=0; !workerInfo; i++)
|
|
533
449
|
{
|
|
534
450
|
workerInfo = await require('../lib/worker-info').getEvaluatorInformation(sawOptions);
|
|
@@ -557,20 +473,19 @@ z */
|
|
|
557
473
|
for (let descriptor in workerInfo.webgpu.info)
|
|
558
474
|
bannerLog(` -\t${descriptor}: ${workerInfo.webgpu.info[descriptor]}`);
|
|
559
475
|
systemStateInfo.gpu = Object.assign({}, workerInfo.webgpu.info);
|
|
560
|
-
|
|
561
|
-
for (let descriptor in bannedGPUs)
|
|
476
|
+
for (let descriptor in worker.config.bannedGPUs)
|
|
562
477
|
{
|
|
563
|
-
if (bannedGPUs[descriptor].test(workerInfo.webgpu.info[descriptor]))
|
|
478
|
+
if (worker.config.bannedGPUs[descriptor].test(workerInfo.webgpu.info[descriptor]))
|
|
564
479
|
{
|
|
565
480
|
console.error(' * This GPU is not supported; disabling');
|
|
566
|
-
|
|
481
|
+
worker.config.cores = { cpu: worker.config.cores.cpu, gpu: 0 };
|
|
567
482
|
systemStateInfo.gpu.disabled = true;
|
|
568
483
|
break;
|
|
569
484
|
}
|
|
570
485
|
}
|
|
571
486
|
}
|
|
572
487
|
|
|
573
|
-
bannerLog(' . Supervisor version: ' +
|
|
488
|
+
bannerLog(' . Supervisor version: ' + worker.supervisorVersion);
|
|
574
489
|
bannerLog(' . Output mode: ' + cliArgs.outputMode);
|
|
575
490
|
|
|
576
491
|
if (parseFloat(cliArgs.reportInterval))
|
|
@@ -587,7 +502,6 @@ z */
|
|
|
587
502
|
setImmediate(async function workerStart() {
|
|
588
503
|
bannerLog(' * Ready.\n');
|
|
589
504
|
require('../lib/check-scheduler-version').check();
|
|
590
|
-
worker = nascentWorker;
|
|
591
505
|
await worker.start();
|
|
592
506
|
});
|
|
593
507
|
}
|
|
@@ -604,20 +518,20 @@ z */
|
|
|
604
518
|
* -u 0.80,0.75 => utilization = { cpu: 0.80, gpu: 0.75 }
|
|
605
519
|
* -m 5 => maxSandboxes = 5
|
|
606
520
|
*/
|
|
607
|
-
function processCoresAndMaxSandboxes (
|
|
521
|
+
function processCoresAndMaxSandboxes (workerConfig, cliArgs)
|
|
608
522
|
{
|
|
609
523
|
if (typeof cliArgs['maxSandboxes'] !== 'undefined')
|
|
610
|
-
|
|
524
|
+
workerConfig.maxSandboxes = Number(cliArgs['maxSandboxes']);
|
|
611
525
|
|
|
612
526
|
const parseArg = (which) => {
|
|
613
527
|
if (cliArgs[which])
|
|
614
528
|
{
|
|
615
|
-
|
|
529
|
+
workerConfig[which] = {};
|
|
616
530
|
const [cpu, gpu] = cliArgs[which].split(',');
|
|
617
531
|
if (cpu?.length > 0)
|
|
618
|
-
|
|
532
|
+
workerConfig[which].cpu = Number(cpu);
|
|
619
533
|
if (gpu?.length > 0)
|
|
620
|
-
|
|
534
|
+
workerConfig[which].gpu = Number(gpu);
|
|
621
535
|
}
|
|
622
536
|
};
|
|
623
537
|
|
|
@@ -626,9 +540,9 @@ function processCoresAndMaxSandboxes (dcpWorkerOptions, cliArgs)
|
|
|
626
540
|
|
|
627
541
|
if (debugging())
|
|
628
542
|
{
|
|
629
|
-
console.debug(`dcp-worker: cores = { cpu: ${
|
|
630
|
-
console.debug('dcp-worker: utilization =',
|
|
631
|
-
console.debug('dcp-worker: maxSandboxes =',
|
|
543
|
+
console.debug(`dcp-worker: cores = { cpu: ${workerConfig.cores.cpu}, gpu: ${workerConfig.cores.gpu} }`);
|
|
544
|
+
console.debug('dcp-worker: utilization =', workerConfig.utilization);
|
|
545
|
+
console.debug('dcp-worker: maxSandboxes =', workerConfig.maxSandboxes);
|
|
632
546
|
}
|
|
633
547
|
}
|
|
634
548
|
|
|
@@ -646,11 +560,16 @@ function logClosing(facility, ...message)
|
|
|
646
560
|
* FUTURE: dashboard API should know how to unregister its hook so that we don't have to clobber
|
|
647
561
|
* it here.
|
|
648
562
|
*/
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
563
|
+
|
|
564
|
+
try
|
|
565
|
+
{
|
|
566
|
+
screen.log(...message);
|
|
567
|
+
screen.destroy();
|
|
568
|
+
screen = false;
|
|
569
|
+
console = new (require('console').Console)(process); // eslint-disable-line no-global-assign
|
|
570
|
+
telnetd.reintercept();
|
|
571
|
+
}
|
|
572
|
+
catch(error) {} // eslint-disable-line no-empty
|
|
654
573
|
}
|
|
655
574
|
|
|
656
575
|
console[facility](...message);
|
|
@@ -668,33 +587,24 @@ function handleUnhandled(error)
|
|
|
668
587
|
worker = false;
|
|
669
588
|
|
|
670
589
|
process.exitCode = process.exitCode || EXIT_UNHANDLED;
|
|
590
|
+
logClosing('error', 'trapped unhandled', error);
|
|
671
591
|
|
|
672
|
-
|
|
592
|
+
if (_worker)
|
|
673
593
|
{
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
console.error('trapped unhandled error:', error)
|
|
679
|
-
else
|
|
680
|
-
{
|
|
681
|
-
console.error('trapped unhandled error -- stopping worker:', error);
|
|
594
|
+
setTimeout(() => {
|
|
595
|
+
logClosing('error', 'handleFatalError timeout - exiting now');
|
|
596
|
+
processExit();
|
|
597
|
+
}, _worker.config.cleanupTimeout * 1e3).unref();
|
|
682
598
|
_worker.on('end', processExit);
|
|
683
599
|
_worker.stop();
|
|
684
600
|
}
|
|
685
601
|
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
try {
|
|
692
|
-
let log = dcpConfig && dcpConfig.worker && dcpConfig.worker.unhandledRejectionLog;
|
|
693
|
-
if (!log) log = process.env.DCP_WORKER_UNHANDLED_REJECTION_LOG;
|
|
694
|
-
if (log) {
|
|
602
|
+
try
|
|
603
|
+
{
|
|
604
|
+
let log = process.env.DCP_WORKER_UNHANDLED_REJECTION_LOG || worker?.config?.unhandledRejectionLog;
|
|
605
|
+
if (log)
|
|
695
606
|
fs.appendFileSync(process.env.DCP_WORKER_UNHANDLED_REJECTION_LOG,
|
|
696
607
|
`${Date.now()}: ${error.message}\n${error.stack}\n\n`);
|
|
697
|
-
}
|
|
698
608
|
} catch(e) {} // eslint-disable-line no-empty
|
|
699
609
|
}
|
|
700
610
|
|
|
@@ -813,7 +723,7 @@ function handleSigDeath(signalName, signal)
|
|
|
813
723
|
console.warn(`trapped ${signalName}, signal ${signal} -- stopping worker`,
|
|
814
724
|
immediate ? 'immediately' : `after ${handleSigDeath.count} slices have finished`);
|
|
815
725
|
worker.stop(immediate);
|
|
816
|
-
setTimeout(die,
|
|
726
|
+
setTimeout(die, worker.config.cleanupTimeout * 1e3).unref();
|
|
817
727
|
}
|
|
818
728
|
process.emit('dcpBeforeExit');
|
|
819
729
|
|
|
@@ -827,81 +737,6 @@ function handleSigDeath(signalName, signal)
|
|
|
827
737
|
}
|
|
828
738
|
globalThis.die = () => handleSigDeath('QUIT', 15);
|
|
829
739
|
|
|
830
|
-
/**
|
|
831
|
-
* Returns the duration of the cleanup timeout in milliseconds. It is possible to specify zero.
|
|
832
|
-
*/
|
|
833
|
-
function getCleanupTimeoutMs()
|
|
834
|
-
{
|
|
835
|
-
const defaultCT = 60;
|
|
836
|
-
var cleanupTimeout = dcpConfig.worker?.cleanupTimeout;
|
|
837
|
-
|
|
838
|
-
if (typeof cleanupTimeout === 'undefined')
|
|
839
|
-
cleanupTimeout = defaultCT;
|
|
840
|
-
if (typeof cleanupTimeout !== 'number')
|
|
841
|
-
cleanupTimeout = Number(cleanupTimeout)
|
|
842
|
-
if (isNaN(cleanupTimeout))
|
|
843
|
-
{
|
|
844
|
-
cleanupTimeout = defaultCT;
|
|
845
|
-
if (!getCleanupTimeoutMs.warned)
|
|
846
|
-
{
|
|
847
|
-
console.error(`warning: dcpConfig.worker.cleanupTimeout is not a number (${dcpConfig.worker.cleanupTimeout})`);
|
|
848
|
-
getCleanupTimeoutMs.warned = true;
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
return cleanupTimeout * 1000;
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
/**
|
|
855
|
-
* Ensure the default configuration hasn't been modified by the end-user-sysadmin. It is an
|
|
856
|
-
* attractive nuisance, as it looks just like the file they should modify, but if they make
|
|
857
|
-
* security changes there that are overwritten in an subsequent update, it will be a problem.
|
|
858
|
-
*
|
|
859
|
-
* Every time a new package is generated, the default config file has its md5 checksum recorded
|
|
860
|
-
* via the pack npm hook; all we do is make sure it hasn't changed.
|
|
861
|
-
*/
|
|
862
|
-
function verifyDefaultConfigIntegrity()
|
|
863
|
-
{
|
|
864
|
-
const workerConfPath = require('dcp-client').__cn;
|
|
865
|
-
const md5sumPath = workerConfPath + '.md5';
|
|
866
|
-
|
|
867
|
-
if (!fs.existsSync(md5sumPath))
|
|
868
|
-
{
|
|
869
|
-
console.error(chalk.bold.red(` ! warning: ${md5sumPath} not found; cannot verify configuration integrity`));
|
|
870
|
-
require('dcp/utils').sleep(2);
|
|
871
|
-
}
|
|
872
|
-
else
|
|
873
|
-
{
|
|
874
|
-
const originalMd5sum = fs.readFileSync(md5sumPath, 'ascii');
|
|
875
|
-
const actualMd5sum = crypto.createHash('md5')
|
|
876
|
-
.update(fs.readFileSync(workerConfPath))
|
|
877
|
-
.digest('hex');
|
|
878
|
-
|
|
879
|
-
if (!originalMd5sum.startsWith(actualMd5sum))
|
|
880
|
-
{
|
|
881
|
-
console.warn(chalk.yellow(` ! Detected modified ${workerConfPath};`));
|
|
882
|
-
console.warn(' ! DCP Worker configuration changes should not be made by updating the default');
|
|
883
|
-
console.warn(' config, as that file will be overwritten on the next npm update. Instead,');
|
|
884
|
-
console.warn(' make changes via one of the following locations:');
|
|
885
|
-
console.warn(' - ~/.dcp/dcp-worker/dcp-config.js');
|
|
886
|
-
console.warn(' - /etc/dcp/dcp-worker/dcp-config.js');
|
|
887
|
-
console.warn(' - /etc/override/dcp/dcp-worker/dcp-config.js');
|
|
888
|
-
console.warn(' - the Windows Registry');
|
|
889
|
-
|
|
890
|
-
if (require('dcp/build').config.build !== 'debug')
|
|
891
|
-
processExit(1);
|
|
892
|
-
|
|
893
|
-
console.log(chalk.bold.red.inverse("If this wasn't a debug build, the worker would exit now."));
|
|
894
|
-
require('dcp/utils').sleep(2);
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
if (dcpConfig.cookie !== process.env.DCP_CONFIG_COOKIE || !dcpConfig.cookie)
|
|
899
|
-
{
|
|
900
|
-
console.error(' ! DCP Worker default configuration was not loaded; exiting.');
|
|
901
|
-
processExit(1);
|
|
902
|
-
}
|
|
903
|
-
}
|
|
904
|
-
|
|
905
740
|
/**
|
|
906
741
|
* Cast b to boolean such that 'false' becomes false, falsey things become false, and everything else
|
|
907
742
|
* becomes true.
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @file dcp-
|
|
3
|
-
*
|
|
2
|
+
* @file dcp-config.js - Sample configuration for the DCP Worker.
|
|
3
|
+
*
|
|
4
4
|
* Remove (or comment out) settings you don't need to change, so that changes to the
|
|
5
|
-
* defaults can be made during an upgrade.
|
|
5
|
+
* defaults can be made during an upgrade. The standard dcp-client path options apply;
|
|
6
|
+
* Suggested locations include:
|
|
6
7
|
* - /etc/dcp/dcp-worker/dcp-config.js, or
|
|
7
8
|
* - ~/.dcp/dcp-worker/dcp-config.js.
|
|
8
9
|
*
|
|
@@ -40,17 +41,20 @@
|
|
|
40
41
|
'in': 0, /* DCC per megabyte of inbound network traffic */
|
|
41
42
|
'out': 0, /* DCC per megabyte of outbound network traffic */
|
|
42
43
|
},
|
|
44
|
+
|
|
45
|
+
/* Regexps of GPUs we cannot use. Properties match device descriptors. */
|
|
43
46
|
bannedGPUs: {
|
|
44
|
-
description: /D3D.*version\s[0-2][0-9]
|
|
47
|
+
description: /D3D.*version\s[0-2][0-9]/,
|
|
48
|
+
device: /llvmpipe/,
|
|
45
49
|
},
|
|
46
50
|
|
|
47
51
|
/* Extra Compute Groups this worker can participate in. Join credentials are supplied by
|
|
48
52
|
* Distributive and/or local IT staff at site-licensed locations.
|
|
49
53
|
*/
|
|
50
54
|
computeGroups: [
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
55
|
+
{ joinKey: 'scott', joinSecret: 'tiger' },
|
|
56
|
+
{ joinKey: 'scott', joinHash: 'eh1-672937c2b944982e071185b888770f8b8ea67c11f56d545e403e0d513c609b87' },
|
|
57
|
+
{ keystore('~/.dcp/scott') },
|
|
54
58
|
],
|
|
55
59
|
},
|
|
56
60
|
|
|
@@ -61,6 +65,4 @@
|
|
|
61
65
|
evaluator: {
|
|
62
66
|
listen: new URL('dcpsaw://localhost:9000/'),
|
|
63
67
|
},
|
|
64
|
-
|
|
65
|
-
cookie: require('process').env.DCP_CONFIG_COOKIE, /* Used to verify that this file was loaded. */
|
|
66
68
|
}
|
package/lib/dashboard-tui.js
CHANGED
|
@@ -135,7 +135,7 @@ exports.init = function dashboard$$init(worker, options, bannerLogCumulative)
|
|
|
135
135
|
function updateWorkerInfo(fetchInfo)
|
|
136
136
|
{
|
|
137
137
|
var gpuInfo = '';
|
|
138
|
-
|
|
138
|
+
|
|
139
139
|
if (fetchInfo)
|
|
140
140
|
lastTask = fetchInfo;
|
|
141
141
|
|
|
@@ -162,10 +162,10 @@ exports.init = function dashboard$$init(worker, options, bannerLogCumulative)
|
|
|
162
162
|
` Bank Account: ${chalk.yellow(worker.paymentAddress || 'Starting...')}`,
|
|
163
163
|
` Identity: ${chalk.yellow(worker.identityKeystore? worker.identityKeystore.address : 'Starting...')}`,
|
|
164
164
|
` GPU: ${gpuInfo}`,
|
|
165
|
-
` Jobs: ${
|
|
166
|
-
` Compute Groups: ${Object.keys(
|
|
167
|
-
`Global Compute Group: ${
|
|
168
|
-
` Worker Id: ${worker.
|
|
165
|
+
` Jobs: ${worker.config.jobAddresses?.length ? worker.config.jobAddresses.join(', ') : '<any>'}`,
|
|
166
|
+
` Compute Groups: ${Object.keys(worker.config.computeGroups).length + (worker.config.leavePublicGroup ? 0 : 1)}`,
|
|
167
|
+
`Global Compute Group: ${worker.config.leavePublicGroup ? 'no' : 'yes'}`,
|
|
168
|
+
` Worker Id: ${worker.id}`,
|
|
169
169
|
` Current Time: ${new Date(Date.now()).toUTCString()}`,
|
|
170
170
|
` Last Task Request: ${lastTask ? new Date(lastTask.fetchStart).toUTCString() : 0}`,
|
|
171
171
|
` Slices in task: ${lastTask ? Object.values(lastTask.slices).reduce((acc, val) => acc + val, 0) : 0}`,
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
'use strict';
|
|
8
8
|
|
|
9
9
|
const process = require('process');
|
|
10
|
+
const usingDebugger = require('module')._cache.niim instanceof require('module').Module;
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Initialize the console logger
|
|
@@ -56,14 +57,14 @@ exports.init = function console$$init(options)
|
|
|
56
57
|
|
|
57
58
|
if (throbArgsCache)
|
|
58
59
|
{
|
|
59
|
-
if (stream.isTTY)
|
|
60
|
+
if (stream.isTTY && !usingDebugger)
|
|
60
61
|
stream.write(throbArgsCache.args.join(' '));
|
|
61
62
|
else
|
|
62
63
|
console[throbArgsCache.throbFacility](...throbArgsCache.args);
|
|
63
64
|
throbArgsCache = false;
|
|
64
65
|
}
|
|
65
66
|
|
|
66
|
-
if (stream.isTTY)
|
|
67
|
+
if (stream.isTTY && !usingDebugger)
|
|
67
68
|
{
|
|
68
69
|
i = (i + 1) % throbChars.length;
|
|
69
70
|
stream.write(throbChars[i] + '\u0008');
|
|
@@ -70,6 +70,7 @@ exports.init = function dashboardLogger$$init(options)
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
throbPos = (throbPos + 1) % throbChars.length;
|
|
73
|
-
dashboardTui.logPane
|
|
73
|
+
if (dashboardTui.logPane?.advanceThrob)
|
|
74
|
+
dashboardTui.logPane.advanceThrob(throbChars[throbPos]);
|
|
74
75
|
}
|
|
75
76
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dcp-worker",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.1",
|
|
4
4
|
"description": "Node.js Worker for Distributive Compute Platform",
|
|
5
5
|
"main": "bin/dcp-worker",
|
|
6
6
|
"keywords": [
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
},
|
|
24
24
|
"directories": {
|
|
25
25
|
"lib": "lib",
|
|
26
|
-
"
|
|
26
|
+
"examples": "examples"
|
|
27
27
|
},
|
|
28
28
|
"scripts": {
|
|
29
29
|
"check": "trunk check",
|
|
@@ -32,7 +32,6 @@
|
|
|
32
32
|
"hook": "PATH=npm-hooks:$PATH &&",
|
|
33
33
|
"lint": "eslint --cache --cache-strategy=content --cache-location=.cache/eslint/ --ignore-path=.gitignore --ext=js .",
|
|
34
34
|
"test": "peter tests",
|
|
35
|
-
"prepack": "node npm-hooks/prepack",
|
|
36
35
|
"postpublish": "npm-hooks/postpublish",
|
|
37
36
|
"prepublishOnly": "npm-hooks/prepublish"
|
|
38
37
|
},
|
|
@@ -40,7 +39,7 @@
|
|
|
40
39
|
"blessed": "^0.1.81",
|
|
41
40
|
"blessed-contrib": "4.11.0",
|
|
42
41
|
"chalk": "^4.1.0",
|
|
43
|
-
"dcp-client": "^4.4.
|
|
42
|
+
"dcp-client": "^4.4.28",
|
|
44
43
|
"kvin": "^1.2.7",
|
|
45
44
|
"posix-getopt": "^1.2.1",
|
|
46
45
|
"semver": "^7.3.8",
|
package/npm-hooks/prepack
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
#! /usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* @file prepack
|
|
4
|
-
* Hook which generates the config md5 on pack (or publish)
|
|
5
|
-
* @author Wes Garland, wes@distributive.network
|
|
6
|
-
* @date April 2023, June 2023
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const fs = require('fs');
|
|
10
|
-
const process = require('process');
|
|
11
|
-
const path = require('path');
|
|
12
|
-
|
|
13
|
-
function md5(content)
|
|
14
|
-
{
|
|
15
|
-
return require('crypto').createHash('md5').update(content).digest('hex');
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
process.chdir(path.resolve(__dirname, '..'));
|
|
19
|
-
fs.writeFileSync('etc/dcp-worker-config.js.md5', ''
|
|
20
|
-
+ `${md5(fs.readFileSync('etc/dcp-worker-config.js'))}\n`
|
|
21
|
-
+ '### DO NOT MODIFY THIS FILE!!! ###\n');
|