Skip to content

Commit faae99d

Browse files
shawkinsmanusa
authored andcommitted
fix #3271: refining the wait logic
the only time to retry is on http gone delete does not need special handling in the watcher
1 parent 019290d commit faae99d

File tree

6 files changed

+22
-110
lines changed

6 files changed

+22
-110
lines changed

kubernetes-client/src/main/java/io/fabric8/kubernetes/client/WatcherException.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,4 @@ public boolean isHttpGone() {
3939
|| (cause.getStatus() != null && cause.getStatus().getCode() == HttpURLConnection.HTTP_GONE);
4040
}
4141

42-
public boolean isShouldRetry() {
43-
return getCause() == null || !isHttpGone();
44-
}
4542
}

kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/Waitable.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ public interface Waitable<T, P> {
3333
* @param backoffUnit the TimeUnit for the initial backoff value
3434
* @param backoffMultiplier what to multiply the backoff by on each subsequent error
3535
* @return the waitable
36+
* @deprecated no longer used
3637
*/
38+
@Deprecated
3739
Waitable<T, P> withWaitRetryBackoff(long initialBackoff, TimeUnit backoffUnit, double backoffMultiplier);
3840
}

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

Lines changed: 18 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
import io.fabric8.kubernetes.client.informers.impl.DefaultSharedIndexInformer;
6060
import io.fabric8.kubernetes.client.internal.readiness.Readiness;
6161
import io.fabric8.kubernetes.client.utils.HttpClientUtils;
62+
import io.fabric8.kubernetes.client.utils.KubernetesResourceUtil;
6263
import io.fabric8.kubernetes.client.utils.URLUtils;
6364
import io.fabric8.kubernetes.client.utils.Utils;
6465
import io.fabric8.kubernetes.client.utils.WatcherToggle;
@@ -107,8 +108,6 @@ public class BaseOperation<T extends HasMetadata, L extends KubernetesResourceLi
107108
private final boolean reloadingFromServer;
108109
private final long gracePeriodSeconds;
109110
private final DeletionPropagation propagationPolicy;
110-
private final long watchRetryInitialBackoffMillis;
111-
private final double watchRetryBackoffMultiplier;
112111

113112
protected String apiVersion;
114113

@@ -124,8 +123,6 @@ protected BaseOperation(OperationContext ctx) {
124123
this.resourceVersion = ctx.getResourceVersion();
125124
this.gracePeriodSeconds = ctx.getGracePeriodSeconds();
126125
this.propagationPolicy = ctx.getPropagationPolicy();
127-
this.watchRetryInitialBackoffMillis = ctx.getWatchRetryInitialBackoffMillis();
128-
this.watchRetryBackoffMultiplier = ctx.getWatchRetryBackoffMultiplier();
129126
}
130127

131128
public BaseOperation<T, L, R> newInstance(OperationContext context) {
@@ -234,7 +231,7 @@ public RootPaths getRootPaths() {
234231
public T edit(UnaryOperator<T> function) {
235232
throw new KubernetesClientException(READ_ONLY_EDIT_EXCEPTION_MESSAGE);
236233
}
237-
234+
238235
@Override
239236
public T editStatus(UnaryOperator<T> function) {
240237
throw new KubernetesClientException(READ_ONLY_EDIT_EXCEPTION_MESSAGE);
@@ -423,7 +420,7 @@ public FilterWatchListDeletable<T, L> withoutLabels(Map<String, String> labels)
423420
public FilterWatchListDeletable<T, L> withLabelIn(String key, String... values) {
424421
return withNewFilter().withLabelIn(key, values).endFilter();
425422
}
426-
423+
427424
@Override
428425
public FilterWatchListDeletable<T, L> withLabelNotIn(String key, String... values) {
429426
return withNewFilter().withLabelNotIn(key, values).endFilter();
@@ -446,7 +443,7 @@ public FilterWatchListDeletable<T, L> withFields(Map<String, String> fields) {
446443

447444
@Override
448445
public FilterWatchListDeletable<T, L> withField(String key, String value) {
449-
return withNewFilter().withField(key, value).endFilter();
446+
return withNewFilter().withField(key, value).endFilter();
450447
}
451448

452449
@Override
@@ -461,7 +458,7 @@ public FilterWatchListDeletable<T, L> withInvolvedObject(ObjectReference objectR
461458
public FilterNested<FilterWatchListDeletable<T, L>> withNewFilter() {
462459
return new FilterNestedImpl<>(this);
463460
}
464-
461+
465462
@Override
466463
public FilterWatchListDeletable<T, L> withoutFields(Map<String, String> fields) {
467464
return withNewFilter().withoutFields(fields).endFilter();
@@ -474,7 +471,7 @@ public FilterWatchListDeletable<T, L> withoutField(String key, String value) {
474471

475472
public String getLabelQueryParam() {
476473
StringBuilder sb = new StringBuilder();
477-
474+
478475
Map<String, String> labels = context.getLabels();
479476
if (labels != null && !labels.isEmpty()) {
480477
for (Iterator<Map.Entry<String, String>> iter = labels.entrySet().iterator(); iter.hasNext(); ) {
@@ -656,7 +653,7 @@ public T updateStatus(T item) {
656653
}
657654

658655
}
659-
656+
660657
@Override
661658
public T patchStatus(T item) {
662659
throw new KubernetesClientException(READ_ONLY_UPDATE_EXCEPTION_MESSAGE);
@@ -760,7 +757,7 @@ public Watch watch(ListOptions options, final Watcher<T> watcher) {
760757
public T replace(T item) {
761758
throw new KubernetesClientException(READ_ONLY_UPDATE_EXCEPTION_MESSAGE);
762759
}
763-
760+
764761
@Override
765762
public T replaceStatus(T item) {
766763
throw new KubernetesClientException(READ_ONLY_UPDATE_EXCEPTION_MESSAGE);
@@ -770,7 +767,7 @@ public T replaceStatus(T item) {
770767
public T patch(PatchContext patchContext, String patch) {
771768
throw new KubernetesClientException(READ_ONLY_UPDATE_EXCEPTION_MESSAGE);
772769
}
773-
770+
774771
@Override
775772
public T patch(PatchContext patchContext, T item) {
776773
throw new KubernetesClientException(READ_ONLY_UPDATE_EXCEPTION_MESSAGE);
@@ -992,42 +989,27 @@ public T waitUntilReady(long amount, TimeUnit timeUnit) throws InterruptedExcept
992989
@Override
993990
public T waitUntilCondition(Predicate<T> condition, long amount, TimeUnit timeUnit)
994991
throws InterruptedException {
995-
return waitUntilConditionWithRetries(condition, timeUnit.toNanos(amount), watchRetryInitialBackoffMillis);
996-
}
997-
998-
private T waitUntilConditionWithRetries(Predicate<T> condition, long timeoutNanos, long backoffMillis)
999-
throws InterruptedException {
1000-
ListOptions options = null;
1001-
1002-
if (resourceVersion != null) {
1003-
options = createListOptions(resourceVersion);
1004-
}
1005992

1006-
long currentBackOff = backoffMillis;
1007-
long remainingNanosToWait = timeoutNanos;
993+
long remainingNanosToWait = timeUnit.toNanos(amount);
1008994
while (remainingNanosToWait > 0) {
1009995

1010996
T item = fromServer().get();
1011997
if (condition.test(item)) {
1012998
return item;
1013-
} else if (options == null) {
1014-
options = createListOptions(getResourceVersion(item));
1015999
}
10161000

10171001
final WaitForConditionWatcher<T> watcher = new WaitForConditionWatcher<>(condition);
10181002
final long startTime = System.nanoTime();
1019-
try (Watch ignored = watch(options, watcher)) {
1003+
try (Watch ignored = watch(KubernetesResourceUtil.getResourceVersion(item), watcher)) {
10201004
return watcher.getFuture().get(remainingNanosToWait, NANOSECONDS);
10211005
} catch (ExecutionException e) {
10221006
Throwable cause = e.getCause();
1023-
if (cause instanceof WatcherException && ((WatcherException) cause).isShouldRetry()) {
1024-
LOG.debug("retryable watch exception encountered, retrying after {} millis", currentBackOff, cause);
1025-
Thread.sleep(currentBackOff);
1026-
currentBackOff *= watchRetryBackoffMultiplier;
1007+
if (cause instanceof WatcherException && ((WatcherException)cause).isHttpGone()) {
1008+
LOG.debug("Restarting the watch due to http gone");
10271009
remainingNanosToWait -= (System.nanoTime() - startTime);
1028-
} else {
1029-
throw KubernetesClientException.launderThrowable(cause);
1010+
continue;
10301011
}
1012+
throw KubernetesClientException.launderThrowable(cause);
10311013
} catch (TimeoutException e) {
10321014
break;
10331015
}
@@ -1037,16 +1019,6 @@ private T waitUntilConditionWithRetries(Predicate<T> condition, long timeoutNano
10371019
throw new IllegalArgumentException(type.getSimpleName() + " with name:[" + name + "] in namespace:[" + namespace + "] matching condition not found!");
10381020
}
10391021

1040-
private static String getResourceVersion(HasMetadata item) {
1041-
return (item == null) ? null : item.getMetadata().getResourceVersion();
1042-
}
1043-
1044-
private static ListOptions createListOptions(String resourceVersion) {
1045-
return new ListOptionsBuilder()
1046-
.withResourceVersion(resourceVersion)
1047-
.build();
1048-
}
1049-
10501022
public void setType(Class<T> type) {
10511023
this.type = type;
10521024
}
@@ -1063,14 +1035,14 @@ public void setNamespace(String namespace) {
10631035
public WritableOperation<T> dryRun(boolean isDryRun) {
10641036
return newInstance(context.withDryRun(isDryRun));
10651037
}
1066-
1038+
10671039
@Override
10681040
public Informable<T> withIndexers(Map<String, Function<T, List<String>>> indexers) {
10691041
BaseOperation<T, L, R> result = newInstance(context);
10701042
result.indexers = indexers;
10711043
return result;
10721044
}
1073-
1045+
10741046
@Override
10751047
public SharedIndexInformer<T> inform(ResourceEventHandler<T> handler, long resync) {
10761048
// convert the name into something listable
@@ -1083,7 +1055,7 @@ public SharedIndexInformer<T> inform(ResourceEventHandler<T> handler, long resyn
10831055
public L list(ListOptions params, String namespace, OperationContext context) {
10841056
return baseOperation.list(params);
10851057
}
1086-
1058+
10871059
@Override
10881060
public Watch watch(ListOptions params, String namespace, OperationContext context, Watcher<T> watcher) {
10891061
return baseOperation.watch(params, watcher);

kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/base/WaitForConditionWatcher.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,6 @@ public void eventReceived(Action action, T resource) {
4848
case DELETED:
4949
if (condition.test(null)) {
5050
future.complete(null);
51-
} else {
52-
future.completeExceptionally(new WatcherException("Unexpected deletion of watched resource, will never satisfy condition"));
5351
}
5452
break;
5553
case ERROR:

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

Lines changed: 0 additions & 46 deletions
This file was deleted.

kubernetes-client/src/test/java/io/fabric8/kubernetes/client/dsl/base/WaitForConditionWatcherTest.java

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -93,19 +93,11 @@ void itDoesNotCompleteOnNoMatchModified() {
9393
}
9494

9595
@Test
96-
void itCompletesExceptionallyOnUnexpectedDeletion() throws Exception {
96+
void itNotDeleted() throws Exception {
9797
TrackingPredicate condition = condition(Objects::nonNull);
9898
WaitForConditionWatcher<ConfigMap> watcher = new WaitForConditionWatcher<>(condition);
9999
watcher.eventReceived(Action.DELETED, configMap);
100-
assertTrue(watcher.getFuture().isDone());
101-
try {
102-
watcher.getFuture().get();
103-
fail("should have thrown exception");
104-
} catch (ExecutionException e) {
105-
assertEquals(WatcherException.class, e.getCause().getClass());
106-
assertEquals("Unexpected deletion of watched resource, will never satisfy condition", e.getCause().getMessage());
107-
}
108-
assertTrue(condition.isCalledWith(null));
100+
assertFalse(watcher.getFuture().isDone());
109101
}
110102

111103
@Test
@@ -136,7 +128,6 @@ void itCompletesExceptionallyWithRetryOnCloseNonGone() throws Exception {
136128
} catch (ExecutionException e) {
137129
assertEquals(WatcherException.class, e.getCause().getClass());
138130
assertEquals("Watcher closed", e.getCause().getMessage());
139-
assertTrue(((WatcherException) e.getCause()).isShouldRetry());
140131
}
141132
assertFalse(condition.isCalled());
142133
}
@@ -153,7 +144,6 @@ void itCompletesExceptionallyWithNoRetryOnCloseGone() throws Exception {
153144
} catch (ExecutionException e) {
154145
assertEquals(WatcherException.class, e.getCause().getClass());
155146
assertEquals("Watcher closed", e.getCause().getMessage());
156-
assertFalse(((WatcherException) e.getCause()).isShouldRetry());
157147
}
158148
assertFalse(condition.isCalled());
159149
}
@@ -170,7 +160,6 @@ void itCompletesExceptionallyWithRetryOnGracefulClose() throws Exception {
170160
} catch (ExecutionException e) {
171161
assertEquals(WatcherException.class, e.getCause().getClass());
172162
assertEquals("Watcher closed", e.getCause().getMessage());
173-
assertTrue(((WatcherException) e.getCause()).isShouldRetry());
174163
}
175164
assertFalse(condition.isCalled());
176165
}

0 commit comments

Comments
 (0)