Skip to content

Commit 07aeb46

Browse files
authored
Merge pull request #902 from oracle/hear-again
Handle watch status that is missing or for 410 doesn't include good resource version
2 parents f1928aa + d343827 commit 07aeb46

File tree

3 files changed

+59
-15
lines changed

3 files changed

+59
-15
lines changed

operator/src/main/java/oracle/kubernetes/operator/Watcher.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -187,17 +187,31 @@ private void handleRegularUpdate(Watch.Response<T> item) {
187187

188188
private void handleErrorResponse(Watch.Response<T> item) {
189189
V1Status status = item.status;
190-
if (status != null && status.getCode() == HTTP_GONE) {
191-
String message = status.getMessage();
190+
if (status == null) {
191+
// The kubernetes client parsing logic can mistakenly parse a status as a type
192+
// with similar fields, such as V1ConfigMap. In this case, the actual status is
193+
// not available to our layer, so respond defensively by resetting resource version.
194+
resourceVersion = 0l;
195+
} else if (status.getCode() == HTTP_GONE) {
196+
resourceVersion = computeNextResourceVersionFromMessage(status);
197+
}
198+
}
199+
200+
private long computeNextResourceVersionFromMessage(V1Status status) {
201+
String message = status.getMessage();
202+
if (message != null) {
192203
int index1 = message.indexOf('(');
193204
if (index1 > 0) {
194205
int index2 = message.indexOf(')', index1 + 1);
195206
if (index2 > 0) {
196207
String val = message.substring(index1 + 1, index2);
197-
resourceVersion = !isNullOrEmptyString(val) ? Long.parseLong(val) : 0;
208+
if (!isNullOrEmptyString(val)) {
209+
return Long.parseLong(val);
210+
}
198211
}
199212
}
200213
}
214+
return 0l;
201215
}
202216

203217
/**

operator/src/test/java/oracle/kubernetes/operator/WatcherTestBase.java

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,14 @@ private Watch.Response createHttpGoneErrorResponse(int nextResourceVersion) {
107107
return WatchEvent.createErrorEvent(HTTP_GONE, nextResourceVersion).toWatchResponse();
108108
}
109109

110+
private Watch.Response createHttpGoneErrorWithoutResourceVersionResponse() {
111+
return WatchEvent.createErrorEvent(HTTP_GONE).toWatchResponse();
112+
}
113+
114+
private Watch.Response createErrorWithoutStatusResponse() {
115+
return WatchEvent.createErrorEventWithoutStatus().toWatchResponse();
116+
}
117+
110118
@SuppressWarnings({"unchecked", "rawtypes"})
111119
@Test
112120
public void receivedEvents_areSentToListeners() {
@@ -138,18 +146,36 @@ public void afterFirstSetOfEvents_nextRequestSendsLastResourceVersion() {
138146
@SuppressWarnings({"unchecked", "rawtypes"})
139147
@Test
140148
public void afterHttpGoneError_nextRequestSendsIncludedResourceVersion() {
141-
try {
142-
StubWatchFactory.addCallResponses(createHttpGoneErrorResponse(NEXT_RESOURCE_VERSION));
143-
scheduleDeleteResponse(createObjectWithMetaData());
144-
145-
createAndRunWatcher(NAMESPACE, stopping, INITIAL_RESOURCE_VERSION);
146-
147-
assertThat(
148-
StubWatchFactory.getRequestParameters().get(1),
149-
hasEntry("resourceVersion", Integer.toString(NEXT_RESOURCE_VERSION)));
150-
} catch (Throwable t) {
151-
t.printStackTrace();
152-
}
149+
StubWatchFactory.addCallResponses(createHttpGoneErrorResponse(NEXT_RESOURCE_VERSION));
150+
scheduleDeleteResponse(createObjectWithMetaData());
151+
152+
createAndRunWatcher(NAMESPACE, stopping, INITIAL_RESOURCE_VERSION);
153+
154+
assertThat(
155+
StubWatchFactory.getRequestParameters().get(1),
156+
hasEntry("resourceVersion", Integer.toString(NEXT_RESOURCE_VERSION)));
157+
}
158+
159+
@SuppressWarnings({"unchecked", "rawtypes"})
160+
@Test
161+
public void afterHttpGoneErrorWithoutResourceVersion_nextRequestSendsResourceVersionZero() {
162+
StubWatchFactory.addCallResponses(createHttpGoneErrorWithoutResourceVersionResponse());
163+
scheduleDeleteResponse(createObjectWithMetaData());
164+
165+
createAndRunWatcher(NAMESPACE, stopping, INITIAL_RESOURCE_VERSION);
166+
167+
assertThat(StubWatchFactory.getRequestParameters().get(1), hasEntry("resourceVersion", "0"));
168+
}
169+
170+
@SuppressWarnings({"unchecked", "rawtypes"})
171+
@Test
172+
public void afterErrorWithoutStatus_nextRequestSendsResourceVersionZero() {
173+
StubWatchFactory.addCallResponses(createErrorWithoutStatusResponse());
174+
scheduleDeleteResponse(createObjectWithMetaData());
175+
176+
createAndRunWatcher(NAMESPACE, stopping, INITIAL_RESOURCE_VERSION);
177+
178+
assertThat(StubWatchFactory.getRequestParameters().get(1), hasEntry("resourceVersion", "0"));
153179
}
154180

155181
@SuppressWarnings({"unchecked", "rawtypes"})

operator/src/test/java/oracle/kubernetes/operator/builders/WatchEvent.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ public static <S> WatchEvent<S> createDeleteEvent(S object) {
4848
return new WatchEvent<>("DELETED", object);
4949
}
5050

51+
public static <S> WatchEvent<S> createErrorEventWithoutStatus() {
52+
return new WatchEvent<>(null);
53+
}
54+
5155
public static <S> WatchEvent<S> createErrorEvent(int statusCode) {
5256
return new WatchEvent<>(new V1Status().code(statusCode).message("Oops"));
5357
}

0 commit comments

Comments
 (0)