Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

set bignumbermode when creating tar for upload to pod #5186

Merged
merged 4 commits into from
Jul 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
### 6.8-SNAPSHOT

#### Bugs
* Fix #5186: Support for Pod uploads with big numbers
* Fix #5298: Prevent requests needing authentication from causing a 403 response
* Fix #5221: Empty kube config file causes NPE
* Fix #5281: Ensure the KubernetesCrudDispatcher's backing map is accessed w/lock
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@

import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -126,6 +128,26 @@ public final TestStandardHttpClient wsExpect(String pathRegex, WsFutureProvider
return this;
}

public final TestStandardHttpClient expect(String pathRegex, int statusCode) {
return expect(pathRegex, statusCode, (byte[]) null);
}

public final TestStandardHttpClient expect(String pathRegex, int statusCode, String body) {
return expect(pathRegex, statusCode, body.getBytes(StandardCharsets.UTF_8));
}

public final TestStandardHttpClient expect(String pathRegex, int statusCode, byte[] body) {
final Expectation expectation = expectations.compute(pathRegex, (k, v) -> v == null ? new Expectation() : v);
expectation.futures.add((r, c) -> {
final AsyncBody asyncBody = new TestAsyncBody();
if (body != null) {
c.consume(Collections.singletonList(ByteBuffer.wrap(body)), asyncBody);
}
return CompletableFuture.completedFuture(new TestHttpResponse<AsyncBody>().withCode(statusCode).withBody(asyncBody));
});
return this;
}

public final TestStandardHttpClient expect(String pathRegex, FutureProvider future) {
final Expectation expectation = expectations.compute(pathRegex, (k, v) -> v == null ? new Expectation() : v);
expectation.futures.add(future);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import lombok.Getter;

import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class TestStandardHttpClientFactory implements HttpClient.Factory {

Expand All @@ -32,4 +34,21 @@ public TestStandardHttpClientBuilder newBuilder() {
public final TestStandardHttpClient getInstance(int index) {
return instances.toArray(new TestStandardHttpClient[0])[index];
}

public final Stream<TestStandardHttpClientFactory> times(int iterations) {
return IntStream.range(0, iterations).mapToObj(i -> this);
}

public final void expect(String pathRegex, int statusCode) {
instances.forEach(c -> c.expect(pathRegex, statusCode));
}

public final void expect(String pathRegex, int statusCode, String body) {
instances.forEach(c -> c.expect(pathRegex, statusCode, body));
}

public final void expect(String pathRegex, int statusCode, byte[] body) {
instances.forEach(c -> c.expect(pathRegex, statusCode, body));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ private static boolean uploadTar(PodOperationsImpl operation, String directory,
boolean uploaded = upload(operation, fileName, os -> {
try (final TarArchiveOutputStream tar = new TarArchiveOutputStream(os)) {
tar.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX);
tar.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_POSIX);
processor.process(tar);
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,14 @@

package io.fabric8.kubernetes.client.behavior;

import io.fabric8.kubernetes.api.model.PodList;
import io.fabric8.kubernetes.api.model.PodListBuilder;
import io.fabric8.kubernetes.client.ConfigBuilder;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
import io.fabric8.kubernetes.client.NamespacedKubernetesClient;
import io.fabric8.kubernetes.client.RequestConfigBuilder;
import io.fabric8.kubernetes.client.dsl.LogWatch;
import io.fabric8.kubernetes.client.http.AsyncBody;
import io.fabric8.kubernetes.client.http.TestAsyncBody;
import io.fabric8.kubernetes.client.http.TestHttpResponse;
import io.fabric8.kubernetes.client.http.TestStandardHttpClient;
import io.fabric8.kubernetes.client.http.TestStandardHttpClientFactory;
import io.fabric8.kubernetes.client.http.WebSocket;
Expand All @@ -36,12 +35,9 @@
import org.junit.jupiter.api.Test;

import java.io.ByteArrayInputStream;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Collections;
import java.util.concurrent.CompletableFuture;
import java.util.stream.IntStream;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
Expand All @@ -65,8 +61,7 @@ void setUp() {
@Test
void standardOperationsHaveDefaultTimeout() {
// Given
factory.getInstances().iterator().next()
.expect("/api/v1/namespaces/.+/pods.+", new TestHttpResponse<AsyncBody>().withCode(200).withBody(new TestAsyncBody()));
factory.expect("/api/v1/namespaces/.+/pods.+", 200);
// When
client.pods().inNamespace("default").withName("foo").get();
// Then
Expand All @@ -88,8 +83,7 @@ void standardOperationsWithOverriddenRequestConfig() {
// Given
final KubernetesClient derivedClient = (KubernetesClient) client
.newClient(new RequestConfigBuilder().withRequestTimeout(1337000).build());
factory.getInstance(1)
.expect("/api/v1/namespaces/.+/pods.+", new TestHttpResponse<AsyncBody>().withCode(200).withBody(new TestAsyncBody()));
factory.getInstance(1).expect("/api/v1/namespaces/.+/pods.+", 200);
// When
derivedClient.pods().inNamespace("default").withName("foo").get();
// Then
Expand All @@ -113,8 +107,7 @@ void standardOperationsWithOverriddenConfig() {
.withHttpClientFactory(factory)
.withConfig(new ConfigBuilder(client.getConfiguration()).withRequestTimeout(1337000).build())
.build();
factory.getInstance(1)
.expect("/api/v1/namespaces/.+/pods.+", new TestHttpResponse<AsyncBody>().withCode(200).withBody(new TestAsyncBody()));
factory.getInstance(1).expect("/api/v1/namespaces/.+/pods.+", 200);
// When
derivedClient.pods().inNamespace("default").withName("foo").get();
// Then
Expand All @@ -136,8 +129,7 @@ void standardOperationsWithOverriddenConfig() {
@Test
void standardOperationsHaveOverriddenTimeoutInConfig() {
// Given
factory.getInstances().iterator().next()
.expect("/api/v1/namespaces/.+/pods.+", new TestHttpResponse<AsyncBody>().withCode(200).withBody(new TestAsyncBody()));
factory.expect("/api/v1/namespaces/.+/pods.+", 200);
// When
client.getConfiguration().getRequestConfig().setRequestTimeout(1337000);
client.pods().inNamespace("default").withName("foo").get();
Expand All @@ -161,9 +153,7 @@ void standardOperationsHaveOverriddenTimeoutWithRequestConfig() {
client.adapt(NamespacedKubernetesClient.class)
.withRequestConfig(new RequestConfigBuilder().withRequestTimeout(3000).build())
.call(c -> {
factory.getInstance(1)
.expect("/api/v1/namespaces/.+/pods.+",
new TestHttpResponse<AsyncBody>().withCode(200).withBody(new TestAsyncBody()));
factory.getInstance(1).expect("/api/v1/namespaces/.+/pods.+", 200);
return c.pods().inNamespace("default").withName("foo").get();
});
// Then
Expand All @@ -186,9 +176,8 @@ void standardOperationsHaveOverriddenTimeoutWithRequestConfig() {
@Test
void httpLogNoFollowOperationsDefaultTimeout() {
// Given
informPodReady("foo")
.expect("/api/v1/namespaces/.+/pods/foo/log",
new TestHttpResponse<AsyncBody>().withCode(200).withBody(new TestAsyncBody()));
informPodReady("foo");
factory.expect("/api/v1/namespaces/.+/pods/foo/log", 200);
// When
client.pods().inNamespace("default").withName("foo").getLog();
// Then
Expand All @@ -209,9 +198,8 @@ void httpLogNoFollowOperationsDefaultTimeout() {
@Test
void httpLogFollowOperationsNoTimeout() {
// Given
informPodReady("foo")
.expect("/api/v1/namespaces/.+/pods/foo/log",
new TestHttpResponse<AsyncBody>().withCode(200).withBody(new TestAsyncBody()));
informPodReady("foo");
factory.expect("/api/v1/namespaces/.+/pods/foo/log", 200);
// When
try (LogWatch ignore = client.pods().inNamespace("default").withName("foo").watchLog()) {
// Then
Expand All @@ -231,16 +219,9 @@ void httpLogFollowOperationsNoTimeout() {
@Test
void scaleHasDefaultTimeout() {
// Given
final TestStandardHttpClient httpClient = factory.getInstances().iterator().next();
final TestStandardHttpClient.FutureProvider future = (r, c) -> {
final AsyncBody body = new TestAsyncBody();
c.consume(Collections.singletonList(
ByteBuffer.wrap(("{\"spec\":{\"replicas\":0}}").getBytes(StandardCharsets.UTF_8))), body);
return CompletableFuture.completedFuture(new TestHttpResponse<AsyncBody>().withCode(200).withBody(body));
};
IntStream.range(0, 2).forEach(i -> httpClient.expect("/apis/apps/v1/namespaces/.+/deployments/foo/scale", future));
httpClient.expect("/apis/apps/v1/namespaces/.+/deployments/foo",
new TestHttpResponse<AsyncBody>().withCode(200).withBody(new TestAsyncBody()));
factory.times(2).forEach(f -> f.expect("/apis/apps/v1/namespaces/.+/deployments/foo/scale",
200, "{\"spec\":{\"replicas\":0}}"));
factory.expect("/apis/apps/v1/namespaces/.+/deployments/foo", 200);
// When
client.apps().deployments().inNamespace("default").withName("foo").scale(1);
// Then
Expand All @@ -257,9 +238,7 @@ void scaleHasDefaultTimeout() {
@Test
void deleteHasDefaultTimeout() {
// Given
factory.getInstances().iterator().next()
.expect("/api/v1/namespaces/.+/pods/foo",
new TestHttpResponse<AsyncBody>().withCode(200).withBody(new TestAsyncBody()));
factory.expect("/api/v1/namespaces/.+/pods/foo", 200);
// When
client.pods().inNamespace("default").withName("foo").delete();
// Then
Expand All @@ -286,8 +265,9 @@ void uploadWsUpgradeHasDefaultTimeout() throws Exception {
l.onClose(webSocket, 0, "done");
return CompletableFuture.completedFuture(new WebSocketResponse(new WebSocketUpgradeResponse(null), webSocket));
};
IntStream.range(0, 2).forEach(i -> {
informPodReady("bar")
factory.times(2).forEach(i -> {
informPodReady("bar");
factory.getInstances().iterator().next()
.wsExpect("/api/v1/namespaces/.+/pods/bar/exec", future);
});
// When
Expand All @@ -305,21 +285,15 @@ void uploadWsUpgradeHasDefaultTimeout() throws Exception {
.allMatch(d -> d.equals(Duration.ofSeconds(10)));
}

private TestStandardHttpClient informPodReady(String podName) {
return factory.getInstances().iterator().next()
.expect("/api/v1/namespaces/.+/pods", (r, c) -> {
final AsyncBody body = new TestAsyncBody();
c.consume(Collections.singletonList(
ByteBuffer.wrap(("{" +
"\"metadata\": {}," +
"\"items\":[{" +
"\"metadata\":{\"name\":\"" + podName + "\"}," +
"\"spec\":{\"containers\":[{\"name\":\"" + podName + "\"}]}," +
"\"status\":{\"conditions\":[{\"type\":\"Ready\",\"status\":true}]}" +
"}]" +
"}").getBytes(StandardCharsets.UTF_8))),
body);
return CompletableFuture.completedFuture(new TestHttpResponse<AsyncBody>().withCode(200).withBody(body));
});
private void informPodReady(String podName) {
final PodList podReadyList = new PodListBuilder()
.withNewMetadata().endMetadata()
.addNewItem()
.withNewMetadata().withName(podName).endMetadata()
.withNewSpec().addNewContainer().withName(podName).endContainer().endSpec()
.withNewStatus().addNewCondition().withType("Ready").withStatus("True").endCondition().endStatus()
.endItem()
.build();
factory.expect("/api/v1/namespaces/.+/pods", 200, client.getKubernetesSerialization().asJson(podReadyList));
}
}
Loading