-
Notifications
You must be signed in to change notification settings - Fork 214
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add default delete condition to managed dependent resources
Signed-off-by: Attila Mészáros <csviri@gmail.com>
- Loading branch information
Showing
8 changed files
with
229 additions
and
1 deletion.
There are no files selected for viewing
This file contains 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
This file contains 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
27 changes: 27 additions & 0 deletions
27
...peratorsdk/operator/processing/dependent/workflow/KubernetesResourceDeletedCondition.java
This file contains 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,27 @@ | ||
package io.javaoperatorsdk.operator.processing.dependent.workflow; | ||
|
||
import io.fabric8.kubernetes.api.model.HasMetadata; | ||
import io.javaoperatorsdk.operator.api.reconciler.Context; | ||
import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; | ||
|
||
/** | ||
* A condition implementation meant to be used as a delete post-condition on Kubernetes dependent | ||
* resources to prevent the workflow from proceeding until the associated resource is actually | ||
* deleted from the server (or, at least, doesn't have any finalizers anymore). This is needed in | ||
* cases where a cleaning process depends on resources being actually removed from the server | ||
* because, by default, workflows simply request the deletion but do NOT wait for the resources to | ||
* be actually deleted. | ||
*/ | ||
public class KubernetesResourceDeletedCondition implements Condition<HasMetadata, HasMetadata> { | ||
|
||
@Override | ||
public boolean isMet(DependentResource<HasMetadata, HasMetadata> dependentResource, | ||
HasMetadata primary, Context<HasMetadata> context) { | ||
var optionalResource = dependentResource.getSecondaryResource(primary, context); | ||
if (optionalResource.isEmpty()) { | ||
return true; | ||
} else { | ||
return optionalResource.orElseThrow().getMetadata().getFinalizers().isEmpty(); | ||
} | ||
} | ||
} |
71 changes: 71 additions & 0 deletions
71
...ramework/src/test/java/io/javaoperatorsdk/operator/ManagedDependentDeleteConditionIT.java
This file contains 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,71 @@ | ||
package io.javaoperatorsdk.operator; | ||
|
||
import java.time.Duration; | ||
import java.util.Set; | ||
|
||
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.manageddependentdeletecondition.ManagedDependentDefaultDeleteConditionCustomResource; | ||
import io.javaoperatorsdk.operator.sample.manageddependentdeletecondition.ManagedDependentDefaultDeleteConditionReconciler; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.awaitility.Awaitility.await; | ||
|
||
public class ManagedDependentDeleteConditionIT { | ||
|
||
public static final String RESOURCE_NAME = "test1"; | ||
public static final String CUSTOM_FINALIZER = "test/customfinalizer"; | ||
|
||
@RegisterExtension | ||
LocallyRunOperatorExtension extension = | ||
LocallyRunOperatorExtension.builder() | ||
.withConfigurationService(o -> o.withDefaultNonSSAResource(Set.of())) | ||
.withReconciler(new ManagedDependentDefaultDeleteConditionReconciler()).build(); | ||
|
||
|
||
@Test | ||
void resourceNotDeletedUntilDependentDeleted() { | ||
var resource = new ManagedDependentDefaultDeleteConditionCustomResource(); | ||
resource.setMetadata(new ObjectMetaBuilder() | ||
.withName(RESOURCE_NAME) | ||
.build()); | ||
resource = extension.create(resource); | ||
|
||
await().timeout(Duration.ofSeconds(300)).untilAsserted(() -> { | ||
var cm = extension.get(ConfigMap.class, RESOURCE_NAME); | ||
var sec = extension.get(Secret.class, RESOURCE_NAME); | ||
assertThat(cm).isNotNull(); | ||
assertThat(sec).isNotNull(); | ||
}); | ||
|
||
var secret = extension.get(Secret.class, RESOURCE_NAME); | ||
secret.getMetadata().getFinalizers().add(CUSTOM_FINALIZER); | ||
secret = extension.replace(secret); | ||
|
||
extension.delete(resource); | ||
|
||
// both resources are present until the finalizer removed | ||
await().pollDelay(Duration.ofMillis(250)).untilAsserted(() -> { | ||
var cm = extension.get(ConfigMap.class, RESOURCE_NAME); | ||
var sec = extension.get(Secret.class, RESOURCE_NAME); | ||
assertThat(cm).isNotNull(); | ||
assertThat(sec).isNotNull(); | ||
}); | ||
|
||
secret.getMetadata().getFinalizers().clear(); | ||
extension.replace(secret); | ||
|
||
await().untilAsserted(() -> { | ||
var cm = extension.get(ConfigMap.class, RESOURCE_NAME); | ||
var sec = extension.get(Secret.class, RESOURCE_NAME); | ||
assertThat(cm).isNull(); | ||
assertThat(sec).isNull(); | ||
}); | ||
} | ||
|
||
} |
29 changes: 29 additions & 0 deletions
29
...o/javaoperatorsdk/operator/sample/manageddependentdeletecondition/ConfigMapDependent.java
This file contains 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,29 @@ | ||
package io.javaoperatorsdk.operator.sample.manageddependentdeletecondition; | ||
|
||
import java.util.Map; | ||
|
||
import io.fabric8.kubernetes.api.model.ConfigMap; | ||
import io.fabric8.kubernetes.api.model.ConfigMapBuilder; | ||
import io.javaoperatorsdk.operator.api.reconciler.Context; | ||
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDNoGCKubernetesDependentResource; | ||
|
||
public class ConfigMapDependent extends | ||
CRUDNoGCKubernetesDependentResource<ConfigMap, ManagedDependentDefaultDeleteConditionCustomResource> { | ||
|
||
public ConfigMapDependent() { | ||
super(ConfigMap.class); | ||
} | ||
|
||
@Override | ||
protected ConfigMap desired(ManagedDependentDefaultDeleteConditionCustomResource primary, | ||
Context<ManagedDependentDefaultDeleteConditionCustomResource> context) { | ||
|
||
return new ConfigMapBuilder() | ||
.withNewMetadata() | ||
.withName(primary.getMetadata().getName()) | ||
.withNamespace(primary.getMetadata().getNamespace()) | ||
.endMetadata() | ||
.withData(Map.of("key", "val")) | ||
.build(); | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
...manageddependentdeletecondition/ManagedDependentDefaultDeleteConditionCustomResource.java
This file contains 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.manageddependentdeletecondition; | ||
|
||
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("mdcc") | ||
public class ManagedDependentDefaultDeleteConditionCustomResource | ||
extends CustomResource<Void, Void> | ||
implements Namespaced { | ||
} |
31 changes: 31 additions & 0 deletions
31
...ple/manageddependentdeletecondition/ManagedDependentDefaultDeleteConditionReconciler.java
This file contains 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,31 @@ | ||
package io.javaoperatorsdk.operator.sample.manageddependentdeletecondition; | ||
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import io.javaoperatorsdk.operator.api.reconciler.*; | ||
import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; | ||
import io.javaoperatorsdk.operator.processing.dependent.workflow.KubernetesResourceDeletedCondition; | ||
|
||
@ControllerConfiguration(dependents = { | ||
@Dependent(name = "ConfigMap", type = ConfigMapDependent.class), | ||
@Dependent(type = SecretDependent.class, dependsOn = "ConfigMap", | ||
deletePostcondition = KubernetesResourceDeletedCondition.class) | ||
}) | ||
public class ManagedDependentDefaultDeleteConditionReconciler | ||
implements Reconciler<ManagedDependentDefaultDeleteConditionCustomResource> { | ||
|
||
private static final Logger log = | ||
LoggerFactory.getLogger(ManagedDependentDefaultDeleteConditionReconciler.class); | ||
|
||
@Override | ||
public UpdateControl<ManagedDependentDefaultDeleteConditionCustomResource> reconcile( | ||
ManagedDependentDefaultDeleteConditionCustomResource resource, | ||
Context<ManagedDependentDefaultDeleteConditionCustomResource> context) { | ||
|
||
log.debug("Reconciled: {}", resource); | ||
|
||
return UpdateControl.noUpdate(); | ||
} | ||
|
||
} |
33 changes: 33 additions & 0 deletions
33
...a/io/javaoperatorsdk/operator/sample/manageddependentdeletecondition/SecretDependent.java
This file contains 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,33 @@ | ||
package io.javaoperatorsdk.operator.sample.manageddependentdeletecondition; | ||
|
||
import java.nio.charset.StandardCharsets; | ||
import java.util.Base64; | ||
import java.util.Map; | ||
|
||
import io.fabric8.kubernetes.api.model.Secret; | ||
import io.fabric8.kubernetes.api.model.SecretBuilder; | ||
import io.javaoperatorsdk.operator.api.reconciler.Context; | ||
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDNoGCKubernetesDependentResource; | ||
|
||
public class SecretDependent | ||
extends | ||
CRUDNoGCKubernetesDependentResource<Secret, ManagedDependentDefaultDeleteConditionCustomResource> { | ||
|
||
public SecretDependent() { | ||
super(Secret.class); | ||
} | ||
|
||
@Override | ||
protected Secret desired(ManagedDependentDefaultDeleteConditionCustomResource primary, | ||
Context<ManagedDependentDefaultDeleteConditionCustomResource> context) { | ||
|
||
return new SecretBuilder() | ||
.withNewMetadata() | ||
.withName(primary.getMetadata().getName()) | ||
.withNamespace(primary.getMetadata().getNamespace()) | ||
.endMetadata() | ||
.withData(Map.of("key", | ||
new String(Base64.getEncoder().encode("val".getBytes(StandardCharsets.UTF_16))))) | ||
.build(); | ||
} | ||
} |