diff --git a/OWNERS b/OWNERS index f227f81a1d..3a63d7d7d0 100644 --- a/OWNERS +++ b/OWNERS @@ -2,8 +2,12 @@ approvers: - csviri - metacosm - andreaTP +- xstefank reviewers: - gyfora - mbalassi -- adam-sandor - scrocquesel +- csviri +- metacosm +- xstefank + diff --git a/bootstrapper-maven-plugin/pom.xml b/bootstrapper-maven-plugin/pom.xml index fd69aa63af..725b904c9f 100644 --- a/bootstrapper-maven-plugin/pom.xml +++ b/bootstrapper-maven-plugin/pom.xml @@ -5,7 +5,7 @@ io.javaoperatorsdk java-operator-sdk - 5.0.1-SNAPSHOT + 5.0.2-SNAPSHOT bootstrapper diff --git a/caffeine-bounded-cache-support/pom.xml b/caffeine-bounded-cache-support/pom.xml index 2ceb4882d7..1696a57e37 100644 --- a/caffeine-bounded-cache-support/pom.xml +++ b/caffeine-bounded-cache-support/pom.xml @@ -4,7 +4,7 @@ io.javaoperatorsdk java-operator-sdk - 5.0.1-SNAPSHOT + 5.0.2-SNAPSHOT caffeine-bounded-cache-support diff --git a/micrometer-support/pom.xml b/micrometer-support/pom.xml index 8522b6250d..1cfd506336 100644 --- a/micrometer-support/pom.xml +++ b/micrometer-support/pom.xml @@ -4,7 +4,7 @@ io.javaoperatorsdk java-operator-sdk - 5.0.1-SNAPSHOT + 5.0.2-SNAPSHOT micrometer-support diff --git a/operator-framework-bom/pom.xml b/operator-framework-bom/pom.xml index dc28ac8bf2..bf598ef822 100644 --- a/operator-framework-bom/pom.xml +++ b/operator-framework-bom/pom.xml @@ -4,7 +4,7 @@ io.javaoperatorsdk operator-framework-bom - 5.0.1-SNAPSHOT + 5.0.2-SNAPSHOT pom Operator SDK - Bill of Materials Java SDK for implementing Kubernetes operators diff --git a/operator-framework-core/pom.xml b/operator-framework-core/pom.xml index 9b202cb59e..d18f46d0f5 100644 --- a/operator-framework-core/pom.xml +++ b/operator-framework-core/pom.xml @@ -4,7 +4,7 @@ io.javaoperatorsdk java-operator-sdk - 5.0.1-SNAPSHOT + 5.0.2-SNAPSHOT ../pom.xml diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java index 45fc3705e6..4c8b857d47 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java @@ -73,7 +73,7 @@ public Optional getSecondaryResource(Class expectedType, String eventS * If a workflow has an activation condition there can be event sources which are only * registered if the activation condition holds, but to provide a consistent API we return an * Optional instead of throwing an exception. - * + * * Note that not only the resource which has an activation condition might not be registered * but dependents which depend on it. */ @@ -116,4 +116,8 @@ public DefaultContext

setRetryInfo(RetryInfo retryInfo) { this.retryInfo = retryInfo; return this; } + + public P getPrimaryResource() { + return primaryResource; + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ReconciliationDispatcher.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ReconciliationDispatcher.java index 1ad3b65910..8c502d41ff 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ReconciliationDispatcher.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ReconciliationDispatcher.java @@ -86,7 +86,7 @@ private PostExecutionControl

handleDispatch(ExecutionScope

executionScope) } Context

context = - new DefaultContext<>(executionScope.getRetryInfo(), controller, originalResource); + new DefaultContext<>(executionScope.getRetryInfo(), controller, resourceForExecution); if (markedForDeletion) { return handleCleanup(resourceForExecution, originalResource, context); } else { @@ -234,29 +234,29 @@ private void updatePostExecutionControlWithReschedule( baseControl.getScheduleDelay().ifPresent(postExecutionControl::withReSchedule); } - private PostExecutionControl

handleCleanup(P resource, + private PostExecutionControl

handleCleanup(P resourceForExecution, P originalResource, Context

context) { if (log.isDebugEnabled()) { log.debug( "Executing delete for resource: {} with version: {}", - ResourceID.fromResource(resource), - getVersion(resource)); + ResourceID.fromResource(resourceForExecution), + getVersion(resourceForExecution)); } - DeleteControl deleteControl = controller.cleanup(resource, context); + DeleteControl deleteControl = controller.cleanup(resourceForExecution, context); final var useFinalizer = controller.useFinalizer(); if (useFinalizer) { // note that we don't reschedule here even if instructed. Removing finalizer means that - // cleanup is finished, nothing left to done + // cleanup is finished, nothing left to be done final var finalizerName = configuration().getFinalizerName(); - if (deleteControl.isRemoveFinalizer() && resource.hasFinalizer(finalizerName)) { - P customResource = conflictRetryingPatch(resource, originalResource, r -> { + if (deleteControl.isRemoveFinalizer() && resourceForExecution.hasFinalizer(finalizerName)) { + P customResource = conflictRetryingPatch(resourceForExecution, originalResource, r -> { // the operator might not be allowed to retrieve the resource on a retry, e.g. when its // permissions are removed by deleting the namespace concurrently if (r == null) { log.warn( "Could not remove finalizer on null resource: {} with version: {}", - getUID(resource), - getVersion(resource)); + getUID(resourceForExecution), + getVersion(resourceForExecution)); return false; } return r.removeFinalizer(finalizerName); @@ -266,8 +266,8 @@ private PostExecutionControl

handleCleanup(P resource, } log.debug( "Skipping finalizer remove for resource: {} with version: {}. delete control: {}, uses finalizer: {}", - getUID(resource), - getVersion(resource), + getUID(resourceForExecution), + getVersion(resourceForExecution), deleteControl, useFinalizer); PostExecutionControl

postExecutionControl = PostExecutionControl.defaultDispatch(); diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/ReconciliationDispatcherTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/ReconciliationDispatcherTest.java index efec8c4228..e5fe1c5882 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/ReconciliationDispatcherTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/ReconciliationDispatcherTest.java @@ -7,6 +7,8 @@ import java.util.function.BiFunction; import java.util.function.Supplier; +import io.fabric8.kubernetes.client.utils.KubernetesSerialization; +import io.javaoperatorsdk.operator.api.reconciler.DefaultContext; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; @@ -68,6 +70,9 @@ void setup() { } static void initConfigService(boolean useSSA) { + initConfigService(useSSA,true); + } + static void initConfigService(boolean useSSA, boolean noCloning) { /* * We need this for mock reconcilers to properly generate the expected UpdateControl: without * this, calls such as `when(reconciler.reconcile(eq(testCustomResource), @@ -77,14 +82,18 @@ static void initConfigService(boolean useSSA) { */ configurationService = ConfigurationService.newOverriddenConfigurationService(new BaseConfigurationService(), - overrider -> overrider.checkingCRDAndValidateLocalModel(false) + overrider -> overrider.checkingCRDAndValidateLocalModel(false) + .withResourceCloner(new Cloner() { @Override public R clone(R object) { + if (noCloning) { return object; + }else { + return new KubernetesSerialization().clone(object); } - }) - .withUseSSAToPatchPrimaryResource(useSSA)); + }}) + .withUseSSAToPatchPrimaryResource(useSSA)); } private ReconciliationDispatcher init(R customResource, @@ -659,10 +668,24 @@ void reSchedulesFromErrorHandler() { } @Test - void addsFinalizerToPatchWithSSA() { + void reconcilerContextUsesTheSameInstanceOfResourceAsParam() { + initConfigService(false,false); - } + final ReconciliationDispatcher dispatcher = + init(testCustomResource, reconciler, null, customResourceFacade, true); + testCustomResource.addFinalizer(DEFAULT_FINALIZER); + ArgumentCaptor contextArgumentCaptor = ArgumentCaptor.forClass(DefaultContext.class); + ArgumentCaptor customResourceCaptor = ArgumentCaptor.forClass(TestCustomResource.class); + + dispatcher.handleExecution(executionScopeWithCREvent(testCustomResource)); + verify(reconciler, times(1)) + .reconcile(customResourceCaptor.capture(), contextArgumentCaptor.capture()); + + assertThat(contextArgumentCaptor.getValue().getPrimaryResource()) + .isSameAs(customResourceCaptor.getValue()) + .isNotSameAs(testCustomResource); + } private ObservedGenCustomResource createObservedGenCustomResource() { ObservedGenCustomResource observedGenCustomResource = new ObservedGenCustomResource(); diff --git a/operator-framework-junit5/pom.xml b/operator-framework-junit5/pom.xml index c03a20ad93..465bbc0d20 100644 --- a/operator-framework-junit5/pom.xml +++ b/operator-framework-junit5/pom.xml @@ -4,7 +4,7 @@ io.javaoperatorsdk java-operator-sdk - 5.0.1-SNAPSHOT + 5.0.2-SNAPSHOT operator-framework-junit-5 diff --git a/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/LocallyRunOperatorExtension.java b/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/LocallyRunOperatorExtension.java index a2c92bf48b..45cd85b468 100644 --- a/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/LocallyRunOperatorExtension.java +++ b/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/LocallyRunOperatorExtension.java @@ -8,14 +8,21 @@ import java.nio.file.Files; import java.nio.file.Path; import java.time.Duration; +import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Deque; import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Stream; +import io.fabric8.kubernetes.client.dsl.NamespaceListVisitFromServerGetDeleteRecreateWaitApplicable; import org.junit.jupiter.api.extension.ExtensionContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,6 +47,9 @@ public class LocallyRunOperatorExtension extends AbstractOperatorExtension { private static final Logger LOGGER = LoggerFactory.getLogger(LocallyRunOperatorExtension.class); + private static final int CRD_DELETE_TIMEOUT = 1000; + private static final Set appliedCRDs = new HashSet<>(); + private static final boolean deleteCRDs = Boolean.parseBoolean(System.getProperty("testsuite.deleteCRDs", "true")); private final Operator operator; private final List reconcilers; @@ -144,6 +154,7 @@ private static void applyCrd(String crdString, String path, KubernetesClient cli LOGGER.debug("Applying CRD: {}", crdString); final var crd = client.load(new ByteArrayInputStream(crdString.getBytes())); crd.serverSideApply(); + appliedCRDs.add(new AppliedCRD(crdString, path)); Thread.sleep(CRD_READY_WAIT); // readiness is not applicable for CRD, just wait a little LOGGER.debug("Applied CRD with path: {}", path); } catch (InterruptedException ex) { @@ -290,6 +301,16 @@ protected void before(ExtensionContext context) { protected void after(ExtensionContext context) { super.after(context); + var kubernetesClient = getKubernetesClient(); + + var iterator = appliedCRDs.iterator(); + while (iterator.hasNext()) { + deleteCrd(iterator.next(), kubernetesClient); + iterator.remove(); + } + + kubernetesClient.close(); + try { this.operator.stop(); } catch (Exception e) { @@ -306,6 +327,23 @@ protected void after(ExtensionContext context) { localPortForwards.clear(); } + private void deleteCrd(AppliedCRD appliedCRD, KubernetesClient client) { + if (!deleteCRDs) { + LOGGER.debug("Skipping deleting CRD because of configuration: {}", appliedCRD); + return; + } + try { + LOGGER.debug("Deleting CRD: {}", appliedCRD.crdString); + final var crd = client.load(new ByteArrayInputStream(appliedCRD.crdString.getBytes())); + crd.withTimeoutInMillis(CRD_DELETE_TIMEOUT).delete(); + LOGGER.debug("Deleted CRD with path: {}", appliedCRD.path); + } catch (Exception ex) { + throw new IllegalStateException("Cannot delete CRD yaml: " + appliedCRD.path, ex); + } + } + + private record AppliedCRD(String crdString, String path) {} + @SuppressWarnings("rawtypes") public static class Builder extends AbstractBuilder { private final List reconcilers; diff --git a/operator-framework/pom.xml b/operator-framework/pom.xml index 3b8e9bf31c..1f505fae78 100644 --- a/operator-framework/pom.xml +++ b/operator-framework/pom.xml @@ -4,7 +4,7 @@ io.javaoperatorsdk java-operator-sdk - 5.0.1-SNAPSHOT + 5.0.2-SNAPSHOT operator-framework diff --git a/pom.xml b/pom.xml index 4c35c76a32..3d894384b2 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ io.javaoperatorsdk java-operator-sdk - 5.0.1-SNAPSHOT + 5.0.2-SNAPSHOT pom Operator SDK for Java Java SDK for implementing Kubernetes operators @@ -71,7 +71,7 @@ 3.27.3 4.2.2 2.7.3 - 1.14.3 + 1.14.4 3.2.0 0.9.14 2.18.0 @@ -313,38 +313,6 @@ - - com.diffplug.spotless - spotless-maven-plugin - - - - pom.xml - ./**/pom.xml - - - false - - - - - contributing/eclipse-google-style.xml - - - contributing/eclipse.importorder - - - - - - - - apply - - compile - - - org.apache.maven.plugins maven-surefire-plugin @@ -362,6 +330,50 @@ + + spotless + + + contributing + + + + + + com.diffplug.spotless + spotless-maven-plugin + + + + pom.xml + ./**/pom.xml + + + false + + + + + contributing/eclipse-google-style.xml + + + contributing/eclipse.importorder + + + + + + + + apply + + compile + + + + + + integration-tests diff --git a/sample-operators/controller-namespace-deletion/pom.xml b/sample-operators/controller-namespace-deletion/pom.xml index 532853b311..c5f6e7abc3 100644 --- a/sample-operators/controller-namespace-deletion/pom.xml +++ b/sample-operators/controller-namespace-deletion/pom.xml @@ -5,7 +5,7 @@ io.javaoperatorsdk sample-operators - 5.0.1-SNAPSHOT + 5.0.2-SNAPSHOT sample-controller-namespace-deletion diff --git a/sample-operators/leader-election/pom.xml b/sample-operators/leader-election/pom.xml index d35018c1de..42b54bf5be 100644 --- a/sample-operators/leader-election/pom.xml +++ b/sample-operators/leader-election/pom.xml @@ -5,7 +5,7 @@ io.javaoperatorsdk sample-operators - 5.0.1-SNAPSHOT + 5.0.2-SNAPSHOT sample-leader-election diff --git a/sample-operators/mysql-schema/pom.xml b/sample-operators/mysql-schema/pom.xml index b96230370c..ecd3398a76 100644 --- a/sample-operators/mysql-schema/pom.xml +++ b/sample-operators/mysql-schema/pom.xml @@ -5,7 +5,7 @@ io.javaoperatorsdk sample-operators - 5.0.1-SNAPSHOT + 5.0.2-SNAPSHOT sample-mysql-schema-operator diff --git a/sample-operators/pom.xml b/sample-operators/pom.xml index 1a214c0b2a..7b13dc5d7e 100644 --- a/sample-operators/pom.xml +++ b/sample-operators/pom.xml @@ -5,7 +5,7 @@ io.javaoperatorsdk java-operator-sdk - 5.0.1-SNAPSHOT + 5.0.2-SNAPSHOT sample-operators diff --git a/sample-operators/tomcat-operator/pom.xml b/sample-operators/tomcat-operator/pom.xml index 2b0dccc1ec..9a126f5995 100644 --- a/sample-operators/tomcat-operator/pom.xml +++ b/sample-operators/tomcat-operator/pom.xml @@ -5,7 +5,7 @@ io.javaoperatorsdk sample-operators - 5.0.1-SNAPSHOT + 5.0.2-SNAPSHOT sample-tomcat-operator diff --git a/sample-operators/tomcat-operator/src/test/java/io/javaoperatorsdk/operator/sample/TomcatOperatorE2E.java b/sample-operators/tomcat-operator/src/test/java/io/javaoperatorsdk/operator/sample/TomcatOperatorE2E.java index 929af47e5d..3095e7db8c 100644 --- a/sample-operators/tomcat-operator/src/test/java/io/javaoperatorsdk/operator/sample/TomcatOperatorE2E.java +++ b/sample-operators/tomcat-operator/src/test/java/io/javaoperatorsdk/operator/sample/TomcatOperatorE2E.java @@ -117,6 +117,11 @@ void test() { throw new AssertionError(ex); } }); + + log.info("Deleting test Tomcat object: {}", tomcat); + tomcatClient.inNamespace(operator.getNamespace()).resource(tomcat).delete(); + log.info("Deleting test Webapp object: {}", webapp1); + webappClient.inNamespace(operator.getNamespace()).resource(webapp1).delete(); } } diff --git a/sample-operators/webpage/pom.xml b/sample-operators/webpage/pom.xml index ac9a5808da..5128cf0d0f 100644 --- a/sample-operators/webpage/pom.xml +++ b/sample-operators/webpage/pom.xml @@ -5,7 +5,7 @@ io.javaoperatorsdk sample-operators - 5.0.1-SNAPSHOT + 5.0.2-SNAPSHOT sample-webpage-operator