More attempts to fix procRunner hangs in CI (#17325) (#17443)

* [kbn-dev-utils/procRunner] try using execa to avoid never-exitting procs

* [kbn-dev-utils/procRunner] don't listen for close event, rely on stdio streams ending
This commit is contained in:
Spencer 2018-03-28 13:36:49 -07:00 committed by GitHub
parent d6a823e62d
commit b36e7aff43
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 111 additions and 55 deletions

View file

@ -10,15 +10,14 @@
},
"dependencies": {
"chalk": "^2.3.0",
"execa": "^0.10.0",
"moment": "^2.20.1",
"tree-kill": "1.1.0"
"tree-kill": "^1.2.0"
},
"devDependencies": {
"babel-cli": "^6.26.0",
"@kbn/babel-preset": "link:../kbn-babel-preset",
"babel-cli": "^6.26.0",
"chance": "1.0.6",
"expect.js": "0.3.1"
}
}

View file

@ -1,38 +0,0 @@
import Rx from 'rxjs/Rx';
import { createCliError } from './errors';
/**
* Creates an Observable from a childProcess that:
* - provides the exit code as it's only value (which may be null)
* as soon as the process exits
* - completes once all stdio streams for the child process have closed
* - fails if the childProcess emits an error event
*
* @param {ChildProcess} childProcess
* @return {Rx.Observable}
*/
export function observeChildProcess(name, childProcess) {
// observe first exit event
const exit$ = Rx.Observable.fromEvent(childProcess, 'exit')
.first()
.map(code => {
// JVM exits with 143 on SIGTERM and 130 on SIGINT, dont' treat then as errors
if (code > 0 && !(code === 143 || code === 130)) {
throw createCliError(`[${name}] exitted with code ${code}`);
}
return code;
});
// observe first close event
const close$ = Rx.Observable.fromEvent(childProcess, 'close').first();
// observe first error event until there is a close event
const error$ = Rx.Observable.fromEvent(childProcess, 'error')
.first()
.mergeMap(err => Rx.Observable.throw(err))
.takeUntil(close$);
return Rx.Observable.merge(exit$, close$.ignoreElements(), error$);
}

View file

@ -1,4 +1,4 @@
import { spawn } from 'child_process';
import execa from 'execa';
import { statSync } from 'fs';
import Rx from 'rxjs/Rx';
@ -10,7 +10,7 @@ const treeKillAsync = promisify(treeKill);
import { log } from './log';
import { observeLines } from './observe_lines';
import { observeChildProcess } from './observe_child_process';
import { createCliError } from './errors';
const SECOND = 1000;
const STOP_TIMEOUT = 30 * SECOND;
@ -50,14 +50,16 @@ export function createProc(name, { cmd, args, cwd, env, stdin }) {
}
}
const childProcess = spawn(cmd, args, {
const childProcess = execa(cmd, args, {
cwd,
env,
stdio: [stdin ? 'pipe' : 'ignore', 'pipe', 'pipe'],
stdio: ['pipe', 'pipe', 'pipe'],
});
if (stdin) {
childProcess.stdin.end(stdin, 'utf8');
} else {
childProcess.stdin.end();
}
return new class Proc {
@ -70,15 +72,32 @@ export function createProc(name, { cmd, args, cwd, env, stdin }) {
.do(line => log.write(` ${gray('proc')} [${gray(name)}] ${line}`))
.share();
outcome$ = observeChildProcess(name, childProcess).share();
outcome$ = Rx.Observable.defer(() => {
// observe first exit event
const exit$ = Rx.Observable.fromEvent(childProcess, 'exit')
.take(1)
.map(code => {
// JVM exits with 143 on SIGTERM and 130 on SIGINT, dont' treat then as errors
if (code > 0 && !(code === 143 || code === 130)) {
throw createCliError(`[${name}] exitted with code ${code}`);
}
return code;
});
// observe first error event until there is a close event
const error$ = Rx.Observable.fromEvent(childProcess, 'error')
.take(1)
.mergeMap(err => Rx.Observable.throw(err));
return Rx.Observable.race(exit$, error$);
}).share()
outcomePromise = Rx.Observable.merge(
this.lines$.ignoreElements(),
this.outcome$
).toPromise();
closedPromise = this.outcomePromise.then(() => {}, () => {});
async stop(signal) {
await withTimeout(
async () => {
@ -93,11 +112,15 @@ export function createProc(name, { cmd, args, cwd, env, stdin }) {
await withTimeout(
async () => {
await this.closedPromise;
try {
await this.outcomePromise;
} catch (error) {
// ignore
}
},
STOP_TIMEOUT,
async () => {
throw new Error(`Proc "${name}" was stopped but never emiited either the "close" or "exit" events after ${STOP_TIMEOUT} ms`);
throw new Error(`Proc "${name}" was stopped but never emiited either the "exit" or "error" event after ${STOP_TIMEOUT} ms`);
}
);
}

View file

@ -833,6 +833,16 @@ core-util-is@1.0.2, core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
cross-spawn@^6.0.0:
version "6.0.5"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
dependencies:
nice-try "^1.0.4"
path-key "^2.0.1"
semver "^5.5.0"
shebang-command "^1.2.0"
which "^1.2.9"
cryptiles@2.x.x:
version "2.0.5"
resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8"
@ -891,6 +901,18 @@ esutils@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
execa@^0.10.0:
version "0.10.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-0.10.0.tgz#ff456a8f53f90f8eccc71a96d11bdfc7f082cb50"
dependencies:
cross-spawn "^6.0.0"
get-stream "^3.0.0"
is-stream "^1.1.0"
npm-run-path "^2.0.0"
p-finally "^1.0.0"
signal-exit "^3.0.0"
strip-eof "^1.0.0"
expand-brackets@^0.1.4:
version "0.1.5"
resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b"
@ -1006,6 +1028,10 @@ gauge@~2.7.3:
strip-ansi "^3.0.1"
wide-align "^1.1.0"
get-stream@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
getpass@^0.1.1:
version "0.1.7"
resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
@ -1184,6 +1210,10 @@ is-primitive@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575"
is-stream@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
is-typedarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
@ -1192,6 +1222,10 @@ isarray@1.0.0, isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
isexe@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
isobject@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89"
@ -1335,6 +1369,10 @@ nan@^2.3.0:
version "2.9.2"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.9.2.tgz#f564d75f5f8f36a6d9456cca7a6c4fe488ab7866"
nice-try@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.4.tgz#d93962f6c52f2c1558c0fbda6d512819f1efe1c4"
node-pre-gyp@^0.6.39:
version "0.6.39"
resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz#c00e96860b23c0e1420ac7befc5044e1d78d8649"
@ -1364,6 +1402,12 @@ normalize-path@^2.0.0, normalize-path@^2.0.1:
dependencies:
remove-trailing-separator "^1.0.1"
npm-run-path@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
dependencies:
path-key "^2.0.0"
npmlog@^4.0.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
@ -1421,6 +1465,10 @@ output-file-sync@^1.1.2:
mkdirp "^0.5.1"
object-assign "^4.1.0"
p-finally@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
parse-glob@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c"
@ -1434,6 +1482,10 @@ path-is-absolute@^1.0.0, path-is-absolute@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
path-key@^2.0.0, path-key@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
performance-now@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5"
@ -1594,7 +1646,7 @@ safe-buffer@^5.0.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"
semver@^5.3.0:
semver@^5.3.0, semver@^5.5.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
@ -1606,6 +1658,16 @@ set-immediate-shim@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61"
shebang-command@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
dependencies:
shebang-regex "^1.0.0"
shebang-regex@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
signal-exit@^3.0.0:
version "3.0.2"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
@ -1668,6 +1730,10 @@ strip-ansi@^3.0.0, strip-ansi@^3.0.1:
dependencies:
ansi-regex "^2.0.0"
strip-eof@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
strip-json-comments@~2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
@ -1717,9 +1783,9 @@ traverse@0.6.6:
version "0.6.6"
resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137"
tree-kill@1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.1.0.tgz#c963dcf03722892ec59cba569e940b71954d1729"
tree-kill@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.0.tgz#5846786237b4239014f05db156b643212d4c6f36"
trim-right@^1.0.1:
version "1.0.1"
@ -1765,6 +1831,12 @@ verror@1.10.0:
core-util-is "1.0.2"
extsprintf "^1.2.0"
which@^1.2.9:
version "1.3.0"
resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a"
dependencies:
isexe "^2.0.0"
wide-align@^1.1.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710"