Skip to content

Commit bef5d7a

Browse files
committed
Only use v4 streaming protocol and parse exec status channel response as V1Status
1 parent 6fb1018 commit bef5d7a

File tree

3 files changed

+63
-38
lines changed

3 files changed

+63
-38
lines changed

util/src/main/java/io/kubernetes/client/Exec.java

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,19 @@
1212
*/
1313
package io.kubernetes.client;
1414

15+
import com.google.gson.reflect.TypeToken;
1516
import io.kubernetes.client.models.V1Pod;
17+
import io.kubernetes.client.models.V1Status;
18+
import io.kubernetes.client.models.V1StatusCause;
19+
import io.kubernetes.client.models.V1StatusDetails;
1620
import io.kubernetes.client.util.WebSocketStreamHandler;
1721
import io.kubernetes.client.util.WebSockets;
1822
import java.io.IOException;
1923
import java.io.InputStream;
2024
import java.io.OutputStream;
25+
import java.lang.reflect.Type;
2126
import java.util.HashMap;
27+
import java.util.List;
2228
import java.util.Map;
2329
import java.util.concurrent.TimeUnit;
2430
import org.apache.commons.lang3.StringUtils;
@@ -184,7 +190,7 @@ public Process exec(
184190
return exec;
185191
}
186192

187-
static int parseExitCode(InputStream inputStream) {
193+
static int parseExitCode(ApiClient client, InputStream inputStream) {
188194
try {
189195
int available = inputStream.available();
190196

@@ -193,24 +199,38 @@ static int parseExitCode(InputStream inputStream) {
193199

194200
byte[] b = new byte[available];
195201
inputStream.read(b);
196-
String result = new String(b, "UTF-8");
197-
int idx = result.lastIndexOf(':');
198-
if (idx > 0) {
199-
try {
200-
return Integer.parseInt(result.substring(idx + 1).trim());
201-
} catch (NumberFormatException nfe) {
202-
log.error("Error parsing exit code from status channel response", nfe);
202+
String body = new String(b, "UTF-8");
203+
204+
Type returnType = new TypeToken<V1Status>() {}.getType();
205+
V1Status status = client.getJSON().deserialize(body, returnType);
206+
if ("Success".equals(status.getStatus())) return 0;
207+
208+
if ("NonZeroExitCode".equals(status.getReason())) {
209+
V1StatusDetails details = status.getDetails();
210+
if (details != null) {
211+
List<V1StatusCause> causes = details.getCauses();
212+
if (causes != null) {
213+
for (V1StatusCause cause : causes) {
214+
if ("ExitCode".equals(cause.getReason())) {
215+
try {
216+
return Integer.parseInt(cause.getMessage());
217+
} catch (NumberFormatException nfe) {
218+
log.error("Error parsing exit code from status channel response", nfe);
219+
}
220+
}
221+
}
222+
}
203223
}
204224
}
205-
} catch (IOException io) {
206-
log.error("Error parsing exit code from status channel response", io);
225+
} catch (Throwable t) {
226+
log.error("Error parsing exit code from status channel response", t);
207227
}
208228

209229
// Unable to parse the exit code from the content
210230
return -1;
211231
}
212232

213-
private static class ExecProcess extends Process {
233+
private class ExecProcess extends Process {
214234
private final WebSocketStreamHandler streamHandler;
215235
private volatile int statusCode;
216236
private final Map<Integer, InputStream> input = new HashMap<>();
@@ -222,7 +242,7 @@ public ExecProcess() throws IOException {
222242
@Override
223243
public void close() {
224244
if (statusCode == -1) {
225-
int exitCode = parseExitCode(ExecProcess.this.getInputStream(3));
245+
int exitCode = parseExitCode(apiClient, ExecProcess.this.getInputStream(3));
226246

227247
// notify of process completion
228248
synchronized (ExecProcess.this) {

util/src/main/java/io/kubernetes/client/util/WebSockets.java

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,9 @@
3838
public class WebSockets {
3939
private static final Logger log = LoggerFactory.getLogger(WebSockets.class);
4040

41+
// Only support v4 stream protocol as it was available since k8s 1.4
4142
public static final String V4_STREAM_PROTOCOL = "v4.channel.k8s.io";
42-
public static final String V3_STREAM_PROTOCOL = "v3.channel.k8s.io";
43-
public static final String V2_STREAM_PROTOCOL = "v2.channel.k8s.io";
44-
public static final String V1_STREAM_PROTOCOL = "channel.k8s.io";
45-
public static final String STREAM_PROTOCOL_HEADER = "X-Stream-Protocol-Version";
43+
public static final String STREAM_PROTOCOL_HEADER = "Sec-WebSocket-Protocol";
4644
public static final String SPDY_3_1 = "SPDY/3.1";
4745

4846
/** A simple interface for a listener on a web socket */
@@ -86,11 +84,7 @@ public static void stream(
8684
throws ApiException, IOException {
8785

8886
HashMap<String, String> headers = new HashMap<String, String>();
89-
String allProtocols =
90-
String.format(
91-
"%s,%s,%s,%s",
92-
V4_STREAM_PROTOCOL, V3_STREAM_PROTOCOL, V2_STREAM_PROTOCOL, V1_STREAM_PROTOCOL);
93-
headers.put(STREAM_PROTOCOL_HEADER, allProtocols);
87+
headers.put(STREAM_PROTOCOL_HEADER, V4_STREAM_PROTOCOL);
9488
headers.put(HttpHeaders.CONNECTION, HttpHeaders.UPGRADE);
9589
headers.put(HttpHeaders.UPGRADE, SPDY_3_1);
9690
String[] localVarAuthNames = new String[] {"BearerToken"};

util/src/test/java/io/kubernetes/client/ExecTest.java

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,57 +14,68 @@
1414

1515
import static org.junit.Assert.*;
1616

17+
import io.kubernetes.client.util.ClientBuilder;
1718
import java.io.ByteArrayInputStream;
19+
import java.io.IOException;
1820
import java.io.InputStream;
1921
import java.nio.charset.StandardCharsets;
22+
import org.junit.Before;
2023
import org.junit.Test;
2124

2225
/** Tests for the Exec helper class */
2326
public class ExecTest {
27+
28+
private static final String OUTPUT_EXIT0 = "{\"metadata\":{},\"status\":\"Success\"}";
2429
private static final String OUTPUT_EXIT1 =
25-
"command terminated with non-zero exit code: Error executing in Docker Container: 1";
30+
"{\"metadata\":{},\"status\":\"Failure\",\"message\":\"command terminated with non-zero exit code: Error executing in Docker Container: 1\",\"reason\":\"NonZeroExitCode\",\"details\":{\"causes\":[{\"reason\":\"ExitCode\",\"message\":\"1\"}]}}";
2631
private static final String OUTPUT_EXIT126 =
27-
"command terminated with non-zero exit code: Error executing in Docker Container: 126";
32+
"{\"metadata\":{},\"status\":\"Failure\",\"message\":\"command terminated with non-zero exit code: Error executing in Docker Container: 126\",\"reason\":\"NonZeroExitCode\",\"details\":{\"causes\":[{\"reason\":\"ExitCode\",\"message\":\"126\"}]}}";
2833
private static final String BAD_OUTPUT_INCOMPLETE_MSG1 =
29-
"command terminated with non-zero exit code: Error ";
30-
private static final String BAD_OUTPUT_INCOMPLETE_MSG2 = "command terminated with non-zero";
34+
"{\"metadata\":{},\"status\":\"Failure\",\"message\":\"command terminated with non-zero exit code: Error executing in Docker Container: 1\",\"reas";
35+
36+
private ApiClient client;
37+
38+
@Before
39+
public void setup() throws IOException {
40+
client = ClientBuilder.defaultClient();
41+
}
3142

3243
@Test
33-
public void testExit0() {
44+
public void testExit0NoMessage() {
3445
InputStream inputStream = new ByteArrayInputStream(new byte[0]);
35-
int exitCode = Exec.parseExitCode(inputStream);
46+
int exitCode = Exec.parseExitCode(client, inputStream);
47+
assertEquals(0, exitCode);
48+
}
49+
50+
@Test
51+
public void testExit0() {
52+
InputStream inputStream =
53+
new ByteArrayInputStream(OUTPUT_EXIT0.getBytes(StandardCharsets.UTF_8));
54+
int exitCode = Exec.parseExitCode(client, inputStream);
3655
assertEquals(0, exitCode);
3756
}
3857

3958
@Test
4059
public void testExit1() {
4160
InputStream inputStream =
4261
new ByteArrayInputStream(OUTPUT_EXIT1.getBytes(StandardCharsets.UTF_8));
43-
int exitCode = Exec.parseExitCode(inputStream);
62+
int exitCode = Exec.parseExitCode(client, inputStream);
4463
assertEquals(1, exitCode);
4564
}
4665

4766
@Test
4867
public void testExit126() {
4968
InputStream inputStream =
5069
new ByteArrayInputStream(OUTPUT_EXIT126.getBytes(StandardCharsets.UTF_8));
51-
int exitCode = Exec.parseExitCode(inputStream);
70+
int exitCode = Exec.parseExitCode(client, inputStream);
5271
assertEquals(126, exitCode);
5372
}
5473

5574
@Test
5675
public void testIncompleteData1() {
5776
InputStream inputStream =
5877
new ByteArrayInputStream(BAD_OUTPUT_INCOMPLETE_MSG1.getBytes(StandardCharsets.UTF_8));
59-
int exitCode = Exec.parseExitCode(inputStream);
60-
assertEquals(-1, exitCode);
61-
}
62-
63-
@Test
64-
public void testIncompleteData2() {
65-
InputStream inputStream =
66-
new ByteArrayInputStream(BAD_OUTPUT_INCOMPLETE_MSG2.getBytes(StandardCharsets.UTF_8));
67-
int exitCode = Exec.parseExitCode(inputStream);
78+
int exitCode = Exec.parseExitCode(client, inputStream);
6879
assertEquals(-1, exitCode);
6980
}
7081
}

0 commit comments

Comments
 (0)