Skip to content

Commit

Permalink
fix #5041: adding raw methods
Browse files Browse the repository at this point in the history
  • Loading branch information
shawkins authored and manusa committed Apr 28, 2023
1 parent 4bd80e1 commit f7273dc
Show file tree
Hide file tree
Showing 13 changed files with 144 additions and 21 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

#### New Features
* Fix #5037: OkHttp-specific logging interceptor replacement. Introducing a generic HTTP interceptor to log HTTP and WS requests.
* Fix #5041: exposed Client.raw methods for arbitrary calls

#### _**Note**_: Breaking changes
* Fix #4875: Removed unused options from the java-generator
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,4 +189,23 @@ default <T extends HasMetadata, L extends KubernetesResourceList<T>> MixedOperat

Config getConfiguration();

/**
* GET the response from the given uri as a String
*
* @param uri must start with / if relative
* @return the response, or null if a 404 code
*/
default String raw(String uri) {
return raw(uri, "GET", null);
}

/**
* The response from the given uri as a String
*
* @param uri must start with / if relative
* @param method an http method verb such as GET, DELETE, PUT, POST
* @param payload a non-String value will be converted to json
* @return the response
*/
String raw(String uri, String method, Object payload);
}
Original file line number Diff line number Diff line change
Expand Up @@ -420,4 +420,9 @@ public void visitResources(ApiVisitor visitor) {
getClient().visitResources(visitor);
}

@Override
public String raw(String uri, String method, Object payload) {
return getClient().raw(uri, method, payload);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -153,4 +153,9 @@ public Client newClient(RequestConfig requestConfig) {

public abstract C newInstance();

@Override
public String raw(String uri, String method, Object payload) {
return client.raw(uri, method, payload);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,9 @@ public HttpRequest.Builder method(String method, String contentType, String body
this.method = method;
this.contentType = contentType;
this.bodyAsString = body;
this.body = new StringBodyContent(body);
if (body != null) {
this.body = new StringBodyContent(body);
}
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
package io.fabric8.kubernetes.client.http;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Optional;

Expand Down Expand Up @@ -82,7 +84,8 @@ public HttpResponse<T> withPreviousResponse(HttpResponse<T> previousResponse) {
return this;
}

public static TestHttpResponse<byte[]> from(int code, String body) {
return new TestHttpResponse<byte[]>().withCode(code).withBody(body.getBytes(StandardCharsets.UTF_8));
public static TestHttpResponse<InputStream> from(int code, String body) {
return new TestHttpResponse<InputStream>().withCode(code)
.withBody(new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
Expand Down Expand Up @@ -488,11 +487,7 @@ protected <T> T handleGet(URL resourceUrl, Class<T> type) throws InterruptedExce
* Send a raw get - where the type should be one of String, Reader, InputStream
*/
protected <T> T handleRawGet(URL resourceUrl, Class<T> type) throws IOException {
HttpRequest.Builder requestBuilder = httpClient.newHttpRequestBuilder().url(resourceUrl);
HttpRequest request = requestBuilder.build();
HttpResponse<T> response = waitForResult(httpClient.sendAsync(request, type));
assertResponseCode(request, response);
return response.body();
return handleRaw(type, resourceUrl.toString(), "GET", null);
}

/**
Expand Down Expand Up @@ -569,11 +564,11 @@ protected <T> CompletableFuture<T> handleResponse(HttpClient client, HttpRequest
VersionUsageUtils.log(this.resourceT, this.apiGroupVersion);
HttpRequest request = requestBuilder.build();

return client.sendAsync(request, byte[].class).thenApply(response -> {
return client.sendAsync(request, InputStream.class).thenApply(response -> {
try {
assertResponseCode(request, response);
if (type != null && type.getType() != null) {
return Serialization.unmarshal(new ByteArrayInputStream(response.body()), type);
return Serialization.unmarshal(response.body(), type);
} else {
return null;
}
Expand Down Expand Up @@ -760,4 +755,28 @@ public <R1> R1 restCall(Class<R1> result, String... path) {
}
}

/**
* Send a raw request - where the type should be one of String, Reader, InputStream
*/
public <R1> R1 handleRaw(Class<R1> result, String uri, String method, Object payload) {
try {
if (uri.startsWith("/")) {
// master ends with slash
uri = config.getMasterUrl() + uri.substring(1, uri.length());
}
String body = null;
if (payload instanceof String) {
body = (String) payload;
} else if (payload != null) {
body = Serialization.asJson(payload);
}
HttpRequest request = httpClient.newHttpRequestBuilder().uri(uri).method(method, JSON, body).build();
HttpResponse<R1> response = waitForResult(httpClient.sendAsync(request, result));
assertResponseCode(request, response);
return response.body();
} catch (IOException e) {
throw KubernetesClientException.launderThrowable(e);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import io.fabric8.kubernetes.client.utils.ApiVersionUtil;
import io.fabric8.kubernetes.client.utils.Utils;

import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
Expand Down Expand Up @@ -354,4 +355,21 @@ public Executor getExecutor() {
return executor;
}

@Override
public String raw(String uri) {
try {
return raw(uri, "GET", null);
} catch (KubernetesClientException e) {
if (e.getCode() != HttpURLConnection.HTTP_NOT_FOUND) {
throw e;
}
return null;
}
}

@Override
public String raw(String uri, String method, Object payload) {
return this.getOperationSupport().handleRaw(String.class, uri, method, payload);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ private HttpClient newHttpClientWithSomeFailures(final AtomicInteger httpExecuti
HttpRequest.Builder mockRequestBuilder = mock(HttpRequest.Builder.class, Mockito.RETURNS_SELF);
when(mockClient.newHttpRequestBuilder()).thenReturn(mockRequestBuilder);
when(mockRequestBuilder.build()).thenReturn(new StandardHttpRequest.Builder().uri("https://k8s.example.com").build());
when(mockClient.sendAsync(Mockito.any(), Mockito.eq(byte[].class))).thenAnswer(
when(mockClient.sendAsync(Mockito.any(), Mockito.eq(InputStream.class))).thenAnswer(
invocation -> {
int count = httpExecutionCounter.getAndIncrement();
if (count < numFailures) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.mockito.Mockito;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
Expand All @@ -58,7 +59,7 @@ public void setUp() throws IOException {
builders = new ArrayList<>();
this.mockClient = Mockito.mock(HttpClient.class, Mockito.RETURNS_DEEP_STUBS);
Config config = new ConfigBuilder().withMasterUrl("https://localhost:8443/").build();
when(mockClient.sendAsync(any(), Mockito.eq(byte[].class)))
when(mockClient.sendAsync(any(), Mockito.eq(InputStream.class)))
.thenReturn(CompletableFuture.completedFuture(TestHttpResponse.from(200,
"{\"kind\":\"Pod\", \"apiVersion\":\"v1\"}")));
kubernetesClient = new KubernetesClientImpl(mockClient, config);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.mockito.Mockito;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
Expand All @@ -59,7 +60,7 @@ public void setUp() throws IOException {
// TODO: fully mocking makes this logic more difficult and basically copied in other tests, we may want to rely on an actual implementation instead
builders = new ArrayList<>();
this.mockClient = Mockito.mock(HttpClient.class, Mockito.RETURNS_DEEP_STUBS);
when(mockClient.sendAsync(any(), Mockito.eq(byte[].class)))
when(mockClient.sendAsync(any(), Mockito.eq(InputStream.class)))
.thenReturn(CompletableFuture.completedFuture(TestHttpResponse.from(200, "{}")));
Config config = new ConfigBuilder().withMasterUrl("https://localhost:8443/").build();
kubernetesClient = new KubernetesClientImpl(mockClient, config);
Expand Down Expand Up @@ -118,8 +119,8 @@ void testYamlPatchConvertedToJson() {
@Test
void testPatchThrowExceptionWhenResourceNotFound() {
// Given
when(mockClient.sendAsync(any(), Mockito.eq(byte[].class)))
.thenReturn(CompletableFuture.completedFuture(new TestHttpResponse<byte[]>().withCode(404)));
when(mockClient.sendAsync(any(), Mockito.eq(InputStream.class)))
.thenReturn(CompletableFuture.completedFuture(new TestHttpResponse<InputStream>().withCode(404)));

// When
PodResource podResource = kubernetesClient.pods()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* 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;

import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.utils.Serialization;
import org.junit.jupiter.api.Test;

import java.util.Map;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class ClientIT {

KubernetesClient client;

@Test
void getRaw() {
String node = client.nodes().list().getItems().stream().map(n -> n.getMetadata().getName()).findFirst().orElse("minikube");
String result = client.raw(String.format("/api/v1/nodes/%s/proxy/stats/summary", node));
assertTrue(Serialization.unmarshal(result, Map.class).containsKey("node"));
}

@Test
void postRaw() {
String result = client.raw(String.format("/api/v1/namespaces/%s/configmaps", client.getNamespace()), "POST",
new ConfigMapBuilder().withNewMetadata().withName("test").endMetadata().build());
assertEquals("test", Serialization.unmarshal(result, ConfigMap.class).getMetadata().getName());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
Expand Down Expand Up @@ -106,9 +107,9 @@ protected String getRecentEvents() {

// When
ByteArrayInputStream inputStream = new ByteArrayInputStream(new byte[0]);
CompletableFuture<HttpResponse<byte[]>> future = new CompletableFuture<>();
CompletableFuture<HttpResponse<InputStream>> future = new CompletableFuture<>();
future.completeExceptionally(new IOException());
when(httpClient.sendAsync(any(), eq(byte[].class))).thenReturn(future);
when(httpClient.sendAsync(any(), eq(InputStream.class))).thenReturn(future);

KubernetesClientException exception = assertThrows(KubernetesClientException.class,
() -> impl.submitToApiServer(inputStream, 0));
Expand All @@ -127,11 +128,11 @@ protected String getRecentEvents() {
};
};

HttpResponse<byte[]> response = mock(HttpResponse.class, Mockito.CALLS_REAL_METHODS);
HttpResponse<InputStream> response = mock(HttpResponse.class, Mockito.CALLS_REAL_METHODS);
when(response.code()).thenReturn(200);
when(response.body()).thenReturn(new byte[0]);
when(response.body()).thenReturn(new ByteArrayInputStream(new byte[0]));

when(httpClient.sendAsync(any(), eq(byte[].class))).thenReturn(CompletableFuture.completedFuture(response));
when(httpClient.sendAsync(any(), eq(InputStream.class))).thenReturn(CompletableFuture.completedFuture(response));
impl.submitToApiServer(new ByteArrayInputStream(new byte[0]), 0);

Mockito.verify(response, Mockito.times(1)).body();
Expand Down

0 comments on commit f7273dc

Please sign in to comment.