diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java index 1421109eee..e9790b63e0 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java @@ -358,10 +358,13 @@ private void validateCRDWithLocalModelIfRequired(Class

resClass, String contr } public void changeNamespaces(Set namespaces) { - if (namespaces.contains(Constants.WATCH_ALL_NAMESPACES) - || namespaces.contains(WATCH_CURRENT_NAMESPACE)) { + if (namespaces.contains(WATCH_CURRENT_NAMESPACE)) { throw new OperatorException("Unexpected value in target namespaces: " + namespaces); } + if (namespaces.contains(Constants.WATCH_ALL_NAMESPACES) && namespaces.size() > 1) { + throw new OperatorException( + "Watching all namespaces, but additional specific namespace is present"); + } eventProcessor.stop(); eventSourceManager.changeNamespaces(namespaces); eventProcessor.start(); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerManager.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerManager.java index 4d7d547356..d51301c385 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerManager.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerManager.java @@ -58,17 +58,12 @@ void initSources(MixedOperation, Resource> clien final var labelSelector = configuration.getLabelSelector(); if (ResourceConfiguration.allNamespacesWatched(targetNamespaces)) { - final var filteredBySelectorClient = - client.inAnyNamespace().withLabelSelector(labelSelector); - final var source = - createEventSource(filteredBySelectorClient, eventHandler, WATCH_ALL_NAMESPACES); + var source = createEventSourceForNamespace(WATCH_ALL_NAMESPACES); log.debug("Registered {} -> {} for any namespace", this, source); } else { targetNamespaces.forEach( ns -> { - final var source = - createEventSource(client.inNamespace(ns).withLabelSelector(labelSelector), - eventHandler, ns); + final var source = createEventSourceForNamespace(ns); log.debug("Registered {} -> {} for namespace: {}", this, source, ns); }); @@ -87,10 +82,7 @@ public void changeNamespaces(Set namespaces) { namespaces.forEach(ns -> { if (!sources.containsKey(ns)) { - final var source = - createEventSource( - client.inNamespace(ns).withLabelSelector(configuration.getLabelSelector()), - eventHandler, ns); + final InformerWrapper source = createEventSourceForNamespace(ns); source.addIndexers(this.indexers); source.start(); log.debug("Registered new {} -> {} for namespace: {}", this, source, @@ -100,6 +92,19 @@ public void changeNamespaces(Set namespaces) { } + private InformerWrapper createEventSourceForNamespace(String namespace) { + final InformerWrapper source; + if (namespace.equals(WATCH_ALL_NAMESPACES)) { + final var filteredBySelectorClient = + client.inAnyNamespace().withLabelSelector(configuration.getLabelSelector()); + source = createEventSource(filteredBySelectorClient, eventHandler, WATCH_ALL_NAMESPACES); + } else { + source = createEventSource( + client.inNamespace(namespace).withLabelSelector(configuration.getLabelSelector()), + eventHandler, namespace); + } + return source; + } private InformerWrapper createEventSource( FilterWatchListDeletable, Resource> filteredBySelectorClient, diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/ChangeNamespaceIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/ChangeNamespaceIT.java index 56eda890a1..c7d3b04c4a 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/ChangeNamespaceIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/ChangeNamespaceIT.java @@ -4,6 +4,8 @@ import java.util.Map; import java.util.Set; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -12,6 +14,7 @@ import io.fabric8.kubernetes.api.model.NamespaceBuilder; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.client.KubernetesClient; +import io.javaoperatorsdk.operator.api.reconciler.Constants; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; import io.javaoperatorsdk.operator.sample.changenamespace.ChangeNamespaceTestCustomResource; import io.javaoperatorsdk.operator.sample.changenamespace.ChangeNamespaceTestReconciler; @@ -25,63 +28,98 @@ class ChangeNamespaceIT { public static final String TEST_RESOURCE_NAME_2 = "test2"; public static final String TEST_RESOURCE_NAME_3 = "test3"; public static final String ADDITIONAL_TEST_NAMESPACE = "additional-test-namespace"; + @RegisterExtension LocallyRunOperatorExtension operator = LocallyRunOperatorExtension.builder().withReconciler(new ChangeNamespaceTestReconciler()) .build(); + @BeforeEach + void setup() { + client().namespaces().resource(additionalTestNamespace()).create(); + } + + @AfterEach + void cleanup() { + client().namespaces().resource(additionalTestNamespace()).delete(); + } + @SuppressWarnings("rawtypes") @Test void addNewAndRemoveOldNamespaceTest() { - try { - var reconciler = operator.getReconcilerOfType(ChangeNamespaceTestReconciler.class); - var defaultNamespaceResource = operator.create(customResource(TEST_RESOURCE_NAME_1)); + var reconciler = operator.getReconcilerOfType(ChangeNamespaceTestReconciler.class); + var defaultNamespaceResource = operator.create(customResource(TEST_RESOURCE_NAME_1)); + + assertReconciled(reconciler, defaultNamespaceResource); + var resourceInAdditionalTestNamespace = createResourceInAdditionalNamespace(); + + assertNotReconciled(reconciler, resourceInAdditionalTestNamespace); + // adding additional namespace + RegisteredController registeredController = + operator.getRegisteredControllerForReconcile(ChangeNamespaceTestReconciler.class); + registeredController + .changeNamespaces(Set.of(operator.getNamespace(), ADDITIONAL_TEST_NAMESPACE)); + + assertReconciled(reconciler, resourceInAdditionalTestNamespace); - await().pollDelay(Duration.ofMillis(100)).untilAsserted(() -> assertThat( - reconciler.numberOfResourceReconciliations(defaultNamespaceResource)).isEqualTo(2)); + // removing a namespace + registeredController.changeNamespaces(Set.of(ADDITIONAL_TEST_NAMESPACE)); - client().namespaces().create(additionalTestNamespace()); - var resourceInAdditionalTestNamespace = createResourceInTestNamespace(); - await().pollDelay(Duration.ofMillis(200)).untilAsserted( - () -> assertThat( - reconciler.numberOfResourceReconciliations(resourceInAdditionalTestNamespace)) - .isZero()); + var newResourceInDefaultNamespace = operator.create(customResource(TEST_RESOURCE_NAME_3)); + assertNotReconciled(reconciler, newResourceInDefaultNamespace); - // adding additional namespace - RegisteredController registeredController = - operator.getRegisteredControllerForReconcile(ChangeNamespaceTestReconciler.class); - registeredController - .changeNamespaces(Set.of(operator.getNamespace(), ADDITIONAL_TEST_NAMESPACE)); + ConfigMap firstMap = operator.get(ConfigMap.class, TEST_RESOURCE_NAME_1); + firstMap.setData(Map.of("data", "newdata")); + operator.replace(firstMap); + assertReconciled(reconciler, defaultNamespaceResource); + } + + @Test + void changeToWatchAllNamespaces() { + var reconciler = operator.getReconcilerOfType(ChangeNamespaceTestReconciler.class); + var resourceInAdditionalTestNamespace = createResourceInAdditionalNamespace(); + + assertNotReconciled(reconciler, resourceInAdditionalTestNamespace); - await().untilAsserted( - () -> assertThat( - reconciler.numberOfResourceReconciliations(resourceInAdditionalTestNamespace)) - .isEqualTo(2)); + var registeredController = + operator.getRegisteredControllerForReconcile(ChangeNamespaceTestReconciler.class); - // removing a namespace - registeredController.changeNamespaces(Set.of(ADDITIONAL_TEST_NAMESPACE)); + registeredController + .changeNamespaces(Set.of(Constants.WATCH_ALL_NAMESPACES)); - var newResourceInDefaultNamespace = operator.create(customResource(TEST_RESOURCE_NAME_3)); - await().pollDelay(Duration.ofMillis(200)) - .untilAsserted(() -> assertThat( - reconciler.numberOfResourceReconciliations(newResourceInDefaultNamespace)).isZero()); + assertReconciled(reconciler, resourceInAdditionalTestNamespace); + registeredController.changeNamespaces(Set.of(operator.getNamespace())); - ConfigMap firstMap = operator.get(ConfigMap.class, TEST_RESOURCE_NAME_1); - firstMap.setData(Map.of("data", "newdata")); - operator.replace(firstMap); + var defaultNamespaceResource = operator.create(customResource(TEST_RESOURCE_NAME_1)); + var resource2InAdditionalResource = createResourceInAdditionalNamespace(TEST_RESOURCE_NAME_3); + assertReconciled(reconciler, defaultNamespaceResource); + assertNotReconciled(reconciler, resource2InAdditionalResource); + } + + private static void assertReconciled(ChangeNamespaceTestReconciler reconciler, + ChangeNamespaceTestCustomResource resourceInAdditionalTestNamespace) { + await().untilAsserted( + () -> assertThat( + reconciler.numberOfResourceReconciliations(resourceInAdditionalTestNamespace)) + .isEqualTo(2)); + } - await().untilAsserted(() -> assertThat( - reconciler.numberOfResourceReconciliations(defaultNamespaceResource)).isEqualTo(2)); + private static void assertNotReconciled(ChangeNamespaceTestReconciler reconciler, + ChangeNamespaceTestCustomResource resourceInAdditionalTestNamespace) { + await().pollDelay(Duration.ofMillis(200)).untilAsserted( + () -> assertThat( + reconciler.numberOfResourceReconciliations(resourceInAdditionalTestNamespace)) + .isZero()); + } - } finally { - client().namespaces().delete(additionalTestNamespace()); - } + private ChangeNamespaceTestCustomResource createResourceInAdditionalNamespace() { + return createResourceInAdditionalNamespace(TEST_RESOURCE_NAME_2); } - private ChangeNamespaceTestCustomResource createResourceInTestNamespace() { - var res = customResource(TEST_RESOURCE_NAME_2); + private ChangeNamespaceTestCustomResource createResourceInAdditionalNamespace(String name) { + var res = customResource(name); return client().resources(ChangeNamespaceTestCustomResource.class) .inNamespace(ADDITIONAL_TEST_NAMESPACE) .create(res);