dcp-worker 3.2.24 → 3.2.26

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.
@@ -0,0 +1,36 @@
1
+ # @file CODEOWNERS - Defines who develops and maintains a feature, and own
2
+ # the resulting files or directories in the repository.
3
+ #
4
+ # See https://docs.gitlab.com/ee/user/project/code_owners.html
5
+ #
6
+ # @author Bryan Hoang <bryan@distributive.network>
7
+ # @date August 2022
8
+
9
+ # Username mapping:
10
+ # @wesgarland -> Wes Garland <wes@distributive.network>
11
+
12
+
13
+ []
14
+
15
+ [Brandon]
16
+
17
+ [Eddie]
18
+ /package-lock.json @eroosenmaallen
19
+
20
+ [Wes]
21
+ /README.md @wesgarland
22
+ /docs/ @wesgarland
23
+ /.eslintrc.json @wesgarland
24
+ /.tidelift @wesgarland
25
+ /lib/ @wesgarland
26
+ /LICENSE.md @wesgarland
27
+ /.gitignore @wesgarland
28
+ /etc/ @wesgarland
29
+ /.gitlab-ci.yml @wesgarland
30
+ /bin/ @wesgarland
31
+ /etc/ @wesgarland
32
+ /lib/ @wesgarland
33
+ /bin/ @wesgarland
34
+
35
+ [Wes or Eddie]
36
+ /package.json @wesgarland @eroosenmaallen
@@ -0,0 +1,10 @@
1
+ # @file CODEOWNERS - Defines who develops and maintains a feature, and own
2
+ # the resulting files or directories in the repository.
3
+ #
4
+ # See https://docs.gitlab.com/ee/user/project/code_owners.html
5
+ #
6
+ # @author Bryan Hoang <bryan@distributive.network>
7
+ # @date August 2022
8
+
9
+ # Username mapping:
10
+ # @wesgarland -> Wes Garland <wes@distributive.network>
@@ -0,0 +1,65 @@
1
+ /**
2
+ * @file dcp-worker-config.js
3
+ * Default configuration for the standalone DCP Worker package.
4
+ * Copy this file before modifying, so that changes are preserved
5
+ * during the upgrade cycle. Suggested locations:
6
+ * - /etc/dcp/dcp-worker/dcp-config.js, or
7
+ * - ~/.dcp/dcp-worker/dcp-config.js.
8
+ *
9
+ * Those files have a higher precedence than the configuration
10
+ * that ships with the npm package; changes made in those files
11
+ * will be merged into the running configuration, overriding the
12
+ * defaults specified here.
13
+ *
14
+ * Windows users can also affect these changes by adding entries
15
+ * to the registry. This is the preferred method for enterprise
16
+ * deployment.
17
+ *
18
+ * @author Wes Garland
19
+ * @date Feb 2021
20
+ */
21
+ {
22
+ worker: {
23
+ trustComputeGroupOrigins: true, /* Trust the scheduler to modify allowOrigins via Compute Group configuration */
24
+
25
+ /* Allow lists permitting supervisor network access beyond DCP messages to services */
26
+ allowOrigins: {
27
+ fetchWorkFunctions: [ dcpConfig.scheduler.location.origin ],
28
+ fetchArguments: [ dcpConfig.scheduler.location.origin ],
29
+ fetchData: [ dcpConfig.scheduler.location.origin ],
30
+ sendResults: [ dcpConfig.scheduler.location.origin ],
31
+ any: [],
32
+ },
33
+
34
+ /* Vector describing the lowest-value work this worker will accept. */
35
+ minimumWage: {
36
+ 'CPU': 0, /* DCC per second of CPU time */
37
+ 'GPU': 0, /* DCC per second of GPU time */
38
+ 'in': 0, /* DCC per byte of inbound network traffic */
39
+ 'out': 0, /* DCC per byte of outbound network traffic */
40
+ },
41
+
42
+ /* Extra Compute Groups this worker can participate in. Join credentials are supplied by
43
+ * Distributive and/or local IT staff at site-licensed locations.
44
+ */
45
+ computeGroups: [
46
+ // { joinKey: 'scott', joinSecret: 'tiger' },
47
+ // { joinKey: 'scott', joinHash: 'eh1-672937c2b944982e071185b888770f8b8ea67c11f56d545e403e0d513c609b87' },
48
+ // keystore('~/.dcp/scott'),
49
+ ],
50
+
51
+ jobAddresses: [], /* If specified, restrict the worker to only these jobs */
52
+ paymentAddress: undefined, /* Bank account where earned funds are transfered if not specified on command-line */
53
+ },
54
+
55
+ /* The evaluator is a secure environment for creating DCP Worker sandboxes, used when the Worker
56
+ * is running in Node.js. This configuration specifies where this worker's evaluator daemon is
57
+ * listening. Killing the evaluator stops all work from happening on this worker; the worker
58
+ * will run in the background waiting for it to re-launch when this happens.
59
+ */
60
+ evaluator: {
61
+ listen: new URL('dcpsaw://localhost:9000/'),
62
+ },
63
+
64
+ cookie: require('process').env.DCP_CONFIG_COOKIE, /* used to verify that configuration file was actually loaded */
65
+ }
@@ -0,0 +1,2 @@
1
+ ee17a8c00be52fbefd8c642d312317e7 etc/dcp-worker-config.js
2
+ ### DO NOT MODIFY THIS FILE!!! ###
package/lib/pidfile.js ADDED
@@ -0,0 +1,96 @@
1
+ /**
2
+ * @file pidfile.js
3
+ * Library code to record/remove pid files
4
+ *
5
+ * @author Robert Mirandola, robert@distributive.network
6
+ * @author Wes Garland, wes@distributive.network
7
+ * @date April 2023
8
+ */
9
+ 'use strict';
10
+
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+
14
+ /**
15
+ * Write a pid file for the current process. This file contains only the process' pid. Before the file
16
+ * is written, we to check to see if there is already a file there. If there is we warn, and -- if there
17
+ * is a process running with that pid -- we exit the process after briefly blocking the event loop.
18
+ *
19
+ * As the pid file is created, we register a cleanup which removes the file when the process exits. The
20
+ * cleanup depends on the process<dcpExit> event fired by dcp-client during process tear-down.
21
+ *
22
+ * @param {string} filename the location of the pid file
23
+ */
24
+ exports.write = async function pidfile$$write(filename)
25
+ {
26
+ var fd;
27
+
28
+ if (fs.existsSync(filename))
29
+ {
30
+ console.warn(`Warning: found pidfile at ${filename}`);
31
+ let oldPid = parseInt(fs.readFileSync(filename, 'utf8'), 10);
32
+
33
+ try
34
+ {
35
+ if (oldPid)
36
+ process.kill(oldPid, 0);
37
+ }
38
+ catch(error)
39
+ {
40
+ if (error.code === 'ESRCH') /* no such process */
41
+ {
42
+ oldPid = 0;
43
+ removePidFile();
44
+ }
45
+ }
46
+
47
+ if (oldPid)
48
+ {
49
+ console.error(`Process at PID ${oldPid} is still running; cannot start new process with pidfile ${filename}`);
50
+ require('dcp/utils').sleep(3); /* put the brakes on a respawn loop */
51
+ process.exit(1);
52
+ }
53
+ }
54
+
55
+ try
56
+ {
57
+ fd = fs.openSync(filename, 'wx');
58
+ fs.writeSync(fd, Buffer.from(process.pid + '\n'), 0);
59
+ process.on('dcpExit', removePidFile); // Cleanup PID file on exit
60
+ }
61
+ catch (error)
62
+ {
63
+ console.warn(`Warning: Could not create pidfile at ${filename} (${error.code || error.message})`);
64
+ removePidFile();
65
+ }
66
+
67
+ function removePidFile()
68
+ {
69
+ try
70
+ {
71
+ fs.unlinkSync(filename);
72
+ if (typeof fd === 'number')
73
+ fs.closeSync(fd);
74
+ }
75
+ catch (error)
76
+ {
77
+ console.warn(`Warning: Could not remove pidfile at ${filename} (${error.code})`);
78
+ }
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Generate a default pidfile name for the current process
84
+ *
85
+ * @param {string} basename optional, name of current process
86
+ * @returns string which contains an absolute path
87
+ */
88
+ exports.getDefaultPidFileName = function getDefaultPidFileName(basename)
89
+ {
90
+ var defaultPidFileName = basename || path.basename(require.main.filename, '.js') + '.pid';
91
+
92
+ if (!path.isAbsolute(defaultPidFileName))
93
+ defaultPidFileName = path.resolve(__dirname, '..', 'run', defaultPidFileName);
94
+
95
+ return defaultPidFileName;
96
+ }
@@ -8,7 +8,7 @@
8
8
  * troubleshooting, as any attacker on the network will have the same OS-level
9
9
  * priviledges as the DCP Service Worker.
10
10
  *
11
- * To enable this feature, create a file in ../etc name enabled-debug-console. That
11
+ * To enable this feature, create a file in ../etc named enable-debug-console. That
12
12
  * file should contain a number which identities the port number this service will
13
13
  * listen on.
14
14
  *
@@ -29,6 +29,9 @@ var dcpConfig;
29
29
  var mainEval;
30
30
  var ci;
31
31
 
32
+ require('dcp-client'); /* plumb in modules from bundle even if library has not been initialized */
33
+ const debugging = require('dcp/internal/debugging').scope('dcp-worker');
34
+
32
35
  function daemonEval()
33
36
  {
34
37
  try {
@@ -43,7 +46,7 @@ function daemonEval()
43
46
  }
44
47
 
45
48
  function callbackTelnet(port, client, registry) {
46
- console.log(' ! telnetd - listening on port', port);
49
+ debugging() && console.debug(' ! telnetd - listening on port', port);
47
50
  }
48
51
 
49
52
  /**
@@ -55,42 +58,33 @@ exports.setMainEval = function removeConsole$$setMainEval()
55
58
  mainEval = arguments[0];
56
59
  }
57
60
 
58
- exports.init = function remoteConsole$$init(port, ...commands)
61
+ exports.init = function remoteConsole$$init(...commands)
59
62
  {
60
63
  try
61
64
  {
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;
65
+ /* invoked before main */
66
+ const edcFilename = path.resolve(__dirname, '..', 'etc', 'enable-debug-console');
67
+ if (!fs.existsSync(edcFilename))
68
+ return;
69
+ const edc = fs.readFileSync(edcFilename, 'ascii').trim();
70
+ if (edc === 'false')
71
+ return;
72
+ const port = parseInt(edc, 10);
73
+ if (!port && port !== 0)
74
+ throw new Error('invalid port: ' + edc);
75
+
76
+ console.warn('*** Enabling telnet daemon on port', port, '(security risk) ***');
77
+
78
+ ci = require('telnet-console').start({
79
+ port,
80
+ callbackTelnet,
81
+ eval: daemonEval,
82
+ histfile: edcFilename + '.history',
83
+ }, ...commands);
70
84
 
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
- }
85
+ exports.init = () => {
86
+ throw new Error('remote console has already been initialized');
87
+ };
94
88
  }
95
89
  catch(e)
96
90
  {
@@ -41,8 +41,6 @@ const workerEvents = {
41
41
  submit: 'onSubmit',
42
42
  submitError: 'onSubmitError',
43
43
  payment: 'onPayment',
44
- error: 'onError',
45
- warning: 'onWarning',
46
44
  }
47
45
  const supervisorEvents = {
48
46
  submittingResult: 'onSubmitStart',
@@ -5,8 +5,6 @@
5
5
  * @property {function} onFetchingSlices
6
6
  * @property {function} onFetchedSlices
7
7
  * @property {function} onFetchSlicesFailed
8
- * @property {function} onError
9
- * @property {function} onWarning
10
8
  * @property {SandboxCallback} sandbox$onSliceStart
11
9
  * @property {SandboxCallback} sandbox$onSliceProgress
12
10
  * @property {SandboxCallback} sandbox$onSliceFinish
@@ -23,4 +21,4 @@
23
21
  * @typedef {function} SandboxCallback
24
22
  * @param {Sandbox} sandbox
25
23
  * @param {object} workerData
26
- */
24
+ */
@@ -80,16 +80,6 @@ const consoleLogger = {
80
80
  onSubmitError(ev) {
81
81
  console.log(" ! Failed to submit results:", ev);
82
82
  },
83
-
84
- onError(ev)
85
- {
86
- this.options.verbose && console.error(" ! Worker Error:", ev);
87
- },
88
-
89
- onWarning(ev)
90
- {
91
- this.options.verbose && console.warn(" ! Worker Warning:", ev);
92
- }
93
83
  };
94
84
 
95
85
  Object.assign(exports, consoleLogger);
@@ -35,8 +35,6 @@ const dashboardLogger = {
35
35
  const grid = new contrib.grid({rows: 3, cols: 5, screen: this.screen});
36
36
 
37
37
  const log = grid.set(0, 2, 2, 3, components.log, {
38
- fg: 'green',
39
- selectedFg: 'green',
40
38
  label: 'Worker Log',
41
39
  scrollable: true,
42
40
  alwaysScroll: true,
@@ -55,6 +53,7 @@ const dashboardLogger = {
55
53
  }
56
54
  log.log.apply(log, arguments);
57
55
  }
56
+ require('../remote-console').reintercept();
58
57
 
59
58
  this.sandboxes = grid.set(0, 0, 2, 2, components.sandboxes, {
60
59
  label: 'Sandboxes',
@@ -70,18 +69,27 @@ const dashboardLogger = {
70
69
  this.workerInfo = grid.set(2, 0, 1, 5, blessed.text);
71
70
  this.updateWorkerInfo();
72
71
 
73
- setInterval(() => this.screen.render(), 1000);
72
+ setInterval(() => this.screen.render(), 1000).unref();
74
73
 
75
- this.screen.key(['escape', 'C-c'], () => {
76
- console.log('076: Exit requested...');
77
- if (typeof options.exitGuard?.exit === 'function')
78
- options.exitGuard.exit(0);
79
- else
80
- process.exit(0);
74
+ function raise(sig)
75
+ {
76
+ process.kill(process.pid, sig);
77
+ }
78
+
79
+ /* Apply key bindings which mimic canonical input mode */
80
+ this.screen.key(['C-c'], () => raise('SIGINT'));
81
+ this.screen.key(['C-z'], () => raise('SIGTSTP'));
82
+ this.screen.key(['C-\\'], () => raise('SIGQUIT'));
83
+
84
+ this.screen.key(['escape'], () => {
85
+ console.log('Stopping worker...');
86
+ worker.stop();
81
87
  });
82
88
  },
83
89
 
84
90
  updateWorkerInfo() {
91
+ const workerOptions = dcpConfig.worker;
92
+
85
93
  this.workerInfo.setLabel(`Worker Status [${this.sliceFetchStatus}]`);
86
94
  this.workerInfo.setContent([
87
95
  chalk.green(` DCCs Earned: ${chalk.bold(this.totalDCCs.toFixed(7))}`),
@@ -90,6 +98,9 @@ const dashboardLogger = {
90
98
  ` Bank: ${chalk.yellow(dcpConfig.bank.location.href)}`,
91
99
  `Bank Account: ${chalk.yellow(this.supervisor.paymentAddress || 'Starting...')}`,
92
100
  ` Identity: ${chalk.yellow(this.supervisor.identityKeystore? this.supervisor.identityKeystore.address : 'Starting...')}`,
101
+ ` Jobs: ${workerOptions.jobAddresses?.length ? workerOptions.jobAddresses.join(', ') : '<any>'}`,
102
+ ` Priv Groups: ${Object.keys(workerOptions.computeGroups).length}`,
103
+ ` Pub Group: ${workerOptions.leavePublicGroup ? 'no' : 'yes'}`,
93
104
  ].join('\n'));
94
105
  },
95
106
 
@@ -118,7 +129,7 @@ const dashboardLogger = {
118
129
  sandboxData.progressData.progress = 0;
119
130
  this.sandboxes.update();
120
131
  }
121
- }, 500);
132
+ }, 500).unref();
122
133
  } else {
123
134
  sandboxData.progressData.progress = ev.progress;
124
135
  sandboxData.progressData.indeterminate = false;
@@ -173,14 +184,6 @@ const dashboardLogger = {
173
184
  this.sliceFetchStatus = SLICE_FETCH_STATUS.NO_WORK;
174
185
  this.updateWorkerInfo();
175
186
  },
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
- },
184
187
  };
185
188
 
186
189
  Object.assign(exports, dashboardLogger);
@@ -0,0 +1,11 @@
1
+ #! /bin/bash
2
+ #
3
+ # @file post-publish
4
+ # Hook which tags the package in git with the package version
5
+ # as it gets published.
6
+ # @author Wes Garland, wes@distributive.network
7
+ # @date Nov 2022
8
+ #
9
+ cd `dirname "$0"`/..
10
+ PACKAGE_VERSION=$(cat package.json | egrep '^\s*"version":' | head -1 | awk -F: '{ print $2 }' | sed 's/[\",]//g' | tr -d '[[:space:]]')
11
+ git tag v$PACKAGE_VERSION && git push --tags
@@ -0,0 +1,17 @@
1
+ #! /bin/bash
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
7
+ #
8
+ set -e
9
+ set -u
10
+ set -o pipefail
11
+
12
+ cd `dirname "$0"`/..
13
+
14
+ (
15
+ md5sum etc/dcp-worker-config.js
16
+ echo "### DO NOT MODIFY THIS FILE!!! ###"
17
+ ) > etc/dcp-worker-config.js.md5
@@ -0,0 +1,42 @@
1
+ #! /bin/bash
2
+ #
3
+ # @file pre-publish
4
+ # Hook which prevents package publishing if the repository
5
+ # isn't in a nice, clean state.
6
+ # @author Wes Garland, wes@distributive.network
7
+ # @date Nov 2022
8
+ #
9
+ cd `dirname "$0"`/..
10
+
11
+ yellow='\e[33m'
12
+ normal='\e[0m'
13
+
14
+ git ls-files --error-unmatch `find . -type f | egrep -v '^\./(node_modules|\.git)'` >/dev/null
15
+ if [ "$?" != "0" ]; then
16
+ echo
17
+ echo -e "${yellow}pre-publish: abort due to files in package which aren't in repo${normal}" >/dev/stderr
18
+ echo
19
+ exit 1
20
+ fi
21
+
22
+ (git status --porcelain; echo DONE DONE) \
23
+ | while read status filename
24
+ do
25
+ case "$status" in
26
+ "M")
27
+ echo "${filename} is not checked in"
28
+ EXITCODE=1
29
+ ;;
30
+ "DONE")
31
+ exit $EXITCODE
32
+ ;;
33
+ *)
34
+ ;;
35
+ esac
36
+ done
37
+ if [ "$?" != "0" ]; then
38
+ echo
39
+ echo -e "${yellow}pre-publish: abort due to modified files${normal}" >/dev/stderr
40
+ echo
41
+ exit 1
42
+ fi
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dcp-worker",
3
- "version": "3.2.24",
3
+ "version": "3.2.26",
4
4
  "description": "JavaScript portion of DCP Workers for Node.js",
5
5
  "main": "bin/dcp-worker",
6
6
  "keywords": [
@@ -29,14 +29,16 @@
29
29
  "scripts": {
30
30
  "start": "node bin/dcp-worker start",
31
31
  "start-evaluator": "node bin/dcp-evaluator-start",
32
- "test": "echo \"Error: no test specified\" && exit 1"
32
+ "test": "peter tests",
33
+ "x-prepack": "npm-hooks/prepack",
34
+ "postpublish": "npm-hooks/postpublish",
35
+ "prepublishOnly": "npm-hooks/prepublish"
33
36
  },
34
37
  "dependencies": {
35
38
  "blessed": "^0.1.81",
36
39
  "blessed-contrib": "^4.11.0",
37
- "bravojs": "^1.0.16",
38
40
  "chalk": "^4.1.0",
39
- "dcp-client": "^4.2.27",
41
+ "dcp-client": "^4.2.28",
40
42
  "semver": "^7.3.8"
41
43
  },
42
44
  "optionalDependencies": {
package/etc/dcp-config.js DELETED
@@ -1,51 +0,0 @@
1
- /**
2
- * @file dcp-config.js
3
- * Configuration values for the standalone Worker package.
4
- * These values have a higher precedence than the scheduler-provided values,
5
- * but can be overridden via the usual dcp-config semantics -- .dcp, /etc,
6
- * Windows Registry, etc. See dcp-client library documentation for details.
7
- * Specify DCP_CONFIG in the environment to use an alternate module.
8
- *
9
- * @author Wes Garland
10
- * @date Feb 2021
11
- */
12
-
13
- const dcpConfig =
14
- {
15
- worker: { /******* Note: all properties passed to web; no filtering! ********/
16
-
17
- trustComputeGroupOrigins: true, // trust the scheduler to give network access to origins on a per-compute-group basis
18
-
19
- /* Allow lists permitting supervisor network access beyond DCP messages to services */
20
- allowOrigins: {
21
- any: [],
22
- fetchWorkFunctions: [],
23
- fetchArguments: [],
24
- fetchData: [],
25
- sendResults: [],
26
- },
27
-
28
- minimumWage: {
29
- CPU: 0,
30
- GPU: 0,
31
- 'in': 0,
32
- out: 0,
33
- },
34
-
35
- computeGroups: {}, // integer-one-indexed; format is 1:{ joinKey,joinHash } or 2:{ joinKey, joinSecret }
36
- jobAddresses: [], // Specific job addresses the worker may work on. If not empty, worker will only work on those jobs.
37
- maxWorkingSandboxes: 1,
38
- paymentAddress: null, // user must to specify
39
- evaluatorOptions: {} /** @todo: add default options here. Note: right now they will be a bit off until we get localexec's evaluator a bit less special cased. */
40
- },
41
- evaluator:
42
- {
43
- listen: new URL('dcpsaw://localhost:9000/'),
44
- libDir: '../libexec/evaluator', // relative to prefix
45
- },
46
- }
47
-
48
- if (!dcpConfig.evaluator.location)
49
- dcpConfig.evaluator.location = new URL(dcpConfig.evaluator.listen.href);
50
-
51
- Object.assign(exports, dcpConfig);