Skip to content

Commit 3ddad4a

Browse files
authored
Handle unexpected exit code from server process (#87098)
When running Elasticsearch in the foreground, the cli process waits indefinitely on the server. If the server dies unexpectedly, the ServerProcess throws an exception. However, the exit code is hidden in the exception message. This commit changes waitFor to return the exit code, so it can be propagated to the cli main. Note that when stopping in a shutdown hook the exit code must be ignored because calling System.exit from a shutdownhook results in a deadlock.
1 parent 30bc7b0 commit 3ddad4a

File tree

4 files changed

+21
-12
lines changed

4 files changed

+21
-12
lines changed

distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,10 @@ public void execute(Terminal terminal, OptionSet options, Environment env, Proce
9595
}
9696

9797
// we are running in the foreground, so wait for the server to exit
98-
server.waitFor();
98+
int exitCode = server.waitFor();
99+
if (exitCode != ExitCodes.OK) {
100+
throw new UserException(exitCode, "Elasticsearch exited unexpectedly");
101+
}
99102
}
100103

101104
private void printVersion(Terminal terminal) {

distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerProcess.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -149,12 +149,9 @@ public synchronized void detach() throws IOException {
149149
/**
150150
* Waits for the subprocess to exit.
151151
*/
152-
public void waitFor() {
152+
public int waitFor() {
153153
errorPump.drain();
154-
int exitCode = nonInterruptible(jvmProcess::waitFor);
155-
if (exitCode != ExitCodes.OK) {
156-
throw new RuntimeException("server process exited with status code " + exitCode);
157-
}
154+
return nonInterruptible(jvmProcess::waitFor);
158155
}
159156

160157
/**
@@ -171,7 +168,7 @@ public synchronized void stop() {
171168
}
172169

173170
sendShutdownMarker();
174-
waitFor();
171+
waitFor(); // ignore exit code, we are already shutting down
175172
}
176173

177174
private static void sendArgs(ServerArgs args, OutputStream processStdin) {

distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,12 +271,19 @@ public void testIgnoreNullExceptionOutput() throws Exception {
271271
assertThat(terminal.getErrorOutput(), not(containsString("null")));
272272
}
273273

274+
public void testServerExitsNonZero() throws Exception {
275+
mockServerExitCode = 140;
276+
int exitCode = executeMain();
277+
assertThat(exitCode, equalTo(140));
278+
}
279+
274280
interface AutoConfigMethod {
275281
void autoconfig(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws UserException;
276282
}
277283

278284
Consumer<ServerArgs> argsValidator;
279285
private final MockServerProcess mockServer = new MockServerProcess();
286+
int mockServerExitCode = 0;
280287

281288
AutoConfigMethod autoConfigCallback;
282289
private final MockAutoConfigCli AUTO_CONFIG_CLI = new MockAutoConfigCli();
@@ -285,6 +292,7 @@ interface AutoConfigMethod {
285292
public void resetCommand() {
286293
argsValidator = null;
287294
autoConfigCallback = null;
295+
mockServerExitCode = 0;
288296
}
289297

290298
private class MockAutoConfigCli extends EnvironmentAwareCommand {
@@ -330,9 +338,10 @@ public void detach() {
330338
}
331339

332340
@Override
333-
public void waitFor() {
341+
public int waitFor() {
334342
assert waitForCalled == false;
335343
waitForCalled = true;
344+
return mockServerExitCode;
336345
}
337346

338347
@Override

distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerProcessTests.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -407,8 +407,9 @@ public void testWaitFor() throws Exception {
407407
nonInterruptibleVoid(mainReady::await);
408408
server.stop();
409409
}).start();
410-
server.waitFor();
410+
int exitCode = server.waitFor();
411411
assertThat(process.main.isDone(), is(true));
412+
assertThat(exitCode, equalTo(0));
412413
assertThat(terminal.getErrorOutput(), containsString("final message"));
413414
}
414415

@@ -423,8 +424,7 @@ public void testProcessDies() throws Exception {
423424
};
424425
var server = startProcess(false, false, "");
425426
mainExit.countDown();
426-
var e = expectThrows(RuntimeException.class, server::waitFor);
427-
assertThat(e.getMessage(), equalTo("server process exited with status code -9"));
428-
assertThat(terminal.getErrorOutput(), containsString("fatal message"));
427+
int exitCode = server.waitFor();
428+
assertThat(exitCode, equalTo(-9));
429429
}
430430
}

0 commit comments

Comments
 (0)