Skip to content

Commit 9ce04a6

Browse files
shawkinsmanusa
authored andcommitted
fix #4924: allows non-okhttp clients to handle invalid status
1 parent bca31cd commit 9ce04a6

File tree

4 files changed

+54
-24
lines changed

4 files changed

+54
-24
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
* Fix #4899: BuildConfigs.instantiateBinary().fromFile() does not time out
2121
* Fix #4908: using the response headers in the vertx response
2222
* Fix #4947: typo in HttpClient.Factory scoring system logic
23+
* Fix #4928: allows non-okhttp clients to handle invalid status
2324

2425
#### Improvements
2526
* Fix #4675: adding a fully client side timeout for informer watches

kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/BaseOperation.java

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -642,28 +642,27 @@ public CompletableFuture<AbstractWatchManager<T>> submitWatch(ListOptions option
642642
if (t instanceof KubernetesClientException) {
643643
KubernetesClientException ke = (KubernetesClientException) t;
644644
List<Integer> furtherProcessedCodes = Arrays.asList(200, 503);
645-
if (!furtherProcessedCodes.contains(ke.getCode())) {
646-
throw ke;
647-
}
648-
649-
//release the watch after disabling the watcher (to avoid premature call to onClose)
650-
watcherToggle.disable();
651-
652-
// If the HTTP return code is 200 or 503, we retry the watch again using a persistent hanging
653-
// HTTP GET. This is meant to handle cases like kubectl local proxy which does not support
654-
// websockets. Issue: https://github.com/kubernetes/kubernetes/issues/25126
655-
try {
656-
return new WatchHTTPManager<>(
657-
httpClient,
658-
this,
659-
optionsToUse,
660-
watcher,
661-
getRequestConfig().getWatchReconnectInterval(),
662-
getRequestConfig().getWatchReconnectLimit());
663-
} catch (MalformedURLException e) {
664-
throw KubernetesClientException.launderThrowable(forOperationType(WATCH), e);
645+
if (furtherProcessedCodes.contains(ke.getCode())) {
646+
//release the watch after disabling the watcher (to avoid premature call to onClose)
647+
watcherToggle.disable();
648+
649+
// If the HTTP return code is 200 or 503, we retry the watch again using a persistent hanging
650+
// HTTP GET. This is meant to handle cases like kubectl local proxy which does not support
651+
// websockets. Issue: https://github.com/kubernetes/kubernetes/issues/25126
652+
try {
653+
return new WatchHTTPManager<>(
654+
httpClient,
655+
this,
656+
optionsToUse,
657+
watcher,
658+
getRequestConfig().getWatchReconnectInterval(),
659+
getRequestConfig().getWatchReconnectLimit());
660+
} catch (MalformedURLException e) {
661+
throw KubernetesClientException.launderThrowable(forOperationType(WATCH), e);
662+
}
665663
}
666664
}
665+
throw KubernetesClientException.launderThrowable(t);
667666
} finally {
668667
watch.close();
669668
}

kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/OperationSupport.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -651,10 +651,12 @@ public static Status createStatus(HttpResponse<?> response) {
651651
String bodyString = response.bodyString();
652652
if (Utils.isNotNullOrEmpty(bodyString)) {
653653
Status status = JSON_MAPPER.readValue(bodyString, Status.class);
654-
if (status.getCode() == null) {
655-
status = new StatusBuilder(status).withCode(statusCode).build();
654+
if (status != null) {
655+
if (status.getCode() == null) {
656+
status = new StatusBuilder(status).withCode(statusCode).build();
657+
}
658+
return status;
656659
}
657-
return status;
658660
}
659661
} catch (IOException e) {
660662
// ignored

kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/WatchTest.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import io.fabric8.kubernetes.api.model.ListOptionsBuilder;
2020
import io.fabric8.kubernetes.api.model.Pod;
2121
import io.fabric8.kubernetes.api.model.PodBuilder;
22+
import io.fabric8.kubernetes.api.model.PodList;
2223
import io.fabric8.kubernetes.api.model.StatusBuilder;
2324
import io.fabric8.kubernetes.api.model.WatchEvent;
2425
import io.fabric8.kubernetes.api.model.WatchEventBuilder;
@@ -27,6 +28,8 @@
2728
import io.fabric8.kubernetes.client.Watch;
2829
import io.fabric8.kubernetes.client.Watcher;
2930
import io.fabric8.kubernetes.client.WatcherException;
31+
import io.fabric8.kubernetes.client.dsl.MixedOperation;
32+
import io.fabric8.kubernetes.client.dsl.PodResource;
3033
import io.fabric8.kubernetes.client.dsl.Watchable;
3134
import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient;
3235
import io.fabric8.kubernetes.client.server.mock.KubernetesMockServer;
@@ -48,6 +51,7 @@
4851
import static org.junit.jupiter.api.Assertions.assertEquals;
4952
import static org.junit.jupiter.api.Assertions.assertNotNull;
5053
import static org.junit.jupiter.api.Assertions.assertNull;
54+
import static org.junit.jupiter.api.Assertions.assertThrows;
5155
import static org.junit.jupiter.api.Assertions.assertTrue;
5256
import static org.junit.jupiter.api.Assertions.fail;
5357

@@ -431,14 +435,38 @@ public void onClose(WatcherException cause) {
431435
assertTrue(latch.await(10, TimeUnit.SECONDS));
432436
}
433437

438+
@Test
439+
void testUnknownResponse() throws InterruptedException {
440+
// Given
441+
442+
// trigger the usage of the http watch
443+
server.expect()
444+
.withPath("/api/v1/namespaces/test/pods?allowWatchBookmarks=true&watch=true")
445+
.andReturn(404, "null")
446+
.times(2);
447+
448+
MixedOperation<Pod, PodList, PodResource> pods = client.pods();
449+
450+
assertThrows(KubernetesClientException.class, () -> pods.watch(new Watcher<Pod>() {
451+
452+
@Override
453+
public void eventReceived(Action action, Pod resource) {
454+
}
455+
456+
@Override
457+
public void onClose(WatcherException cause) {
458+
}
459+
}));
460+
}
461+
434462
@Test
435463
void testHttpWatch() throws InterruptedException {
436464
// Given
437465

438466
// trigger the usage of the http watch
439467
server.expect()
440468
.withPath("/api/v1/namespaces/test/pods?allowWatchBookmarks=true&watch=true")
441-
.andReturn(200, null)
469+
.andReturn(200, "")
442470
.once();
443471

444472
String dummyEvent = Serialization.asJson(new WatchEventBuilder().withType("MODIFIED")

0 commit comments

Comments
 (0)