Skip to content

Commit dfc46e2

Browse files
mutantcornholioaddaleax
authored andcommitted
cluster: overriding inspector port
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 <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Colin Ihrig <[email protected]>
1 parent d651a01 commit dfc46e2

File tree

3 files changed

+251
-23
lines changed

3 files changed

+251
-23
lines changed

doc/api/cluster.md

+3
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,9 @@ changes:
746746
`'ipc'` entry. When this option is provided, it overrides `silent`.
747747
* `uid` {number} Sets the user identity of the process. (See setuid(2).)
748748
* `gid` {number} Sets the group identity of the process. (See setgid(2).)
749+
* `inspectPort` {number|function} Sets inspector port of worker.
750+
Accepts number, or function that evaluates to number. By default
751+
each worker gets port, incremented from master's `process.debugPort`.
749752

750753
After calling `.setupMaster()` (or `.fork()`) this settings object will contain
751754
the settings, including the default values.

lib/internal/cluster/master.js

+18-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const cluster = new EventEmitter();
1212
const intercom = new EventEmitter();
1313
const SCHED_NONE = 1;
1414
const SCHED_RR = 2;
15+
const {isLegalPort} = require('internal/net');
1516

1617
module.exports = cluster;
1718

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

106107
if (execArgv.some((arg) => arg.match(debugArgRegex))) {
107-
execArgv.push(`--inspect-port=${process.debugPort + debugPortOffset}`);
108-
debugPortOffset++;
108+
let inspectPort;
109+
if ('inspectPort' in cluster.settings) {
110+
if (typeof cluster.settings.inspectPort === 'function')
111+
inspectPort = cluster.settings.inspectPort();
112+
else
113+
inspectPort = cluster.settings.inspectPort;
114+
115+
if (!isLegalPort(inspectPort)) {
116+
throw new TypeError('cluster.settings.inspectPort' +
117+
' is invalid');
118+
}
119+
} else {
120+
inspectPort = process.debugPort + debugPortOffset;
121+
debugPortOffset++;
122+
}
123+
124+
execArgv.push(`--inspect-port=${inspectPort}`);
109125
}
110126

111127
return fork(cluster.settings.exec, cluster.settings.args, {

test/inspector/test-inspector-port-cluster.js

+230-21
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ let offset = 0;
1818
*/
1919

2020
function testRunnerMain() {
21-
spawnMaster({
21+
let defaultPortCase = spawnMaster({
2222
execArgv: ['--inspect'],
2323
workers: [{expectedPort: 9230}]
2424
});
@@ -77,42 +77,251 @@ function testRunnerMain() {
7777
workers: [{expectedPort: port + 1, expectedHost: '::1'}]
7878
});
7979
}
80-
}
8180

81+
// These tests check that setting inspectPort in cluster.settings
82+
// would take effect and override port incrementing behavior
83+
84+
port = debuggerPort + offset++ * 5;
85+
86+
spawnMaster({
87+
execArgv: [`--inspect=${port}`],
88+
clusterSettings: {inspectPort: port + 2},
89+
workers: [{expectedPort: port + 2}]
90+
});
91+
92+
port = debuggerPort + offset++ * 5;
93+
94+
spawnMaster({
95+
execArgv: [`--inspect=${port}`],
96+
clusterSettings: {inspectPort: 'addTwo'},
97+
workers: [
98+
{expectedPort: port + 2},
99+
{expectedPort: port + 4}
100+
]
101+
});
102+
103+
port = debuggerPort + offset++ * 5;
104+
105+
spawnMaster({
106+
execArgv: [`--inspect=${port}`],
107+
clusterSettings: {inspectPort: 'string'},
108+
workers: [{}]
109+
});
110+
111+
port = debuggerPort + offset++ * 5;
112+
113+
spawnMaster({
114+
execArgv: [`--inspect=${port}`],
115+
clusterSettings: {inspectPort: 'null'},
116+
workers: [{}]
117+
});
118+
119+
port = debuggerPort + offset++ * 5;
120+
121+
spawnMaster({
122+
execArgv: [`--inspect=${port}`],
123+
clusterSettings: {inspectPort: 'bignumber'},
124+
workers: [{}]
125+
});
126+
127+
port = debuggerPort + offset++ * 5;
128+
129+
spawnMaster({
130+
execArgv: [`--inspect=${port}`],
131+
clusterSettings: {inspectPort: 'negativenumber'},
132+
workers: [{}]
133+
});
134+
135+
port = debuggerPort + offset++ * 5;
136+
137+
spawnMaster({
138+
execArgv: [`--inspect=${port}`],
139+
clusterSettings: {inspectPort: 'bignumberfunc'},
140+
workers: [{}]
141+
});
142+
143+
port = debuggerPort + offset++ * 5;
144+
145+
spawnMaster({
146+
execArgv: [`--inspect=${port}`],
147+
clusterSettings: {inspectPort: 'strfunc'},
148+
workers: [{}]
149+
});
150+
151+
port = debuggerPort + offset++ * 5;
152+
153+
spawnMaster({
154+
execArgv: [],
155+
clusterSettings: {inspectPort: port, execArgv: ['--inspect']},
156+
workers: [
157+
{expectedPort: port}
158+
]
159+
});
160+
161+
port = debuggerPort + offset++ * 5;
162+
163+
spawnMaster({
164+
execArgv: [`--inspect=${port}`],
165+
clusterSettings: {inspectPort: 0},
166+
workers: [
167+
{expectedInitialPort: 0},
168+
{expectedInitialPort: 0},
169+
{expectedInitialPort: 0}
170+
]
171+
});
172+
173+
port = debuggerPort + offset++ * 5;
174+
175+
spawnMaster({
176+
execArgv: [],
177+
clusterSettings: {inspectPort: 0},
178+
workers: [
179+
{expectedInitialPort: 0},
180+
{expectedInitialPort: 0},
181+
{expectedInitialPort: 0}
182+
]
183+
});
184+
185+
defaultPortCase.then(() => {
186+
port = debuggerPort + offset++ * 5;
187+
defaultPortCase = spawnMaster({
188+
execArgv: ['--inspect'],
189+
clusterSettings: {inspectPort: port + 2},
190+
workers: [
191+
{expectedInitialPort: port + 2}
192+
]
193+
});
194+
});
195+
}
82196
function masterProcessMain() {
83197
const workers = JSON.parse(process.env.workers);
198+
const clusterSettings = JSON.parse(process.env.clusterSettings);
199+
let debugPort = process.debugPort;
84200

85201
for (const worker of workers) {
86-
cluster.fork({
87-
expectedPort: worker.expectedPort,
88-
expectedHost: worker.expectedHost
89-
}).on('exit', common.mustCall(checkExitCode));
202+
const params = {};
203+
204+
if (worker.expectedPort) {
205+
params.expectedPort = worker.expectedPort;
206+
}
207+
208+
if (worker.expectedInitialPort) {
209+
params.expectedInitialPort = worker.expectedInitialPort;
210+
}
211+
212+
if (worker.expectedHost) {
213+
params.expectedHost = worker.expectedHost;
214+
}
215+
216+
if (clusterSettings) {
217+
if (clusterSettings.inspectPort === 'addTwo') {
218+
clusterSettings.inspectPort = common.mustCall(
219+
() => { return debugPort += 2; },
220+
workers.length
221+
);
222+
} else if (clusterSettings.inspectPort === 'string') {
223+
clusterSettings.inspectPort = 'string';
224+
cluster.setupMaster(clusterSettings);
225+
226+
assert.throws(() => {
227+
cluster.fork(params).on('exit', common.mustCall(checkExitCode));
228+
}, TypeError);
229+
230+
return;
231+
} else if (clusterSettings.inspectPort === 'null') {
232+
clusterSettings.inspectPort = null;
233+
cluster.setupMaster(clusterSettings);
234+
235+
assert.throws(() => {
236+
cluster.fork(params).on('exit', common.mustCall(checkExitCode));
237+
}, TypeError);
238+
239+
return;
240+
} else if (clusterSettings.inspectPort === 'bignumber') {
241+
clusterSettings.inspectPort = 1293812;
242+
cluster.setupMaster(clusterSettings);
243+
244+
assert.throws(() => {
245+
cluster.fork(params).on('exit', common.mustCall(checkExitCode));
246+
}, TypeError);
247+
248+
return;
249+
} else if (clusterSettings.inspectPort === 'negativenumber') {
250+
clusterSettings.inspectPort = -9776;
251+
cluster.setupMaster(clusterSettings);
252+
253+
assert.throws(() => {
254+
cluster.fork(params).on('exit', common.mustCall(checkExitCode));
255+
}, TypeError);
256+
257+
return;
258+
} else if (clusterSettings.inspectPort === 'bignumberfunc') {
259+
clusterSettings.inspectPort = common.mustCall(
260+
() => 123121,
261+
workers.length
262+
);
263+
264+
cluster.setupMaster(clusterSettings);
265+
266+
assert.throws(() => {
267+
cluster.fork(params).on('exit', common.mustCall(checkExitCode));
268+
}, TypeError);
269+
270+
return;
271+
} else if (clusterSettings.inspectPort === 'strfunc') {
272+
clusterSettings.inspectPort = common.mustCall(
273+
() => 'invalidPort',
274+
workers.length
275+
);
276+
277+
cluster.setupMaster(clusterSettings);
278+
279+
assert.throws(() => {
280+
cluster.fork(params).on('exit', common.mustCall(checkExitCode));
281+
}, TypeError);
282+
283+
return;
284+
}
285+
cluster.setupMaster(clusterSettings);
286+
}
287+
288+
cluster.fork(params).on('exit', common.mustCall(checkExitCode));
90289
}
91290
}
92291

93292
function workerProcessMain() {
94-
const {expectedPort, expectedHost} = process.env;
293+
const {expectedPort, expectedInitialPort, expectedHost} = process.env;
294+
const debugOptions = process.binding('config').debugOptions;
295+
296+
if ('expectedPort' in process.env) {
297+
assert.strictEqual(process.debugPort, +expectedPort);
298+
}
95299

96-
assert.strictEqual(process.debugPort, +expectedPort);
300+
if ('expectedInitialPort' in process.env) {
301+
assert.strictEqual(debugOptions.port, +expectedInitialPort);
302+
}
97303

98-
if (expectedHost !== 'undefined') {
99-
assert.strictEqual(
100-
process.binding('config').debugOptions.host,
101-
expectedHost
102-
);
304+
if ('expectedHost' in process.env) {
305+
assert.strictEqual(debugOptions.host, expectedHost);
103306
}
104307

105308
process.exit();
106309
}
107310

108-
function spawnMaster({execArgv, workers}) {
109-
childProcess.fork(__filename, {
110-
env: {
111-
workers: JSON.stringify(workers),
112-
testProcess: true
113-
},
114-
execArgv
115-
}).on('exit', common.mustCall(checkExitCode));
311+
function spawnMaster({execArgv, workers, clusterSettings = {}}) {
312+
return new Promise((resolve) => {
313+
childProcess.fork(__filename, {
314+
env: {
315+
workers: JSON.stringify(workers),
316+
clusterSettings: JSON.stringify(clusterSettings),
317+
testProcess: true
318+
},
319+
execArgv
320+
}).on('exit', common.mustCall((code, signal) => {
321+
checkExitCode(code, signal);
322+
resolve();
323+
}));
324+
});
116325
}
117326

118327
function checkExitCode(code, signal) {

0 commit comments

Comments
 (0)