Skip to content

Reschedule delete #600

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 12 commits into from
Oct 13, 2021
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package io.javaoperatorsdk.operator.api;

import java.util.Optional;
import java.util.concurrent.TimeUnit;

public abstract class BaseControl<T extends BaseControl> {

private Long scheduleDelay = null;

public T rescheduleAfter(long delay) {
this.scheduleDelay = delay;
return (T) this;
}

public T rescheduleAfter(long delay, TimeUnit timeUnit) {
return rescheduleAfter(timeUnit.toMillis(delay));
}

public Optional<Long> getScheduleDelay() {
return Optional.ofNullable(scheduleDelay);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,30 @@
package io.javaoperatorsdk.operator.api;

public enum DeleteControl {
DEFAULT_DELETE, NO_FINALIZER_REMOVAL
public class DeleteControl extends BaseControl<DeleteControl> {

private final boolean removeFinalizer;

private DeleteControl(boolean removeFinalizer) {
this.removeFinalizer = removeFinalizer;
}

public static DeleteControl defaultDelete() {
return new DeleteControl(true);
}

public static DeleteControl noFinalizerRemoval() {
return new DeleteControl(false);
}

public boolean isRemoveFinalizer() {
return removeFinalizer;
}

@Override
public DeleteControl rescheduleAfter(long delay) {
if (removeFinalizer == true) {
throw new IllegalStateException("Cannot reschedule deleteResource if removing finalizer");
}
return super.rescheduleAfter(delay);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public interface ResourceController<R extends CustomResource> {
* The implementation should delete the associated component(s). Note that this is method is
* called when an object is marked for deletion. After it's executed the custom resource finalizer
* is automatically removed by the framework; unless the return value is
* {@link DeleteControl#NO_FINALIZER_REMOVAL}, which indicates that the controller has determined
* {@link DeleteControl#noFinalizerRemoval()}, which indicates that the controller has determined
* that the resource should not be deleted yet, in which case it is up to the controller to
* restore the resource's status so that it's not marked for deletion anymore.
*
Expand All @@ -21,13 +21,13 @@ public interface ResourceController<R extends CustomResource> {
*
* @param resource the resource that is marked for deletion
* @param context the context with which the operation is executed
* @return {@link DeleteControl#DEFAULT_DELETE} - so the finalizer is automatically removed after
* the call. {@link DeleteControl#NO_FINALIZER_REMOVAL} if you don't want to remove the
* @return {@link DeleteControl#defaultDelete()} - so the finalizer is automatically removed after
* the call. {@link DeleteControl#noFinalizerRemoval()} if you don't want to remove the
* finalizer to indicate that the resource should not be deleted after all, in which case
* the controller should restore the resource's state appropriately.
*/
default DeleteControl deleteResource(R resource, Context<R> context) {
return DeleteControl.DEFAULT_DELETE;
return DeleteControl.defaultDelete();
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
package io.javaoperatorsdk.operator.api;

import java.util.Optional;
import java.util.concurrent.TimeUnit;

import io.fabric8.kubernetes.client.CustomResource;

public class UpdateControl<T extends CustomResource> {
public class UpdateControl<T extends CustomResource> extends BaseControl<UpdateControl<T>> {

private final T customResource;
private final boolean updateStatusSubResource;
private final boolean updateCustomResource;
private Long reScheduleDelay = null;

private UpdateControl(
T customResource, boolean updateStatusSubResource, boolean updateCustomResource) {
Expand Down Expand Up @@ -47,19 +43,6 @@ public static <T extends CustomResource> UpdateControl<T> noUpdate() {
return new UpdateControl<>(null, false, false);
}

public UpdateControl withReSchedule(long delay, TimeUnit timeUnit) {
return withReSchedule(timeUnit.toMillis(delay));
}

public UpdateControl withReSchedule(long delay) {
this.reScheduleDelay = delay;
return this;
}

public Optional<Long> getReScheduleDelay() {
return Optional.ofNullable(reScheduleDelay);
}

public T getCustomResource() {
return customResource;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,8 @@ public String controllerName() {
}

@Override
public String successTypeName(DeleteControl result) {
switch (result) {
case DEFAULT_DELETE:
return "delete";
case NO_FINALIZER_REMOVAL:
return "finalizerNotRemoved";
default:
return "unknown";
}
public String successTypeName(DeleteControl deleteControl) {
return deleteControl.isRemoveFinalizer() ? "delete" : "finalizerNotRemoved";
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,7 @@
import io.fabric8.kubernetes.client.KubernetesClientException;
import io.fabric8.kubernetes.client.dsl.MixedOperation;
import io.fabric8.kubernetes.client.dsl.Resource;
import io.javaoperatorsdk.operator.api.Context;
import io.javaoperatorsdk.operator.api.DefaultContext;
import io.javaoperatorsdk.operator.api.DeleteControl;
import io.javaoperatorsdk.operator.api.ResourceController;
import io.javaoperatorsdk.operator.api.UpdateControl;
import io.javaoperatorsdk.operator.api.*;
import io.javaoperatorsdk.operator.api.config.ControllerConfiguration;
import io.javaoperatorsdk.operator.processing.event.EventList;

Expand Down Expand Up @@ -152,10 +148,16 @@ private PostExecutionControl<R> createPostExecutionControl(R updatedCustomResour
} else {
postExecutionControl = PostExecutionControl.defaultDispatch();
}
updateControl.getReScheduleDelay().ifPresent(postExecutionControl::withReSchedule);
updatePostExecutionControlWithReschedule(postExecutionControl, updateControl);
return postExecutionControl;
}

private void updatePostExecutionControlWithReschedule(
PostExecutionControl<R> postExecutionControl,
BaseControl<?> baseControl) {
baseControl.getScheduleDelay().ifPresent(postExecutionControl::withReSchedule);
}

private PostExecutionControl<R> handleDelete(R resource, Context<R> context) {
log.debug(
"Executing delete for resource: {} with version: {}",
Expand All @@ -165,7 +167,9 @@ private PostExecutionControl<R> handleDelete(R resource, Context<R> context) {
DeleteControl deleteControl = controller.deleteResource(resource, context);
final var useFinalizer = configuration().useFinalizer();
if (useFinalizer) {
if (deleteControl == DeleteControl.DEFAULT_DELETE
// note that we don't reschedule here even if instructed. Removing finalizer means that
// cleanup is finished, nothing left to done
if (deleteControl.isRemoveFinalizer()
&& resource.hasFinalizer(configuration().getFinalizer())) {
R customResource = removeFinalizer(resource);
return PostExecutionControl.customResourceUpdated(customResource);
Expand All @@ -177,7 +181,9 @@ private PostExecutionControl<R> handleDelete(R resource, Context<R> context) {
getVersion(resource),
deleteControl,
useFinalizer);
return PostExecutionControl.defaultDispatch();
PostExecutionControl<R> postExecutionControl = PostExecutionControl.defaultDispatch();
updatePostExecutionControlWithReschedule(postExecutionControl, deleteControl);
return postExecutionControl;
}

private void updateCustomResourceWithFinalizer(R resource) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.javaoperatorsdk.operator.api;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

class DeleteControlTest {

@Test
void cannotReScheduleForDefaultDelete() {
Assertions.assertThrows(IllegalStateException.class, () -> {
DeleteControl.defaultDelete().rescheduleAfter(1000L);
});
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ void setup() {
when(controller.createOrUpdateResource(eq(testCustomResource), any()))
.thenReturn(UpdateControl.updateCustomResource(testCustomResource));
when(controller.deleteResource(eq(testCustomResource), any()))
.thenReturn(DeleteControl.DEFAULT_DELETE);
.thenReturn(DeleteControl.defaultDelete());
when(customResourceFacade.replaceWithLock(any())).thenReturn(null);
}

Expand Down Expand Up @@ -202,7 +202,7 @@ void doesNotRemovesTheSetFinalizerIfTheDeleteNotMethodInstructsIt() {
testCustomResource.addFinalizer(DEFAULT_FINALIZER);

when(controller.deleteResource(eq(testCustomResource), any()))
.thenReturn(DeleteControl.NO_FINALIZER_REMOVAL);
.thenReturn(DeleteControl.noFinalizerRemoval());
markForDeletion(testCustomResource);

eventDispatcher.handleExecution(
Expand Down Expand Up @@ -297,14 +297,29 @@ void setReScheduleToPostExecutionControlFromUpdateControl() {

when(controller.createOrUpdateResource(eq(testCustomResource), any()))
.thenReturn(
UpdateControl.updateStatusSubResource(testCustomResource).withReSchedule(1000L));
UpdateControl.updateStatusSubResource(testCustomResource).rescheduleAfter(1000L));

PostExecutionControl control = eventDispatcher.handleExecution(
executionScopeWithCREvent(ADDED, testCustomResource));

assertThat(control.getReScheduleDelay().get()).isEqualTo(1000L);
}

@Test
void reScheduleOnDeleteWithoutFinalizerRemoval() {
testCustomResource.addFinalizer(DEFAULT_FINALIZER);
markForDeletion(testCustomResource);

when(controller.deleteResource(eq(testCustomResource), any()))
.thenReturn(
DeleteControl.noFinalizerRemoval().rescheduleAfter(1000L));

PostExecutionControl control = eventDispatcher.handleExecution(
executionScopeWithCREvent(UPDATED, testCustomResource));

assertThat(control.getReScheduleDelay().get()).isEqualTo(1000L);
}

private void markForDeletion(CustomResource customResource) {
customResource.getMetadata().setDeletionTimestamp("2019-8-10");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public DeleteControl deleteResource(
resource.getSpec().getConfigMapName(),
resource.getMetadata().getName());
}
return DeleteControl.DEFAULT_DELETE;
return DeleteControl.defaultDelete();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public DeleteControl deleteResource(
resource.getSpec().getConfigMapName(),
resource.getMetadata().getName());
}
return DeleteControl.DEFAULT_DELETE;
return DeleteControl.defaultDelete();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ public UpdateControl<MyCustomResource> createOrUpdateResource(MyCustomResource c

@Override
public DeleteControl deleteResource(MyCustomResource customResource, Context<MyCustomResource> context) {
return DeleteControl.DEFAULT_DELETE;
return DeleteControl.defaultDelete();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@ public UpdateControl<AbstractController.MyCustomResource> createOrUpdateResource

public DeleteControl deleteResource(AbstractController.MyCustomResource customResource,
Context<AbstractController.MyCustomResource> context) {
return DeleteControl.DEFAULT_DELETE;
return DeleteControl.defaultDelete();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public UpdateControl<MultilevelController.MyCustomResource> createOrUpdateResour

public DeleteControl deleteResource(MultilevelController.MyCustomResource customResource,
Context<MultilevelController.MyCustomResource> context) {
return DeleteControl.DEFAULT_DELETE;
return DeleteControl.defaultDelete();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public CustomServiceController(KubernetesClient kubernetesClient) {
@Override
public DeleteControl deleteResource(CustomService resource, Context<CustomService> context) {
log.info("Execution deleteResource for: {}", resource.getMetadata().getName());
return DeleteControl.DEFAULT_DELETE;
return DeleteControl.defaultDelete();
}

@Override
Expand Down