Skip to content

Commit e499c21

Browse files
committed
Improve unit testing.
1 parent 52f847c commit e499c21

File tree

9 files changed

+571
-23
lines changed

9 files changed

+571
-23
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,8 @@ public void copyDirectoryFromPod(
116116
false,
117117
false);
118118
InputStream is = new Base64InputStream(new BufferedInputStream(proc.getInputStream()));
119-
try (ArchiveInputStream archive =
120-
new TarArchiveInputStream(is)) { // new GzipCompressorInputStream(is))) {
119+
try (ArchiveInputStream archive = new TarArchiveInputStream(is)) {
120+
// TODO Use new GzipCompressorInputStream(is))) here {
121121
for (ArchiveEntry entry = archive.getNextEntry();
122122
entry != null;
123123
entry = archive.getNextEntry()) {

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ public Process exec(
199199
throws ApiException, IOException {
200200
String path = makePath(namespace, name, command, container, stdin, tty);
201201

202-
ExecProcess exec = new ExecProcess();
202+
ExecProcess exec = new ExecProcess(apiClient);
203203
WebSocketStreamHandler handler = exec.getHandler();
204204
WebSockets.stream(path, "GET", apiClient, handler);
205205

@@ -245,13 +245,13 @@ static int parseExitCode(ApiClient client, InputStream inputStream) {
245245
return -1;
246246
}
247247

248-
private class ExecProcess extends Process {
248+
protected static class ExecProcess extends Process {
249249
private final WebSocketStreamHandler streamHandler;
250250
private int statusCode = -1;
251251
private boolean isAlive = true;
252252
private final Map<Integer, InputStream> input = new HashMap<>();
253253

254-
public ExecProcess() throws IOException {
254+
public ExecProcess(final ApiClient apiClient) throws IOException {
255255
this.streamHandler =
256256
new WebSocketStreamHandler() {
257257
@Override

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ public ApiClient getApiClient() {
4949
}
5050

5151
public InputStream streamNamespacedPodLog(V1Pod pod) throws ApiException, IOException {
52+
if (pod.getSpec() == null) {
53+
throw new ApiException("pod.spec is null and container isn't specified.");
54+
}
55+
if (pod.getSpec().getContainers() == null || pod.getSpec().getContainers().size() < 1) {
56+
throw new ApiException("pod.spec.containers has no containers");
57+
}
5258
return streamNamespacedPodLog(
5359
pod.getMetadata().getNamespace(),
5460
pod.getMetadata().getName(),
@@ -86,7 +92,7 @@ public InputStream streamNamespacedPodLog(
8692
null);
8793
Response response = call.execute();
8894
if (!response.isSuccessful()) {
89-
throw new ApiException("Logs request failed: " + response.code());
95+
throw new ApiException(response.code(), "Logs request failed: " + response.code());
9096
}
9197
return response.body().byteStream();
9298
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
Copyright 2018 The Kubernetes Authors.
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
package io.kubernetes.client;
14+
15+
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
16+
import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
17+
import static com.github.tomakehurst.wiremock.client.WireMock.get;
18+
import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor;
19+
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
20+
import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;
21+
import static com.github.tomakehurst.wiremock.client.WireMock.verify;
22+
23+
import com.github.tomakehurst.wiremock.junit.WireMockRule;
24+
import io.kubernetes.client.Attach.AttachResult;
25+
import io.kubernetes.client.util.ClientBuilder;
26+
import java.io.IOException;
27+
import org.junit.Before;
28+
import org.junit.Rule;
29+
import org.junit.Test;
30+
31+
/** Tests for the Attach helper class */
32+
public class AttachTest {
33+
private String namespace;
34+
private String podName;
35+
private String container;
36+
37+
private ApiClient client;
38+
39+
private static final int PORT = 8089;
40+
@Rule public WireMockRule wireMockRule = new WireMockRule(PORT);
41+
42+
@Before
43+
public void setup() throws IOException {
44+
client = new ClientBuilder().setBasePath("http://localhost:" + PORT).build();
45+
46+
namespace = "default";
47+
podName = "apod";
48+
container = "acontainer";
49+
}
50+
51+
@Test
52+
public void testUrl() throws IOException, ApiException, InterruptedException {
53+
Attach attach = new Attach(client);
54+
55+
stubFor(
56+
get(urlPathEqualTo("/api/v1/namespaces/" + namespace + "/pods/" + podName + "/attach"))
57+
.willReturn(
58+
aResponse()
59+
.withStatus(404)
60+
.withHeader("Content-Type", "application/json")
61+
.withBody("{}")));
62+
63+
AttachResult res1 = attach.attach(namespace, podName, container, false, false);
64+
AttachResult res2 = attach.attach(namespace, podName, true);
65+
66+
// TODO: Kill this sleep, the trouble is that the test tries to validate before the connection
67+
// event has happened
68+
Thread.sleep(2000);
69+
70+
res1.close();
71+
res2.close();
72+
73+
verify(
74+
getRequestedFor(
75+
urlPathEqualTo("/api/v1/namespaces/" + namespace + "/pods/" + podName + "/attach"))
76+
.withQueryParam("stdin", equalTo("false"))
77+
.withQueryParam("tty", equalTo("false"))
78+
.withQueryParam("container", equalTo(container)));
79+
80+
verify(
81+
getRequestedFor(
82+
urlPathEqualTo("/api/v1/namespaces/" + namespace + "/pods/" + podName + "/attach"))
83+
.withQueryParam("stdin", equalTo("true"))
84+
.withQueryParam("tty", equalTo("false")));
85+
}
86+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
Copyright 2018 The Kubernetes Authors.
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
package io.kubernetes.client;
14+
15+
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
16+
import static com.github.tomakehurst.wiremock.client.WireMock.get;
17+
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
18+
import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;
19+
20+
import com.github.tomakehurst.wiremock.junit.WireMockRule;
21+
import io.kubernetes.client.models.V1ObjectMeta;
22+
import io.kubernetes.client.models.V1Pod;
23+
import io.kubernetes.client.util.ClientBuilder;
24+
import java.io.IOException;
25+
import java.io.InputStream;
26+
import org.junit.Before;
27+
import org.junit.Rule;
28+
import org.junit.Test;
29+
30+
/** Tests for the Copy helper class */
31+
public class CopyTest {
32+
private String namespace;
33+
private String podName;
34+
private String[] cmd;
35+
36+
private ApiClient client;
37+
38+
private static final int PORT = 8089;
39+
@Rule public WireMockRule wireMockRule = new WireMockRule(PORT);
40+
41+
@Before
42+
public void setup() throws IOException {
43+
client = new ClientBuilder().setBasePath("http://localhost:" + PORT).build();
44+
45+
namespace = "default";
46+
podName = "apod";
47+
}
48+
49+
@Test
50+
public void testUrl() throws IOException, ApiException, InterruptedException {
51+
Copy copy = new Copy(client);
52+
53+
V1Pod pod = new V1Pod().metadata(new V1ObjectMeta().name(podName).namespace(namespace));
54+
55+
stubFor(
56+
get(urlPathEqualTo("/api/v1/namespaces/" + namespace + "/pods/" + podName + "/exec"))
57+
.willReturn(
58+
aResponse()
59+
.withStatus(404)
60+
.withHeader("Content-Type", "application/json")
61+
.withBody("{}")));
62+
63+
InputStream is = copy.copyFileFromPod(pod, "/some/path/to/file");
64+
65+
// verify(
66+
// getRequestedFor(
67+
// urlPathEqualTo("/api/v1/namespaces/" + namespace + "/pods/" + podName +
68+
// "/exec"))
69+
// .withQueryParam("stdin", equalTo("false"))
70+
// .withQueryParam("stdout", equalTo("true"))
71+
// .withQueryParam("stderr", equalTo("true"))
72+
// .withQueryParam("tty", equalTo("false"))
73+
// .withQueryParam("command", new AnythingPattern()));
74+
}
75+
}

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

Lines changed: 89 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,21 @@
1717
import static com.github.tomakehurst.wiremock.client.WireMock.get;
1818
import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor;
1919
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
20-
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
2120
import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;
2221
import static com.github.tomakehurst.wiremock.client.WireMock.verify;
2322
import static org.junit.Assert.assertEquals;
2423

2524
import com.github.tomakehurst.wiremock.junit.WireMockRule;
26-
27-
import io.kubernetes.client.ApiException;
25+
import com.google.common.io.ByteStreams;
26+
import io.kubernetes.client.Exec.ExecProcess;
27+
import io.kubernetes.client.models.V1ObjectMeta;
28+
import io.kubernetes.client.models.V1Pod;
2829
import io.kubernetes.client.util.ClientBuilder;
29-
3030
import java.io.ByteArrayInputStream;
31+
import java.io.ByteArrayOutputStream;
3132
import java.io.IOException;
3233
import java.io.InputStream;
34+
import java.io.OutputStream;
3335
import java.nio.charset.StandardCharsets;
3436
import org.junit.Before;
3537
import org.junit.Rule;
@@ -45,6 +47,8 @@ public class ExecTest {
4547
"{\"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\"}]}}";
4648
private static final String BAD_OUTPUT_INCOMPLETE_MSG1 =
4749
"{\"metadata\":{},\"status\":\"Failure\",\"message\":\"command terminated with non-zero exit code: Error executing in Docker Container: 1\",\"reas";
50+
private static final String OUTPUT_EXIT_BAD_INT =
51+
"{\"metadata\":{},\"status\":\"Failure\",\"message\":\"command terminated with non-zero exit code: Error executing in Docker Container: 126\",\"reason\":\"NonZeroExitCode\",\"details\":{\"causes\":[{\"reason\":\"ExitCode\",\"message\":\"not a number\"}]}}";
4852

4953
private String namespace;
5054
private String podName;
@@ -58,37 +62,97 @@ public class ExecTest {
5862
@Before
5963
public void setup() throws IOException {
6064
client = new ClientBuilder().setBasePath("http://localhost:" + PORT).build();
61-
65+
6266
namespace = "default";
6367
podName = "apod";
64-
// TODO: When WireMock supports multiple query params with the same name expand this
68+
// TODO: When WireMock supports multiple query params with the same name expand
69+
// this
6570
// See: https://github.com/tomakehurst/wiremock/issues/398
6671
cmd = new String[] {"cmd"};
6772
}
6873

74+
public static InputStream makeStream(int streamNum, byte[] data) {
75+
return makeStream(new byte[] {(byte) streamNum}, data);
76+
}
77+
78+
public static InputStream makeStream(byte[] prefix, byte[] data) {
79+
byte[] out = new byte[prefix.length + data.length];
80+
System.arraycopy(prefix, 0, out, 0, prefix.length);
81+
System.arraycopy(data, 0, out, prefix.length, data.length);
82+
return new ByteArrayInputStream(out);
83+
}
84+
85+
public static Thread asyncCopy(final InputStream is, final OutputStream os) {
86+
Thread t =
87+
new Thread(
88+
new Runnable() {
89+
public void run() {
90+
try {
91+
ByteStreams.copy(is, os);
92+
} catch (IOException ex) {
93+
ex.printStackTrace();
94+
}
95+
}
96+
});
97+
t.start();
98+
return t;
99+
}
100+
101+
@Test
102+
public void testExecProcess() throws IOException, InterruptedException {
103+
final ExecProcess process = new ExecProcess(client);
104+
process.getHandler().open("wss", null);
105+
String msgData = "This is the stdout message";
106+
String errData = "This is the stderr message";
107+
108+
process.getHandler().bytesMessage(makeStream(1, msgData.getBytes(StandardCharsets.UTF_8)));
109+
process.getHandler().bytesMessage(makeStream(2, errData.getBytes(StandardCharsets.UTF_8)));
110+
process.getHandler().bytesMessage(makeStream(3, OUTPUT_EXIT0.getBytes(StandardCharsets.UTF_8)));
111+
112+
final ByteArrayOutputStream stdout = new ByteArrayOutputStream();
113+
final ByteArrayOutputStream stderr = new ByteArrayOutputStream();
114+
115+
Thread t1 = asyncCopy(process.getInputStream(), stdout);
116+
Thread t2 = asyncCopy(process.getErrorStream(), stderr);
117+
118+
// TODO: Fix this asap!
119+
Thread.sleep(1000);
120+
121+
process.destroy();
122+
123+
assertEquals(msgData, stdout.toString());
124+
assertEquals(errData, stderr.toString());
125+
assertEquals(false, process.isAlive());
126+
assertEquals(0, process.exitValue());
127+
}
128+
69129
@Test
70130
public void testUrl() throws IOException, ApiException, InterruptedException {
71-
Exec exec = new Exec(client);
131+
Exec exec = new Exec(client);
132+
133+
V1Pod pod = new V1Pod().metadata(new V1ObjectMeta().name(podName).namespace(namespace));
72134

73-
stubFor(
135+
stubFor(
74136
get(urlPathEqualTo("/api/v1/namespaces/" + namespace + "/pods/" + podName + "/exec"))
75137
.willReturn(
76138
aResponse()
77139
.withStatus(404)
78140
.withHeader("Content-Type", "application/json")
79141
.withBody("{}")));
80142

81-
Process p = exec.exec(namespace, podName, cmd, false);
82-
p.waitFor();
143+
Process p = exec.exec(pod, cmd, true, false);
144+
p.waitFor();
83145

84-
verify(getRequestedFor(urlPathEqualTo("/api/v1/namespaces/" + namespace + "/pods/" + podName + "/exec"))
85-
.withQueryParam("stdin", equalTo("false"))
86-
.withQueryParam("stdout", equalTo("true"))
87-
.withQueryParam("stderr", equalTo("true"))
88-
.withQueryParam("tty", equalTo("false"))
89-
.withQueryParam("command", equalTo("cmd")));
146+
verify(
147+
getRequestedFor(
148+
urlPathEqualTo("/api/v1/namespaces/" + namespace + "/pods/" + podName + "/exec"))
149+
.withQueryParam("stdin", equalTo("true"))
150+
.withQueryParam("stdout", equalTo("true"))
151+
.withQueryParam("stderr", equalTo("true"))
152+
.withQueryParam("tty", equalTo("false"))
153+
.withQueryParam("command", equalTo("cmd")));
90154

91-
assertEquals(-1, p.exitValue());
155+
assertEquals(-1, p.exitValue());
92156
}
93157

94158
@Test
@@ -122,4 +186,12 @@ public void testIncompleteData1() {
122186
int exitCode = Exec.parseExitCode(client, inputStream);
123187
assertEquals(-1, exitCode);
124188
}
189+
190+
@Test
191+
public void testNonZeroBadIntExit() {
192+
InputStream inputStream =
193+
new ByteArrayInputStream(OUTPUT_EXIT_BAD_INT.getBytes(StandardCharsets.UTF_8));
194+
int exitCode = Exec.parseExitCode(client, inputStream);
195+
assertEquals(-1, exitCode);
196+
}
125197
}

0 commit comments

Comments
 (0)