created: 2020-02-14T02:26:41.000Z
nodejsで標準入出力/エラーを共有した子プロセスを作る
cloudrunを使う際に必要になった
cloudrunはhttpエンドポイントから処理がキックされるので、コンテナでbashコマンドを実行したいときはnodejsのhttpサーバの子プロセスがスクリプトを実行するという形になる(つかいにくい)
子プロセスの出力をログとしてstackdriverに流したいので、nodejsで標準入出力/エラーを共有した子プロセスを作る必要があった
コード
こんなコードを書いて動作確認
$ node index.js out
などで親プロセスを起動- サブコマンドごとに子プロセスが以下の動作
- 標準出力を吐いて終了
- エラー出力を吐いて終了
- 異常終了
const { spawn } = require('child_process');
const pSpawn = (command, args, options) => new Promise((resolve, reject) => {
const childProcess = spawn(command, args, options);
childProcess.on('exit', code => code ? reject(code) : resolve(code));
});
(async () => {
const subcommand = process.argv[2];
const promise = pSpawn('./entrypoint.sh', [subcommand], {
env: process.env,
cwd: __dirname,
stdio: 'inherit',
});
let exitCode;
try {
exitCode = await promise
} catch (error) {
exitCode = error;
}
console.log(`exitCode: ${exitCode}`);
})();
entrypoint.sh
#!/bin/bash
set -eu
scase "$1" in
out)
shift
echo "one!"
;;
err)
shift
perl -E 'warn "two"'
;;
die)
shift
exit 77
;;
esac
動作
期待通りに動作した
- stdoutはstdoutに
- stderrはstderrに
- exitcodeが0でないときはrejectされた
$ node index.js out
one!
exitCode: 0
# エラー出力を確認する
$ node index.js err 1>/dev/null
two at -e line 1.
$ node index.js die
exitCode: 77
以下は実装時のメモ
spawnのstdio引数
pSpawn('./entrypoint.sh', [subcommand], {
env: process.env,
cwd: __dirname,
stdio: 'inherit',
});
このstdio
引数で標準出力/エラー出力を制御している
stdio: 'inherit'
で今回やりたい動作にできる
'inherit': Pass through the corresponding stdio stream to/from the parent process. In the first three positions, this is equivalent to process.stdin, process.stdout, and process.stderr, respectively. In any other position, equivalent to 'ignore'.
pSpawn
const pSpawn = (command, args, options) => new Promise((resolve, reject) => {
const childProcess = spawn(command, args, options);
childProcess.on('exit', code => code ? reject(code) : resolve(code));
});
毎回 on.('exit')
などを書きたくないのでヘルパー
- spawnと引数は一緒
- ただし異常終了した場合はexitcodeをrejectする
pidが欲しい場合などはちゃんとしたのをつかうのがよい