Skip to content

Commit 86e0ce4

Browse files
committed
add tests for informer components
1 parent 7cbdc2d commit 86e0ce4

12 files changed

+693
-96
lines changed
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package io.kubernetes.client.informer.cache;
2+
3+
import static org.junit.Assert.*;
4+
5+
import io.kubernetes.client.models.V1ObjectMeta;
6+
import io.kubernetes.client.models.V1Pod;
7+
import java.util.Arrays;
8+
import java.util.Collection;
9+
import java.util.List;
10+
import org.junit.Rule;
11+
import org.junit.Test;
12+
import org.junit.contrib.java.lang.system.EnvironmentVariables;
13+
import org.junit.runner.RunWith;
14+
import org.junit.runners.Parameterized;
15+
16+
@RunWith(Parameterized.class)
17+
public class CacheTest {
18+
19+
@Rule public final EnvironmentVariables environmentVariables = new EnvironmentVariables();
20+
21+
private static String mockIndexName = "mock";
22+
23+
private static List<String> mockIndexFunc(Object obj) {
24+
if (obj == null) {
25+
return Arrays.asList("null");
26+
}
27+
return Arrays.asList(obj.getClass().getName());
28+
}
29+
30+
private static String mockKeyFunc(Object obj) {
31+
if (obj == null) {
32+
return "null";
33+
}
34+
return String.valueOf(System.identityHashCode(obj));
35+
}
36+
37+
private static Cache cache =
38+
new Cache<>(mockIndexName, CacheTest::mockIndexFunc, CacheTest::mockKeyFunc);
39+
40+
public CacheTest(Object obj, String index) {
41+
this.obj = obj;
42+
this.index = index;
43+
}
44+
45+
private Object obj;
46+
private String index;
47+
48+
@Parameterized.Parameters
49+
public static Collection data() {
50+
51+
V1Pod normalPod = new V1Pod();
52+
V1ObjectMeta normalPodMeta = new V1ObjectMeta();
53+
normalPodMeta.setName("foo");
54+
normalPodMeta.setNamespace("default");
55+
normalPod.setMetadata(normalPodMeta);
56+
57+
V1Pod missingNamespacePod = new V1Pod();
58+
V1ObjectMeta missingNamespacePodMeta = new V1ObjectMeta();
59+
missingNamespacePodMeta.setName("foo");
60+
missingNamespacePodMeta.setNamespace(null);
61+
missingNamespacePod.setMetadata(missingNamespacePodMeta);
62+
63+
V1Pod missingNamePod = new V1Pod();
64+
V1ObjectMeta missingNamePodMeta = new V1ObjectMeta();
65+
missingNamePodMeta.setName(null);
66+
missingNamePodMeta.setNamespace("default");
67+
missingNamePod.setMetadata(missingNamePodMeta);
68+
69+
return Arrays.asList(
70+
new Object[][] {
71+
{normalPod, "io.kubernetes.client.models.V1Pod"},
72+
{missingNamespacePod, "io.kubernetes.client.models.V1Pod"},
73+
{missingNamePod, "io.kubernetes.client.models.V1Pod"},
74+
{null, "null"},
75+
});
76+
}
77+
78+
@Test
79+
public void testCacheIndex() {
80+
cache.replace(Arrays.asList(this.obj), "0");
81+
82+
String index = mockIndexFunc(this.obj).get(0);
83+
String key = mockKeyFunc(this.obj);
84+
assertEquals(this.index, index);
85+
86+
List indexedObjectList = cache.byIndex(mockIndexName, index);
87+
assertEquals(this.obj, indexedObjectList.get(0));
88+
89+
List indexedObjectlist2 = cache.index(mockIndexName, this.obj);
90+
assertEquals(this.obj, indexedObjectlist2.get(0));
91+
92+
List<String> allExistingKeys = cache.listKeys();
93+
assertEquals(1, allExistingKeys.size());
94+
assertEquals(key, allExistingKeys.get(0));
95+
}
96+
97+
@Test
98+
public void testCacheStore() {
99+
if (this.obj == null) {
100+
// skip null object storing test b/c it should be checked before invoking cache
101+
return;
102+
}
103+
104+
cache.replace(Arrays.asList(this.obj), "0");
105+
106+
cache.delete(this.obj);
107+
108+
V1Pod pod = ((V1Pod) this.obj);
109+
List indexedObjectList = cache.byIndex(mockIndexName, this.index);
110+
assertEquals(0, indexedObjectList.size());
111+
assertEquals(null, pod.getMetadata().getClusterName());
112+
113+
cache.add(this.obj);
114+
115+
// replace cached object w/ null value
116+
String newClusterName = "test_cluster";
117+
pod.getMetadata().setClusterName(newClusterName);
118+
cache.update(this.obj);
119+
120+
assertEquals(1, cache.list().size());
121+
assertEquals(newClusterName, pod.getMetadata().getClusterName());
122+
}
123+
124+
@Test
125+
public void testDefaultNamespaceIndex() {
126+
if (this.obj == null) {
127+
// skip null object storing test b/c it should be checked before invoking cache
128+
return;
129+
}
130+
131+
V1Pod pod = ((V1Pod) this.obj);
132+
133+
List<String> indices = Cache.metaNamespaceIndexFunc(this.obj);
134+
assertEquals(pod.getMetadata().getNamespace(), indices.get(0));
135+
}
136+
137+
@Test
138+
public void testDefaultNamespaceNameKey() {
139+
if (this.obj == null) {
140+
// skip null object storing test b/c it should be checked before invoking cache
141+
return;
142+
}
143+
144+
Cache.metaNamespaceKeyFunc(this.obj);
145+
}
146+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package io.kubernetes.client.informer.cache;
2+
3+
import static org.junit.Assert.*;
4+
5+
import io.kubernetes.client.ApiException;
6+
import io.kubernetes.client.informer.EventType;
7+
import io.kubernetes.client.informer.ListerWatcher;
8+
import io.kubernetes.client.models.V1ListMeta;
9+
import io.kubernetes.client.models.V1ObjectMeta;
10+
import io.kubernetes.client.models.V1Pod;
11+
import io.kubernetes.client.models.V1PodList;
12+
import io.kubernetes.client.util.CallGeneratorParams;
13+
import io.kubernetes.client.util.Watch;
14+
import io.kubernetes.client.util.Watchable;
15+
import java.util.Arrays;
16+
import java.util.concurrent.atomic.AtomicBoolean;
17+
import java.util.concurrent.atomic.AtomicInteger;
18+
import org.junit.Rule;
19+
import org.junit.Test;
20+
import org.junit.contrib.java.lang.system.EnvironmentVariables;
21+
22+
public class ControllerTest {
23+
24+
@Rule public final EnvironmentVariables environmentVariables = new EnvironmentVariables();
25+
26+
@Test
27+
public void testControllerProcessDeltas() throws InterruptedException {
28+
29+
AtomicInteger receivingDeltasCount = new AtomicInteger(0);
30+
V1Pod foo1 = new V1Pod().metadata(new V1ObjectMeta().name("foo1").namespace("default"));
31+
V1Pod foo2 = new V1Pod().metadata(new V1ObjectMeta().name("foo2").namespace("default"));
32+
V1Pod foo3 = new V1Pod().metadata(new V1ObjectMeta().name("foo3").namespace("default"));
33+
34+
V1PodList podList =
35+
new V1PodList().metadata(new V1ListMeta()).items(Arrays.asList(foo1, foo2, foo3));
36+
DeltaFIFO<V1Pod> deltaFIFO =
37+
new DeltaFIFO<>(Cache::deletionHandlingMetaNamespaceKeyFunc, new Cache());
38+
39+
AtomicBoolean runOnce = new AtomicBoolean(false);
40+
ListerWatcher<V1Pod, V1PodList> listerWatcher =
41+
new ListerWatcher<V1Pod, V1PodList>() {
42+
@Override
43+
public V1PodList list(CallGeneratorParams params) throws ApiException {
44+
return podList;
45+
}
46+
47+
@Override
48+
public Watchable<V1Pod> watch(CallGeneratorParams params) throws ApiException {
49+
if (!runOnce.get()) {
50+
runOnce.set(true);
51+
return new MockWatch<V1Pod>(
52+
new Watch.Response<V1Pod>(EventType.MODIFIED.name(), foo3));
53+
} else {
54+
return new MockWatch<V1Pod>();
55+
}
56+
}
57+
};
58+
59+
Controller<V1Pod, V1PodList> controller =
60+
new Controller<>(
61+
V1Pod.class,
62+
deltaFIFO,
63+
listerWatcher,
64+
(deltas) -> {
65+
receivingDeltasCount.incrementAndGet();
66+
});
67+
Thread controllerThread = new Thread(controller::run);
68+
controllerThread.setDaemon(true);
69+
controllerThread.start();
70+
71+
// sleep 1s for processing all the deltas
72+
Thread.sleep(1000);
73+
74+
try {
75+
assertEquals(4, receivingDeltasCount.get());
76+
77+
} catch (Throwable t) {
78+
throw new RuntimeException(t);
79+
} finally {
80+
controller.stop();
81+
}
82+
}
83+
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package io.kubernetes.client.informer.cache;
2+
3+
import static org.junit.Assert.*;
4+
5+
import io.kubernetes.client.models.V1ObjectMeta;
6+
import io.kubernetes.client.models.V1Pod;
7+
import java.util.Arrays;
8+
import java.util.Deque;
9+
import java.util.LinkedList;
10+
import org.apache.commons.lang3.tuple.MutablePair;
11+
import org.junit.Rule;
12+
import org.junit.Test;
13+
import org.junit.contrib.java.lang.system.EnvironmentVariables;
14+
15+
public class DeltaFIFOTest {
16+
17+
@Rule public final EnvironmentVariables environmentVariables = new EnvironmentVariables();
18+
19+
@Test
20+
public void testDeltaFIFOBasic() throws InterruptedException {
21+
Deque<MutablePair<DeltaFIFO.DeltaType, Object>> receivingDeltas = new LinkedList<>();
22+
V1Pod foo1 = new V1Pod().metadata(new V1ObjectMeta().name("foo1").namespace("default"));
23+
Cache cache = new Cache();
24+
DeltaFIFO<V1Pod> deltaFIFO =
25+
new DeltaFIFO<>(Cache::deletionHandlingMetaNamespaceKeyFunc, cache);
26+
27+
MutablePair<DeltaFIFO.DeltaType, Object> receivingDelta;
28+
// basic add operation
29+
deltaFIFO.add(foo1);
30+
cache.add(foo1);
31+
deltaFIFO.pop(
32+
(deltas) -> {
33+
MutablePair<DeltaFIFO.DeltaType, Object> delta = deltas.peekFirst();
34+
receivingDeltas.add(delta);
35+
});
36+
receivingDelta = receivingDeltas.peekFirst();
37+
receivingDeltas.removeFirst();
38+
assertEquals(foo1, receivingDelta.getRight());
39+
assertEquals(DeltaFIFO.DeltaType.Added, receivingDelta.getLeft());
40+
41+
// basic update operation
42+
deltaFIFO.update(foo1);
43+
cache.update(foo1);
44+
deltaFIFO.pop(
45+
(deltas) -> {
46+
MutablePair<DeltaFIFO.DeltaType, Object> delta = deltas.peekFirst();
47+
receivingDeltas.add(delta);
48+
});
49+
receivingDelta = receivingDeltas.peekFirst();
50+
receivingDeltas.removeFirst();
51+
assertEquals(foo1, receivingDelta.getRight());
52+
assertEquals(DeltaFIFO.DeltaType.Updated, receivingDelta.getLeft());
53+
54+
// basic delete operation
55+
deltaFIFO.delete(foo1);
56+
cache.delete(foo1);
57+
deltaFIFO.pop(
58+
(deltas) -> {
59+
MutablePair<DeltaFIFO.DeltaType, Object> delta = deltas.peekFirst();
60+
receivingDeltas.add(delta);
61+
});
62+
receivingDelta = receivingDeltas.peekFirst();
63+
receivingDeltas.removeFirst();
64+
assertEquals(foo1, receivingDelta.getRight());
65+
assertEquals(DeltaFIFO.DeltaType.Deleted, receivingDelta.getLeft());
66+
67+
// basic sync operation
68+
deltaFIFO.replace(Arrays.asList(foo1), "0");
69+
cache.replace(Arrays.asList(foo1), "0");
70+
deltaFIFO.pop(
71+
(deltas) -> {
72+
MutablePair<DeltaFIFO.DeltaType, Object> delta = deltas.peekFirst();
73+
receivingDeltas.add(delta);
74+
});
75+
receivingDelta = receivingDeltas.peekFirst();
76+
receivingDeltas.removeFirst();
77+
assertEquals(foo1, receivingDelta.getRight());
78+
assertEquals(DeltaFIFO.DeltaType.Sync, receivingDelta.getLeft());
79+
}
80+
81+
@Test
82+
public void testDeltaFIFODedup() {
83+
V1Pod foo1 = new V1Pod().metadata(new V1ObjectMeta().name("foo1").namespace("default"));
84+
Cache cache = new Cache();
85+
DeltaFIFO<V1Pod> deltaFIFO =
86+
new DeltaFIFO<>(Cache::deletionHandlingMetaNamespaceKeyFunc, cache);
87+
Deque<MutablePair<DeltaFIFO.DeltaType, Object>> deltas;
88+
89+
// add-delete dedup
90+
deltaFIFO.add(foo1);
91+
deltaFIFO.delete(foo1);
92+
deltas = deltaFIFO.getItems().get(Cache.deletionHandlingMetaNamespaceKeyFunc(foo1));
93+
assertEquals(DeltaFIFO.DeltaType.Deleted, deltas.peekLast().getLeft());
94+
assertEquals(foo1, deltas.peekLast().getRight());
95+
assertEquals(DeltaFIFO.DeltaType.Added, deltas.peekFirst().getLeft());
96+
assertEquals(foo1, deltas.peekFirst().getRight());
97+
assertEquals(2, deltas.size());
98+
deltaFIFO.getItems().remove(Cache.deletionHandlingMetaNamespaceKeyFunc(foo1));
99+
100+
// add-delete-delete dedup
101+
deltaFIFO.add(foo1);
102+
deltaFIFO.delete(foo1);
103+
deltaFIFO.delete(foo1);
104+
deltas = deltaFIFO.getItems().get(Cache.deletionHandlingMetaNamespaceKeyFunc(foo1));
105+
assertEquals(DeltaFIFO.DeltaType.Deleted, deltas.peekLast().getLeft());
106+
assertEquals(foo1, deltas.peekLast().getRight());
107+
assertEquals(DeltaFIFO.DeltaType.Added, deltas.peekFirst().getLeft());
108+
assertEquals(foo1, deltas.peekFirst().getRight());
109+
assertEquals(2, deltas.size());
110+
deltaFIFO.getItems().remove(Cache.deletionHandlingMetaNamespaceKeyFunc(foo1));
111+
}
112+
113+
@Test
114+
public void testDeltaFIFOResync() {
115+
V1Pod foo1 = new V1Pod().metadata(new V1ObjectMeta().name("foo1").namespace("default"));
116+
Cache cache = new Cache();
117+
DeltaFIFO<V1Pod> deltaFIFO =
118+
new DeltaFIFO<>(Cache::deletionHandlingMetaNamespaceKeyFunc, cache);
119+
120+
// sync after add
121+
cache.add(foo1);
122+
deltaFIFO.resync();
123+
124+
Deque<MutablePair<DeltaFIFO.DeltaType, Object>> deltas =
125+
deltaFIFO.getItems().get(Cache.deletionHandlingMetaNamespaceKeyFunc(foo1));
126+
127+
assertEquals(1, deltas.size());
128+
assertEquals(foo1, deltas.peekLast().getRight());
129+
assertEquals(DeltaFIFO.DeltaType.Sync, deltas.peekLast().getLeft());
130+
}
131+
}

0 commit comments

Comments
 (0)