Skip to content

Commit 3365b73

Browse files
authoredMar 13, 2024
feat: distinguish resources based on desired state (#2252)
Signed-off-by: Attila Mészáros <csviri@gmail.com>
1 parent 5f6e654 commit 3365b73

File tree

29 files changed

+672
-108
lines changed

29 files changed

+672
-108
lines changed
 

‎docs/documentation/dependent-resources.md

+29-18
Original file line numberDiff line numberDiff line change
@@ -301,20 +301,31 @@ tests [here](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/op
301301

302302
When dealing with multiple dependent resources of same type, the dependent resource implementation
303303
needs to know which specific resource should be targeted when reconciling a given dependent
304-
resource, since there will be multiple instances of that type which could possibly be used, each
305-
associated with the same primary resource. In order to do this, JOSDK relies on the
306-
[resource discriminator](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java)
307-
concept. Resource discriminators uniquely identify the target resource of a dependent resource.
308-
In the managed Kubernetes dependent resources case, the discriminator can be declaratively set
309-
using the `@KubernetesDependent` annotation:
310-
311-
```java
312-
313-
@KubernetesDependent(resourceDiscriminator = ConfigMap1Discriminator.class)
314-
public class MultipleManagedDependentResourceConfigMap1 {
315-
//...
316-
}
317-
```
304+
resource, since there could be multiple instances of that type which could possibly be used, each
305+
associated with the same primary resource. In this situation, JOSDK automatically selects the appropriate secondary
306+
resource matching the desired state associated with the primary resource. This makes sense because the desired
307+
state computation already needs to be able to discriminate among multiple related secondary resources to tell JOSDK how
308+
they should be reconciled.
309+
310+
There might be casees, though, where it might be problematic to call the `desired` method several times (for example, because it is costly to do so), it is always possible to override this automated discrimination using several means:
311+
312+
- Implement your own `getSecondaryResource` method on your `DependentResource` implementation from scratch.
313+
- Override the `selectManagedSecondaryResource` method, if your `DependentResource` extends `AbstractDependentResource`.
314+
This should be relatively simple to override this method to optimize the matching to your needs. You can see an
315+
example of such an implementation in
316+
the [`ExternalWithStateDependentResource`](https://github.com/operator-framework/java-operator-sdk/blob/6cd0f884a7c9b60c81bd2d52da54adbd64d6e118/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/externalstate/ExternalWithStateDependentResource.java#L43-L49)
317+
class.
318+
- Override the `managedSecondaryResourceID` method, if your `DependentResource` extends `KubernetesDependentResource`,
319+
where it's very often possible to easily determine the `ResourceID` of the secondary resource. This would probably be
320+
the easiest solution if you're working with Kubernetes resources.
321+
- Configure
322+
a [`ResourceDiscriminator`](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ResourceDiscriminator.java)
323+
implementation for your `DependentResource`. This was the approach that was used before JOSDK v5 but should not be
324+
needed anymore as it is simpler and more efficient to override one the methods above instead of creating a separate
325+
class. Discriminators can be declaratively set when using managed Kubernetes dependent resources via
326+
the `resourceDiscriminator` field of the `@KubernetesDependent` annotation.
327+
328+
### Sharing an Event Source Between Dependent Resources
318329

319330
Dependent resources usually also provide event sources. When dealing with multiple dependents of
320331
the same type, one needs to decide whether these dependent resources should track the same
@@ -330,10 +341,10 @@ would look as follows:
330341
useEventSourceWithName = "configMapSource")
331342
```
332343

333-
A sample is provided as an integration test both
334-
for [managed](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentSameTypeIT.java)
335-
and
336-
for [standalone](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleDependentResourceIT.java)
344+
A sample is provided as an integration test both:
345+
for [managed](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleManagedDependentNoDiscriminatorIT.java)
346+
347+
For [standalone](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/MultipleDependentResourceIT.java)
337348
cases.
338349

339350
## Bulk Dependent Resources

‎operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/DependentResource.java

+10
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,16 @@ default Optional<? extends ResourceEventSource<R, P>> eventSource(
4949
return Optional.empty();
5050
}
5151

52+
/**
53+
* Retrieves the secondary resource (if it exists) associated with the specified primary resource
54+
* for this DependentResource.
55+
*
56+
* @param primary the primary resource for which we want to retrieve the secondary resource
57+
* associated with this DependentResource
58+
* @param context the current {@link Context} in which the operation is called
59+
* @return the secondary resource or {@link Optional#empty()} if it doesn't exist
60+
* @throws IllegalStateException if more than one secondary is found to match the primary resource
61+
*/
5262
default Optional<R> getSecondaryResource(P primary, Context<P> context) {
5363
return Optional.empty();
5464
}

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

+35-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.javaoperatorsdk.operator.processing.dependent;
22

33
import java.util.Optional;
4+
import java.util.Set;
45

56
import org.slf4j.Logger;
67
import org.slf4j.LoggerFactory;
@@ -97,8 +98,39 @@ protected ReconcileResult<R> reconcile(P primary, R actualResource, Context<P> c
9798

9899
@Override
99100
public Optional<R> getSecondaryResource(P primary, Context<P> context) {
100-
return resourceDiscriminator == null ? context.getSecondaryResource(resourceType())
101-
: resourceDiscriminator.distinguish(resourceType(), primary, context);
101+
if (resourceDiscriminator != null) {
102+
return resourceDiscriminator.distinguish(resourceType(), primary, context);
103+
} else {
104+
var secondaryResources = context.getSecondaryResources(resourceType());
105+
if (secondaryResources.isEmpty()) {
106+
return Optional.empty();
107+
} else {
108+
return selectManagedSecondaryResource(secondaryResources, primary, context);
109+
}
110+
}
111+
}
112+
113+
/**
114+
* Selects the actual secondary resource matching the desired state derived from the primary
115+
* resource when several resources of the same type are found in the context. This method allows
116+
* for optimized implementations in subclasses since this default implementation will check each
117+
* secondary candidates for equality with the specified desired state, which might end up costly.
118+
*
119+
* @param secondaryResources to select the target resource from
120+
*
121+
* @return the matching secondary resource or {@link Optional#empty()} if none matches
122+
* @throws IllegalStateException if more than one candidate is found, in which case some other
123+
* mechanism might be necessary to distinguish between candidate secondary resources
124+
*/
125+
protected Optional<R> selectManagedSecondaryResource(Set<R> secondaryResources, P primary,
126+
Context<P> context) {
127+
R desired = desired(primary, context);
128+
var targetResources = secondaryResources.stream().filter(r -> r.equals(desired)).toList();
129+
if (targetResources.size() > 1) {
130+
throw new IllegalStateException(
131+
"More than one secondary resource related to primary: " + targetResources);
132+
}
133+
return targetResources.isEmpty() ? Optional.empty() : Optional.of(targetResources.get(0));
102134
}
103135

104136
private void throwIfNull(R desired, P primary, String descriptor) {
@@ -166,8 +198,7 @@ protected void handleDelete(P primary, R secondary, Context<P> context) {
166198
"handleDelete method must be implemented if Deleter trait is supported");
167199
}
168200

169-
public void setResourceDiscriminator(
170-
ResourceDiscriminator<R, P> resourceDiscriminator) {
201+
public void setResourceDiscriminator(ResourceDiscriminator<R, P> resourceDiscriminator) {
171202
this.resourceDiscriminator = resourceDiscriminator;
172203
}
173204

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

+24
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.javaoperatorsdk.operator.processing.dependent.kubernetes;
22

33
import java.util.Map;
4+
import java.util.Objects;
45
import java.util.Optional;
56
import java.util.Set;
67

@@ -294,6 +295,29 @@ protected void addSecondaryToPrimaryMapperAnnotations(R desired, P primary, Stri
294295
}
295296
}
296297

298+
@Override
299+
protected Optional<R> selectManagedSecondaryResource(Set<R> secondaryResources, P primary,
300+
Context<P> context) {
301+
ResourceID managedResourceID = managedSecondaryResourceID(primary, context);
302+
return secondaryResources.stream()
303+
.filter(r -> r.getMetadata().getName().equals(managedResourceID.getName()) &&
304+
Objects.equals(r.getMetadata().getNamespace(),
305+
managedResourceID.getNamespace().orElse(null)))
306+
.findFirst();
307+
}
308+
309+
/**
310+
* Override this method in order to optimize and not compute the desired when selecting the target
311+
* secondary resource. Simply, a static ResourceID can be returned.
312+
*
313+
* @param primary resource
314+
* @param context of current reconciliation
315+
* @return id of the target managed resource
316+
*/
317+
protected ResourceID managedSecondaryResourceID(P primary, Context<P> context) {
318+
return ResourceID.fromResource(desired(primary, context));
319+
}
320+
297321
protected boolean addOwnerReference() {
298322
return garbageCollected;
299323
}

‎operator-framework/src/test/java/io/javaoperatorsdk/operator/ExternalStateBulkIT.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class ExternalStateBulkIT {
3434
.build();
3535

3636
@Test
37-
void reconcilesResourceWithPersistentState() throws InterruptedException {
37+
void reconcilesResourceWithPersistentState() {
3838
var resource = operator.create(testResource());
3939
assertResources(resource, INITIAL_TEST_DATA, INITIAL_BULK_SIZE);
4040

Original file line numberDiff line numberDiff line change
@@ -1,62 +1,80 @@
11
package io.javaoperatorsdk.operator;
22

33
import java.time.Duration;
4-
import java.util.stream.IntStream;
54

65
import org.junit.jupiter.api.Test;
76
import org.junit.jupiter.api.extension.RegisterExtension;
87

98
import io.fabric8.kubernetes.api.model.ConfigMap;
109
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
1110
import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension;
12-
import io.javaoperatorsdk.operator.sample.multipledependentresource.MultipleDependentResourceConfigMap;
1311
import io.javaoperatorsdk.operator.sample.multipledependentresource.MultipleDependentResourceCustomResource;
1412
import io.javaoperatorsdk.operator.sample.multipledependentresource.MultipleDependentResourceReconciler;
13+
import io.javaoperatorsdk.operator.sample.multipledependentresource.MultipleDependentResourceSpec;
14+
import io.javaoperatorsdk.operator.sample.multipledrsametypenodiscriminator.*;
1515

16+
import static io.javaoperatorsdk.operator.sample.multipledependentresource.MultipleDependentResourceConfigMap.DATA_KEY;
17+
import static io.javaoperatorsdk.operator.sample.multipledependentresource.MultipleDependentResourceConfigMap.getConfigMapName;
18+
import static io.javaoperatorsdk.operator.sample.multipledependentresource.MultipleDependentResourceReconciler.FIRST_CONFIG_MAP_ID;
19+
import static io.javaoperatorsdk.operator.sample.multipledependentresource.MultipleDependentResourceReconciler.SECOND_CONFIG_MAP_ID;
1620
import static org.assertj.core.api.Assertions.assertThat;
1721
import static org.awaitility.Awaitility.await;
1822

19-
class MultipleDependentResourceIT {
23+
public class MultipleDependentResourceIT {
24+
25+
public static final String CHANGED_VALUE = "changed value";
26+
public static final String INITIAL_VALUE = "initial value";
2027

21-
public static final String TEST_RESOURCE_NAME = "multipledependentresource-testresource";
2228
@RegisterExtension
23-
LocallyRunOperatorExtension operator =
29+
LocallyRunOperatorExtension extension =
2430
LocallyRunOperatorExtension.builder()
25-
.withReconciler(MultipleDependentResourceReconciler.class)
26-
.waitForNamespaceDeletion(true)
31+
.withReconciler(new MultipleDependentResourceReconciler())
2732
.build();
2833

2934
@Test
30-
void twoConfigMapsHaveBeenCreated() {
31-
MultipleDependentResourceCustomResource customResource = createTestCustomResource();
32-
operator.create(customResource);
33-
34-
var reconciler = operator.getReconcilerOfType(MultipleDependentResourceReconciler.class);
35-
36-
await().pollDelay(Duration.ofMillis(300))
37-
.until(() -> reconciler.getNumberOfExecutions() <= 1);
38-
39-
IntStream.of(MultipleDependentResourceReconciler.FIRST_CONFIG_MAP_ID,
40-
MultipleDependentResourceReconciler.SECOND_CONFIG_MAP_ID).forEach(configMapId -> {
41-
ConfigMap configMap =
42-
operator.get(ConfigMap.class, customResource.getConfigMapName(configMapId));
43-
assertThat(configMap).isNotNull();
44-
assertThat(configMap.getMetadata().getName())
45-
.isEqualTo(customResource.getConfigMapName(configMapId));
46-
assertThat(configMap.getData().get(MultipleDependentResourceConfigMap.DATA_KEY))
47-
.isEqualTo(String.valueOf(configMapId));
48-
});
49-
}
35+
void handlesCRUDOperations() {
36+
var res = extension.create(testResource());
37+
38+
await().untilAsserted(() -> {
39+
var cm1 = extension.get(ConfigMap.class, getConfigMapName(FIRST_CONFIG_MAP_ID));
40+
var cm2 = extension.get(ConfigMap.class, getConfigMapName(SECOND_CONFIG_MAP_ID));
41+
42+
assertThat(cm1).isNotNull();
43+
assertThat(cm2).isNotNull();
44+
assertThat(cm1.getData()).containsEntry(DATA_KEY, INITIAL_VALUE);
45+
assertThat(cm2.getData()).containsEntry(DATA_KEY, INITIAL_VALUE);
46+
});
47+
48+
res.getSpec().setValue(CHANGED_VALUE);
49+
res = extension.replace(res);
50+
51+
await().untilAsserted(() -> {
52+
var cm1 = extension.get(ConfigMap.class, getConfigMapName(FIRST_CONFIG_MAP_ID));
53+
var cm2 = extension.get(ConfigMap.class, getConfigMapName(SECOND_CONFIG_MAP_ID));
5054

51-
public MultipleDependentResourceCustomResource createTestCustomResource() {
52-
MultipleDependentResourceCustomResource resource =
53-
new MultipleDependentResourceCustomResource();
54-
resource.setMetadata(
55-
new ObjectMetaBuilder()
56-
.withName(TEST_RESOURCE_NAME)
57-
.withNamespace(operator.getNamespace())
58-
.build());
59-
return resource;
55+
assertThat(cm1.getData()).containsEntry(DATA_KEY, CHANGED_VALUE);
56+
assertThat(cm2.getData()).containsEntry(DATA_KEY, CHANGED_VALUE);
57+
});
58+
59+
extension.delete(res);
60+
61+
await().timeout(Duration.ofSeconds(120)).untilAsserted(() -> {
62+
var cm1 = extension.get(ConfigMap.class, getConfigMapName(FIRST_CONFIG_MAP_ID));
63+
var cm2 = extension.get(ConfigMap.class, getConfigMapName(SECOND_CONFIG_MAP_ID));
64+
65+
assertThat(cm1).isNull();
66+
assertThat(cm2).isNull();
67+
});
6068
}
6169

70+
MultipleDependentResourceCustomResource testResource() {
71+
var res = new MultipleDependentResourceCustomResource();
72+
res.setMetadata(new ObjectMetaBuilder()
73+
.withName("test1")
74+
.build());
75+
res.setSpec(new MultipleDependentResourceSpec());
76+
res.getSpec().setValue(INITIAL_VALUE);
77+
78+
return res;
79+
}
6280
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package io.javaoperatorsdk.operator;
2+
3+
import java.time.Duration;
4+
import java.util.stream.IntStream;
5+
6+
import org.junit.jupiter.api.Test;
7+
import org.junit.jupiter.api.extension.RegisterExtension;
8+
9+
import io.fabric8.kubernetes.api.model.ConfigMap;
10+
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
11+
import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension;
12+
import io.javaoperatorsdk.operator.sample.multipledependentresourcewithdiscriminator.MultipleDependentResourceConfigMap;
13+
import io.javaoperatorsdk.operator.sample.multipledependentresourcewithdiscriminator.MultipleDependentResourceCustomResourceWithDiscriminator;
14+
import io.javaoperatorsdk.operator.sample.multipledependentresourcewithdiscriminator.MultipleDependentResourceWithDiscriminatorReconciler;
15+
16+
import static org.assertj.core.api.Assertions.assertThat;
17+
import static org.awaitility.Awaitility.await;
18+
19+
class MultipleDependentResourceWithNoDiscriminatorIT {
20+
21+
public static final String TEST_RESOURCE_NAME = "multipledependentresource-testresource";
22+
@RegisterExtension
23+
LocallyRunOperatorExtension operator =
24+
LocallyRunOperatorExtension.builder()
25+
.withReconciler(MultipleDependentResourceWithDiscriminatorReconciler.class)
26+
.waitForNamespaceDeletion(true)
27+
.build();
28+
29+
@Test
30+
void twoConfigMapsHaveBeenCreated() {
31+
MultipleDependentResourceCustomResourceWithDiscriminator customResource =
32+
createTestCustomResource();
33+
operator.create(customResource);
34+
35+
var reconciler =
36+
operator.getReconcilerOfType(MultipleDependentResourceWithDiscriminatorReconciler.class);
37+
38+
await().pollDelay(Duration.ofMillis(300))
39+
.until(() -> reconciler.getNumberOfExecutions() <= 1);
40+
41+
IntStream.of(MultipleDependentResourceWithDiscriminatorReconciler.FIRST_CONFIG_MAP_ID,
42+
MultipleDependentResourceWithDiscriminatorReconciler.SECOND_CONFIG_MAP_ID)
43+
.forEach(configMapId -> {
44+
ConfigMap configMap =
45+
operator.get(ConfigMap.class, customResource.getConfigMapName(configMapId));
46+
assertThat(configMap).isNotNull();
47+
assertThat(configMap.getMetadata().getName())
48+
.isEqualTo(customResource.getConfigMapName(configMapId));
49+
assertThat(configMap.getData().get(MultipleDependentResourceConfigMap.DATA_KEY))
50+
.isEqualTo(String.valueOf(configMapId));
51+
});
52+
}
53+
54+
public MultipleDependentResourceCustomResourceWithDiscriminator createTestCustomResource() {
55+
MultipleDependentResourceCustomResourceWithDiscriminator resource =
56+
new MultipleDependentResourceCustomResourceWithDiscriminator();
57+
resource.setMetadata(
58+
new ObjectMetaBuilder()
59+
.withName(TEST_RESOURCE_NAME)
60+
.withNamespace(operator.getNamespace())
61+
.build());
62+
return resource;
63+
}
64+
65+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package io.javaoperatorsdk.operator;
2+
3+
import java.time.Duration;
4+
5+
import org.junit.jupiter.api.Test;
6+
import org.junit.jupiter.api.extension.RegisterExtension;
7+
8+
import io.fabric8.kubernetes.api.model.ConfigMap;
9+
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
10+
import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension;
11+
import io.javaoperatorsdk.operator.sample.multipledrsametypenodiscriminator.*;
12+
13+
import static io.javaoperatorsdk.operator.sample.multipledrsametypenodiscriminator.MultipleManagedDependentSameTypeNoDiscriminatorReconciler.DATA_KEY;
14+
import static org.assertj.core.api.Assertions.assertThat;
15+
import static org.awaitility.Awaitility.await;
16+
17+
public class MultipleManagedDependentNoDiscriminatorIT {
18+
19+
public static final String RESOURCE_NAME = "test1";
20+
public static final String INITIAL_VALUE = "initial_value";
21+
public static final String CHANGED_VALUE = "changed_value";
22+
23+
@RegisterExtension
24+
LocallyRunOperatorExtension extension =
25+
LocallyRunOperatorExtension.builder()
26+
.withReconciler(new MultipleManagedDependentSameTypeNoDiscriminatorReconciler())
27+
.build();
28+
29+
@Test
30+
void handlesCRUDOperations() {
31+
var res = extension.create(testResource());
32+
33+
await().untilAsserted(() -> {
34+
var cm1 = extension.get(ConfigMap.class,
35+
RESOURCE_NAME + MultipleManagedDependentNoDiscriminatorConfigMap1.NAME_SUFFIX);
36+
var cm2 = extension.get(ConfigMap.class,
37+
RESOURCE_NAME + MultipleManagedDependentNoDiscriminatorConfigMap2.NAME_SUFFIX);
38+
39+
assertThat(cm1).isNotNull();
40+
assertThat(cm2).isNotNull();
41+
assertThat(cm1.getData()).containsEntry(DATA_KEY, INITIAL_VALUE);
42+
assertThat(cm2.getData()).containsEntry(DATA_KEY, INITIAL_VALUE);
43+
});
44+
45+
res.getSpec().setValue(CHANGED_VALUE);
46+
res = extension.replace(res);
47+
48+
await().untilAsserted(() -> {
49+
var cm1 = extension.get(ConfigMap.class,
50+
RESOURCE_NAME + MultipleManagedDependentNoDiscriminatorConfigMap1.NAME_SUFFIX);
51+
var cm2 = extension.get(ConfigMap.class,
52+
RESOURCE_NAME + MultipleManagedDependentNoDiscriminatorConfigMap2.NAME_SUFFIX);
53+
54+
assertThat(cm1.getData()).containsEntry(DATA_KEY, CHANGED_VALUE);
55+
assertThat(cm2.getData()).containsEntry(DATA_KEY, CHANGED_VALUE);
56+
});
57+
58+
extension.delete(res);
59+
60+
await().timeout(Duration.ofSeconds(60)).untilAsserted(() -> {
61+
var cm1 = extension.get(ConfigMap.class,
62+
RESOURCE_NAME + MultipleManagedDependentNoDiscriminatorConfigMap1.NAME_SUFFIX);
63+
var cm2 = extension.get(ConfigMap.class,
64+
RESOURCE_NAME + MultipleManagedDependentNoDiscriminatorConfigMap2.NAME_SUFFIX);
65+
66+
assertThat(cm1).isNull();
67+
assertThat(cm2).isNull();
68+
});
69+
}
70+
71+
MultipleManagedDependentNoDiscriminatorCustomResource testResource() {
72+
var res = new MultipleManagedDependentNoDiscriminatorCustomResource();
73+
res.setMetadata(new ObjectMetaBuilder()
74+
.withName(RESOURCE_NAME)
75+
.build());
76+
res.setSpec(new MultipleManagedDependentNoDiscriminatorSpec());
77+
res.getSpec().setValue(INITIAL_VALUE);
78+
return res;
79+
}
80+
81+
}

‎operator-framework/src/test/java/io/javaoperatorsdk/operator/PrimaryIndexerIT.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@
1313
import io.javaoperatorsdk.operator.sample.primaryindexer.PrimaryIndexerTestCustomResourceSpec;
1414
import io.javaoperatorsdk.operator.sample.primaryindexer.PrimaryIndexerTestReconciler;
1515

16+
import static io.javaoperatorsdk.operator.sample.primaryindexer.AbstractPrimaryIndexerTestReconciler.CONFIG_MAP_NAME;
1617
import static org.assertj.core.api.Assertions.assertThat;
1718
import static org.awaitility.Awaitility.await;
1819

1920
class PrimaryIndexerIT {
2021

21-
public static final String CONFIG_MAP_NAME = "common-config-map";
22+
2223
public static final String RESOURCE_NAME1 = "test1";
2324
public static final String RESOURCE_NAME2 = "test2";
2425

‎operator-framework/src/test/java/io/javaoperatorsdk/operator/PrimaryToSecondaryDependentIT.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@
1414
import io.javaoperatorsdk.operator.sample.primarytosecondaydependent.PrimaryToSecondaryDependentReconciler;
1515
import io.javaoperatorsdk.operator.sample.primarytosecondaydependent.PrimaryToSecondaryDependentSpec;
1616

17+
import static io.javaoperatorsdk.operator.sample.primarytosecondaydependent.ConfigMapDependent.TEST_CONFIG_MAP_NAME;
1718
import static io.javaoperatorsdk.operator.sample.primarytosecondaydependent.ConfigMapReconcilePrecondition.DO_NOT_RECONCILE;
1819
import static io.javaoperatorsdk.operator.sample.primarytosecondaydependent.PrimaryToSecondaryDependentReconciler.DATA_KEY;
1920
import static org.assertj.core.api.Assertions.assertThat;
2021
import static org.awaitility.Awaitility.await;
2122

2223
class PrimaryToSecondaryDependentIT {
2324

24-
public static final String TEST_CONFIG_MAP_NAME = "testconfigmap";
25+
2526
public static final String TEST_CR_NAME = "test1";
2627
public static final String TEST_DATA = "testData";
2728
public

‎operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/externalstate/ExternalStateReconciler.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import io.javaoperatorsdk.operator.support.ExternalResource;
2121
import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider;
2222

23-
@ControllerConfiguration()
23+
@ControllerConfiguration
2424
public class ExternalStateReconciler
2525
implements Reconciler<ExternalStateCustomResource>, Cleaner<ExternalStateCustomResource>,
2626
TestExecutionInfoProvider {

‎operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/externalstate/ExternalWithStateDependentResource.java

+15-5
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,26 @@ public ExternalWithStateDependentResource() {
3434
@SuppressWarnings("unchecked")
3535
public Set<ExternalResource> fetchResources(
3636
ExternalStateCustomResource primaryResource) {
37-
Optional<ConfigMap> configMapOptional =
38-
getExternalStateEventSource().getSecondaryResource(primaryResource);
39-
40-
return configMapOptional.map(configMap -> {
41-
var id = configMap.getData().get(ID_KEY);
37+
return getResourceID(primaryResource).map(id -> {
4238
var externalResource = externalService.read(id);
4339
return externalResource.map(Set::of).orElseGet(Collections::emptySet);
4440
}).orElseGet(Collections::emptySet);
4541
}
4642

43+
@Override
44+
protected Optional<ExternalResource> selectManagedSecondaryResource(
45+
Set<ExternalResource> secondaryResources,
46+
ExternalStateCustomResource primary, Context<ExternalStateCustomResource> context) {
47+
var id = getResourceID(primary);
48+
return id.flatMap(k -> secondaryResources.stream().filter(e -> e.getId().equals(k)).findAny());
49+
}
50+
51+
private Optional<String> getResourceID(ExternalStateCustomResource primaryResource) {
52+
Optional<ConfigMap> configMapOptional =
53+
getExternalStateEventSource().getSecondaryResource(primaryResource);
54+
return configMapOptional.map(cm -> cm.getData().get(ID_KEY));
55+
}
56+
4757
@Override
4858
protected ExternalResource desired(ExternalStateCustomResource primary,
4959
Context<ExternalStateCustomResource> context) {
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package io.javaoperatorsdk.operator.sample.multipledependentresource;
22

3-
import java.util.HashMap;
43
import java.util.Map;
54

65
import io.fabric8.kubernetes.api.model.ConfigMap;
@@ -12,25 +11,27 @@ public class MultipleDependentResourceConfigMap
1211
extends CRUDKubernetesDependentResource<ConfigMap, MultipleDependentResourceCustomResource> {
1312

1413
public static final String DATA_KEY = "key";
15-
private final int value;
14+
private final String value;
1615

17-
public MultipleDependentResourceConfigMap(int value) {
16+
public MultipleDependentResourceConfigMap(String value) {
1817
super(ConfigMap.class);
1918
this.value = value;
2019
}
2120

2221
@Override
2322
protected ConfigMap desired(MultipleDependentResourceCustomResource primary,
2423
Context<MultipleDependentResourceCustomResource> context) {
25-
Map<String, String> data = new HashMap<>();
26-
data.put(DATA_KEY, String.valueOf(value));
2724

2825
return new ConfigMapBuilder()
2926
.withNewMetadata()
30-
.withName(primary.getConfigMapName(value))
27+
.withName(getConfigMapName(value))
3128
.withNamespace(primary.getMetadata().getNamespace())
3229
.endMetadata()
33-
.withData(data)
30+
.withData(Map.of(DATA_KEY, primary.getSpec().getValue()))
3431
.build();
3532
}
33+
34+
public static String getConfigMapName(String id) {
35+
return "configmap" + id;
36+
}
3637
}

‎operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresource/MultipleDependentResourceCustomResource.java

+1-7
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,13 @@
33
import io.fabric8.kubernetes.api.model.Namespaced;
44
import io.fabric8.kubernetes.client.CustomResource;
55
import io.fabric8.kubernetes.model.annotation.Group;
6-
import io.fabric8.kubernetes.model.annotation.Kind;
76
import io.fabric8.kubernetes.model.annotation.ShortNames;
87
import io.fabric8.kubernetes.model.annotation.Version;
98

109
@Group("sample.javaoperatorsdk")
1110
@Version("v1")
12-
@Kind("MultipleDependentResourceCustomResource")
1311
@ShortNames("mdr")
1412
public class MultipleDependentResourceCustomResource
15-
extends CustomResource<Void, MultipleDependentResourceStatus>
13+
extends CustomResource<MultipleDependentResourceSpec, MultipleDependentResourceStatus>
1614
implements Namespaced {
17-
18-
public String getConfigMapName(int id) {
19-
return "configmap" + id;
20-
}
2115
}

‎operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/multipledependentresource/MultipleDependentResourceReconciler.java

+3-26
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,37 @@
11
package io.javaoperatorsdk.operator.sample.multipledependentresource;
22

33
import java.util.Map;
4-
import java.util.concurrent.atomic.AtomicInteger;
54

65
import io.fabric8.kubernetes.api.model.ConfigMap;
76
import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration;
87
import io.javaoperatorsdk.operator.api.reconciler.*;
9-
import io.javaoperatorsdk.operator.processing.event.ResourceID;
108
import io.javaoperatorsdk.operator.processing.event.source.EventSource;
119
import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource;
12-
import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider;
1310

1411
@ControllerConfiguration
1512
public class MultipleDependentResourceReconciler
16-
implements Reconciler<MultipleDependentResourceCustomResource>,
17-
TestExecutionInfoProvider {
13+
implements Reconciler<MultipleDependentResourceCustomResource> {
1814

19-
public static final int FIRST_CONFIG_MAP_ID = 1;
20-
public static final int SECOND_CONFIG_MAP_ID = 2;
21-
private final AtomicInteger numberOfExecutions = new AtomicInteger(0);
15+
public static final String FIRST_CONFIG_MAP_ID = "1";
16+
public static final String SECOND_CONFIG_MAP_ID = "2";
2217

2318
private final MultipleDependentResourceConfigMap firstDependentResourceConfigMap;
2419
private final MultipleDependentResourceConfigMap secondDependentResourceConfigMap;
2520

2621
public MultipleDependentResourceReconciler() {
2722
firstDependentResourceConfigMap = new MultipleDependentResourceConfigMap(FIRST_CONFIG_MAP_ID);
28-
2923
secondDependentResourceConfigMap = new MultipleDependentResourceConfigMap(SECOND_CONFIG_MAP_ID);
30-
31-
firstDependentResourceConfigMap
32-
.setResourceDiscriminator(
33-
new ResourceIDMatcherDiscriminator<>(
34-
p -> new ResourceID(p.getConfigMapName(FIRST_CONFIG_MAP_ID),
35-
p.getMetadata().getNamespace())));
36-
secondDependentResourceConfigMap
37-
.setResourceDiscriminator(
38-
new ResourceIDMatcherDiscriminator<>(
39-
p -> new ResourceID(p.getConfigMapName(SECOND_CONFIG_MAP_ID),
40-
p.getMetadata().getNamespace())));
4124
}
4225

4326
@Override
4427
public UpdateControl<MultipleDependentResourceCustomResource> reconcile(
4528
MultipleDependentResourceCustomResource resource,
4629
Context<MultipleDependentResourceCustomResource> context) {
47-
numberOfExecutions.getAndIncrement();
4830
firstDependentResourceConfigMap.reconcile(resource, context);
4931
secondDependentResourceConfigMap.reconcile(resource, context);
5032
return UpdateControl.noUpdate();
5133
}
5234

53-
54-
public int getNumberOfExecutions() {
55-
return numberOfExecutions.get();
56-
}
57-
5835
@Override
5936
public Map<String, EventSource> prepareEventSources(
6037
EventSourceContext<MultipleDependentResourceCustomResource> context) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package io.javaoperatorsdk.operator.sample.multipledependentresource;
2+
3+
public class MultipleDependentResourceSpec {
4+
5+
private String value;
6+
7+
public String getValue() {
8+
return value;
9+
}
10+
11+
public void setValue(String value) {
12+
this.value = value;
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package io.javaoperatorsdk.operator.sample.multipledependentresourcewithdiscriminator;
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.CRUDKubernetesDependentResource;
10+
11+
public class MultipleDependentResourceConfigMap
12+
extends
13+
CRUDKubernetesDependentResource<ConfigMap, MultipleDependentResourceCustomResourceWithDiscriminator> {
14+
15+
public static final String DATA_KEY = "key";
16+
private final int value;
17+
18+
public MultipleDependentResourceConfigMap(int value) {
19+
super(ConfigMap.class);
20+
this.value = value;
21+
}
22+
23+
@Override
24+
protected ConfigMap desired(MultipleDependentResourceCustomResourceWithDiscriminator primary,
25+
Context<MultipleDependentResourceCustomResourceWithDiscriminator> context) {
26+
Map<String, String> data = new HashMap<>();
27+
data.put(DATA_KEY, String.valueOf(value));
28+
29+
return new ConfigMapBuilder()
30+
.withNewMetadata()
31+
.withName(primary.getConfigMapName(value))
32+
.withNamespace(primary.getMetadata().getNamespace())
33+
.endMetadata()
34+
.withData(data)
35+
.build();
36+
}
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package io.javaoperatorsdk.operator.sample.multipledependentresourcewithdiscriminator;
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("mdwd")
12+
public class MultipleDependentResourceCustomResourceWithDiscriminator
13+
extends CustomResource<Void, MultipleDependentResourceWithDiscriminatorStatus>
14+
implements Namespaced {
15+
16+
public String getConfigMapName(int id) {
17+
return "configmap" + id;
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package io.javaoperatorsdk.operator.sample.multipledependentresourcewithdiscriminator;
2+
3+
import java.util.Map;
4+
import java.util.concurrent.atomic.AtomicInteger;
5+
6+
import io.fabric8.kubernetes.api.model.ConfigMap;
7+
import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration;
8+
import io.javaoperatorsdk.operator.api.reconciler.*;
9+
import io.javaoperatorsdk.operator.processing.event.ResourceID;
10+
import io.javaoperatorsdk.operator.processing.event.source.EventSource;
11+
import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource;
12+
import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider;
13+
14+
@ControllerConfiguration
15+
public class MultipleDependentResourceWithDiscriminatorReconciler
16+
implements Reconciler<MultipleDependentResourceCustomResourceWithDiscriminator>,
17+
TestExecutionInfoProvider {
18+
19+
public static final int FIRST_CONFIG_MAP_ID = 1;
20+
public static final int SECOND_CONFIG_MAP_ID = 2;
21+
private final AtomicInteger numberOfExecutions = new AtomicInteger(0);
22+
23+
private final MultipleDependentResourceConfigMap firstDependentResourceConfigMap;
24+
private final MultipleDependentResourceConfigMap secondDependentResourceConfigMap;
25+
26+
public MultipleDependentResourceWithDiscriminatorReconciler() {
27+
firstDependentResourceConfigMap = new MultipleDependentResourceConfigMap(FIRST_CONFIG_MAP_ID);
28+
secondDependentResourceConfigMap = new MultipleDependentResourceConfigMap(SECOND_CONFIG_MAP_ID);
29+
30+
firstDependentResourceConfigMap
31+
.setResourceDiscriminator(
32+
new ResourceIDMatcherDiscriminator<>(
33+
p -> new ResourceID(p.getConfigMapName(FIRST_CONFIG_MAP_ID),
34+
p.getMetadata().getNamespace())));
35+
secondDependentResourceConfigMap
36+
.setResourceDiscriminator(
37+
new ResourceIDMatcherDiscriminator<>(
38+
p -> new ResourceID(p.getConfigMapName(SECOND_CONFIG_MAP_ID),
39+
p.getMetadata().getNamespace())));
40+
}
41+
42+
@Override
43+
public UpdateControl<MultipleDependentResourceCustomResourceWithDiscriminator> reconcile(
44+
MultipleDependentResourceCustomResourceWithDiscriminator resource,
45+
Context<MultipleDependentResourceCustomResourceWithDiscriminator> context) {
46+
numberOfExecutions.getAndIncrement();
47+
firstDependentResourceConfigMap.reconcile(resource, context);
48+
secondDependentResourceConfigMap.reconcile(resource, context);
49+
return UpdateControl.noUpdate();
50+
}
51+
52+
53+
public int getNumberOfExecutions() {
54+
return numberOfExecutions.get();
55+
}
56+
57+
@Override
58+
public Map<String, EventSource> prepareEventSources(
59+
EventSourceContext<MultipleDependentResourceCustomResourceWithDiscriminator> context) {
60+
InformerEventSource<ConfigMap, MultipleDependentResourceCustomResourceWithDiscriminator> eventSource =
61+
new InformerEventSource<>(InformerConfiguration.from(ConfigMap.class, context)
62+
.build(), context);
63+
firstDependentResourceConfigMap.configureWith(eventSource);
64+
secondDependentResourceConfigMap.configureWith(eventSource);
65+
66+
return EventSourceUtils.nameEventSources(eventSource);
67+
}
68+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package io.javaoperatorsdk.operator.sample.multipledependentresourcewithdiscriminator;
2+
3+
public class MultipleDependentResourceWithDiscriminatorStatus {
4+
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package io.javaoperatorsdk.operator.sample.multipledrsametypenodiscriminator;
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.CRUDKubernetesDependentResource;
10+
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent;
11+
12+
@KubernetesDependent
13+
public class MultipleManagedDependentNoDiscriminatorConfigMap1
14+
extends
15+
CRUDKubernetesDependentResource<ConfigMap, MultipleManagedDependentNoDiscriminatorCustomResource> {
16+
17+
public static final String NAME_SUFFIX = "-1";
18+
19+
public MultipleManagedDependentNoDiscriminatorConfigMap1() {
20+
super(ConfigMap.class);
21+
}
22+
23+
@Override
24+
protected ConfigMap desired(MultipleManagedDependentNoDiscriminatorCustomResource primary,
25+
Context<MultipleManagedDependentNoDiscriminatorCustomResource> context) {
26+
Map<String, String> data = new HashMap<>();
27+
data.put(MultipleManagedDependentSameTypeNoDiscriminatorReconciler.DATA_KEY,
28+
primary.getSpec().getValue());
29+
30+
return new ConfigMapBuilder()
31+
.withNewMetadata()
32+
.withName(primary.getMetadata().getName() + NAME_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,39 @@
1+
package io.javaoperatorsdk.operator.sample.multipledrsametypenodiscriminator;
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.CRUDKubernetesDependentResource;
10+
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent;
11+
12+
import static io.javaoperatorsdk.operator.sample.multiplemanageddependentsametype.MultipleManagedDependentResourceReconciler.DATA_KEY;
13+
14+
@KubernetesDependent
15+
public class MultipleManagedDependentNoDiscriminatorConfigMap2
16+
extends
17+
CRUDKubernetesDependentResource<ConfigMap, MultipleManagedDependentNoDiscriminatorCustomResource> {
18+
19+
public static final String NAME_SUFFIX = "-2";
20+
21+
public MultipleManagedDependentNoDiscriminatorConfigMap2() {
22+
super(ConfigMap.class);
23+
}
24+
25+
@Override
26+
protected ConfigMap desired(MultipleManagedDependentNoDiscriminatorCustomResource primary,
27+
Context<MultipleManagedDependentNoDiscriminatorCustomResource> context) {
28+
Map<String, String> data = new HashMap<>();
29+
data.put(DATA_KEY, primary.getSpec().getValue());
30+
31+
return new ConfigMapBuilder()
32+
.withNewMetadata()
33+
.withName(primary.getMetadata().getName() + NAME_SUFFIX)
34+
.withNamespace(primary.getMetadata().getNamespace())
35+
.endMetadata()
36+
.withData(data)
37+
.build();
38+
}
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package io.javaoperatorsdk.operator.sample.multipledrsametypenodiscriminator;
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("mnd")
12+
public class MultipleManagedDependentNoDiscriminatorCustomResource
13+
extends CustomResource<MultipleManagedDependentNoDiscriminatorSpec, Void>
14+
implements Namespaced {
15+
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package io.javaoperatorsdk.operator.sample.multipledrsametypenodiscriminator;
2+
3+
public class MultipleManagedDependentNoDiscriminatorSpec {
4+
5+
private String value;
6+
7+
public String getValue() {
8+
return value;
9+
}
10+
11+
public MultipleManagedDependentNoDiscriminatorSpec setValue(String value) {
12+
this.value = value;
13+
return this;
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package io.javaoperatorsdk.operator.sample.multipledrsametypenodiscriminator;
2+
3+
import java.util.Map;
4+
import java.util.concurrent.atomic.AtomicInteger;
5+
6+
import io.fabric8.kubernetes.api.model.ConfigMap;
7+
import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration;
8+
import io.javaoperatorsdk.operator.api.reconciler.*;
9+
import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent;
10+
import io.javaoperatorsdk.operator.processing.event.source.EventSource;
11+
import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource;
12+
import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider;
13+
14+
import static io.javaoperatorsdk.operator.sample.multiplemanageddependentsametype.MultipleManagedDependentResourceReconciler.CONFIG_MAP_EVENT_SOURCE;
15+
16+
@Workflow(dependents = {
17+
@Dependent(type = MultipleManagedDependentNoDiscriminatorConfigMap1.class,
18+
useEventSourceWithName = CONFIG_MAP_EVENT_SOURCE),
19+
@Dependent(type = MultipleManagedDependentNoDiscriminatorConfigMap2.class,
20+
useEventSourceWithName = CONFIG_MAP_EVENT_SOURCE)
21+
})
22+
@ControllerConfiguration
23+
public class MultipleManagedDependentSameTypeNoDiscriminatorReconciler
24+
implements Reconciler<MultipleManagedDependentNoDiscriminatorCustomResource>,
25+
TestExecutionInfoProvider {
26+
27+
public static final String CONFIG_MAP_EVENT_SOURCE = "ConfigMapEventSource";
28+
public static final String DATA_KEY = "key";
29+
30+
private final AtomicInteger numberOfExecutions = new AtomicInteger(0);
31+
32+
public MultipleManagedDependentSameTypeNoDiscriminatorReconciler() {}
33+
34+
@Override
35+
public UpdateControl<MultipleManagedDependentNoDiscriminatorCustomResource> reconcile(
36+
MultipleManagedDependentNoDiscriminatorCustomResource resource,
37+
Context<MultipleManagedDependentNoDiscriminatorCustomResource> context) {
38+
numberOfExecutions.getAndIncrement();
39+
40+
return UpdateControl.noUpdate();
41+
}
42+
43+
44+
public int getNumberOfExecutions() {
45+
return numberOfExecutions.get();
46+
}
47+
48+
@Override
49+
public Map<String, EventSource> prepareEventSources(
50+
EventSourceContext<MultipleManagedDependentNoDiscriminatorCustomResource> context) {
51+
InformerEventSource<ConfigMap, MultipleManagedDependentNoDiscriminatorCustomResource> ies =
52+
new InformerEventSource<>(InformerConfiguration.from(ConfigMap.class, context)
53+
.build(), context);
54+
55+
return Map.of(CONFIG_MAP_EVENT_SOURCE, ies);
56+
}
57+
}

‎operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primaryindexer/AbstractPrimaryIndexerTestReconciler.java

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
public class AbstractPrimaryIndexerTestReconciler implements
1616
Reconciler<PrimaryIndexerTestCustomResource> {
1717

18+
public static final String CONFIG_MAP_NAME = "common-config-map";
19+
1820
private final Map<String, AtomicInteger> numberOfExecutions = new ConcurrentHashMap<>();
1921

2022
protected static final String CONFIG_MAP_RELATION_INDEXER = "cm-indexer";

‎operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/primaryindexer/DependentPrimaryIndexerTestReconciler.java

+14
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
import java.util.stream.Collectors;
66

77
import io.fabric8.kubernetes.api.model.ConfigMap;
8+
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
9+
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
10+
import io.javaoperatorsdk.operator.api.reconciler.Context;
811
import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration;
912
import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext;
1013
import io.javaoperatorsdk.operator.api.reconciler.Reconciler;
@@ -32,6 +35,17 @@ public ReadOnlyConfigMapDependent() {
3235
super(ConfigMap.class);
3336
}
3437

38+
@Override
39+
protected ConfigMap desired(PrimaryIndexerTestCustomResource primary,
40+
Context<PrimaryIndexerTestCustomResource> context) {
41+
return new ConfigMapBuilder()
42+
.withMetadata(new ObjectMetaBuilder()
43+
.withName(CONFIG_MAP_NAME)
44+
.withNamespace(primary.getMetadata().getNamespace())
45+
.build())
46+
.build();
47+
}
48+
3549
@Override
3650
public Set<ResourceID> toPrimaryResourceIDs(ConfigMap resource) {
3751
return cache.byIndex(CONFIG_MAP_RELATION_INDEXER, resource.getMetadata().getName())
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,28 @@
11
package io.javaoperatorsdk.operator.sample.primarytosecondaydependent;
22

33
import io.fabric8.kubernetes.api.model.ConfigMap;
4+
import io.fabric8.kubernetes.api.model.ConfigMapBuilder;
5+
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
6+
import io.javaoperatorsdk.operator.api.reconciler.Context;
47
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource;
58

69
public class ConfigMapDependent extends
710
KubernetesDependentResource<ConfigMap, PrimaryToSecondaryDependentCustomResource> {
811

12+
public static final String TEST_CONFIG_MAP_NAME = "testconfigmap";
13+
914
public ConfigMapDependent() {
1015
super(ConfigMap.class);
1116
}
17+
18+
@Override
19+
protected ConfigMap desired(PrimaryToSecondaryDependentCustomResource primary,
20+
Context<PrimaryToSecondaryDependentCustomResource> context) {
21+
return new ConfigMapBuilder()
22+
.withMetadata(new ObjectMetaBuilder()
23+
.withName(TEST_CONFIG_MAP_NAME)
24+
.withNamespace(primary.getMetadata().getNamespace())
25+
.build())
26+
.build();
27+
}
1228
}

‎operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/unmodifiabledependentpart/UnmodifiablePartConfigMapDependent.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public UnmodifiablePartConfigMapDependent() {
2121
@Override
2222
protected ConfigMap desired(UnmodifiableDependentPartCustomResource primary,
2323
Context<UnmodifiableDependentPartCustomResource> context) {
24-
var actual = getSecondaryResource(primary, context);
24+
var actual = context.getSecondaryResource(ConfigMap.class);
2525
ConfigMap res = new ConfigMapBuilder()
2626
.withMetadata(new ObjectMetaBuilder()
2727
.withName(primary.getMetadata().getName())

0 commit comments

Comments
 (0)
Please sign in to comment.