From 5e944f18a04b6d56d788dcfc9a63f13238ad3492 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Fri, 6 May 2022 15:19:28 +0200 Subject: [PATCH] feat: make RegisteredController a little more useful Allows to retrieve information from registered controllers from Operator, in particular if there are any registered controllers, which the Quarkus extension uses to determine whether or not to start the Operator. --- .../io/javaoperatorsdk/operator/Operator.java | 52 +++++++++++++++---- .../operator/RegisteredController.java | 5 +- .../operator/processing/Controller.java | 2 +- .../operator/OperatorTest.java | 44 ++++++++++++++-- 4 files changed, 87 insertions(+), 16 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/Operator.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/Operator.java index c464e445c4..7679267a78 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/Operator.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/Operator.java @@ -1,7 +1,11 @@ package io.javaoperatorsdk.operator; +import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Optional; +import java.util.Set; import java.util.function.Consumer; import org.slf4j.Logger; @@ -120,10 +124,10 @@ public void stop() throws OperatorException { * registration of the reconciler is delayed till the operator is started. * * @param reconciler the reconciler to register - * @param the {@code CustomResource} type associated with the reconciler + * @param

the {@code CustomResource} type associated with the reconciler * @throws OperatorException if a problem occurred during the registration process */ - public RegisteredController register(Reconciler reconciler) + public

RegisteredController

register(Reconciler

reconciler) throws OperatorException { final var controllerConfiguration = ConfigurationServiceProvider.instance().getConfigurationFor(reconciler); @@ -139,11 +143,11 @@ public RegisteredController register(Reconciler recon * * @param reconciler part of the reconciler to register * @param configuration the configuration with which we want to register the reconciler - * @param the {@code HasMetadata} type associated with the reconciler + * @param

the {@code HasMetadata} type associated with the reconciler * @throws OperatorException if a problem occurred during the registration process */ - public RegisteredController register(Reconciler reconciler, - ControllerConfiguration configuration) + public

RegisteredController

register(Reconciler

reconciler, + ControllerConfiguration

configuration) throws OperatorException { if (configuration == null) { @@ -175,10 +179,10 @@ public RegisteredController register(Reconciler recon * * @param reconciler part of the reconciler to register * @param configOverrider consumer to use to change config values - * @param the {@code HasMetadata} type associated with the reconciler + * @param

the {@code HasMetadata} type associated with the reconciler */ - public RegisteredController register(Reconciler reconciler, - Consumer> configOverrider) { + public

RegisteredController

register(Reconciler

reconciler, + Consumer> configOverrider) { final var controllerConfiguration = ConfigurationServiceProvider.instance().getConfigurationFor(reconciler); var configToOverride = ControllerConfigurationOverrider.override(controllerConfiguration); @@ -186,6 +190,18 @@ public RegisteredController register(Reconciler recon return register(reconciler, configToOverride.build()); } + public Optional getRegisteredController(String name) { + return controllers.get(name).map(RegisteredController.class::cast); + } + + public Set getRegisteredControllers() { + return new HashSet<>(controllers.controllers()); + } + + public int getRegisteredControllersNumber() { + return controllers.size(); + } + static class ControllerManager implements LifecycleAware { private final Map controllers = new HashMap<>(); private boolean started = false; @@ -200,12 +216,12 @@ public synchronized void shouldStart() { } public synchronized void start() { - controllers.values().parallelStream().forEach(Controller::start); + controllers().parallelStream().forEach(Controller::start); started = true; } public synchronized void stop() { - this.controllers.values().parallelStream().forEach(closeable -> { + controllers().parallelStream().forEach(closeable -> { log.debug("closing {}", closeable); closeable.stop(); }); @@ -224,10 +240,24 @@ synchronized void add(Controller controller) { + "': another controller named '" + existing.getConfiguration().getName() + "' is already registered for resource '" + resourceTypeName + "'"); } - this.controllers.put(resourceTypeName, controller); + controllers.put(resourceTypeName, controller); if (started) { controller.start(); } } + + synchronized Optional get(String name) { + return controllers().stream() + .filter(c -> name.equals(c.getConfiguration().getName())) + .findFirst(); + } + + synchronized Collection controllers() { + return controllers.values(); + } + + synchronized int size() { + return controllers.size(); + } } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/RegisteredController.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/RegisteredController.java index f1fa1f37c4..832c2df6ee 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/RegisteredController.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/RegisteredController.java @@ -1,6 +1,9 @@ package io.javaoperatorsdk.operator; +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.api.config.ControllerConfiguration; import io.javaoperatorsdk.operator.api.config.NamespaceChangeable; -public interface RegisteredController extends NamespaceChangeable { +public interface RegisteredController

extends NamespaceChangeable { + ControllerConfiguration

getConfiguration(); } 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 7723d19007..50411ac4e8 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 @@ -33,7 +33,7 @@ @SuppressWarnings({"unchecked", "rawtypes"}) @Ignore public class Controller

- implements Reconciler

, Cleaner

, LifecycleAware, RegisteredController { + implements Reconciler

, Cleaner

, LifecycleAware, RegisteredController

{ private static final Logger log = LoggerFactory.getLogger(Controller.class); diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/OperatorTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/OperatorTest.java index b8d00d858e..2e3e8918c1 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/OperatorTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/OperatorTest.java @@ -3,6 +3,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -11,15 +12,18 @@ import io.javaoperatorsdk.operator.api.config.AbstractConfigurationService; import io.javaoperatorsdk.operator.api.config.ConfigurationServiceProvider; import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; import io.javaoperatorsdk.operator.api.reconciler.Reconciler; import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + @SuppressWarnings("rawtypes") class OperatorTest { private final KubernetesClient kubernetesClient = MockKubernetesClient.client(ConfigMap.class); - private final Operator operator = new Operator(kubernetesClient); - private final FooReconciler fooReconciler = new FooReconciler(); + private Operator operator; @BeforeAll @AfterAll @@ -27,6 +31,12 @@ static void setUpConfigurationServiceProvider() { ConfigurationServiceProvider.reset(); } + @BeforeEach + void initOperator() { + ConfigurationServiceProvider.reset(); + operator = new Operator(kubernetesClient); + } + @Test @DisplayName("should throw `OperationException` when Configuration is null") public void shouldThrowOperatorExceptionWhenConfigurationIsNull() { @@ -34,9 +44,37 @@ public void shouldThrowOperatorExceptionWhenConfigurationIsNull() { ConfigurationServiceProvider.reset(); ConfigurationServiceProvider.set(new AbstractConfigurationService(null)); - Assertions.assertThrows(OperatorException.class, () -> operator.register(fooReconciler)); + Assertions.assertThrows(OperatorException.class, () -> operator.register(new FooReconciler())); + } + + @Test + void shouldBePossibleToRetrieveNumberOfRegisteredControllers() { + assertEquals(0, operator.getRegisteredControllersNumber()); + + operator.register(new FooReconciler()); + assertEquals(1, operator.getRegisteredControllersNumber()); + } + + @Test + void shouldBePossibleToRetrieveRegisteredControllerByName() { + final var reconciler = new FooReconciler(); + final var name = ReconcilerUtils.getNameFor(reconciler); + + var registeredControllers = operator.getRegisteredControllers(); + assertTrue(operator.getRegisteredController(name).isEmpty()); + assertTrue(registeredControllers.isEmpty()); + + operator.register(reconciler); + final var maybeController = operator.getRegisteredController(name); + assertTrue(maybeController.isPresent()); + assertEquals(name, maybeController.map(rc -> rc.getConfiguration().getName()).orElseThrow()); + + registeredControllers = operator.getRegisteredControllers(); + assertEquals(1, registeredControllers.size()); + assertEquals(maybeController.get(), registeredControllers.stream().findFirst().orElseThrow()); } + @ControllerConfiguration private static class FooReconciler implements Reconciler { @Override