Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion lib/God/ClusterMode.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
* @project PM2
*/
var cluster = require('cluster');
var path = require('path');
var Utility = require('../Utility.js');
var pkg = require('../../package.json');

Expand All @@ -34,9 +35,12 @@ module.exports = function ClusterMode(God) {
var clu = null;

console.log(`App [${env_copy.name}:${env_copy.pm_id}] starting in -cluster mode-`)
var initEnvScript = path.resolve(__dirname, '..', 'ProcessContainerClusterInitEnv.js');
var execArgv = ['--require', initEnvScript];
if (env_copy.node_args && Array.isArray(env_copy.node_args)) {
cluster.settings.execArgv = env_copy.node_args;
execArgv = execArgv.concat(env_copy.node_args);
}
cluster.settings.execArgv = execArgv;

env_copy._pm2_version = pkg.version;

Expand Down
16 changes: 16 additions & 0 deletions lib/ProcessContainerClusterInitEnv.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Copyright 2013-present the PM2 project authors. All rights reserved.
* Use of this source code is governed by a license that
* can be found in the LICENSE file.
*
* Expand the pm2_env JSON string into individual process.env entries.
* This must run before any user --require modules so they can access
* the configured environment variables.
*/

if (process.env.pm2_env) {
var pm2_env = JSON.parse(process.env.pm2_env);
for (var k in pm2_env) {
process.env[k] = pm2_env[k];
}
}
12 changes: 12 additions & 0 deletions test/fixtures/require-env-capture.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
var fs = require('fs');

// Capture the env var at --require time and write it to a result file.
// PM2_REQUIRE_RESULT_FILE tells us where to write, so cluster and fork
// tests can share this fixture without colliding.
var resultFile = process.env.PM2_REQUIRE_RESULT_FILE;
if (resultFile) {
fs.writeFileSync(
resultFile,
JSON.stringify({ value: process.env.PM2_TEST_REQUIRE_VAR || null })
);
}
87 changes: 87 additions & 0 deletions test/programmatic/require_cluster_env.mocha.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@

process.chdir(__dirname)

var fs = require('fs')
var path = require('path')
var PM2 = require('../..')
var should = require('should')

var CLUSTER_RESULT = path.join(__dirname, '..', 'require-env-cluster-result.json')
var FORK_RESULT = path.join(__dirname, '..', 'require-env-fork-result.json')
var REQUIRE_SCRIPT = path.resolve(__dirname, '../fixtures/require-env-capture.js')

// In cluster mode PM2 serialises env vars into a pm2_env JSON string and the
// entry wrapper (ProcessContainer.js) unpacks it. Node.js processes execArgv
// (including --require flags) *before* that wrapper runs, so without the fix a
// library loaded via --require would see the raw JSON blob rather than the
// individual env vars. ProcessContainerClusterInitEnv.js is prepended as the
// first --require so the env is expanded before any user --require runs.

describe('--require script env var visibility', function () {
this.timeout(15000)

before(function (done) {
try { fs.unlinkSync(CLUSTER_RESULT) } catch (e) {}
try { fs.unlinkSync(FORK_RESULT) } catch (e) {}
PM2.delete('all', function () { done() })
})

after(function (done) {
try { fs.unlinkSync(CLUSTER_RESULT) } catch (e) {}
try { fs.unlinkSync(FORK_RESULT) } catch (e) {}
PM2.kill(done)
})

afterEach(function (done) {
PM2.delete('all', done)
})

it('should expose PM2 env vars to --require scripts in cluster mode', function (done) {
PM2.start({
script: './../fixtures/empty.js',
name: 'test-require-env-cluster',
exec_mode: 'cluster',
instances: 1,
node_args: ['--require', REQUIRE_SCRIPT],
env: {
PM2_TEST_REQUIRE_VAR: 'cluster-env-value',
PM2_REQUIRE_RESULT_FILE: CLUSTER_RESULT
}
}, function (err) {
should(err).be.null()

setTimeout(function () {
var data = JSON.parse(fs.readFileSync(CLUSTER_RESULT, 'utf8'))
data.value.should.eql(
'cluster-env-value',
'PM2_TEST_REQUIRE_VAR should be visible in --require script running in cluster mode'
)
done()
}, 3000)
})
})

it('should expose PM2 env vars to --require scripts in fork mode', function (done) {
PM2.start({
script: './../fixtures/empty.js',
name: 'test-require-env-fork',
exec_mode: 'fork',
node_args: ['--require', REQUIRE_SCRIPT],
env: {
PM2_TEST_REQUIRE_VAR: 'fork-env-value',
PM2_REQUIRE_RESULT_FILE: FORK_RESULT
}
}, function (err) {
should(err).be.null()

setTimeout(function () {
var data = JSON.parse(fs.readFileSync(FORK_RESULT, 'utf8'))
data.value.should.eql(
'fork-env-value',
'PM2_TEST_REQUIRE_VAR should be visible in --require script running in fork mode'
)
done()
}, 3000)
})
})
})
1 change: 1 addition & 0 deletions test/unit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ runUnitTest $D/issue_5990_bun_substring_match.mocha.js
runUnitTest $D/issue_6089_prototype_pollution.mocha.js
runUnitTest $D/issue_6075_redos.mocha.js
runUnitTest $D/issue_6073_object_env.mocha.js
runUnitTest $D/require_cluster_env.mocha.js
runUnitTest $D/treekill.mocha.js
runUnitTest $D/http_interface.mocha.js

Expand Down