Skip to content

Commit

Permalink
feat: integration test to show multiple dependents with activation co…
Browse files Browse the repository at this point in the history
…ndition (#2454)


Signed-off-by: Attila Mészáros <csviri@gmail.com>
  • Loading branch information
csviri authored and metacosm committed Jul 8, 2024
1 parent 0e02573 commit 03862cc
Show file tree
Hide file tree
Showing 9 changed files with 267 additions and 3 deletions.
8 changes: 5 additions & 3 deletions docs/content/en/docs/workflows/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,11 @@ reconciliation process.
with the dependent's resource type is not present on the cluster.
See related [integration test](https://github.com/operator-framework/java-operator-sdk/blob/ba5e33527bf9e3ea0bd33025ccb35e677f9d44b4/operator-framework/src/test/java/io/javaoperatorsdk/operator/CRDPresentActivationConditionIT.java).

Activation condition is semi-experimental at the moment, and it has its limitations.
For example event sources cannot be shared between multiple managed dependent resources which use activation condition.
The intention is to further improve and explore the possibilities with this approach.
To have multiple resources of same type with an activation condition is a bit tricky, since you
don't want to have multiple `InformerEvetnSource` for the same type, you have to explicitly
name the informer for the Dependent Resource (`@KubernetesDependent(informerConfig = @InformerConfig(name = "configMapInformer"))`)
for all resource of same type with activation condition. This will make sure that only one is registered.
See details at [low level api](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceRetriever.java#L20-L52).

## Defining Workflows

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package io.javaoperatorsdk.operator;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
import io.fabric8.kubernetes.api.model.Secret;
import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension;
import io.javaoperatorsdk.operator.sample.multipledependentwithactivation.*;

import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;

public class MultipleDependentWithActivationIT {

public static final String INITIAL_VALUE = "initial_value";
public static final String CHANGED_VALUE = "changed_value";
public static final String TEST_RESOURCE_NAME = "test1";

@RegisterExtension
LocallyRunOperatorExtension extension =
LocallyRunOperatorExtension.builder()
.withReconciler(new MultipleDependentActivationReconciler())
.build();

@Test
void bothDependentsWithActivationAreHandled() {
var resource = extension.create(testResource());

await().untilAsserted(() -> {
var cm1 =
extension.get(ConfigMap.class, TEST_RESOURCE_NAME + ConfigMapDependentResource1.SUFFIX);
var cm2 =
extension.get(ConfigMap.class, TEST_RESOURCE_NAME + ConfigMapDependentResource2.SUFFIX);
var secret = extension.get(Secret.class, TEST_RESOURCE_NAME);
assertThat(secret).isNotNull();
assertThat(cm1).isNull();
assertThat(cm2).isNull();
});

ActivationCondition.MET = true;
resource.getSpec().setValue(CHANGED_VALUE);
extension.replace(resource);

await().untilAsserted(() -> {
var cm1 =
extension.get(ConfigMap.class, TEST_RESOURCE_NAME + ConfigMapDependentResource1.SUFFIX);
var cm2 =
extension.get(ConfigMap.class, TEST_RESOURCE_NAME + ConfigMapDependentResource2.SUFFIX);
var secret = extension.get(Secret.class, TEST_RESOURCE_NAME);

assertThat(secret).isNotNull();
assertThat(cm1).isNotNull();
assertThat(cm2).isNotNull();
assertThat(cm1.getData()).containsEntry(ConfigMapDependentResource1.DATA_KEY,
CHANGED_VALUE + ConfigMapDependentResource1.SUFFIX);
assertThat(cm2.getData()).containsEntry(ConfigMapDependentResource2.DATA_KEY,
CHANGED_VALUE + ConfigMapDependentResource2.SUFFIX);
});

}

MultipleDependentActivationCustomResource testResource() {
var res = new MultipleDependentActivationCustomResource();
res.setMetadata(new ObjectMetaBuilder()
.withName(TEST_RESOURCE_NAME)
.build());
res.setSpec(new MultipleDependentActivationSpec());
res.getSpec().setValue(INITIAL_VALUE);

return res;
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.javaoperatorsdk.operator.sample.multipledependentwithactivation;

import io.fabric8.openshift.api.model.Route;
import io.javaoperatorsdk.operator.api.reconciler.Context;
import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition;

public class ActivationCondition
implements Condition<Route, MultipleDependentActivationCustomResource> {

public static volatile boolean MET = false;

@Override
public boolean isMet(
DependentResource<Route, MultipleDependentActivationCustomResource> dependentResource,
MultipleDependentActivationCustomResource primary,
Context<MultipleDependentActivationCustomResource> context) {
return MET;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package io.javaoperatorsdk.operator.sample.multipledependentwithactivation;

import java.util.Map;

import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
import io.javaoperatorsdk.operator.api.reconciler.Context;
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDNoGCKubernetesDependentResource;
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.InformerConfig;
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent;

@KubernetesDependent(informerConfig = @InformerConfig(name = "configMapInformer"))
public class ConfigMapDependentResource1
extends
CRUDNoGCKubernetesDependentResource<ConfigMap, MultipleDependentActivationCustomResource> {

public static final String DATA_KEY = "data";
public static final String SUFFIX = "1";

public ConfigMapDependentResource1() {
super(ConfigMap.class);
}

@Override
protected ConfigMap desired(MultipleDependentActivationCustomResource primary,
Context<MultipleDependentActivationCustomResource> context) {
ConfigMap configMap = new ConfigMap();
configMap.setMetadata(new ObjectMetaBuilder()
.withName(primary.getMetadata().getName() + SUFFIX)
.withNamespace(primary.getMetadata().getNamespace())
.build());
configMap.setData(Map.of(DATA_KEY, primary.getSpec().getValue() + SUFFIX));
return configMap;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package io.javaoperatorsdk.operator.sample.multipledependentwithactivation;

import java.util.Map;

import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
import io.javaoperatorsdk.operator.api.reconciler.Context;
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDNoGCKubernetesDependentResource;
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.InformerConfig;
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent;

@KubernetesDependent(informerConfig = @InformerConfig(name = "configMapInformer"))
public class ConfigMapDependentResource2
extends
CRUDNoGCKubernetesDependentResource<ConfigMap, MultipleDependentActivationCustomResource> {

public static final String DATA_KEY = "data";
public static final String SUFFIX = "2";

public ConfigMapDependentResource2() {
super(ConfigMap.class);
}

@Override
protected ConfigMap desired(MultipleDependentActivationCustomResource primary,
Context<MultipleDependentActivationCustomResource> context) {
ConfigMap configMap = new ConfigMap();
configMap.setMetadata(new ObjectMetaBuilder()
.withName(primary.getMetadata().getName() + SUFFIX)
.withNamespace(primary.getMetadata().getNamespace())
.build());
configMap.setData(Map.of(DATA_KEY, primary.getSpec().getValue() + SUFFIX));
return configMap;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.javaoperatorsdk.operator.sample.multipledependentwithactivation;

import io.fabric8.kubernetes.api.model.Namespaced;
import io.fabric8.kubernetes.client.CustomResource;
import io.fabric8.kubernetes.model.annotation.Group;
import io.fabric8.kubernetes.model.annotation.ShortNames;
import io.fabric8.kubernetes.model.annotation.Version;

@Group("sample.javaoperatorsdk")
@Version("v1")
@ShortNames("mdar")
public class MultipleDependentActivationCustomResource
extends CustomResource<MultipleDependentActivationSpec, Void>
implements Namespaced {


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.javaoperatorsdk.operator.sample.multipledependentwithactivation;

import java.util.concurrent.atomic.AtomicInteger;

import io.javaoperatorsdk.operator.api.reconciler.*;
import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent;

@Workflow(dependents = {
@Dependent(type = ConfigMapDependentResource1.class,
activationCondition = ActivationCondition.class),
@Dependent(type = ConfigMapDependentResource2.class,
activationCondition = ActivationCondition.class),
@Dependent(type = SecretDependentResource.class)
})
@ControllerConfiguration
public class MultipleDependentActivationReconciler
implements Reconciler<MultipleDependentActivationCustomResource> {

private final AtomicInteger numberOfReconciliationExecution = new AtomicInteger(0);

@Override
public UpdateControl<MultipleDependentActivationCustomResource> reconcile(
MultipleDependentActivationCustomResource resource,
Context<MultipleDependentActivationCustomResource> context) {

numberOfReconciliationExecution.incrementAndGet();

return UpdateControl.noUpdate();
}

public int getNumberOfReconciliationExecution() {
return numberOfReconciliationExecution.get();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package io.javaoperatorsdk.operator.sample.multipledependentwithactivation;

public class MultipleDependentActivationSpec {

private String value;

public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.javaoperatorsdk.operator.sample.multipledependentwithactivation;

import java.util.Base64;
import java.util.Map;

import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
import io.fabric8.kubernetes.api.model.Secret;
import io.javaoperatorsdk.operator.api.reconciler.Context;
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource;

public class SecretDependentResource
extends CRUDKubernetesDependentResource<Secret, MultipleDependentActivationCustomResource> {

public SecretDependentResource() {
super(Secret.class);
}

@Override
protected Secret desired(MultipleDependentActivationCustomResource primary,
Context<MultipleDependentActivationCustomResource> context) {
// basically does not matter since this should not be called
Secret secret = new Secret();
secret.setMetadata(new ObjectMetaBuilder()
.withName(primary.getMetadata().getName())
.withNamespace(primary.getMetadata().getNamespace())
.build());
secret.setData(Map.of("data",
Base64.getEncoder().encodeToString(primary.getSpec().getValue().getBytes())));
return secret;
}
}

0 comments on commit 03862cc

Please sign in to comment.