Exit with an error code in the face of unhandled errors (#495)

As part of fixing the exit bug recently, we accidentally made errors
lead to zero exit codes.  As a result, the Pulumi CLI thought the
prgoram exited ordinarily, and proceeded to do its usual planning and
deployment, rather than terminating abruptly.

This is a byproduct of how Node's process.uncaughtException handler
works.  It hijacks and replaces all usual error logic, including the
process.exit part.  This change simply adds back the non-zero exit.

I also added a test (and fixed one other that began failing
afterwards), so that we can prevent regressions down the road.
This commit is contained in:
Joe Duffy 2017-10-28 17:05:05 -07:00 committed by GitHub
parent 2344f9e079
commit cdb2c79e8e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 13 additions and 4 deletions

View file

@ -126,6 +126,7 @@ export function main(args: string[]): void {
// Set up the process unhandled exception handler and the program exit handler.
process.on("uncaughtException", (err: Error) => {
// First, log the error.
if (err instanceof RunError) {
// For errors that are subtypes of RunError, we will print the message without hitting the unhandled error
// logic, which will dump all sorts of verbose spew like the origin source and stack trace.
@ -135,6 +136,8 @@ export function main(args: string[]): void {
console.log(`Running program '${program}' failed with an unhandled exception:`);
console.log(err);
}
// And next, exit with a non-zero exit code.
process.exit(1);
});
process.on("exit", () => { runtime.disconnectSync(); });

View file

@ -179,4 +179,3 @@ function stripEOL(data: string | Buffer): string {
}
return dataString;
}

View file

@ -6,7 +6,7 @@ const pulumi = require("../../../../../");
assert.equal(pulumi.getProject(), "runtimeSettingsProject");
assert.equal(pulumi.getStack(), "runtimeSettingsStack");
const config = new pulumi.Config("myBag");
const config = new pulumi.Config("myBag:config");
assert.equal(config.getNumber("A"), 42);
assert.equal(config.requireNumber("A"), 42);
assert.equal(config.get("bbbb"), "a string o' b's");

View file

@ -0,0 +1 @@
throw new Error("💥 goes the dynamite");

View file

@ -276,12 +276,18 @@ describe("rpc", () => {
project: "runtimeSettingsProject",
stack: "runtimeSettingsStack",
config: {
"myBag:A": 42,
"myBag:bbbb": "a string o' b's",
"myBag:config:A": "42",
"myBag:config:bbbb": "a string o' b's",
},
program: path.join(base, "010.runtime_settings"),
expectResourceCount: 0,
},
// A program that throws an ordinary unhandled error.
"unhandled_error": {
program: path.join(base, "011.unhandled_error"),
expectResourceCount: 0,
expectError: "Program exited with non-zero exit code: 1",
},
};
for (const casename of Object.keys(cases)) {