Skip to content

Commit 0494505

Browse files
authored
Improvements (#569)
* feat: add Mappers class to provide convenient mapper implementations * feat: add getAssociated method on InformerEventSource Actually not sure if this makes sense: could there be cases where a given primary resource maps to several secondary resources? * refactor: use Function instead of dedicated classes. Clearer?
1 parent 327e93a commit 0494505

File tree

3 files changed

+74
-37
lines changed

3 files changed

+74
-37
lines changed

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/internal/InformerEventSource.java

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,57 @@
11
package io.javaoperatorsdk.operator.processing.event.internal;
22

33
import java.io.IOException;
4-
import java.util.List;
4+
import java.util.Objects;
5+
import java.util.Set;
6+
import java.util.function.Function;
57

68
import io.fabric8.kubernetes.api.model.HasMetadata;
79
import io.fabric8.kubernetes.client.KubernetesClient;
810
import io.fabric8.kubernetes.client.informers.ResourceEventHandler;
911
import io.fabric8.kubernetes.client.informers.SharedInformer;
12+
import io.fabric8.kubernetes.client.informers.cache.Cache;
1013
import io.fabric8.kubernetes.client.informers.cache.Store;
1114
import io.javaoperatorsdk.operator.processing.event.AbstractEventSource;
1215

1316
public class InformerEventSource<T extends HasMetadata> extends AbstractEventSource {
1417

1518
private final SharedInformer<T> sharedInformer;
16-
private final ResourceToRelatedCustomResourceUIDMapper<T> mapper;
19+
private final Function<T, Set<String>> resourceToUIDs;
20+
private final Function<HasMetadata, T> associatedWith;
1721
private final boolean skipUpdateEventPropagationIfNoChange;
1822

1923
public InformerEventSource(SharedInformer<T> sharedInformer,
20-
ResourceToRelatedCustomResourceUIDMapper<T> mapper) {
21-
this(sharedInformer, mapper, true);
24+
Function<T, Set<String>> resourceToUIDs) {
25+
this(sharedInformer, resourceToUIDs, null, true);
2226
}
2327

24-
InformerEventSource(KubernetesClient client, Class<T> type,
25-
ResourceToRelatedCustomResourceUIDMapper<T> mapper) {
26-
this(client, type, mapper, false);
28+
public InformerEventSource(KubernetesClient client, Class<T> type,
29+
Function<T, Set<String>> resourceToUIDs) {
30+
this(client, type, resourceToUIDs, false);
2731
}
2832

2933
InformerEventSource(KubernetesClient client, Class<T> type,
30-
ResourceToRelatedCustomResourceUIDMapper<T> mapper,
34+
Function<T, Set<String>> resourceToUIDs,
3135
boolean skipUpdateEventPropagationIfNoChange) {
32-
this(client.informers().sharedIndexInformerFor(type, 0), mapper,
36+
this(client.informers().sharedIndexInformerFor(type, 0), resourceToUIDs, null,
3337
skipUpdateEventPropagationIfNoChange);
3438
}
3539

3640
public InformerEventSource(SharedInformer<T> sharedInformer,
37-
ResourceToRelatedCustomResourceUIDMapper<T> mapper,
41+
Function<T, Set<String>> resourceToUIDs,
42+
Function<HasMetadata, T> associatedWith,
3843
boolean skipUpdateEventPropagationIfNoChange) {
3944
this.sharedInformer = sharedInformer;
40-
this.mapper = mapper;
45+
this.resourceToUIDs = resourceToUIDs;
4146
this.skipUpdateEventPropagationIfNoChange = skipUpdateEventPropagationIfNoChange;
4247

43-
sharedInformer.addEventHandler(new ResourceEventHandler<T>() {
48+
this.associatedWith = Objects.requireNonNullElseGet(associatedWith, () -> cr -> {
49+
final var metadata = cr.getMetadata();
50+
return getStore().getByKey(Cache.namespaceKeyFunc(metadata.getNamespace(),
51+
metadata.getName()));
52+
});
53+
54+
sharedInformer.addEventHandler(new ResourceEventHandler<>() {
4455
@Override
4556
public void onAdd(T t) {
4657
propagateEvent(InformerEvent.Action.ADD, t, null);
@@ -64,7 +75,7 @@ public void onDelete(T t, boolean b) {
6475
}
6576

6677
private void propagateEvent(InformerEvent.Action action, T object, T oldObject) {
67-
var uids = mapper.map(object);
78+
var uids = resourceToUIDs.apply(object);
6879
if (uids.isEmpty()) {
6980
return;
7081
}
@@ -88,12 +99,19 @@ public Store<T> getStore() {
8899
return sharedInformer.getStore();
89100
}
90101

91-
public SharedInformer<T> getSharedInformer() {
92-
return sharedInformer;
102+
/**
103+
* Retrieves the informed resource associated with the specified primary resource as defined by
104+
* the function provided when this InformerEventSource was created
105+
*
106+
* @param resource the primary resource we want to retrieve the associated resource for
107+
* @return the informed resource associated with the specified primary resource
108+
*/
109+
public T getAssociated(HasMetadata resource) {
110+
return associatedWith.apply(resource);
93111
}
94112

95-
public interface ResourceToRelatedCustomResourceUIDMapper<T> {
96-
List<String> map(T resource);
97-
}
98113

114+
public SharedInformer<T> getSharedInformer() {
115+
return sharedInformer;
116+
}
99117
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package io.javaoperatorsdk.operator.processing.event.internal;
2+
3+
import java.util.Collections;
4+
import java.util.Set;
5+
import java.util.function.Function;
6+
7+
import io.fabric8.kubernetes.api.model.HasMetadata;
8+
9+
public class Mappers {
10+
public static <T extends HasMetadata> Function<T, Set<String>> fromAnnotation(
11+
String annotationKey) {
12+
return fromMetadata(annotationKey, false);
13+
}
14+
15+
public static <T extends HasMetadata> Function<T, Set<String>> fromLabel(
16+
String labelKey) {
17+
return fromMetadata(labelKey, true);
18+
}
19+
20+
private static <T extends HasMetadata> Function<T, Set<String>> fromMetadata(
21+
String key, boolean isLabel) {
22+
return resource -> {
23+
final var metadata = resource.getMetadata();
24+
if (metadata == null) {
25+
return Collections.emptySet();
26+
} else {
27+
final var map = isLabel ? metadata.getLabels() : metadata.getAnnotations();
28+
return map != null ? Set.of(map.get(key)) : Collections.emptySet();
29+
}
30+
};
31+
}
32+
}

operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/informereventsource/InformerEventSourceTestCustomResourceController.java

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,18 @@
11
package io.javaoperatorsdk.operator.sample.informereventsource;
22

3-
import java.util.Arrays;
4-
import java.util.Collections;
5-
63
import org.slf4j.Logger;
74
import org.slf4j.LoggerFactory;
85

96
import io.fabric8.kubernetes.api.model.ConfigMap;
107
import io.fabric8.kubernetes.client.KubernetesClient;
11-
import io.fabric8.kubernetes.client.informers.SharedInformer;
12-
import io.fabric8.kubernetes.client.informers.SharedInformerFactory;
13-
import io.fabric8.kubernetes.client.informers.cache.Cache;
148
import io.javaoperatorsdk.operator.api.Context;
159
import io.javaoperatorsdk.operator.api.Controller;
1610
import io.javaoperatorsdk.operator.api.ResourceController;
1711
import io.javaoperatorsdk.operator.api.UpdateControl;
1812
import io.javaoperatorsdk.operator.junit.KubernetesClientAware;
1913
import io.javaoperatorsdk.operator.processing.event.EventSourceManager;
2014
import io.javaoperatorsdk.operator.processing.event.internal.InformerEventSource;
15+
import io.javaoperatorsdk.operator.processing.event.internal.Mappers;
2116

2217
import static io.javaoperatorsdk.operator.api.Controller.NO_FINALIZER;
2318

@@ -36,19 +31,13 @@ public class InformerEventSourceTestCustomResourceController implements
3631
public static final String TARGET_CONFIG_MAP_KEY = "targetStatus";
3732

3833
private KubernetesClient kubernetesClient;
39-
private SharedInformer<ConfigMap> informer;
34+
private InformerEventSource<ConfigMap> eventSource;
4035

4136
@Override
4237
public void init(EventSourceManager eventSourceManager) {
43-
SharedInformerFactory sharedInformerFactory = kubernetesClient.informers();
44-
informer = sharedInformerFactory.sharedIndexInformerFor(ConfigMap.class, 0);
45-
eventSourceManager.registerEventSource("configmap", new InformerEventSource<>(informer,
46-
resource -> {
47-
if (resource.getMetadata() == null || resource.getMetadata().getAnnotations() == null) {
48-
return Collections.emptyList();
49-
}
50-
return Arrays.asList(resource.getMetadata().getAnnotations().get(RELATED_RESOURCE_UID));
51-
}));
38+
eventSource = new InformerEventSource<>(kubernetesClient, ConfigMap.class,
39+
Mappers.fromAnnotation(RELATED_RESOURCE_UID));
40+
eventSourceManager.registerEventSource("configmap", eventSource);
5241
}
5342

5443
@Override
@@ -58,9 +47,7 @@ public UpdateControl<InformerEventSourceTestCustomResource> createOrUpdateResour
5847

5948
// Reading the config map from the informer not from the API
6049
// name of the config map same as custom resource for sake of simplicity
61-
ConfigMap configMap =
62-
informer.getStore().getByKey(Cache.namespaceKeyFunc(resource.getMetadata().getNamespace(),
63-
resource.getMetadata().getName()));
50+
ConfigMap configMap = eventSource.getAssociated(resource);
6451

6552
String targetStatus = configMap.getData().get(TARGET_CONFIG_MAP_KEY);
6653
LOGGER.debug("Setting target status for CR: {}", targetStatus);

0 commit comments

Comments
 (0)