Skip to content

Commit c4fc798

Browse files
authored
fix: condition for bulk resources (#1688)
1 parent 75eec9c commit c4fc798

File tree

7 files changed

+132
-3
lines changed

7 files changed

+132
-3
lines changed

docs/documentation/dependent-resources.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,8 @@ interface.
382382
Various examples are provided as [integration tests](https://github.com/java-operator-sdk/java-operator-sdk/tree/f5ffcfb6f546d79b4bab04ea503c8bad9d6acce6/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent)
383383
.
384384

385+
To see how to add conditions on a bulk dependent resource [see integration test](https://github.com/java-operator-sdk/java-operator-sdk/blob/ad8759856cc59380ea4b0973478daa1140ec93e7/operator-framework/src/test/java/io/javaoperatorsdk/operator/bulkdependent/BulkDependentWithConditionIT.java).
386+
385387
## Dependent Resources with Explicit State
386388

387389
For cases when an external (non-Kubernetes) resource generates an ID during creation and from that point

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import io.fabric8.kubernetes.api.model.HasMetadata;
1515
import io.javaoperatorsdk.operator.api.reconciler.Context;
1616
import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
17+
import io.javaoperatorsdk.operator.processing.dependent.BulkDependentResource;
1718

1819
@SuppressWarnings("rawtypes")
1920
public abstract class AbstractWorkflowExecutor<P extends HasMetadata> {
@@ -101,9 +102,15 @@ protected synchronized void handleNodeExecutionFinish(
101102

102103
protected <R> boolean isConditionMet(Optional<Condition<R, P>> condition,
103104
DependentResource<R, P> dependentResource) {
105+
if (condition.isEmpty()) {
106+
return true;
107+
}
108+
var resources = dependentResource instanceof BulkDependentResource
109+
? ((BulkDependentResource) dependentResource).getSecondaryResources(primary, context)
110+
: dependentResource.getSecondaryResource(primary, context).orElse(null);
111+
104112
return condition.map(c -> c.isMet(primary,
105-
dependentResource.getSecondaryResource(primary, context).orElse(null),
106-
context))
113+
(R) resources, context))
107114
.orElse(true);
108115
}
109116
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package io.javaoperatorsdk.operator.bulkdependent;
2+
3+
import org.junit.jupiter.api.Test;
4+
import org.junit.jupiter.api.extension.RegisterExtension;
5+
6+
import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension;
7+
import io.javaoperatorsdk.operator.sample.bulkdependent.BulkDependentTestCustomResource;
8+
import io.javaoperatorsdk.operator.sample.bulkdependent.ManagedBulkDependentWithReadyConditionReconciler;
9+
10+
import static io.javaoperatorsdk.operator.bulkdependent.BulkDependentTestBase.INITIAL_NUMBER_OF_CONFIG_MAPS;
11+
import static io.javaoperatorsdk.operator.bulkdependent.BulkDependentTestBase.testResource;
12+
import static io.javaoperatorsdk.operator.sample.bulkdependent.ConfigMapDeleterBulkDependentResource.LABEL_KEY;
13+
import static io.javaoperatorsdk.operator.sample.bulkdependent.ConfigMapDeleterBulkDependentResource.LABEL_VALUE;
14+
import static org.assertj.core.api.Assertions.*;
15+
import static org.awaitility.Awaitility.await;
16+
17+
class BulkDependentWithConditionIT {
18+
19+
@RegisterExtension
20+
LocallyRunOperatorExtension extension =
21+
LocallyRunOperatorExtension.builder()
22+
.withReconciler(new ManagedBulkDependentWithReadyConditionReconciler())
23+
.build();
24+
25+
@Test
26+
void handlesBulkDependentWithPrecondition() {
27+
var resource = testResource();
28+
extension.create(resource);
29+
30+
await().untilAsserted(() -> {
31+
var res = extension.get(BulkDependentTestCustomResource.class,
32+
testResource().getMetadata().getName());
33+
assertThat(res.getStatus()).isNotNull();
34+
assertThat(res.getStatus().getReady()).isTrue();
35+
36+
var cms = extension.getKubernetesClient().configMaps().inNamespace(extension.getNamespace())
37+
.withLabel(LABEL_KEY, LABEL_VALUE)
38+
.list().getItems();
39+
assertThat(cms).hasSize(INITIAL_NUMBER_OF_CONFIG_MAPS);
40+
41+
});
42+
}
43+
44+
45+
46+
}

operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/bulkdependent/BulkDependentTestCustomResource.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@
1010
@Version("v1")
1111
@ShortNames("sbd")
1212
public class BulkDependentTestCustomResource
13-
extends CustomResource<BulkDependentTestSpec, Void>
13+
extends CustomResource<BulkDependentTestSpec, BulkDependentTestStatus>
1414
implements Namespaced {
1515
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package io.javaoperatorsdk.operator.sample.bulkdependent;
2+
3+
public class BulkDependentTestStatus {
4+
5+
private Boolean ready;
6+
7+
public Boolean getReady() {
8+
return ready;
9+
}
10+
11+
public BulkDependentTestStatus setReady(boolean ready) {
12+
this.ready = ready;
13+
return this;
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package io.javaoperatorsdk.operator.sample.bulkdependent;
2+
3+
import java.util.concurrent.atomic.AtomicInteger;
4+
5+
import io.javaoperatorsdk.operator.api.reconciler.Context;
6+
import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration;
7+
import io.javaoperatorsdk.operator.api.reconciler.Reconciler;
8+
import io.javaoperatorsdk.operator.api.reconciler.UpdateControl;
9+
import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent;
10+
11+
@ControllerConfiguration(dependents = @Dependent(readyPostcondition = SampleBulkCondition.class,
12+
type = CRUDConfigMapBulkDependentResource.class))
13+
public class ManagedBulkDependentWithReadyConditionReconciler
14+
implements Reconciler<BulkDependentTestCustomResource> {
15+
16+
private final AtomicInteger numberOfExecutions = new AtomicInteger(0);
17+
18+
@Override
19+
public UpdateControl<BulkDependentTestCustomResource> reconcile(
20+
BulkDependentTestCustomResource resource,
21+
Context<BulkDependentTestCustomResource> context) throws Exception {
22+
numberOfExecutions.incrementAndGet();
23+
24+
var ready = context.managedDependentResourceContext().getWorkflowReconcileResult()
25+
.map(res -> res.allDependentResourcesReady()).orElseThrow();
26+
27+
resource.setStatus(new BulkDependentTestStatus());
28+
resource.getStatus().setReady(ready);
29+
30+
return UpdateControl.patchStatus(resource);
31+
}
32+
33+
public int getNumberOfExecutions() {
34+
return numberOfExecutions.get();
35+
}
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package io.javaoperatorsdk.operator.sample.bulkdependent;
2+
3+
import java.util.Map;
4+
5+
import io.fabric8.kubernetes.api.model.ConfigMap;
6+
import io.javaoperatorsdk.operator.api.reconciler.Context;
7+
import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition;
8+
9+
public class SampleBulkCondition
10+
implements Condition<Map<String, ConfigMap>, BulkDependentTestCustomResource> {
11+
12+
// We use ConfigMaps here just to show how to check some properties of resources managed by a
13+
// BulkDependentResource. In real life example this would be rather based on some status of those
14+
// resources, like Pods.
15+
16+
@Override
17+
public boolean isMet(BulkDependentTestCustomResource primary, Map<String, ConfigMap> secondary,
18+
Context<BulkDependentTestCustomResource> context) {
19+
20+
return secondary.values().stream().allMatch(cm -> !cm.getData().isEmpty());
21+
22+
}
23+
}

0 commit comments

Comments
 (0)