> headers) {
+ this.headers.clear();
+ this.headers.putAll(headers);
+ }
+}
diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/http/HttpClient.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/http/HttpClient.java
index 4802c291454..88bc7b9177e 100644
--- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/http/HttpClient.java
+++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/http/HttpClient.java
@@ -54,34 +54,65 @@ interface DerivedClientBuilder {
DerivedClientBuilder readTimeout(long readTimeout, TimeUnit unit);
- DerivedClientBuilder forStreaming();
-
- DerivedClientBuilder authenticatorNone();
+ DerivedClientBuilder writeTimeout(long writeTimeout, TimeUnit unit);
- DerivedClientBuilder writeTimeout(long timeout, TimeUnit timeoutUnit);
+ /**
+ * Sets the HttpClient to be used to perform HTTP requests whose responses
+ * will be streamed.
+ *
+ * @return this Builder instance.
+ */
+ DerivedClientBuilder forStreaming();
DerivedClientBuilder addOrReplaceInterceptor(String name, Interceptor interceptor);
+
+ /**
+ * Prevents any built-in authenticator to respond to challenges from origin server.
+ *
+ * OkHttp specific option.
+ *
+ * @return this Builder instance.
+ */
+ DerivedClientBuilder authenticatorNone();
}
interface Builder extends DerivedClientBuilder {
+ /**
+ * {@inheritDoc}
+ */
@Override
HttpClient build();
+ /**
+ * {@inheritDoc}
+ */
@Override
Builder readTimeout(long readTimeout, TimeUnit unit);
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ Builder writeTimeout(long writeTimeout, TimeUnit unit);
+
Builder connectTimeout(long connectTimeout, TimeUnit unit);
+ /**
+ * {@inheritDoc}
+ */
@Override
Builder forStreaming();
- @Override
- Builder writeTimeout(long timeout, TimeUnit timeoutUnit);
-
+ /**
+ * {@inheritDoc}
+ */
@Override
Builder addOrReplaceInterceptor(String name, Interceptor interceptor);
+ /**
+ * {@inheritDoc}
+ */
@Override
Builder authenticatorNone();
@@ -93,7 +124,7 @@ interface Builder extends DerivedClientBuilder {
Builder proxyAuthorization(String credentials);
- Builder tlsVersions(TlsVersion[] tlsVersions);
+ Builder tlsVersions(TlsVersion... tlsVersions);
Builder preferHttp11();
}
diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/http/HttpHeaders.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/http/HttpHeaders.java
index 727c6827b4c..1a89e6a84a5 100644
--- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/http/HttpHeaders.java
+++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/http/HttpHeaders.java
@@ -17,15 +17,23 @@
package io.fabric8.kubernetes.client.http;
import java.util.List;
+import java.util.Map;
public interface HttpHeaders {
/**
* Returns a List of all the Header String values for the provided key/name.
- *
+ *
* @param key The header key/name for which to provide the values.
* @return the List of header values for the provided key.
*/
List headers(String key);
+ /**
+ * Returns a Map containing a list of String values for each Header.
+ *
+ * @return the Map of Headers and their list of values.
+ */
+ Map> headers();
+
}
diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/http/StandardHttpHeaders.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/http/StandardHttpHeaders.java
new file mode 100644
index 00000000000..67ae5272265
--- /dev/null
+++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/http/StandardHttpHeaders.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.fabric8.kubernetes.client.http;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+public class StandardHttpHeaders implements HttpHeaders {
+
+ public static final String CONTENT_TYPE = "Content-Type";
+ public static final String CONTENT_LENGTH = "Content-Length";
+ public static final String EXPECT = "Expect";
+ public static final String EXPECT_CONTINUE = "100-Continue";
+
+ private final Map> headers;
+
+ public StandardHttpHeaders(Map> headers) {
+ this.headers = headers;
+ }
+
+ @Override
+ public List headers(String key) {
+ return Collections.unmodifiableList(headers.getOrDefault(key, Collections.emptyList()));
+ }
+
+ @Override
+ public Map> headers() {
+ return Collections.unmodifiableMap(headers);
+ }
+}
diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/http/StandardHttpRequest.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/http/StandardHttpRequest.java
new file mode 100644
index 00000000000..0893f8d7b54
--- /dev/null
+++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/http/StandardHttpRequest.java
@@ -0,0 +1,152 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.fabric8.kubernetes.client.http;
+
+import io.fabric8.kubernetes.client.KubernetesClientException;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+public class StandardHttpRequest extends StandardHttpHeaders implements HttpRequest {
+
+ public static final String METHOD_POST = "POST";
+
+ private final URI uri;
+ private final String method;
+ private final String bodyString;
+ private final InputStream bodyStream;
+
+ public StandardHttpRequest(Map> headers, URI uri, String method, String bodyString,
+ InputStream bodyStream) {
+ super(headers);
+ this.uri = uri;
+ this.method = method;
+ this.bodyString = bodyString;
+ this.bodyStream = bodyStream;
+ }
+
+ @Override
+ public URI uri() {
+ return uri;
+ }
+
+ @Override
+ public String method() {
+ return method;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String bodyString() {
+ return bodyString;
+ }
+
+ /**
+ * Return the body as a string, but only if one of the byte[] or InputStream valued {@link HttpRequest.Builder}
+ * methods were used otherwise null.
+ *
+ * @return the body as InputStream.
+ */
+ public InputStream bodyStream() {
+ return bodyStream;
+ }
+
+ public Builder toBuilder() {
+ return new Builder(this);
+ }
+
+ public static final class Builder extends AbstractBasicBuilder implements HttpRequest.Builder {
+
+ private String method = "GET";
+ private InputStream bodyAsStream;
+ private String bodyAsString;
+
+ public Builder() {
+ }
+
+ public Builder(StandardHttpRequest original) {
+ super.uri(original.uri());
+ super.setHeaders(original.headers());
+ method = original.method;
+ bodyAsString = original.bodyString;
+ bodyAsStream = original.bodyStream;
+ }
+
+ @Override
+ public StandardHttpRequest build() {
+ return new StandardHttpRequest(
+ getHeaders(), Objects.requireNonNull(getUri()), method, bodyAsString, bodyAsStream);
+ }
+
+ @Override
+ public HttpRequest.Builder uri(String uri) {
+ return super.uri(URI.create(uri));
+ }
+
+ @Override
+ public HttpRequest.Builder url(URL url) {
+ try {
+ return super.uri(url.toURI());
+ } catch (URISyntaxException e) {
+ throw KubernetesClientException.launderThrowable(e);
+ }
+ }
+
+ @Override
+ public HttpRequest.Builder post(String contentType, byte[] writeValueAsBytes) {
+ return post(contentType, new ByteArrayInputStream(writeValueAsBytes), writeValueAsBytes.length);
+ }
+
+ @Override
+ public HttpRequest.Builder post(String contentType, InputStream stream, long length) {
+ if (length >= 0) {
+ header(CONTENT_LENGTH, Long.toString(length));
+ }
+ method = METHOD_POST;
+ contentType(contentType);
+ bodyAsStream = stream;
+ return this;
+ }
+
+ @Override
+ public HttpRequest.Builder method(String method, String contentType, String body) {
+ this.method = method;
+ contentType(contentType);
+ this.bodyAsString = body;
+ return this;
+ }
+
+ private void contentType(String contentType) {
+ if (contentType != null) {
+ setHeader(CONTENT_TYPE, contentType);
+ }
+ }
+
+ @Override
+ public HttpRequest.Builder expectContinue() {
+ setHeader(EXPECT, EXPECT_CONTINUE);
+ return this;
+ }
+ }
+}
diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/http/StandardMediaTypes.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/http/StandardMediaTypes.java
new file mode 100644
index 00000000000..5efdd75463f
--- /dev/null
+++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/http/StandardMediaTypes.java
@@ -0,0 +1,25 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.fabric8.kubernetes.client.http;
+
+public class StandardMediaTypes {
+
+ public static final String APPLICATION_OCTET_STREAM = "application/octet-stream";
+ public static final String TEXT_PLAIN = "text/plain";
+
+ private StandardMediaTypes() {
+ }
+}
diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/http/WebSocket.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/http/WebSocket.java
index a8ceaf5f5a1..9d2c32ec59a 100644
--- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/http/WebSocket.java
+++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/http/WebSocket.java
@@ -74,6 +74,21 @@ interface Builder extends BasicBuilder {
*/
CompletableFuture buildAsync(Listener listener);
+ /**
+ * Protocol used for WebSocket message exchange.
+ *
+ *
+ * The client can request that the server use a specific subprotocol by
+ * including the |Sec-WebSocket-Protocol| field in its handshake. If it
+ * is specified, the server needs to include the same field and one of
+ * the selected subprotocol values in its response for the connection to
+ * be established.
+ *
+ * RFC 6455: Section 1.9, Subprotocols Using the WebSocket Protocol
+ *
+ * @param protocol the protocol to be used.
+ * @return this builder.
+ */
Builder subprotocol(String protocol);
@Override
@@ -122,4 +137,23 @@ interface Builder extends BasicBuilder {
*/
void request();
+ /**
+ * Converts http or https URIs to ws or wss URIs.
+ *
+ * Clients such as JDK or Jetty require URIs to be performed to the ws protocol, other clients perform
+ * this same transformation automatically.
+ *
+ * @param httpUri the original URI to transform.
+ * @return a new URI with the converted protocol (if applicable).
+ */
+ static URI toWebSocketUri(URI httpUri) {
+ if (httpUri != null && httpUri.getScheme().startsWith("http")) {
+ // the jdk logic expects a ws uri
+ // after the https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8245245 it just does the reverse of this
+ // to convert back to http(s) ...
+ return URI.create("ws" + httpUri.toString().substring(4));
+ }
+ return httpUri;
+ }
+
}
diff --git a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/http/AbstractAsyncBodyTest.java b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/http/AbstractAsyncBodyTest.java
new file mode 100644
index 00000000000..f15715f5221
--- /dev/null
+++ b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/http/AbstractAsyncBodyTest.java
@@ -0,0 +1,116 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.fabric8.kubernetes.client.http;
+
+import io.fabric8.mockwebserver.DefaultMockServer;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import java.nio.CharBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public abstract class AbstractAsyncBodyTest {
+
+ private static DefaultMockServer server;
+
+ @BeforeAll
+ static void beforeAll() {
+ server = new DefaultMockServer(false);
+ server.start();
+ }
+
+ @AfterAll
+ static void afterAll() {
+ server.shutdown();
+ }
+
+ protected abstract HttpClient.Factory getHttpClientFactory();
+
+ @Test
+ @DisplayName("Lines are processed and consumed only after the consume() invocation")
+ public void consumeLinesProcessedAfterConsume() throws Exception {
+ try (final HttpClient client = getHttpClientFactory().newBuilder().build()) {
+ server.expect().withPath("/consume-lines")
+ .andReturn(200, "This is the response body\n")
+ .always();
+ final StringBuffer responseText = new StringBuffer();
+ final HttpResponse asyncBodyResponse = client.consumeLines(
+ client.newHttpRequestBuilder().uri(server.url("/consume-lines")).build(),
+ (value, asyncBody) -> {
+ responseText.append(value);
+ asyncBody.done().complete(null); // OkHttp requires this, not sure if it should
+ })
+ .get(10L, TimeUnit.SECONDS);
+ assertThat(responseText).isEmpty();
+ asyncBodyResponse.body().consume();
+ asyncBodyResponse.body().done().get(10L, TimeUnit.SECONDS);
+ assertThat(responseText).contains("This is the response body");
+ }
+ }
+
+ @Test
+ @DisplayName("Lines are not processed when cancel() invocation")
+ public void consumeLinesNotProcessedIfCancelled() throws Exception {
+ try (final HttpClient client = getHttpClientFactory().newBuilder().build()) {
+ server.expect().withPath("/cancel")
+ .andReturn(200, "This would be the response body")
+ .always();
+ final StringBuffer responseText = new StringBuffer();
+ final HttpResponse asyncBodyResponse = client
+ .consumeLines(client.newHttpRequestBuilder()
+ .uri(server.url("/cancel")).build(), (value, asyncBody) -> responseText.append(value))
+ .get(10L, TimeUnit.SECONDS);
+ asyncBodyResponse.body().cancel();
+ asyncBodyResponse.body().consume();
+ final CompletableFuture doneFuture = asyncBodyResponse.body().done();
+ assertThrows(CancellationException.class, () -> doneFuture.get(10L, TimeUnit.SECONDS));
+ assertThat(responseText).isEmpty();
+ }
+ }
+
+ @Test
+ @DisplayName("Bytes are processed and consumed only after the consume() invocation")
+ public void consumeByteBufferLinesProcessedAfterConsume() throws Exception {
+ try (final HttpClient client = getHttpClientFactory().newBuilder().build()) {
+ server.expect().withPath("/consume-bytes")
+ .andReturn(200, "This is the response body as bytes")
+ .always();
+ final StringBuffer responseText = new StringBuffer();
+ final HttpResponse asyncBodyResponse = client.consumeBytes(
+ client.newHttpRequestBuilder().uri(server.url("/consume-bytes")).build(),
+ (value, asyncBody) -> {
+ responseText.append(value.stream().map(StandardCharsets.UTF_8::decode)
+ .map(CharBuffer::toString).collect(Collectors.joining()));
+ asyncBody.done().complete(null); // OkHttp requires this, not sure if it should
+ })
+ .get(10L, TimeUnit.SECONDS);
+ assertThat(responseText).isEmpty();
+ asyncBodyResponse.body().consume();
+ asyncBodyResponse.body().done().get(10L, TimeUnit.SECONDS);
+ assertThat(responseText).contains("This is the response body as bytes");
+ }
+ }
+
+}
diff --git a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/http/AbstractHttpClientProxyTest.java b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/http/AbstractHttpClientProxyTest.java
new file mode 100644
index 00000000000..cb9b3ce5da7
--- /dev/null
+++ b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/http/AbstractHttpClientProxyTest.java
@@ -0,0 +1,69 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.fabric8.kubernetes.client.http;
+
+import io.fabric8.mockwebserver.DefaultMockServer;
+import okhttp3.Headers;
+import okhttp3.mockwebserver.RecordedRequest;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import java.net.InetSocketAddress;
+import java.util.Collections;
+import java.util.concurrent.TimeUnit;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public abstract class AbstractHttpClientProxyTest {
+
+ private static DefaultMockServer server;
+
+ @BeforeAll
+ static void beforeAll() {
+ server = new DefaultMockServer(false);
+ server.start();
+ }
+
+ @AfterAll
+ static void afterAll() {
+ server.shutdown();
+ }
+
+ protected abstract HttpClient.Factory getHttpClientFactory();
+
+ @Test
+ @DisplayName("Proxied HttpClient adds required headers to the request")
+ void proxyConfigurationAddsRequiredHeaders() throws Exception {
+ // Given
+ final HttpClient.Builder builder = getHttpClientFactory().newBuilder()
+ .proxyAddress(new InetSocketAddress("localhost", server.getPort()))
+ .proxyAuthorization("auth:cred");
+ try (HttpClient client = builder.build()) {
+ // When
+ client.sendAsync(client.newHttpRequestBuilder()
+ .uri(String.format("http://0.0.0.0:%s/not-found", server.getPort())).build(), String.class)
+ .get(10L, TimeUnit.SECONDS);
+ // Then
+ assertThat(server.getLastRequest())
+ .extracting(RecordedRequest::getHeaders)
+ .extracting(Headers::toMultimap)
+ .hasFieldOrPropertyWithValue("Host", Collections.singletonList("0.0.0.0:" + server.getPort()))
+ .hasFieldOrPropertyWithValue("Proxy-Authorization", Collections.singletonList("auth:cred"));
+ }
+ }
+}
diff --git a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/http/AbstractHttpPostTest.java b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/http/AbstractHttpPostTest.java
new file mode 100644
index 00000000000..19e7327e64c
--- /dev/null
+++ b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/http/AbstractHttpPostTest.java
@@ -0,0 +1,128 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.fabric8.kubernetes.client.http;
+
+import io.fabric8.mockwebserver.DefaultMockServer;
+import okhttp3.mockwebserver.RecordedRequest;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayInputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.concurrent.TimeUnit;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public abstract class AbstractHttpPostTest {
+
+ private static DefaultMockServer server;
+
+ @BeforeAll
+ static void beforeAll() {
+ server = new DefaultMockServer(false);
+ server.start();
+ }
+
+ @AfterAll
+ static void afterAll() {
+ server.shutdown();
+ }
+
+ protected abstract HttpClient.Factory getHttpClientFactory();
+
+ @Test
+ @DisplayName("String body, should send a POST request with body")
+ public void postStringBody() throws Exception {
+ // When
+ try (HttpClient client = getHttpClientFactory().newBuilder().build()) {
+ client
+ .sendAsync(client.newHttpRequestBuilder()
+ .uri(server.url("/post-string"))
+ .post("text/plain", "A string body")
+ .build(), String.class)
+ .get(10L, TimeUnit.SECONDS);
+ }
+ // Then
+ assertThat(server.getLastRequest())
+ .returns("POST", RecordedRequest::getMethod)
+ .returns("A string body", rr -> rr.getBody().readUtf8())
+ .extracting(rr -> rr.getHeader("Content-Type")).asString()
+ .startsWith("text/plain");
+ }
+
+ @Test
+ @DisplayName("InputStream body, should send a POST request with body")
+ public void postInputStreamBody() throws Exception {
+ // When
+ try (HttpClient client = getHttpClientFactory().newBuilder().build()) {
+ client
+ .sendAsync(client.newHttpRequestBuilder()
+ .uri(server.url("/post-input-stream"))
+ .post("text/plain", new ByteArrayInputStream("A string body".getBytes(StandardCharsets.UTF_8)), -1)
+ .build(), String.class)
+ .get(10L, TimeUnit.SECONDS);
+ }
+ // Then
+ assertThat(server.getLastRequest())
+ .returns("POST", RecordedRequest::getMethod)
+ .returns("A string body", rr -> rr.getBody().readUtf8())
+ .extracting(rr -> rr.getHeader("Content-Type")).asString()
+ .startsWith("text/plain");
+ }
+
+ @Test
+ @DisplayName("byte[] body, should send a POST request with body")
+ public void postBytesBody() throws Exception {
+ // When
+ try (HttpClient client = getHttpClientFactory().newBuilder().build()) {
+ client
+ .sendAsync(client.newHttpRequestBuilder()
+ .uri(server.url("/post-bytes"))
+ .post("text/plain", "A string body".getBytes(StandardCharsets.UTF_8))
+ .build(), String.class)
+ .get(10L, TimeUnit.SECONDS);
+ }
+ // Then
+ assertThat(server.getLastRequest())
+ .returns("POST", RecordedRequest::getMethod)
+ .returns("A string body", rr -> rr.getBody().readUtf8())
+ .extracting(rr -> rr.getHeader("Content-Type")).asString()
+ .startsWith("text/plain");
+ }
+
+ @Test
+ @DisplayName("FormData body, should send a POST request with body")
+ public void postFormDataBody() throws Exception {
+ // When
+ try (HttpClient client = getHttpClientFactory().newBuilder().build()) {
+ client
+ .sendAsync(client.newHttpRequestBuilder()
+ .uri(server.url("/post-bytes"))
+ .post(Collections.singletonMap("field", "value"))
+ .build(), String.class)
+ .get(10L, TimeUnit.SECONDS);
+ }
+ // Then
+ assertThat(server.getLastRequest())
+ .returns("POST", RecordedRequest::getMethod)
+ .returns("field=value", rr -> rr.getBody().readUtf8())
+ .extracting(rr -> rr.getHeader("Content-Type")).asString()
+ .startsWith("application/x-www-form-urlencoded");
+ }
+}
diff --git a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/http/AbstractInterceptorTest.java b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/http/AbstractInterceptorTest.java
new file mode 100644
index 00000000000..958abd804c5
--- /dev/null
+++ b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/http/AbstractInterceptorTest.java
@@ -0,0 +1,175 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.fabric8.kubernetes.client.http;
+
+import io.fabric8.mockwebserver.DefaultMockServer;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public abstract class AbstractInterceptorTest {
+
+ private static DefaultMockServer server;
+
+ @BeforeAll
+ static void beforeAll() {
+ server = new DefaultMockServer(false);
+ server.start();
+ }
+
+ @AfterAll
+ static void afterAll() {
+ server.shutdown();
+ }
+
+ protected abstract HttpClient.Factory getHttpClientFactory();
+
+ @Test
+ @DisplayName("before, should add a header to the HTTP request")
+ public void beforeAddsHeaderToRequest() throws Exception {
+ // Given
+ final HttpClient.Builder builder = getHttpClientFactory().newBuilder()
+ .addOrReplaceInterceptor("test", new Interceptor() {
+ @Override
+ public void before(BasicBuilder builder, HttpHeaders headers) {
+ builder.header("Test-Header", "Test-Value");
+ }
+ });
+ // When
+ try (HttpClient client = builder.build()) {
+ client.sendAsync(client.newHttpRequestBuilder().uri(server.url("/intercept-before")).build(), String.class)
+ .get(10L, TimeUnit.SECONDS);
+ }
+ // Then
+ assertThat(server.getLastRequest().getHeaders().toMultimap())
+ .containsEntry("test-header", Collections.singletonList("Test-Value"));
+ }
+
+ @Test
+ @DisplayName("afterFailure (HTTP), replaces the HttpResponse produced by HttpClient.sendAsync")
+ public void afterHttpFailureReplacesResponseInSendAsync() throws Exception {
+ // Given
+ server.expect().withPath("/intercepted-url").andReturn(200, "This works").always();
+ final HttpClient.Builder builder = getHttpClientFactory().newBuilder()
+ .addOrReplaceInterceptor("test", new Interceptor() {
+ @Override
+ public CompletableFuture afterFailure(BasicBuilder builder, HttpResponse> response) {
+ builder.uri(URI.create(server.url("/intercepted-url")));
+ return CompletableFuture.completedFuture(true);
+ }
+ });
+ // When
+ try (HttpClient client = builder.build()) {
+ final HttpResponse result = client
+ .sendAsync(client.newHttpRequestBuilder().uri(server.url("/not-found")).build(), String.class)
+ .get(10L, TimeUnit.SECONDS);
+ // Then
+ assertThat(result)
+ .returns("This works", HttpResponse::body)
+ .returns(200, HttpResponse::code);
+ }
+ }
+
+ @Test
+ @DisplayName("afterFailure (HTTP), replaces the HttpResponse produced by HttpClient.consumeLines")
+ public void afterHttpFailureReplacesResponseInConsumeLines() throws Exception {
+ // Given
+ server.expect().withPath("/intercepted-url").andReturn(200, "This works").always();
+ final HttpClient.Builder builder = getHttpClientFactory().newBuilder()
+ .addOrReplaceInterceptor("test", new Interceptor() {
+ @Override
+ public CompletableFuture afterFailure(BasicBuilder builder, HttpResponse> response) {
+ builder.uri(URI.create(server.url("/intercepted-url")));
+ return CompletableFuture.completedFuture(true);
+ }
+ });
+ final AtomicReference result = new AtomicReference<>();
+ // When
+ try (HttpClient client = builder.build()) {
+ final HttpResponse asyncR = client.consumeLines(
+ client.newHttpRequestBuilder().uri(server.url("/not-found")).build(), (s, ab) -> result.set(s))
+ .get(10L, TimeUnit.SECONDS);
+ asyncR.body().consume();
+ asyncR.body().done().get(10L, TimeUnit.SECONDS);
+ // Then
+ assertThat(result).hasValue("This works");
+ }
+ }
+
+ @Test
+ @DisplayName("afterFailure (HTTP), replaces the HttpResponse produced by HttpClient.consumeBytes")
+ public void afterHttpFailureReplacesResponseInConsumeBytes() throws Exception {
+ // Given
+ server.expect().withPath("/intercepted-url").andReturn(200, "This works").always();
+ final HttpClient.Builder builder = getHttpClientFactory().newBuilder()
+ .addOrReplaceInterceptor("test", new Interceptor() {
+ @Override
+ public CompletableFuture afterFailure(BasicBuilder builder, HttpResponse> response) {
+ builder.uri(URI.create(server.url("/intercepted-url")));
+ return CompletableFuture.completedFuture(true);
+ }
+ });
+ final AtomicReference result = new AtomicReference<>();
+ // When
+ try (HttpClient client = builder.build()) {
+ final HttpResponse asyncR = client.consumeBytes(
+ client.newHttpRequestBuilder().uri(server.url("/not-found")).build(),
+ (s, ab) -> result.set(StandardCharsets.UTF_8.decode(s.iterator().next()).toString()))
+ .get(10L, TimeUnit.SECONDS);
+ asyncR.body().consume();
+ asyncR.body().done().get(10L, TimeUnit.SECONDS);
+ // Then
+ assertThat(result).hasValue("This works");
+ }
+ }
+
+ @Test
+ @DisplayName("interceptors should be applied in the order they were added")
+ public void interceptorsAreAppliedInOrder() throws Exception {
+ // Given
+ final HttpClient.Builder builder = getHttpClientFactory().newBuilder()
+ .addOrReplaceInterceptor("first", new Interceptor() {
+ @Override
+ public void before(BasicBuilder builder, HttpHeaders headers) {
+ builder.header("Test-Header", "Test-Value");
+ }
+ })
+ .addOrReplaceInterceptor("second", new Interceptor() {
+ @Override
+ public void before(BasicBuilder builder, HttpHeaders headers) {
+ builder.setHeader("Test-Header", "Test-Value-Override");
+ }
+ });
+ // When
+ try (HttpClient client = builder.build()) {
+ client.sendAsync(client.newHttpRequestBuilder().uri(server.url("/intercept-before")).build(), String.class)
+ .get(10L, TimeUnit.SECONDS);
+ }
+ // Then
+ assertThat(server.getLastRequest().getHeaders().toMultimap())
+ .containsEntry("test-header", Collections.singletonList("Test-Value-Override"));
+ }
+}
diff --git a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/http/AbstractWebSocketSendTest.java b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/http/AbstractWebSocketSendTest.java
new file mode 100644
index 00000000000..69a35845d0c
--- /dev/null
+++ b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/http/AbstractWebSocketSendTest.java
@@ -0,0 +1,85 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.fabric8.kubernetes.client.http;
+
+import io.fabric8.mockwebserver.DefaultMockServer;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import java.net.URI;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public abstract class AbstractWebSocketSendTest {
+
+ private static DefaultMockServer server;
+
+ @BeforeAll
+ static void beforeAll() {
+ server = new DefaultMockServer(false);
+ server.start();
+ }
+
+ @AfterAll
+ static void afterAll() {
+ server.shutdown();
+ }
+
+ protected abstract HttpClient.Factory getHttpClientFactory();
+
+ @Test
+ @DisplayName("send, emits a message to the server (the server responds to this message)")
+ void sendEmitsMessageToWebSocketServer() throws Exception {
+ try (final HttpClient client = getHttpClientFactory().newBuilder().build()) {
+ // Given
+ server.expect().withPath("/send-text")
+ .andUpgradeToWebSocket()
+ .open()
+ .expect("GiveMeSomething")
+ .andEmit("received")
+ .always()
+ .done()
+ .always();
+ final BlockingQueue receivedText = new ArrayBlockingQueue<>(1);
+ final WebSocket ws = client.newWebSocketBuilder()
+ // TODO: JDK HttpClient implementation doesn't work with ws URIs
+ // - Currently we are using an HttpRequest.Builder which is then
+ // mapped to a WebSocket.Builder. We should probably user the WebSocket.Builder
+ // directly
+ //.uri(URI.create(String.format("ws://%s:%s/send-text", server.getHostName(), server.getPort())))
+ .uri(URI.create(server.url("send-text")))
+ .buildAsync(new WebSocket.Listener() {
+ public void onMessage(WebSocket webSocket, String text) {
+ assertTrue(receivedText.offer(text));
+ }
+ }).get(10L, TimeUnit.SECONDS);
+ // When
+ ws.send(ByteBuffer.wrap("GiveMeSomething".getBytes(StandardCharsets.UTF_8)));
+ final String result = receivedText.poll(10L, TimeUnit.SECONDS);
+ // Then
+ assertThat(result).isEqualTo("received");
+ }
+ }
+
+}
diff --git a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/http/StandardHttpRequestTest.java b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/http/StandardHttpRequestTest.java
new file mode 100644
index 00000000000..1a9beb72cfb
--- /dev/null
+++ b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/http/StandardHttpRequestTest.java
@@ -0,0 +1,107 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.fabric8.kubernetes.client.http;
+
+import io.fabric8.kubernetes.client.utils.IOHelpers;
+import org.assertj.core.api.InstanceOfAssertFactories;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.entry;
+
+class StandardHttpRequestTest {
+
+ @Test
+ void builderMethod() {
+ // Given
+ final HttpRequest.Builder b = new StandardHttpRequest.Builder()
+ .header("h1", "v1")
+ .header("h1", "v2")
+ .uri("https://example.com/aitana")
+ .expectContinue()
+ .method("PUT", "application/json", "{\"v\":true}");
+ // When
+ final HttpRequest result = b.build();
+ // Then
+ assertThat(result)
+ .isInstanceOf(StandardHttpRequest.class)
+ .returns(URI.create("https://example.com/aitana"), HttpRequest::uri)
+ .returns("PUT", HttpRequest::method)
+ .returns("{\"v\":true}", HttpRequest::bodyString)
+ .extracting(HttpHeaders::headers, InstanceOfAssertFactories.map(String.class, List.class))
+ .containsOnly(
+ entry("h1", Arrays.asList("v1", "v2")),
+ entry("Content-Type", Collections.singletonList("application/json")),
+ entry("Expect", Collections.singletonList("100-Continue")));
+ }
+
+ @Test
+ void builderPostBytes() {
+ // Given
+ final HttpRequest.Builder b = new StandardHttpRequest.Builder()
+ .uri("https://example.com/alex")
+ .post("text/plain", "The bytes".getBytes(StandardCharsets.UTF_8));
+ // When
+ final HttpRequest result = b.build();
+ // Then
+ assertThat(result)
+ .isInstanceOf(StandardHttpRequest.class)
+ .asInstanceOf(InstanceOfAssertFactories.type(StandardHttpRequest.class))
+ .returns(URI.create("https://example.com/alex"), HttpRequest::uri)
+ .returns("POST", HttpRequest::method)
+ .returns("The bytes", r -> toString(r.bodyStream()))
+ .extracting(HttpHeaders::headers, InstanceOfAssertFactories.map(String.class, List.class))
+ .containsOnly(
+ entry("Content-Type", Collections.singletonList("text/plain")),
+ entry("Content-Length", Collections.singletonList("9")));
+ }
+
+ @Test
+ void toBuilderReturnsNewBuilderWithPreservedSettings() {
+ // Given
+ final HttpRequest.Builder b = new StandardHttpRequest.Builder()
+ .header("h1", "v1")
+ .uri("https://example.com/julia");
+ // When
+ final HttpRequest.Builder result = ((StandardHttpRequest) b.build()).toBuilder();
+ // Then
+ assertThat(result)
+ .isNotSameAs(b)
+ .extracting(HttpRequest.Builder::build)
+ .isInstanceOf(StandardHttpRequest.class)
+ .returns(URI.create("https://example.com/julia"), HttpRequest::uri)
+ .returns("GET", HttpRequest::method)
+ .extracting(HttpHeaders::headers, InstanceOfAssertFactories.map(String.class, List.class))
+ .containsOnly(
+ entry("h1", Collections.singletonList("v1")));
+ }
+
+ static String toString(InputStream is) {
+ try {
+ return IOHelpers.readFully(is);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/http/TestHttpHeaders.java b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/http/TestHttpHeaders.java
index 17573871dd3..48101b1622a 100644
--- a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/http/TestHttpHeaders.java
+++ b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/http/TestHttpHeaders.java
@@ -23,7 +23,7 @@
/**
* Basic {@link HttpHeaders} implementation to be used in tests instead of mocks or real headers.
- *
+ *
* @param type for the return type of chained methods.
*/
public class TestHttpHeaders implements HttpHeaders {
@@ -35,6 +35,11 @@ public List headers(String key) {
return headers.getOrDefault(key, Collections.emptyList());
}
+ @Override
+ public Map> headers() {
+ return headers;
+ }
+
public T clearHeaders() {
headers.clear();
return (T) this;
diff --git a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/http/WebSocketTest.java b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/http/WebSocketTest.java
new file mode 100644
index 00000000000..6fe10b76233
--- /dev/null
+++ b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/http/WebSocketTest.java
@@ -0,0 +1,49 @@
+/**
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.fabric8.kubernetes.client.http;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.net.URI;
+import java.util.stream.Stream;
+
+import static io.fabric8.kubernetes.client.http.WebSocket.toWebSocketUri;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.params.provider.Arguments.arguments;
+
+class WebSocketTest {
+
+ @ParameterizedTest(name = "{index}: toWebSocketUri: from ''{0}'' changes protocol to ''{1}''")
+ @MethodSource("toWebSocketUriInput")
+ void toWebSocketUriFromHttp(String uri, String expectedScheme) {
+ // When
+ final URI result = toWebSocketUri(URI.create(uri));
+ // Then
+ assertThat(result).hasScheme(expectedScheme);
+ }
+
+ static Stream toWebSocketUriInput() {
+ return Stream.of(
+ arguments("http://example.com", "ws"),
+ arguments("https://example.com", "wss"),
+ arguments("wss://example.com", "wss"),
+ arguments("ws://example.com", "ws"));
+ }
+
+}
diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/BaseClient.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/BaseClient.java
index 4b311bde3ca..a98364ba686 100644
--- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/BaseClient.java
+++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/BaseClient.java
@@ -189,10 +189,14 @@ public boolean supports(Class type) {
if (Utils.isNullOrEmpty(typeApiVersion) || Utils.isNullOrEmpty(typeKind)) {
return false;
}
- return getApiResources(ApiVersionUtil.joinApiGroupAndVersion(
- HasMetadata.getGroup(type), HasMetadata.getVersion(type))).getResources()
- .stream()
- .anyMatch(r -> typeKind.equals(r.getKind()));
+ final APIResourceList apiResources = getApiResources(ApiVersionUtil.joinApiGroupAndVersion(
+ HasMetadata.getGroup(type), HasMetadata.getVersion(type)));
+ if (apiResources == null) {
+ return false;
+ }
+ return apiResources.getResources()
+ .stream()
+ .anyMatch(r -> typeKind.equals(r.getKind()));
}
@Override
diff --git a/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/BaseClientTest.java b/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/BaseClientTest.java
index 8c4f303c393..ea0d06356e1 100644
--- a/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/BaseClientTest.java
+++ b/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/BaseClientTest.java
@@ -97,6 +97,19 @@ void supportsPodInServer() {
}
}
+ @Test
+ @DisplayName("supports Ingress, with no group+version registered in server, should check API server and return false")
+ void supportsNetworkingApiNotFound() {
+ try (MockedConstruction ignore = mockConstruction(OperationSupport.class,
+ (mock, ctx) -> when(mock.restCall(APIResourceList.class, "/apis", "networking.k8s.io/v1"))
+ .thenReturn(null))) {
+ // When
+ final boolean result = baseClient.supports(Ingress.class);
+ // Then
+ assertThat(result).isFalse();
+ }
+ }
+
@Test
@DisplayName("supports Ingress, with support in server, should check API server and return true")
void supportsIngressInServer() {
diff --git a/kubernetes-itests/pom.xml b/kubernetes-itests/pom.xml
index 27109ea293a..cb6abe4c453 100644
--- a/kubernetes-itests/pom.xml
+++ b/kubernetes-itests/pom.xml
@@ -89,5 +89,33 @@
+
+ httpclient-jetty
+
+
+ io.fabric8
+ openshift-client
+ test
+
+
+ io.fabric8
+ kubernetes-httpclient-okhttp
+
+
+
+
+ io.fabric8
+ kubernetes-httpclient-jetty
+
+
+
+
+ sonatype-snapshots
+ https://oss.sonatype.org/content/repositories/snapshots
+ false
+ true
+
+
+
diff --git a/kubernetes-itests/src/test/java/io/fabric8/kubernetes/PodIT.java b/kubernetes-itests/src/test/java/io/fabric8/kubernetes/PodIT.java
index 95a8209f531..db29edf993e 100644
--- a/kubernetes-itests/src/test/java/io/fabric8/kubernetes/PodIT.java
+++ b/kubernetes-itests/src/test/java/io/fabric8/kubernetes/PodIT.java
@@ -32,7 +32,6 @@
import io.fabric8.kubernetes.client.dsl.ExecWatch;
import io.fabric8.kubernetes.client.dsl.PodResource;
import io.fabric8.kubernetes.client.readiness.Readiness;
-import io.fabric8.kubernetes.client.utils.IOHelpers;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -46,7 +45,9 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
+import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -137,31 +138,28 @@ void evict() {
.withSpec(pod1.getSpec())
.build();
- client.pods().withName(pod1.getMetadata().getName())
- .waitUntilReady(POD_READY_WAIT_IN_SECONDS, TimeUnit.SECONDS);
+ client.pods().resource(pod1).waitUntilReady(POD_READY_WAIT_IN_SECONDS, TimeUnit.SECONDS);
- client.pods().createOrReplace(pod2);
- client.pods().withName(pod2.getMetadata().getName())
- .waitUntilReady(POD_READY_WAIT_IN_SECONDS, TimeUnit.SECONDS);
+ client.pods().resource(pod2).createOrReplace();
+ client.pods().resource(pod2).waitUntilReady(POD_READY_WAIT_IN_SECONDS, TimeUnit.SECONDS);
- client.policy().v1beta1().podDisruptionBudget().createOrReplace(pdb);
+ client.resource(pdb).createOrReplace();
// the server needs to process the pdb before the eviction can proceed, so we'll need to wait here
await().atMost(5, TimeUnit.MINUTES)
.until(() -> client.pods().withName(pod2.getMetadata().getName()).evict());
// cant evict because only one left
- assertFalse(client.pods().withName(pod1.getMetadata().getName()).evict());
+ assertFalse(client.pods().resource(pod1).evict());
// ensure it really is still up
- assertTrue(Readiness.getInstance().isReady(client.pods().withName(pod1.getMetadata().getName()).fromServer().get()));
+ assertTrue(Readiness.getInstance().isReady(client.pods().resource(pod1).fromServer().get()));
// create another pod to satisfy PDB
- client.pods().createOrReplace(pod3);
- client.pods().withName(pod3.getMetadata().getName())
- .waitUntilReady(POD_READY_WAIT_IN_SECONDS, TimeUnit.SECONDS);
+ client.pods().resource(pod3).createOrReplace();
+ client.pods().resource(pod3).waitUntilReady(POD_READY_WAIT_IN_SECONDS, TimeUnit.SECONDS);
// can now evict
- assertTrue(client.pods().withName(pod1.getMetadata().getName()).evict());
+ assertTrue(client.pods().resource(pod3).evict());
}
@Test
@@ -172,16 +170,17 @@ void log() {
}
@Test
- void exec() throws InterruptedException, IOException {
+ void exec() throws Exception {
client.pods().withName("pod-standard").waitUntilReady(POD_READY_WAIT_IN_SECONDS, TimeUnit.SECONDS);
final CountDownLatch execLatch = new CountDownLatch(1);
ByteArrayOutputStream out = new ByteArrayOutputStream();
- AtomicBoolean closed = new AtomicBoolean();
+ final AtomicBoolean closed = new AtomicBoolean();
+ final AtomicBoolean failed = new AtomicBoolean();
+ final CompletableFuture exitStatus = new CompletableFuture<>();
int[] exitCode = new int[] { Integer.MAX_VALUE };
- ExecWatch execWatch = client.pods().withName("pod-standard")
+ client.pods().withName("pod-standard")
.writingOutput(out)
- .redirectingErrorChannel()
- .withTTY().usingListener(new ExecListener() {
+ .usingListener(new ExecListener() {
@Override
public void onOpen() {
logger.info("Shell was opened");
@@ -190,6 +189,7 @@ public void onOpen() {
@Override
public void onFailure(Throwable t, Response failureResponse) {
logger.info("Shell barfed");
+ failed.set(true);
execLatch.countDown();
}
@@ -203,13 +203,15 @@ public void onClose(int i, String s) {
@Override
public void onExit(int code, Status status) {
exitCode[0] = code;
+ exitStatus.complete(status);
}
- }).exec("date");
- // the stream must be read or closed to receive onClose
- assertEquals("{\"metadata\":{},\"status\":\"Success\"}", IOHelpers.readFully(execWatch.getErrorChannel()));
- execLatch.await(5, TimeUnit.SECONDS);
+ }).exec("sh", "-c", "echo 'hello world!'");
+ assertThat(exitStatus.get(5, TimeUnit.SECONDS))
+ .hasFieldOrPropertyWithValue("status", "Success");
+ assertTrue(execLatch.await(5, TimeUnit.SECONDS));
assertEquals(0, exitCode[0]);
assertTrue(closed.get());
+ assertFalse(failed.get());
assertNotNull(out.toString());
}
@@ -242,7 +244,7 @@ void uploadFile() throws IOException {
client.pods().withName("pod-standard").waitUntilReady(POD_READY_WAIT_IN_SECONDS, TimeUnit.SECONDS);
// Wait for resources to get ready
final Path tmpFile = Files.createTempFile("PodIT", "toBeUploaded");
- Files.write(tmpFile, Arrays.asList("I'm uploaded"));
+ Files.write(tmpFile, Collections.singletonList("I'm uploaded"));
assertUploaded("pod-standard", tmpFile, "/tmp/toBeUploaded");
assertUploaded("pod-standard", tmpFile, "/tmp/001_special_!@#\\$^&(.mp4");
diff --git a/pom.xml b/pom.xml
index 0007f2b77da..8127fc2132b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -86,6 +86,7 @@
1.15.0
1.15.0_1
2.13.3
+ 11.0.9
0.2.2
3.8.5
3.6.4
@@ -94,24 +95,21 @@
3.0.2
+ 5.8.2
3.23.1
4.2.0
15.6.0
+ 4.6.1
- 2.5.0_1
- 1.4.2
1.4.2_1
1.0.2
1.0.1_1
- 1.11-8
1.11-8_1
1.3.0
8.0.1
${jackson.version}
- 5.8.2
0.3.0
1.7.36
- 4.6.1
1.18.24
1.30
1.70
@@ -177,7 +175,7 @@
false
2020-11-14T12:24:00Z
2.0.1.Final
-
+
io.fabric8.kubernetes.api.model