Skip to content

Commit ded98e5

Browse files
authored
Fix flake on ReaperTest#testRemoveWatchWhenCloudRemoved (#1516)
* Fix Flake on ReaperTest#testRemoveWatchWhenCloudRemoved Failure reproduced with ``` Index: src/main/java/org/csanchez/jenkins/plugins/kubernetes/pod/retention/Reaper.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pod/retention/Reaper.java b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pod/retention/Reaper.java --- a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pod/retention/Reaper.java (revision a81cd9f) +++ b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pod/retention/Reaper.java (date 1709649329339) @@ -198,6 +198,11 @@ // close any cloud watchers that have been removed cloudNames.stream().map(this.watchers::get).filter(Objects::nonNull).forEach(cpw -> { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } LOGGER.info(() -> "stopping pod watcher for deleted kubernetes cloud " + cpw.cloudName); cpw.stop(); }); ``` The cause is that watcher really gets removed in a separate thread, so the test must await and avoid checking immediately. * Use structured assertion everywhere Also happened to find a problem with org.csanchez.jenkins.plugins.kubernetes.pod.retention.ReaperTest.testKeepWatchingOnStatusWatchEvent so fixed it.
1 parent a81cd9f commit ded98e5

File tree

1 file changed

+35
-30
lines changed
  • src/test/java/org/csanchez/jenkins/plugins/kubernetes/pod/retention

1 file changed

+35
-30
lines changed

src/test/java/org/csanchez/jenkins/plugins/kubernetes/pod/retention/ReaperTest.java

+35-30
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ public void testMaybeActivate() throws IOException, InterruptedException {
9797
assertEquals("node removed from jenkins", j.jenkins.getNodes().size(), 0);
9898

9999
// watch was created
100-
assertTrue(r.isWatchingCloud(cloud.name));
100+
assertShouldBeWatching(r, cloud);
101101

102102
kubeClientRequests()
103103
// expect pod not running to be removed
@@ -129,7 +129,7 @@ public void testWatchFailOnActivate() throws IOException, InterruptedException {
129129
kubeClientRequests()
130130
.assertRequestCountAtLeast("/api/v1/namespaces/foo/pods?allowWatchBookmarks=true&watch=true", 1);
131131
// watch failed to register
132-
assertFalse(r.isWatchingCloud(cloud.name));
132+
assertShouldNotBeWatching(r, cloud);
133133
}
134134

135135
@Test
@@ -151,15 +151,13 @@ public void testActivateOnNewComputer() throws IOException, InterruptedException
151151
KubernetesComputer kc = new KubernetesComputer(n2);
152152

153153
// should not be watching the newly created cloud at this point
154-
assertFalse("should not be watching cloud", r.isWatchingCloud(cloud.name));
154+
assertShouldNotBeWatching(r, cloud);
155155

156156
// fire compute on-line event
157157
r.preLaunch(kc, tl);
158158

159159
// expect new cloud registered
160-
while (!r.isWatchingCloud(cloud.name)) {
161-
Thread.sleep(100);
162-
}
160+
assertShouldBeWatching(r, cloud);
163161
kubeClientRequests()
164162
.assertRequestCountAtLeast("/api/v1/namespaces/foo/pods?allowWatchBookmarks=true&watch=true", 1);
165163
}
@@ -202,9 +200,7 @@ public void testReconnectOnNewComputer() throws InterruptedException, IOExceptio
202200

203201
// wait until watch is removed
204202
System.out.println("Waiting for watch to be removed");
205-
while (r.isWatchingCloud(cloud.name)) {
206-
Thread.sleep(250L);
207-
}
203+
assertShouldNotBeWatching(r, cloud);
208204
System.out.println("Watch removed");
209205

210206
// launch computer
@@ -215,9 +211,7 @@ public void testReconnectOnNewComputer() throws InterruptedException, IOExceptio
215211

216212
// should have started new watch
217213
System.out.println("Waiting for a new watch to be started");
218-
while (!r.isWatchingCloud(cloud.name)) {
219-
Thread.sleep(100);
220-
}
214+
assertShouldBeWatching(r, cloud);
221215
System.out.println("Watch started");
222216
}
223217

@@ -244,7 +238,7 @@ public void testAddWatchWhenCloudAdded() throws InterruptedException, IOExceptio
244238
j.jenkins.clouds.add(cloud);
245239

246240
// watch is added
247-
assertTrue("should be watching cloud", r.isWatchingCloud(cloud.name));
241+
assertShouldBeWatching(r, cloud);
248242
kubeClientRequests().assertRequestCountAtLeast(watchPodsPath, 1);
249243
}
250244

@@ -264,13 +258,15 @@ public void testRemoveWatchWhenCloudRemoved() throws InterruptedException, IOExc
264258
r.maybeActivate();
265259

266260
// should not be watching the newly created cloud at this point
267-
assertTrue("should be watching cloud", r.isWatchingCloud(cloud.name));
261+
assertShouldBeWatching(r, cloud);
268262

269263
// invalidate client
270264
j.jenkins.clouds.remove(cloud);
271265

272266
// watch is removed
273-
assertFalse("should not be watching cloud", r.isWatchingCloud(cloud.name));
267+
// org.csanchez.jenkins.plugins.kubernetes.pod.retention.Reaper.CloudPodWatcher.onClose() is called in a
268+
// separate thread
269+
assertShouldNotBeWatching(r, cloud);
274270
}
275271

276272
@Test(timeout = 10_000)
@@ -313,7 +309,7 @@ public void testReplaceWatchWhenCloudUpdated() throws InterruptedException, IOEx
313309
r.maybeActivate();
314310

315311
// should not be watching the newly created cloud at this point
316-
assertTrue("should be watching cloud", r.isWatchingCloud(cloud.name));
312+
assertShouldBeWatching(r, cloud);
317313

318314
// invalidate client
319315
cloud.setNamespace("bar");
@@ -322,7 +318,7 @@ public void testReplaceWatchWhenCloudUpdated() throws InterruptedException, IOEx
322318
KubernetesSlave node = addNode(cloud, "node-123", "node");
323319

324320
// watch is still active
325-
assertTrue("should be watching cloud", r.isWatchingCloud(cloud.name));
321+
assertShouldBeWatching(r, cloud);
326322

327323
listener.waitForEvents().expectEvent(Watcher.Action.MODIFIED, node);
328324
kubeClientRequests().assertRequestCountAtLeast(watchBarPodsPath, 1);
@@ -358,10 +354,7 @@ public void testStopWatchingOnCloseException() throws InterruptedException {
358354
listener.expectNoEvents();
359355

360356
// watch is removed
361-
while (r.isWatchingCloud(cloud.name)) {
362-
Thread.sleep(250L);
363-
}
364-
assertFalse(r.isWatchingCloud(cloud.name));
357+
assertShouldNotBeWatching(r, cloud);
365358
}
366359

367360
@Test(timeout = 10_000)
@@ -402,7 +395,7 @@ public void testKeepWatchingOnKubernetesApiServerError() throws InterruptedExcep
402395
listener.expectNoEvents();
403396

404397
// watch is still active
405-
assertTrue(r.isWatchingCloud(cloud.name));
398+
assertShouldBeWatching(r, cloud);
406399
}
407400

408401
@Test(timeout = 10_000)
@@ -416,7 +409,11 @@ public void testKeepWatchingOnStatusWatchEvent() throws InterruptedException {
416409
status.setCode(200);
417410
server.expect()
418411
.withPath(watchPodsPath)
419-
.andReturnChunked(200, new WatchEvent(status, "ERROR"))
412+
.andUpgradeToWebSocket()
413+
.open()
414+
.immediately()
415+
.andEmit(new WatchEvent(status, "ERROR"))
416+
.done()
420417
.once();
421418

422419
// activate reaper
@@ -429,7 +426,7 @@ public void testKeepWatchingOnStatusWatchEvent() throws InterruptedException {
429426
listener.expectNoEvents();
430427

431428
// watch is still active
432-
assertTrue(r.isWatchingCloud(cloud.name));
429+
assertShouldBeWatching(r, cloud);
433430
}
434431

435432
@Test
@@ -453,17 +450,13 @@ public void testCloseWatchersOnShutdown() throws InterruptedException {
453450
r.maybeActivate();
454451

455452
// watching cloud
456-
assertTrue(r.isWatchingCloud(cloud.name));
457-
assertTrue(r.isWatchingCloud(cloud2.name));
458-
assertTrue(r.isWatchingCloud(cloud3.name));
453+
assertShouldBeWatching(r, cloud, cloud2, cloud3);
459454

460455
// trigger shutdown listener
461456
new Reaper.ReaperShutdownListener().onBeforeShutdown();
462457

463458
// watchers removed
464-
await().until(() -> r.isWatchingCloud(cloud.name), is(false));
465-
await().until(() -> r.isWatchingCloud(cloud2.name), is(false));
466-
await().until(() -> r.isWatchingCloud(cloud3.name), is(false));
459+
assertShouldNotBeWatching(r, cloud, cloud2, cloud3);
467460
}
468461

469462
@Test(timeout = 10_000)
@@ -895,4 +888,16 @@ private static WatchEvent errorEvent() {
895888
.endStatusObject()
896889
.build();
897890
}
891+
892+
private static void assertShouldBeWatching(Reaper r, KubernetesCloud... clouds) {
893+
for (KubernetesCloud cloud : clouds) {
894+
await("should be watching cloud " + cloud.name).until(() -> r.isWatchingCloud(cloud.name));
895+
}
896+
}
897+
898+
private static void assertShouldNotBeWatching(Reaper r, KubernetesCloud... clouds) {
899+
for (KubernetesCloud cloud : clouds) {
900+
await("should not be watching cloud " + cloud.name).until(() -> !r.isWatchingCloud(cloud.name));
901+
}
902+
}
898903
}

0 commit comments

Comments
 (0)