|
18 | 18 |
|
19 | 19 | import static org.csanchez.jenkins.plugins.kubernetes.pipeline.Constants.*;
|
20 | 20 |
|
| 21 | +import java.io.ByteArrayOutputStream; |
21 | 22 | import java.io.Closeable;
|
22 | 23 | import java.io.IOException;
|
23 | 24 | import java.io.InterruptedIOException;
|
24 | 25 | import java.io.OutputStream;
|
25 | 26 | import java.io.PrintStream;
|
26 | 27 | import java.io.Serializable;
|
27 |
| -import java.nio.ByteBuffer; |
28 | 28 | import java.nio.charset.StandardCharsets;
|
29 | 29 | import java.util.ArrayList;
|
30 | 30 | import java.util.Arrays;
|
31 | 31 | import java.util.HashMap;
|
32 | 32 | import java.util.List;
|
33 | 33 | import java.util.Map;
|
34 |
| -import java.util.Objects; |
35 | 34 | import java.util.concurrent.CountDownLatch;
|
36 | 35 | import java.util.concurrent.TimeUnit;
|
37 | 36 | import java.util.concurrent.atomic.AtomicBoolean;
|
@@ -268,22 +267,18 @@ private Proc doLaunch(boolean quiet, String[] cmdEnvs, OutputStream outputForCal
|
268 | 267 | printStream = new PrintStream(stream, false, StandardCharsets.UTF_8.toString());
|
269 | 268 | }
|
270 | 269 |
|
271 |
| - // we need to keep the last bytes in the stream to parse the exit code as it is printed there |
272 |
| - // so we use a buffer |
273 |
| - ExitCodeOutputStream exitCodeOutputStream = new ExitCodeOutputStream(); |
274 |
| - // send container output to all 3 streams (pid, out, job). |
275 |
| - stream = new TeeOutputStream(exitCodeOutputStream, stream); |
276 | 270 | // Send to proc caller as well if they sent one
|
277 | 271 | if (outputForCaller != null) {
|
278 | 272 | stream = new TeeOutputStream(outputForCaller, stream);
|
279 | 273 | }
|
| 274 | + ByteArrayOutputStream error = new ByteArrayOutputStream(); |
280 | 275 |
|
281 | 276 | String msg = "Executing shell script inside container [" + containerName + "] of pod [" + podName + "]";
|
282 | 277 | LOGGER.log(Level.FINEST, msg);
|
283 | 278 | printStream.println(msg);
|
284 | 279 |
|
285 | 280 | Execable<String, ExecWatch> execable = client.pods().inNamespace(namespace).withName(podName).inContainer(containerName)
|
286 |
| - .redirectingInput().writingOutput(stream).writingError(stream) |
| 281 | + .redirectingInput().writingOutput(stream).writingError(stream).writingErrorChannel(error) |
287 | 282 | .usingListener(new ExecListener() {
|
288 | 283 | @Override
|
289 | 284 | public void onOpen(Response response) {
|
@@ -374,7 +369,7 @@ public void onClose(int i, String s) {
|
374 | 369 |
|
375 | 370 | int pid = readPidFromPidFile(commands);
|
376 | 371 | LOGGER.log(Level.INFO, "Created process inside pod: ["+podName+"], container: ["+containerName+"] with pid:["+pid+"]");
|
377 |
| - ContainerExecProc proc = new ContainerExecProc(watch, alive, finished, exitCodeOutputStream::getExitCode); |
| 372 | + ContainerExecProc proc = new ContainerExecProc(watch, alive, finished, error); |
378 | 373 | processes.put(pid, proc);
|
379 | 374 | closables.add(proc);
|
380 | 375 | return proc;
|
@@ -481,10 +476,10 @@ private static void doExec(ExecWatch watch, PrintStream out, boolean[] masks, St
|
481 | 476 |
|
482 | 477 | // get the command exit code and print it padded so it is easier to parse in ContainerExecProc
|
483 | 478 | // We need to exit so that we know when the command has finished.
|
484 |
| - sb.append(ExitCodeOutputStream.EXIT_COMMAND); |
485 |
| - out.print(ExitCodeOutputStream.EXIT_COMMAND); |
| 479 | + sb.append(EXIT + NEWLINE); |
| 480 | + out.print(EXIT + NEWLINE); |
486 | 481 | LOGGER.log(Level.FINEST, "Executing command: {0}", sb);
|
487 |
| - watch.getInput().write(ExitCodeOutputStream.EXIT_COMMAND.getBytes(StandardCharsets.UTF_8)); |
| 482 | + watch.getInput().write((EXIT + NEWLINE).getBytes(StandardCharsets.UTF_8)); |
488 | 483 |
|
489 | 484 | out.flush();
|
490 | 485 | watch.getInput().flush();
|
@@ -569,49 +564,4 @@ private static void closeWatch(ExecWatch watch) {
|
569 | 564 | public void setKubernetesClient(KubernetesClient client) {
|
570 | 565 | this.client = client;
|
571 | 566 | }
|
572 |
| - |
573 |
| - /** |
574 |
| - * Keeps the last bytes of the output stream to parse the exit code |
575 |
| - */ |
576 |
| - static class ExitCodeOutputStream extends OutputStream { |
577 |
| - |
578 |
| - public static final String EXIT_COMMAND_TXT = "EXITCODE"; |
579 |
| - public static final String EXIT_COMMAND = "printf \"" + EXIT_COMMAND_TXT + " %3d\" $?; " + EXIT + NEWLINE; |
580 |
| - |
581 |
| - private EvictingQueue<Integer> queue = EvictingQueue.create(20); |
582 |
| - |
583 |
| - public ExitCodeOutputStream() { |
584 |
| - } |
585 |
| - |
586 |
| - @Override |
587 |
| - public void write(int b) throws IOException { |
588 |
| - queue.add(b); |
589 |
| - byte[] bb = new byte[]{(byte) b}; |
590 |
| - System.out.print(new String(bb, StandardCharsets.UTF_8)); |
591 |
| - } |
592 |
| - |
593 |
| - public int getExitCode() { |
594 |
| - ByteBuffer b = ByteBuffer.allocate(queue.size()); |
595 |
| - queue.stream().filter(Objects::nonNull).forEach((i) -> b.put((byte) i.intValue())); |
596 |
| - // output ends in a 3 digit padded exit code + newline (13 10) |
597 |
| - // as defined in ContainerExecDecorator#doExec |
598 |
| - // ie. 32 32 49 13 10 for exit code 1 |
599 |
| - int i = 1; |
600 |
| - String s = new String(b.array(), StandardCharsets.UTF_8); |
601 |
| - if (s.indexOf(EXIT_COMMAND_TXT) < 0) { |
602 |
| - LOGGER.log(Level.WARNING, "Unable to find \"{0}\" in {1}", new Object[]{EXIT_COMMAND_TXT, s}); |
603 |
| - return i; |
604 |
| - } |
605 |
| - // parse the exitcode int printed after EXITCODE |
606 |
| - int start = s.indexOf(EXIT_COMMAND_TXT) + EXIT_COMMAND_TXT.length(); |
607 |
| - s = s.substring(start, start + 4).trim(); |
608 |
| - try { |
609 |
| - i = Integer.parseInt(s); |
610 |
| - } catch (NumberFormatException e) { |
611 |
| - LOGGER.log(Level.WARNING, "Unable to parse exit code as integer: \"{0}\" {1} / {2}", |
612 |
| - new Object[]{s, queue.toString(), Arrays.toString(b.array())}); |
613 |
| - } |
614 |
| - return i; |
615 |
| - } |
616 |
| - } |
617 | 567 | }
|
0 commit comments