Skip to content

Commit f0ba3f2

Browse files
authored
Merge pull request #649 from yue9944882/refactor/allow-customize-index-func
Refactor: Add constructor for customizing index funcs
2 parents f4ec11b + 274f5b7 commit f0ba3f2

File tree

7 files changed

+209
-77
lines changed

7 files changed

+209
-77
lines changed

util/src/main/java/io/kubernetes/client/informer/SharedInformerFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
/** SharedInformerFactory class constructs and caches informers for api types. */
1919
public class SharedInformerFactory {
2020

21-
private Map<Type, SharedIndexInformer> informers;
21+
protected Map<Type, SharedIndexInformer> informers;
2222

2323
private Map<Type, Future> startedInformers;
2424

util/src/main/java/io/kubernetes/client/informer/cache/Cache.java

Lines changed: 54 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
package io.kubernetes.client.informer.cache;
22

3-
import com.google.common.base.Strings;
4-
import io.kubernetes.client.informer.exception.BadObjectException;
5-
import io.kubernetes.client.models.V1ObjectMeta;
6-
import io.kubernetes.client.util.Reflect;
7-
import io.kubernetes.client.util.exception.ObjectMetaReflectException;
8-
import java.util.*;
3+
import java.util.ArrayList;
4+
import java.util.Arrays;
5+
import java.util.HashMap;
6+
import java.util.HashSet;
7+
import java.util.List;
8+
import java.util.Map;
9+
import java.util.Set;
910
import java.util.concurrent.locks.ReentrantLock;
10-
import java.util.function.*;
11+
import java.util.function.Function;
1112

1213
/**
1314
* Cache is a java port of k/client-go's ThreadSafeStore. It basically saves and indexes all the
@@ -19,8 +20,12 @@ public class Cache<ApiType> implements Indexer<ApiType> {
1920
/** keyFunc defines how to map objects into indices */
2021
private Function<ApiType, String> keyFunc;
2122

22-
/** NAMESPACE_INDEX is the default index function for caching objects */
23-
public static final String NAMESPACE_INDEX = "namespace";
23+
/**
24+
* DEPRECATE: use Caches#NAMESPACE_INDEX instead. TODO: remove after 7.0.0
25+
*
26+
* <p>NAMESPACE_INDEX is the default index function for caching objects
27+
*/
28+
@Deprecated public static final String NAMESPACE_INDEX = "namespace";
2429

2530
/** indexers stores index functions by their names */
2631
private Map<String, Function<ApiType, List<String>>> indexers = new HashMap<>();
@@ -35,12 +40,11 @@ public class Cache<ApiType> implements Indexer<ApiType> {
3540
// TODO: might remove the lock here and make the methods synchronized
3641
private ReentrantLock lock = new ReentrantLock();
3742

38-
/** Constructor. */
3943
public Cache() {
4044
this(
41-
NAMESPACE_INDEX,
42-
Cache::metaNamespaceIndexFunc,
43-
Cache::deletionHandlingMetaNamespaceKeyFunc);
45+
Caches.NAMESPACE_INDEX,
46+
Caches::metaNamespaceIndexFunc,
47+
Caches::deletionHandlingMetaNamespaceKeyFunc);
4448
}
4549

4650
/**
@@ -376,67 +380,65 @@ private void deleteFromIndices(ApiType oldObj, String key) {
376380
}
377381

378382
/**
379-
* deletionHandlingMetaNamespaceKeyFunc checks for DeletedFinalStateUnknown objects before calling
380-
* metaNamespaceKeyFunc.
383+
* Add index func.
384+
*
385+
* @param indexName the index name
386+
* @param indexFunc the index func
387+
*/
388+
public void addIndexFunc(String indexName, Function<ApiType, List<String>> indexFunc) {
389+
this.indices.put(indexName, new HashMap<>());
390+
this.indexers.put(indexName, indexFunc);
391+
}
392+
393+
public Function<ApiType, String> getKeyFunc() {
394+
return keyFunc;
395+
}
396+
397+
public void setKeyFunc(Function<ApiType, String> keyFunc) {
398+
this.keyFunc = keyFunc;
399+
}
400+
401+
/**
402+
* DEPRECATE: use Caches#deletionHandlingMetaNamespaceKeyFunc instead. TODO: remove after 7.0.0
381403
*
404+
* <p>deletionHandlingMetaNamespaceKeyFunc checks for DeletedFinalStateUnknown objects before
405+
* calling metaNamespaceKeyFunc.
406+
*
407+
* @param <ApiType> the type parameter
382408
* @param object specific object
383409
* @return the key
384410
*/
411+
@Deprecated
385412
public static <ApiType> String deletionHandlingMetaNamespaceKeyFunc(ApiType object) {
386-
if (object instanceof DeltaFIFO.DeletedFinalStateUnknown) {
387-
DeltaFIFO.DeletedFinalStateUnknown deleteObj = (DeltaFIFO.DeletedFinalStateUnknown) object;
388-
return deleteObj.getKey();
389-
}
390-
return metaNamespaceKeyFunc(object);
413+
return Caches.deletionHandlingMetaNamespaceKeyFunc(object);
391414
}
392415

393416
/**
394-
* MetaNamespaceKeyFunc is a convenient default KeyFunc which knows how to make keys for API
417+
* DEPRECATE: use Caches#metaNamespaceKeyFunc instead. TODO: remove after 7.0.0
418+
*
419+
* <p>MetaNamespaceKeyFunc is a convenient default KeyFunc which knows how to make keys for API
395420
* objects which implement HasMetadata Interface. The key uses the format <namespace>/<name>
396421
* unless <namespace> is empty, then it's just <name>.
397422
*
398423
* @param obj specific object
399424
* @return the key
400425
*/
426+
@Deprecated
401427
public static String metaNamespaceKeyFunc(Object obj) {
402-
try {
403-
V1ObjectMeta metadata;
404-
if (obj instanceof String) {
405-
return (String) obj;
406-
} else if (obj instanceof V1ObjectMeta) {
407-
metadata = (V1ObjectMeta) obj;
408-
} else {
409-
metadata = Reflect.objectMetadata(obj);
410-
if (metadata == null) {
411-
throw new BadObjectException(obj);
412-
}
413-
}
414-
if (!Strings.isNullOrEmpty(metadata.getNamespace())) {
415-
return metadata.getNamespace() + "/" + metadata.getName();
416-
}
417-
return metadata.getName();
418-
} catch (ObjectMetaReflectException e) {
419-
// NOTE(yue9944882): might want to handle this as a checked exception
420-
throw new RuntimeException(e);
421-
}
428+
return Caches.metaNamespaceKeyFunc(obj);
422429
}
423430

424431
/**
425-
* metaNamespaceIndexFunc is a default index function that indexes based on an object's namespace.
432+
* DEPRECATE: use Caches#metaNamespaceIndexFunc instead. TODO: remove after 7.0.0
433+
*
434+
* <p>metaNamespaceIndexFunc is a default index function that indexes based on an object's
435+
* namespace.
426436
*
427437
* @param obj specific object
428438
* @return the indexed value
429439
*/
440+
@Deprecated
430441
public static List<String> metaNamespaceIndexFunc(Object obj) {
431-
try {
432-
V1ObjectMeta metadata = Reflect.objectMetadata(obj);
433-
if (metadata == null) {
434-
return Collections.emptyList();
435-
}
436-
return Collections.singletonList(metadata.getNamespace());
437-
} catch (ObjectMetaReflectException e) {
438-
// NOTE(yue9944882): might want to handle this as a checked exception
439-
throw new RuntimeException(e);
440-
}
442+
return Caches.metaNamespaceIndexFunc(obj);
441443
}
442444
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package io.kubernetes.client.informer.cache;
2+
3+
import com.google.common.base.Strings;
4+
import io.kubernetes.client.informer.exception.BadObjectException;
5+
import io.kubernetes.client.models.V1ObjectMeta;
6+
import io.kubernetes.client.util.Reflect;
7+
import io.kubernetes.client.util.exception.ObjectMetaReflectException;
8+
import java.util.Collections;
9+
import java.util.List;
10+
11+
/** A set of helper utilities for constructing a cache. */
12+
public class Caches {
13+
14+
/** NAMESPACE_INDEX is the default index function for caching objects */
15+
public static final String NAMESPACE_INDEX = "namespace";
16+
17+
/**
18+
* deletionHandlingMetaNamespaceKeyFunc checks for DeletedFinalStateUnknown objects before calling
19+
* metaNamespaceKeyFunc.
20+
*
21+
* @param <ApiType> the type parameter
22+
* @param object specific object
23+
* @return the key
24+
*/
25+
public static <ApiType> String deletionHandlingMetaNamespaceKeyFunc(ApiType object) {
26+
if (object instanceof DeltaFIFO.DeletedFinalStateUnknown) {
27+
DeltaFIFO.DeletedFinalStateUnknown deleteObj = (DeltaFIFO.DeletedFinalStateUnknown) object;
28+
return deleteObj.getKey();
29+
}
30+
return metaNamespaceKeyFunc(object);
31+
}
32+
33+
/**
34+
* MetaNamespaceKeyFunc is a convenient default KeyFunc which knows how to make keys for API
35+
* objects which implement HasMetadata Interface. The key uses the format <namespace>/<name>
36+
* unless <namespace> is empty, then it's just <name>.
37+
*
38+
* @param obj specific object
39+
* @return the key
40+
*/
41+
public static String metaNamespaceKeyFunc(Object obj) {
42+
try {
43+
V1ObjectMeta metadata;
44+
if (obj instanceof String) {
45+
return (String) obj;
46+
} else if (obj instanceof V1ObjectMeta) {
47+
metadata = (V1ObjectMeta) obj;
48+
} else {
49+
metadata = Reflect.objectMetadata(obj);
50+
if (metadata == null) {
51+
throw new BadObjectException(obj);
52+
}
53+
}
54+
if (!Strings.isNullOrEmpty(metadata.getNamespace())) {
55+
return metadata.getNamespace() + "/" + metadata.getName();
56+
}
57+
return metadata.getName();
58+
} catch (ObjectMetaReflectException e) {
59+
// NOTE(yue9944882): might want to handle this as a checked exception
60+
throw new RuntimeException(e);
61+
}
62+
}
63+
64+
/**
65+
* metaNamespaceIndexFunc is a default index function that indexes based on an object's namespace.
66+
*
67+
* @param obj specific object
68+
* @return the indexed value
69+
*/
70+
public static List<String> metaNamespaceIndexFunc(Object obj) {
71+
try {
72+
V1ObjectMeta metadata = Reflect.objectMetadata(obj);
73+
if (metadata == null) {
74+
return Collections.emptyList();
75+
}
76+
return Collections.singletonList(metadata.getNamespace());
77+
} catch (ObjectMetaReflectException e) {
78+
// NOTE(yue9944882): might want to handle this as a checked exception
79+
throw new RuntimeException(e);
80+
}
81+
}
82+
}

util/src/main/java/io/kubernetes/client/informer/cache/Lister.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ public class Lister<ApiType> {
1313
private Indexer<ApiType> indexer;
1414

1515
public Lister(Indexer<ApiType> indexer) {
16-
this(indexer, null, Cache.NAMESPACE_INDEX);
16+
this(indexer, null, Caches.NAMESPACE_INDEX);
1717
}
1818

1919
public Lister(Indexer<ApiType> indexer, String namespace) {
20-
this(indexer, namespace, Cache.NAMESPACE_INDEX);
20+
this(indexer, namespace, Caches.NAMESPACE_INDEX);
2121
}
2222

2323
public Lister(Indexer<ApiType> indexer, String namespace, String indexName) {

util/src/main/java/io/kubernetes/client/informer/impl/DefaultSharedIndexInformer.java

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,37 @@ public class DefaultSharedIndexInformer<ApiType, ApiListType>
4040

4141
public DefaultSharedIndexInformer(
4242
Class<ApiType> apiTypeClass, ListerWatcher listerWatcher, long resyncPeriod) {
43+
this(apiTypeClass, listerWatcher, resyncPeriod, new Cache<>());
44+
}
45+
46+
public DefaultSharedIndexInformer(
47+
Class<ApiType> apiTypeClass,
48+
ListerWatcher listerWatcher,
49+
long resyncPeriod,
50+
Cache<ApiType> cache) {
51+
this(
52+
apiTypeClass,
53+
listerWatcher,
54+
resyncPeriod,
55+
new DeltaFIFO<>(cache.getKeyFunc(), cache),
56+
cache);
57+
}
58+
59+
public DefaultSharedIndexInformer(
60+
Class<ApiType> apiTypeClass,
61+
ListerWatcher<ApiType, ApiListType> listerWatcher,
62+
long resyncPeriod,
63+
DeltaFIFO<ApiType> deltaFIFO,
64+
Indexer<ApiType> indexer) {
4365
this.resyncCheckPeriodMillis = resyncPeriod;
4466
this.defaultEventHandlerResyncPeriod = resyncPeriod;
4567

4668
this.processor = new SharedProcessor<>();
47-
this.indexer = new Cache();
48-
49-
DeltaFIFO<ApiType> fifo = new DeltaFIFO<ApiType>(Cache::metaNamespaceKeyFunc, this.indexer);
50-
69+
this.indexer = indexer;
5170
this.controller =
5271
new Controller<ApiType, ApiListType>(
5372
apiTypeClass,
54-
fifo,
73+
deltaFIFO,
5574
listerWatcher,
5675
this::handleDeltas,
5776
processor::shouldResync,

util/src/test/java/io/kubernetes/client/informer/cache/CacheTest.java

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import io.kubernetes.client.models.V1ObjectMeta;
66
import io.kubernetes.client.models.V1Pod;
7+
import io.kubernetes.client.models.V1PodSpec;
78
import java.util.Arrays;
89
import java.util.Collection;
910
import java.util.List;
@@ -122,25 +123,25 @@ public void testCacheStore() {
122123
}
123124

124125
@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-
}
126+
public void testMultiIndexFuncCacheStore() {
127+
String testIndexFuncName = "test-idx-func";
128+
Cache<V1Pod> podCache = new Cache<>();
129+
podCache.addIndexFunc(
130+
testIndexFuncName,
131+
(V1Pod pod) -> {
132+
return Arrays.asList(pod.getSpec().getNodeName());
133+
});
130134

131-
V1Pod pod = ((V1Pod) this.obj);
135+
V1Pod testPod =
136+
new V1Pod()
137+
.metadata(new V1ObjectMeta().namespace("ns").name("n"))
138+
.spec(new V1PodSpec().nodeName("node1"));
139+
podCache.add(testPod);
132140

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-
}
141+
List<V1Pod> namespaceIndexedPods = podCache.byIndex(Caches.NAMESPACE_INDEX, "ns");
142+
assertEquals(1, namespaceIndexedPods.size());
143143

144-
Cache.metaNamespaceKeyFunc(this.obj);
144+
List<V1Pod> nodeNameIndexedPods = podCache.byIndex(testIndexFuncName, "node1");
145+
assertEquals(1, nodeNameIndexedPods.size());
145146
}
146147
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package io.kubernetes.client.informer.cache;
2+
3+
import static org.junit.Assert.assertEquals;
4+
5+
import io.kubernetes.client.models.V1ObjectMeta;
6+
import io.kubernetes.client.models.V1Pod;
7+
import java.util.List;
8+
import org.junit.Test;
9+
10+
public class CachesTest {
11+
12+
@Test
13+
public void testDefaultNamespaceNameKey() {
14+
String testName = "test-name";
15+
String testNamespace = "test-namespace";
16+
V1Pod pod = new V1Pod().metadata(new V1ObjectMeta().name(testName).namespace(testNamespace));
17+
assertEquals(testNamespace + "/" + testName, Caches.metaNamespaceKeyFunc(pod));
18+
}
19+
20+
@Test
21+
public void testDefaultNamespaceIndex() {
22+
String testName = "test-name";
23+
String testNamespace = "test-namespace";
24+
V1Pod pod = new V1Pod().metadata(new V1ObjectMeta().name(testName).namespace(testNamespace));
25+
List<String> indices = Caches.metaNamespaceIndexFunc(pod);
26+
assertEquals(pod.getMetadata().getNamespace(), indices.get(0));
27+
}
28+
}

0 commit comments

Comments
 (0)