Skip to content

Commit b275b0d

Browse files
committed
index based discriminator
1 parent d33cd5f commit b275b0d

14 files changed

+329
-21
lines changed

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@
99
public interface ResourceDiscriminator<R, P extends HasMetadata> {
1010

1111
Optional<R> distinguish(Class<R> resource, P primary, Context<P> context,
12-
EventSourceRetriever<P> eventSourceManager);
12+
EventSourceRetriever<P> eventSourceRetriever);
1313

1414
}

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceIDMatcherDiscriminator.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public ResourceIDMatcherDiscriminator(Function<P, ResourceID> mapper) {
1818

1919
@Override
2020
public Optional<R> distinguish(Class<R> resource, P primary, Context<P> context,
21-
EventSourceRetriever<P> eventSourceManager) {
21+
EventSourceRetriever<P> eventSourceRetriever) {
2222
var resourceID = mapper.apply(primary);
2323
return context.getSecondaryResources(resource).stream()
2424
.filter(r -> r.getMetadata().getName()

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/VoidResourceDiscriminator.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public class VoidResourceDiscriminator<R, P extends HasMetadata>
1010

1111
@Override
1212
public Optional<R> distinguish(Class<R> resource, P primary, Context<P> context,
13-
EventSourceRetriever<P> eventSourceManager) {
13+
EventSourceRetriever<P> eventSourceRetriever) {
1414
throw new UnsupportedOperationException();
1515
}
1616
}

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResource.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -134,11 +134,12 @@ protected R desired(P primary, Context<P> context) {
134134
"desired method must be implemented if this DependentResource can be created and/or updated");
135135
}
136136

137-
protected ResourceDiscriminator<R, P> getResourceDiscriminator() {
137+
// todo review & refactor configuration to cover all cases
138+
public ResourceDiscriminator<R, P> getResourceDiscriminator() {
138139
return resourceDiscriminator;
139140
}
140141

141-
protected AbstractDependentResource<R, P> setResourceDiscriminator(
142+
public AbstractDependentResource<R, P> setResourceDiscriminator(
142143
ResourceDiscriminator<R, P> resourceDiscriminator) {
143144
this.resourceDiscriminator = resourceDiscriminator;
144145
return this;

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResource.java

+10-8
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,15 @@ private void configureWith(String labelSelector, Set<String> namespaces,
6767
namespaces = context.getControllerConfiguration().getNamespaces();
6868
}
6969

70-
var ic = InformerConfiguration.from(resourceType())
71-
.withLabelSelector(labelSelector)
72-
.withSecondaryToPrimaryMapper(getSecondaryToPrimaryMapper())
73-
.withNamespaces(namespaces, inheritNamespacesOnChange)
74-
.build();
75-
76-
configureWith(new InformerEventSource<>(ic, context));
70+
if (eventSource() == null) {
71+
var ic = InformerConfiguration.from(resourceType())
72+
.withLabelSelector(labelSelector)
73+
.withSecondaryToPrimaryMapper(getSecondaryToPrimaryMapper())
74+
.withNamespaces(namespaces, inheritNamespacesOnChange)
75+
.build();
76+
77+
configureWith(new InformerEventSource<>(ic, context));
78+
}
7779
}
7880

7981
@SuppressWarnings("unchecked")
@@ -137,7 +139,7 @@ public Result<R> match(R actualResource, P primary, Context<P> context) {
137139
}
138140

139141
public void delete(P primary, Context<P> context) {
140-
var resource = context.getSecondaryResource(resourceType());
142+
var resource = getSecondaryResource(primary, context);
141143
resource.ifPresent(r -> client.resource(r).delete());
142144
}
143145

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package io.javaoperatorsdk.operator;
2+
3+
import org.junit.jupiter.api.Test;
4+
import org.junit.jupiter.api.extension.RegisterExtension;
5+
6+
import io.fabric8.kubernetes.api.model.ConfigMap;
7+
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
8+
import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension;
9+
import io.javaoperatorsdk.operator.sample.indexdiscriminator.IndexDiscriminatorTestCustomResource;
10+
import io.javaoperatorsdk.operator.sample.indexdiscriminator.IndexDiscriminatorTestReconciler;
11+
import io.javaoperatorsdk.operator.sample.indexdiscriminator.IndexDiscriminatorTestSpec;
12+
13+
import static io.javaoperatorsdk.operator.sample.indexdiscriminator.IndexDiscriminatorTestDRConfigMap.DATA_KEY;
14+
import static io.javaoperatorsdk.operator.sample.indexdiscriminator.IndexDiscriminatorTestReconciler.FIRST_CONFIG_MAP_SUFFIX_1;
15+
import static io.javaoperatorsdk.operator.sample.indexdiscriminator.IndexDiscriminatorTestReconciler.FIRST_CONFIG_MAP_SUFFIX_2;
16+
import static org.assertj.core.api.Assertions.assertThat;
17+
import static org.awaitility.Awaitility.await;
18+
19+
class IndexDiscriminatorIT {
20+
21+
public static final String TEST_RESOURCE_1 = "test1";
22+
public static final String CHANGED_SPEC_VALUE = "otherValue";
23+
@RegisterExtension
24+
LocallyRunOperatorExtension operator =
25+
LocallyRunOperatorExtension.builder().withReconciler(IndexDiscriminatorTestReconciler.class)
26+
.build();
27+
28+
@Test
29+
void resourcesFoundAndReconciled() {
30+
var res = operator.create(createTestCustomResource());
31+
var reconciler = operator.getReconcilerOfType(IndexDiscriminatorTestReconciler.class);
32+
33+
await().untilAsserted(() -> {
34+
assertThat(reconciler.getNumberOfExecutions()).isEqualTo(1);
35+
assertThat(operator.get(ConfigMap.class, TEST_RESOURCE_1 + FIRST_CONFIG_MAP_SUFFIX_1))
36+
.isNotNull();
37+
assertThat(operator.get(ConfigMap.class, TEST_RESOURCE_1 + FIRST_CONFIG_MAP_SUFFIX_2))
38+
.isNotNull();
39+
});
40+
41+
res.getSpec().setValue(CHANGED_SPEC_VALUE);
42+
res = operator.replace(res);
43+
44+
await().untilAsserted(() -> {
45+
assertThat(reconciler.getNumberOfExecutions()).isEqualTo(2);
46+
var cm1 = operator.get(ConfigMap.class, TEST_RESOURCE_1 + FIRST_CONFIG_MAP_SUFFIX_1);
47+
var cm2 = operator.get(ConfigMap.class, TEST_RESOURCE_1 + FIRST_CONFIG_MAP_SUFFIX_2);
48+
assertThat(cm1).isNotNull();
49+
assertThat(cm2).isNotNull();
50+
assertThat(cm1.getData().get(DATA_KEY)).isEqualTo(CHANGED_SPEC_VALUE);
51+
assertThat(cm2.getData().get(DATA_KEY)).isEqualTo(CHANGED_SPEC_VALUE);
52+
});
53+
54+
operator.delete(res);
55+
56+
await().untilAsserted(() -> {
57+
var cm1 = operator.get(ConfigMap.class, TEST_RESOURCE_1 + FIRST_CONFIG_MAP_SUFFIX_1);
58+
var cm2 = operator.get(ConfigMap.class, TEST_RESOURCE_1 + FIRST_CONFIG_MAP_SUFFIX_2);
59+
assertThat(cm1).isNull();
60+
assertThat(cm2).isNull();
61+
});
62+
}
63+
64+
public IndexDiscriminatorTestCustomResource createTestCustomResource() {
65+
IndexDiscriminatorTestCustomResource resource =
66+
new IndexDiscriminatorTestCustomResource();
67+
resource.setMetadata(
68+
new ObjectMetaBuilder()
69+
.withName(TEST_RESOURCE_1)
70+
.withNamespace(operator.getNamespace())
71+
.build());
72+
resource.setSpec(new IndexDiscriminatorTestSpec());
73+
resource.getSpec().setValue("default");
74+
return resource;
75+
}
76+
77+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package io.javaoperatorsdk.operator.sample.indexdiscriminator;
2+
3+
import java.util.Optional;
4+
5+
import io.fabric8.kubernetes.api.model.ConfigMap;
6+
import io.javaoperatorsdk.operator.api.reconciler.Context;
7+
import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator;
8+
import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever;
9+
import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource;
10+
11+
import static io.javaoperatorsdk.operator.sample.indexdiscriminator.IndexDiscriminatorTestReconciler.configMapKeyFromPrimary;
12+
13+
public class IndexDiscriminator
14+
implements ResourceDiscriminator<ConfigMap, IndexDiscriminatorTestCustomResource> {
15+
16+
private final String indexName;
17+
private final String nameSuffix;
18+
19+
public IndexDiscriminator(String indexName, String nameSuffix) {
20+
this.indexName = indexName;
21+
this.nameSuffix = nameSuffix;
22+
}
23+
24+
@Override
25+
public Optional<ConfigMap> distinguish(Class<ConfigMap> resource,
26+
IndexDiscriminatorTestCustomResource primary,
27+
Context<IndexDiscriminatorTestCustomResource> context,
28+
EventSourceRetriever<IndexDiscriminatorTestCustomResource> eventSourceRetriever) {
29+
30+
InformerEventSource<ConfigMap, IndexDiscriminatorTestCustomResource> eventSource =
31+
(InformerEventSource<ConfigMap, IndexDiscriminatorTestCustomResource>) eventSourceRetriever
32+
.getResourceEventSourceFor(ConfigMap.class);
33+
var resources = eventSource.byIndex(indexName, configMapKeyFromPrimary(primary, nameSuffix));
34+
if (resources.isEmpty()) {
35+
return Optional.empty();
36+
} else if (resources.size() > 1) {
37+
throw new IllegalStateException("more than one resource");
38+
} else {
39+
return Optional.of(resources.get(0));
40+
}
41+
}
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package io.javaoperatorsdk.operator.sample.indexdiscriminator;
2+
3+
import io.fabric8.kubernetes.api.model.Namespaced;
4+
import io.fabric8.kubernetes.client.CustomResource;
5+
import io.fabric8.kubernetes.model.annotation.Group;
6+
import io.fabric8.kubernetes.model.annotation.ShortNames;
7+
import io.fabric8.kubernetes.model.annotation.Version;
8+
9+
@Group("sample.javaoperatorsdk")
10+
@Version("v1")
11+
@ShortNames("idt")
12+
public class IndexDiscriminatorTestCustomResource
13+
extends CustomResource<IndexDiscriminatorTestSpec, IndexDiscriminatorTestStatus>
14+
implements Namespaced {
15+
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package io.javaoperatorsdk.operator.sample.indexdiscriminator;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
6+
import io.fabric8.kubernetes.api.model.ConfigMap;
7+
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
8+
import io.javaoperatorsdk.operator.api.reconciler.Context;
9+
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDNoGCKubernetesDependentResource;
10+
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent;
11+
12+
@KubernetesDependent
13+
public class IndexDiscriminatorTestDRConfigMap
14+
extends CRUDNoGCKubernetesDependentResource<ConfigMap, IndexDiscriminatorTestCustomResource> {
15+
16+
public static final String DATA_KEY = "key";
17+
private final String suffix;
18+
19+
public IndexDiscriminatorTestDRConfigMap(String value) {
20+
super(ConfigMap.class);
21+
this.suffix = value;
22+
}
23+
24+
@Override
25+
protected ConfigMap desired(IndexDiscriminatorTestCustomResource primary,
26+
Context<IndexDiscriminatorTestCustomResource> context) {
27+
Map<String, String> data = new HashMap<>();
28+
data.put(DATA_KEY, primary.getSpec().getValue());
29+
30+
return new ConfigMapBuilder()
31+
.withNewMetadata()
32+
.withName(primary.getMetadata().getName() + suffix)
33+
.withNamespace(primary.getMetadata().getNamespace())
34+
.endMetadata()
35+
.withData(data)
36+
.build();
37+
}
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package io.javaoperatorsdk.operator.sample.indexdiscriminator;
2+
3+
import java.util.Collections;
4+
import java.util.List;
5+
import java.util.Map;
6+
import java.util.concurrent.atomic.AtomicInteger;
7+
8+
import io.fabric8.kubernetes.api.model.ConfigMap;
9+
import io.fabric8.kubernetes.client.KubernetesClient;
10+
import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration;
11+
import io.javaoperatorsdk.operator.api.reconciler.*;
12+
import io.javaoperatorsdk.operator.junit.KubernetesClientAware;
13+
import io.javaoperatorsdk.operator.processing.event.source.EventSource;
14+
import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource;
15+
import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider;
16+
17+
@ControllerConfiguration
18+
public class IndexDiscriminatorTestReconciler
19+
implements Reconciler<IndexDiscriminatorTestCustomResource>,
20+
Cleaner<IndexDiscriminatorTestCustomResource>,
21+
TestExecutionInfoProvider, EventSourceInitializer<IndexDiscriminatorTestCustomResource>,
22+
KubernetesClientAware {
23+
24+
public static final String FIRST_CONFIG_MAP_SUFFIX_1 = "-1";
25+
public static final String FIRST_CONFIG_MAP_SUFFIX_2 = "-2";
26+
public static final String CONFIG_MAP_INDEX_1 = "CONFIG_MAP_INDEX1";
27+
public static final String CONFIG_MAP_INDEX_2 = "CONFIG_MAP_INDEX2";
28+
29+
private final AtomicInteger numberOfExecutions = new AtomicInteger(0);
30+
31+
private final IndexDiscriminatorTestDRConfigMap firstDependentResourceConfigMap;
32+
private final IndexDiscriminatorTestDRConfigMap secondDependentResourceConfigMap;
33+
private KubernetesClient client;
34+
35+
public IndexDiscriminatorTestReconciler() {
36+
firstDependentResourceConfigMap =
37+
new IndexDiscriminatorTestDRConfigMap(FIRST_CONFIG_MAP_SUFFIX_1);
38+
secondDependentResourceConfigMap =
39+
new IndexDiscriminatorTestDRConfigMap(FIRST_CONFIG_MAP_SUFFIX_2);
40+
}
41+
42+
@Override
43+
public UpdateControl<IndexDiscriminatorTestCustomResource> reconcile(
44+
IndexDiscriminatorTestCustomResource resource,
45+
Context<IndexDiscriminatorTestCustomResource> context) {
46+
numberOfExecutions.getAndIncrement();
47+
firstDependentResourceConfigMap.reconcile(resource, context);
48+
secondDependentResourceConfigMap.reconcile(resource, context);
49+
return UpdateControl.noUpdate();
50+
}
51+
52+
public int getNumberOfExecutions() {
53+
return numberOfExecutions.get();
54+
}
55+
56+
@Override
57+
public Map<String, EventSource> prepareEventSources(
58+
EventSourceContext<IndexDiscriminatorTestCustomResource> context) {
59+
60+
InformerEventSource<ConfigMap, IndexDiscriminatorTestCustomResource> eventSource =
61+
new InformerEventSource<>(InformerConfiguration.from(ConfigMap.class, context)
62+
.build(), context);
63+
64+
eventSource.addIndexer(CONFIG_MAP_INDEX_1, cm -> {
65+
if (cm.getMetadata().getName().endsWith(FIRST_CONFIG_MAP_SUFFIX_1)) {
66+
return List.of(configMapKey(cm));
67+
} else {
68+
return Collections.emptyList();
69+
}
70+
});
71+
eventSource.addIndexer(CONFIG_MAP_INDEX_2, cm -> {
72+
if (cm.getMetadata().getName().endsWith(FIRST_CONFIG_MAP_SUFFIX_2)) {
73+
return List.of(configMapKey(cm));
74+
} else {
75+
return Collections.emptyList();
76+
}
77+
});
78+
79+
firstDependentResourceConfigMap.configureWith(eventSource);
80+
secondDependentResourceConfigMap.configureWith(eventSource);
81+
82+
firstDependentResourceConfigMap
83+
.setResourceDiscriminator(
84+
new IndexDiscriminator(CONFIG_MAP_INDEX_1, FIRST_CONFIG_MAP_SUFFIX_1));
85+
secondDependentResourceConfigMap
86+
.setResourceDiscriminator(
87+
new IndexDiscriminator(CONFIG_MAP_INDEX_2, FIRST_CONFIG_MAP_SUFFIX_2));
88+
return EventSourceInitializer.nameEventSources(eventSource);
89+
}
90+
91+
@Override
92+
public KubernetesClient getKubernetesClient() {
93+
return client;
94+
}
95+
96+
@Override
97+
public void setKubernetesClient(KubernetesClient kubernetesClient) {
98+
this.client = kubernetesClient;
99+
firstDependentResourceConfigMap.setKubernetesClient(kubernetesClient);
100+
secondDependentResourceConfigMap.setKubernetesClient(kubernetesClient);
101+
}
102+
103+
public static String configMapKey(ConfigMap configMap) {
104+
return configMap.getMetadata().getName() + "#" + configMap.getMetadata().getNamespace();
105+
}
106+
107+
public static String configMapKeyFromPrimary(IndexDiscriminatorTestCustomResource primary,
108+
String nameSuffix) {
109+
return primary.getMetadata().getName() + nameSuffix + "#"
110+
+ primary.getMetadata().getNamespace();
111+
}
112+
113+
@Override
114+
public DeleteControl cleanup(IndexDiscriminatorTestCustomResource resource,
115+
Context<IndexDiscriminatorTestCustomResource> context) {
116+
firstDependentResourceConfigMap.delete(resource, context);
117+
secondDependentResourceConfigMap.delete(resource, context);
118+
return DeleteControl.defaultDelete();
119+
}
120+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package io.javaoperatorsdk.operator.sample.indexdiscriminator;
2+
3+
public class IndexDiscriminatorTestSpec {
4+
5+
private String value;
6+
7+
public String getValue() {
8+
return value;
9+
}
10+
11+
public IndexDiscriminatorTestSpec setValue(String value) {
12+
this.value = value;
13+
return this;
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package io.javaoperatorsdk.operator.sample.indexdiscriminator;
2+
3+
public class IndexDiscriminatorTestStatus {
4+
5+
}

0 commit comments

Comments
 (0)