-
Notifications
You must be signed in to change notification settings - Fork 218
Primary to seconday dr informer sample #1744
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
02e34ff
feat: integration test showcasing how to use primary to secondary map…
csviri c2ae4dd
docs
csviri c63bc56
changes for CR
csviri 81f7e30
improved javadoc
csviri 6b644b7
docs
csviri abe91a9
docs
csviri 6d1ef4f
format
csviri b20fcdc
docs
csviri File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
76 changes: 76 additions & 0 deletions
76
...or-framework/src/test/java/io/javaoperatorsdk/operator/PrimaryToSecondaryDependentIT.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package io.javaoperatorsdk.operator; | ||
|
||
import java.time.Duration; | ||
import java.util.Map; | ||
|
||
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.primarytosecondaydependent.PrimaryToSecondaryDependentCustomResource; | ||
import io.javaoperatorsdk.operator.sample.primarytosecondaydependent.PrimaryToSecondaryDependentReconciler; | ||
import io.javaoperatorsdk.operator.sample.primarytosecondaydependent.PrimaryToSecondaryDependentSpec; | ||
|
||
import static io.javaoperatorsdk.operator.sample.primarytosecondaydependent.ConfigMapReconcilePrecondition.DO_NOT_RECONCILE; | ||
import static io.javaoperatorsdk.operator.sample.primarytosecondaydependent.PrimaryToSecondaryDependentReconciler.DATA_KEY; | ||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.awaitility.Awaitility.await; | ||
|
||
class PrimaryToSecondaryDependentIT { | ||
|
||
public static final String TEST_CONFIG_MAP_NAME = "testconfigmap"; | ||
public static final String TEST_CR_NAME = "test1"; | ||
public static final String TEST_DATA = "testData"; | ||
public | ||
|
||
@RegisterExtension LocallyRunOperatorExtension operator = | ||
LocallyRunOperatorExtension.builder() | ||
.withReconciler(new PrimaryToSecondaryDependentReconciler()) | ||
.build(); | ||
|
||
@Test | ||
void testPrimaryToSecondaryInDependentResources() { | ||
var reconciler = operator.getReconcilerOfType(PrimaryToSecondaryDependentReconciler.class); | ||
var cm = operator.create(configMap(DO_NOT_RECONCILE)); | ||
operator.create(testCustomResource()); | ||
|
||
await().pollDelay(Duration.ofMillis(250)).untilAsserted(() -> { | ||
assertThat(reconciler.getNumberOfExecutions()).isPositive(); | ||
assertThat(operator.get(Secret.class, TEST_CR_NAME)).isNull(); | ||
}); | ||
|
||
cm.setData(Map.of(DATA_KEY, TEST_DATA)); | ||
var executions = reconciler.getNumberOfExecutions(); | ||
operator.replace(cm); | ||
|
||
await().pollDelay(Duration.ofMillis(250)).untilAsserted(() -> { | ||
assertThat(reconciler.getNumberOfExecutions()).isGreaterThan(executions); | ||
var secret = operator.get(Secret.class, TEST_CR_NAME); | ||
assertThat(secret).isNotNull(); | ||
assertThat(secret.getData().get(DATA_KEY)).isEqualTo(TEST_DATA); | ||
}); | ||
} | ||
|
||
PrimaryToSecondaryDependentCustomResource testCustomResource() { | ||
var res = new PrimaryToSecondaryDependentCustomResource(); | ||
res.setMetadata(new ObjectMetaBuilder() | ||
.withName(TEST_CR_NAME) | ||
.build()); | ||
res.setSpec(new PrimaryToSecondaryDependentSpec()); | ||
res.getSpec().setConfigMapName(TEST_CONFIG_MAP_NAME); | ||
return res; | ||
} | ||
|
||
ConfigMap configMap(String data) { | ||
var cm = new ConfigMap(); | ||
cm.setMetadata(new ObjectMetaBuilder() | ||
.withName(TEST_CONFIG_MAP_NAME) | ||
.build()); | ||
cm.setData(Map.of(DATA_KEY, data)); | ||
return cm; | ||
} | ||
|
||
} |
12 changes: 12 additions & 0 deletions
12
...ava/io/javaoperatorsdk/operator/sample/primarytosecondaydependent/ConfigMapDependent.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package io.javaoperatorsdk.operator.sample.primarytosecondaydependent; | ||
|
||
import io.fabric8.kubernetes.api.model.ConfigMap; | ||
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource; | ||
|
||
public class ConfigMapDependent extends | ||
KubernetesDependentResource<ConfigMap, PrimaryToSecondaryDependentCustomResource> { | ||
|
||
public ConfigMapDependent() { | ||
super(ConfigMap.class); | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
...peratorsdk/operator/sample/primarytosecondaydependent/ConfigMapReconcilePrecondition.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package io.javaoperatorsdk.operator.sample.primarytosecondaydependent; | ||
|
||
import io.fabric8.kubernetes.api.model.ConfigMap; | ||
import io.javaoperatorsdk.operator.api.reconciler.Context; | ||
import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; | ||
import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition; | ||
|
||
import static io.javaoperatorsdk.operator.sample.primarytosecondaydependent.PrimaryToSecondaryDependentReconciler.DATA_KEY; | ||
|
||
public class ConfigMapReconcilePrecondition | ||
implements Condition<ConfigMap, PrimaryToSecondaryDependentCustomResource> { | ||
|
||
public static final String DO_NOT_RECONCILE = "doNotReconcile"; | ||
|
||
@Override | ||
public boolean isMet( | ||
DependentResource<ConfigMap, PrimaryToSecondaryDependentCustomResource> dependentResource, | ||
PrimaryToSecondaryDependentCustomResource primary, | ||
Context<PrimaryToSecondaryDependentCustomResource> context) { | ||
return dependentResource.getSecondaryResource(primary, context).map(cm -> { | ||
var data = cm.getData().get(DATA_KEY); | ||
return data != null && !data.equals(DO_NOT_RECONCILE); | ||
}) | ||
.orElse(false); | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
...operator/sample/primarytosecondaydependent/PrimaryToSecondaryDependentCustomResource.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package io.javaoperatorsdk.operator.sample.primarytosecondaydependent; | ||
|
||
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("ptsd") | ||
public class PrimaryToSecondaryDependentCustomResource | ||
extends CustomResource<PrimaryToSecondaryDependentSpec, Void> | ||
implements Namespaced { | ||
} |
92 changes: 92 additions & 0 deletions
92
...sdk/operator/sample/primarytosecondaydependent/PrimaryToSecondaryDependentReconciler.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package io.javaoperatorsdk.operator.sample.primarytosecondaydependent; | ||
|
||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Set; | ||
import java.util.concurrent.atomic.AtomicInteger; | ||
import java.util.stream.Collectors; | ||
|
||
import io.fabric8.kubernetes.api.model.ConfigMap; | ||
import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration; | ||
import io.javaoperatorsdk.operator.api.reconciler.*; | ||
import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; | ||
import io.javaoperatorsdk.operator.processing.event.ResourceID; | ||
import io.javaoperatorsdk.operator.processing.event.source.EventSource; | ||
import io.javaoperatorsdk.operator.processing.event.source.PrimaryToSecondaryMapper; | ||
import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; | ||
import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; | ||
|
||
import static io.javaoperatorsdk.operator.sample.primarytosecondaydependent.PrimaryToSecondaryDependentReconciler.CONFIG_MAP; | ||
import static io.javaoperatorsdk.operator.sample.primarytosecondaydependent.PrimaryToSecondaryDependentReconciler.CONFIG_MAP_EVENT_SOURCE; | ||
|
||
/** | ||
* Sample showcases how it is possible to do a primary to secondary mapper for a dependent resource. | ||
* Note that this is usually just used with read only resources. So it has limited usage, one reason | ||
* to use it is to have nice condition on that resource within a workflow. | ||
*/ | ||
@ControllerConfiguration(dependents = {@Dependent(type = ConfigMapDependent.class, | ||
name = CONFIG_MAP, | ||
reconcilePrecondition = ConfigMapReconcilePrecondition.class, | ||
useEventSourceWithName = CONFIG_MAP_EVENT_SOURCE), | ||
@Dependent(type = SecretDependent.class, dependsOn = CONFIG_MAP)}) | ||
public class PrimaryToSecondaryDependentReconciler | ||
implements Reconciler<PrimaryToSecondaryDependentCustomResource>, TestExecutionInfoProvider, | ||
EventSourceInitializer<PrimaryToSecondaryDependentCustomResource> { | ||
|
||
public static final String DATA_KEY = "data"; | ||
public static final String CONFIG_MAP = "ConfigMap"; | ||
public static final String CONFIG_MAP_INDEX = "ConfigMapIndex"; | ||
public static final String CONFIG_MAP_EVENT_SOURCE = "ConfigMapEventSource"; | ||
|
||
private final AtomicInteger numberOfExecutions = new AtomicInteger(0); | ||
|
||
@Override | ||
public UpdateControl<PrimaryToSecondaryDependentCustomResource> reconcile( | ||
PrimaryToSecondaryDependentCustomResource resource, | ||
Context<PrimaryToSecondaryDependentCustomResource> context) { | ||
numberOfExecutions.addAndGet(1); | ||
return UpdateControl.noUpdate(); | ||
} | ||
|
||
public int getNumberOfExecutions() { | ||
return numberOfExecutions.get(); | ||
} | ||
|
||
/** | ||
* Creating an Event Source and setting it for the Dependent Resource. Since it is not possible to | ||
* do this setup elegantly within the bounds of the KubernetesDependentResource API. However, this | ||
* is quite a corner case; might be covered more out of the box in the future if there will be | ||
* demand for it. | ||
**/ | ||
@Override | ||
public Map<String, EventSource> prepareEventSources( | ||
metacosm marked this conversation as resolved.
Show resolved
Hide resolved
|
||
EventSourceContext<PrimaryToSecondaryDependentCustomResource> context) { | ||
metacosm marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// there is no owner reference in the config map, but we still want to trigger reconciliation if | ||
// the config map changes. So first we add an index which custom resource references the config | ||
// map. | ||
context.getPrimaryCache().addIndexer(CONFIG_MAP_INDEX, (primary -> List | ||
.of(indexKey(primary.getSpec().getConfigMapName(), primary.getMetadata().getNamespace())))); | ||
|
||
var cmES = new InformerEventSource<>(InformerConfiguration | ||
.from(ConfigMap.class, context) | ||
// if there is a many-to-many relationship (thus no direct owner reference) | ||
// PrimaryToSecondaryMapper needs to be added | ||
.withPrimaryToSecondaryMapper( | ||
(PrimaryToSecondaryMapper<PrimaryToSecondaryDependentCustomResource>) p -> Set | ||
.of(new ResourceID(p.getSpec().getConfigMapName(), p.getMetadata().getNamespace()))) | ||
// the index is used to trigger reconciliation of related custom resources if config map | ||
// changes | ||
.withSecondaryToPrimaryMapper(cm -> context.getPrimaryCache() | ||
.byIndex(CONFIG_MAP_INDEX, indexKey(cm.getMetadata().getName(), | ||
cm.getMetadata().getNamespace())) | ||
.stream().map(ResourceID::fromResource).collect(Collectors.toSet())) | ||
.build(), | ||
context); | ||
|
||
return Map.of(CONFIG_MAP_EVENT_SOURCE, cmES); | ||
} | ||
|
||
private String indexKey(String configMapName, String namespace) { | ||
return configMapName + "#" + namespace; | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
...eratorsdk/operator/sample/primarytosecondaydependent/PrimaryToSecondaryDependentSpec.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package io.javaoperatorsdk.operator.sample.primarytosecondaydependent; | ||
|
||
public class PrimaryToSecondaryDependentSpec { | ||
|
||
private String configMapName; | ||
|
||
public String getConfigMapName() { | ||
return configMapName; | ||
} | ||
|
||
public PrimaryToSecondaryDependentSpec setConfigMapName(String configMapName) { | ||
this.configMapName = configMapName; | ||
return this; | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
...t/java/io/javaoperatorsdk/operator/sample/primarytosecondaydependent/SecretDependent.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package io.javaoperatorsdk.operator.sample.primarytosecondaydependent; | ||
|
||
import java.util.Map; | ||
|
||
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.api.reconciler.Context; | ||
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; | ||
|
||
import static io.javaoperatorsdk.operator.sample.primarytosecondaydependent.PrimaryToSecondaryDependentReconciler.DATA_KEY; | ||
|
||
public class SecretDependent | ||
extends CRUDKubernetesDependentResource<Secret, PrimaryToSecondaryDependentCustomResource> { | ||
|
||
public SecretDependent() { | ||
super(Secret.class); | ||
} | ||
|
||
@Override | ||
protected Secret desired(PrimaryToSecondaryDependentCustomResource primary, | ||
Context<PrimaryToSecondaryDependentCustomResource> context) { | ||
Secret secret = new Secret(); | ||
secret.setMetadata(new ObjectMetaBuilder() | ||
.withName(primary.getMetadata().getName()) | ||
.withNamespace(primary.getMetadata().getNamespace()) | ||
.build()); | ||
secret.setData(Map.of(DATA_KEY, context.getSecondaryResource(ConfigMap.class) | ||
.orElseThrow().getData().get(DATA_KEY))); | ||
return secret; | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.