Skip to content

Commit

Permalink
cluster: overriding inspector port
Browse files Browse the repository at this point in the history
Added an option to override inspector port for workers using
`settings.inspectPort` will override default port incrementing behavior.
Also, using this option allows to set 0 port for the whole cluster.

PR-URL: #14140
Fixes: #8495
Fixes: #12941
Refs: #9659
Refs: #13761
Reviewed-By: Refael Ackermann <refack@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
  • Loading branch information
mutantcornholio authored and addaleax committed Jul 18, 2017
1 parent d651a01 commit dfc46e2
Show file tree
Hide file tree
Showing 3 changed files with 251 additions and 23 deletions.
3 changes: 3 additions & 0 deletions doc/api/cluster.md
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,9 @@ changes:
`'ipc'` entry. When this option is provided, it overrides `silent`.
* `uid` {number} Sets the user identity of the process. (See setuid(2).)
* `gid` {number} Sets the group identity of the process. (See setgid(2).)
* `inspectPort` {number|function} Sets inspector port of worker.
Accepts number, or function that evaluates to number. By default
each worker gets port, incremented from master's `process.debugPort`.

After calling `.setupMaster()` (or `.fork()`) this settings object will contain
the settings, including the default values.
Expand Down
20 changes: 18 additions & 2 deletions lib/internal/cluster/master.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const cluster = new EventEmitter();
const intercom = new EventEmitter();
const SCHED_NONE = 1;
const SCHED_RR = 2;
const {isLegalPort} = require('internal/net');

module.exports = cluster;

Expand Down Expand Up @@ -104,8 +105,23 @@ function createWorkerProcess(id, env) {
workerEnv.NODE_UNIQUE_ID = '' + id;

if (execArgv.some((arg) => arg.match(debugArgRegex))) {
execArgv.push(`--inspect-port=${process.debugPort + debugPortOffset}`);
debugPortOffset++;
let inspectPort;
if ('inspectPort' in cluster.settings) {
if (typeof cluster.settings.inspectPort === 'function')
inspectPort = cluster.settings.inspectPort();
else
inspectPort = cluster.settings.inspectPort;

if (!isLegalPort(inspectPort)) {
throw new TypeError('cluster.settings.inspectPort' +
' is invalid');
}
} else {
inspectPort = process.debugPort + debugPortOffset;
debugPortOffset++;
}

execArgv.push(`--inspect-port=${inspectPort}`);
}

return fork(cluster.settings.exec, cluster.settings.args, {
Expand Down
251 changes: 230 additions & 21 deletions test/inspector/test-inspector-port-cluster.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ let offset = 0;
*/

function testRunnerMain() {
spawnMaster({
let defaultPortCase = spawnMaster({
execArgv: ['--inspect'],
workers: [{expectedPort: 9230}]
});
Expand Down Expand Up @@ -77,42 +77,251 @@ function testRunnerMain() {
workers: [{expectedPort: port + 1, expectedHost: '::1'}]
});
}
}

// These tests check that setting inspectPort in cluster.settings
// would take effect and override port incrementing behavior

port = debuggerPort + offset++ * 5;

spawnMaster({
execArgv: [`--inspect=${port}`],
clusterSettings: {inspectPort: port + 2},
workers: [{expectedPort: port + 2}]
});

port = debuggerPort + offset++ * 5;

spawnMaster({
execArgv: [`--inspect=${port}`],
clusterSettings: {inspectPort: 'addTwo'},
workers: [
{expectedPort: port + 2},
{expectedPort: port + 4}
]
});

port = debuggerPort + offset++ * 5;

spawnMaster({
execArgv: [`--inspect=${port}`],
clusterSettings: {inspectPort: 'string'},
workers: [{}]
});

port = debuggerPort + offset++ * 5;

spawnMaster({
execArgv: [`--inspect=${port}`],
clusterSettings: {inspectPort: 'null'},
workers: [{}]
});

port = debuggerPort + offset++ * 5;

spawnMaster({
execArgv: [`--inspect=${port}`],
clusterSettings: {inspectPort: 'bignumber'},
workers: [{}]
});

port = debuggerPort + offset++ * 5;

spawnMaster({
execArgv: [`--inspect=${port}`],
clusterSettings: {inspectPort: 'negativenumber'},
workers: [{}]
});

port = debuggerPort + offset++ * 5;

spawnMaster({
execArgv: [`--inspect=${port}`],
clusterSettings: {inspectPort: 'bignumberfunc'},
workers: [{}]
});

port = debuggerPort + offset++ * 5;

spawnMaster({
execArgv: [`--inspect=${port}`],
clusterSettings: {inspectPort: 'strfunc'},
workers: [{}]
});

port = debuggerPort + offset++ * 5;

spawnMaster({
execArgv: [],
clusterSettings: {inspectPort: port, execArgv: ['--inspect']},
workers: [
{expectedPort: port}
]
});

port = debuggerPort + offset++ * 5;

spawnMaster({
execArgv: [`--inspect=${port}`],
clusterSettings: {inspectPort: 0},
workers: [
{expectedInitialPort: 0},
{expectedInitialPort: 0},
{expectedInitialPort: 0}
]
});

port = debuggerPort + offset++ * 5;

spawnMaster({
execArgv: [],
clusterSettings: {inspectPort: 0},
workers: [
{expectedInitialPort: 0},
{expectedInitialPort: 0},
{expectedInitialPort: 0}
]
});

defaultPortCase.then(() => {
port = debuggerPort + offset++ * 5;
defaultPortCase = spawnMaster({
execArgv: ['--inspect'],
clusterSettings: {inspectPort: port + 2},
workers: [
{expectedInitialPort: port + 2}
]
});
});
}
function masterProcessMain() {
const workers = JSON.parse(process.env.workers);
const clusterSettings = JSON.parse(process.env.clusterSettings);
let debugPort = process.debugPort;

for (const worker of workers) {
cluster.fork({
expectedPort: worker.expectedPort,
expectedHost: worker.expectedHost
}).on('exit', common.mustCall(checkExitCode));
const params = {};

if (worker.expectedPort) {
params.expectedPort = worker.expectedPort;
}

if (worker.expectedInitialPort) {
params.expectedInitialPort = worker.expectedInitialPort;
}

if (worker.expectedHost) {
params.expectedHost = worker.expectedHost;
}

if (clusterSettings) {
if (clusterSettings.inspectPort === 'addTwo') {
clusterSettings.inspectPort = common.mustCall(
() => { return debugPort += 2; },
workers.length
);
} else if (clusterSettings.inspectPort === 'string') {
clusterSettings.inspectPort = 'string';
cluster.setupMaster(clusterSettings);

assert.throws(() => {
cluster.fork(params).on('exit', common.mustCall(checkExitCode));
}, TypeError);

return;
} else if (clusterSettings.inspectPort === 'null') {
clusterSettings.inspectPort = null;
cluster.setupMaster(clusterSettings);

assert.throws(() => {
cluster.fork(params).on('exit', common.mustCall(checkExitCode));
}, TypeError);

return;
} else if (clusterSettings.inspectPort === 'bignumber') {
clusterSettings.inspectPort = 1293812;
cluster.setupMaster(clusterSettings);

assert.throws(() => {
cluster.fork(params).on('exit', common.mustCall(checkExitCode));
}, TypeError);

return;
} else if (clusterSettings.inspectPort === 'negativenumber') {
clusterSettings.inspectPort = -9776;
cluster.setupMaster(clusterSettings);

assert.throws(() => {
cluster.fork(params).on('exit', common.mustCall(checkExitCode));
}, TypeError);

return;
} else if (clusterSettings.inspectPort === 'bignumberfunc') {
clusterSettings.inspectPort = common.mustCall(
() => 123121,
workers.length
);

cluster.setupMaster(clusterSettings);

assert.throws(() => {
cluster.fork(params).on('exit', common.mustCall(checkExitCode));
}, TypeError);

return;
} else if (clusterSettings.inspectPort === 'strfunc') {
clusterSettings.inspectPort = common.mustCall(
() => 'invalidPort',
workers.length
);

cluster.setupMaster(clusterSettings);

assert.throws(() => {
cluster.fork(params).on('exit', common.mustCall(checkExitCode));
}, TypeError);

return;
}
cluster.setupMaster(clusterSettings);
}

cluster.fork(params).on('exit', common.mustCall(checkExitCode));
}
}

function workerProcessMain() {
const {expectedPort, expectedHost} = process.env;
const {expectedPort, expectedInitialPort, expectedHost} = process.env;
const debugOptions = process.binding('config').debugOptions;

if ('expectedPort' in process.env) {
assert.strictEqual(process.debugPort, +expectedPort);
}

assert.strictEqual(process.debugPort, +expectedPort);
if ('expectedInitialPort' in process.env) {
assert.strictEqual(debugOptions.port, +expectedInitialPort);
}

if (expectedHost !== 'undefined') {
assert.strictEqual(
process.binding('config').debugOptions.host,
expectedHost
);
if ('expectedHost' in process.env) {
assert.strictEqual(debugOptions.host, expectedHost);
}

process.exit();
}

function spawnMaster({execArgv, workers}) {
childProcess.fork(__filename, {
env: {
workers: JSON.stringify(workers),
testProcess: true
},
execArgv
}).on('exit', common.mustCall(checkExitCode));
function spawnMaster({execArgv, workers, clusterSettings = {}}) {
return new Promise((resolve) => {
childProcess.fork(__filename, {
env: {
workers: JSON.stringify(workers),
clusterSettings: JSON.stringify(clusterSettings),
testProcess: true
},
execArgv
}).on('exit', common.mustCall((code, signal) => {
checkExitCode(code, signal);
resolve();
}));
});
}

function checkExitCode(code, signal) {
Expand Down

0 comments on commit dfc46e2

Please sign in to comment.