diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index c8969f8a2c..7dd194eea7 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -21,6 +21,8 @@ jobs: uses: actions/setup-java@v1 with: java-version: ${{ matrix.java }} + - name: Check code format + run: mvn fmt:check --file pom.xml - name: Run unit tests run: mvn -B test -P no-integration-tests --file pom.xml - name: Set up Minikube diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/ClassMappingProvider.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/ClassMappingProvider.java index 539c98dfe7..481e27a5a4 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/ClassMappingProvider.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/ClassMappingProvider.java @@ -1,55 +1,55 @@ package io.javaoperatorsdk.operator; -import org.apache.commons.lang3.ClassUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.URL; import java.util.*; import java.util.stream.Collectors; - +import org.apache.commons.lang3.ClassUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; class ClassMappingProvider { - private static final Logger log = LoggerFactory.getLogger(ClassMappingProvider.class); - - static Map provide(final String resourcePath, T key, V value) { - Map result = new HashMap(); - try { - final var classLoader = Thread.currentThread().getContextClassLoader(); - final Enumeration customResourcesMetadataList = classLoader.getResources(resourcePath); - for (Iterator it = customResourcesMetadataList.asIterator(); it.hasNext(); ) { - URL url = it.next(); - - List classNamePairs = retrieveClassNamePairs(url); - classNamePairs.forEach(clazzPair -> { - try { - final String[] classNames = clazzPair.split(","); - if (classNames.length != 2) { - throw new IllegalStateException(String.format("%s is not valid Mapping metadata, defined in %s", clazzPair, url.toString())); - } - - result.put( - (T) ClassUtils.getClass(classNames[0]), - (V) ClassUtils.getClass(classNames[1]) - ); - } catch (ClassNotFoundException e) { - throw new RuntimeException(e); - } - }); - } - log.debug("Loaded Controller to CustomResource mappings {}", result); - return result; - } catch (IOException e) { - throw new RuntimeException(e); - } + private static final Logger log = LoggerFactory.getLogger(ClassMappingProvider.class); + + static Map provide(final String resourcePath, T key, V value) { + Map result = new HashMap(); + try { + final var classLoader = Thread.currentThread().getContextClassLoader(); + final Enumeration customResourcesMetadataList = classLoader.getResources(resourcePath); + for (Iterator it = customResourcesMetadataList.asIterator(); it.hasNext(); ) { + URL url = it.next(); + + List classNamePairs = retrieveClassNamePairs(url); + classNamePairs.forEach( + clazzPair -> { + try { + final String[] classNames = clazzPair.split(","); + if (classNames.length != 2) { + throw new IllegalStateException( + String.format( + "%s is not valid Mapping metadata, defined in %s", + clazzPair, url.toString())); + } + + result.put( + (T) ClassUtils.getClass(classNames[0]), (V) ClassUtils.getClass(classNames[1])); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + }); + } + log.debug("Loaded Controller to CustomResource mappings {}", result); + return result; + } catch (IOException e) { + throw new RuntimeException(e); } + } - private static List retrieveClassNamePairs(URL url) throws IOException { - return new BufferedReader( - new InputStreamReader(url.openStream()) - ).lines().collect(Collectors.toList()); - } + private static List retrieveClassNamePairs(URL url) throws IOException { + return new BufferedReader(new InputStreamReader(url.openStream())) + .lines() + .collect(Collectors.toList()); + } } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/ControllerUtils.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/ControllerUtils.java index c049dcb283..9eeddde1a3 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/ControllerUtils.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/ControllerUtils.java @@ -4,76 +4,76 @@ import io.fabric8.kubernetes.client.CustomResourceDoneable; import io.javaoperatorsdk.operator.api.Controller; import io.javaoperatorsdk.operator.api.ResourceController; - import java.util.Map; - public class ControllerUtils { - private static final String FINALIZER_NAME_SUFFIX = "/finalizer"; - public static final String CONTROLLERS_RESOURCE_PATH = "javaoperatorsdk/controllers"; - public static final String DONEABLES_RESOURCE_PATH = "javaoperatorsdk/doneables"; - private static Map, Class> controllerToCustomResourceMappings; - private static Map, Class> resourceToDoneableMappings; + private static final String FINALIZER_NAME_SUFFIX = "/finalizer"; + public static final String CONTROLLERS_RESOURCE_PATH = "javaoperatorsdk/controllers"; + public static final String DONEABLES_RESOURCE_PATH = "javaoperatorsdk/doneables"; + private static Map, Class> + controllerToCustomResourceMappings; + private static Map, Class> + resourceToDoneableMappings; + static { + controllerToCustomResourceMappings = + ClassMappingProvider.provide( + CONTROLLERS_RESOURCE_PATH, ResourceController.class, CustomResource.class); + resourceToDoneableMappings = + ClassMappingProvider.provide( + DONEABLES_RESOURCE_PATH, CustomResource.class, CustomResourceDoneable.class); + } - static { - controllerToCustomResourceMappings = ClassMappingProvider - .provide(CONTROLLERS_RESOURCE_PATH, - ResourceController.class, - CustomResource.class); - resourceToDoneableMappings = ClassMappingProvider - .provide(DONEABLES_RESOURCE_PATH, - CustomResource.class, - CustomResourceDoneable.class - ); - } - - static String getFinalizer(ResourceController controller) { - final String annotationFinalizerName = getAnnotation(controller).finalizerName(); - if (!Controller.NULL.equals(annotationFinalizerName)) { - return annotationFinalizerName; - } - return getAnnotation(controller).crdName() + FINALIZER_NAME_SUFFIX; + static String getFinalizer(ResourceController controller) { + final String annotationFinalizerName = getAnnotation(controller).finalizerName(); + if (!Controller.NULL.equals(annotationFinalizerName)) { + return annotationFinalizerName; } + return getAnnotation(controller).crdName() + FINALIZER_NAME_SUFFIX; + } - static boolean getGenerationEventProcessing(ResourceController controller) { - return getAnnotation(controller).generationAwareEventProcessing(); - } + static boolean getGenerationEventProcessing(ResourceController controller) { + return getAnnotation(controller).generationAwareEventProcessing(); + } - static Class getCustomResourceClass(ResourceController controller) { - final Class customResourceClass = controllerToCustomResourceMappings - .get(controller.getClass()); - if (customResourceClass == null) { - throw new IllegalArgumentException( - String.format( - "No custom resource has been found for controller %s", - controller.getClass().getCanonicalName() - ) - ); - } - return (Class) customResourceClass; + static Class getCustomResourceClass( + ResourceController controller) { + final Class customResourceClass = + controllerToCustomResourceMappings.get(controller.getClass()); + if (customResourceClass == null) { + throw new IllegalArgumentException( + String.format( + "No custom resource has been found for controller %s", + controller.getClass().getCanonicalName())); } + return (Class) customResourceClass; + } - static String getCrdName(ResourceController controller) { - return getAnnotation(controller).crdName(); - } + static String getCrdName(ResourceController controller) { + return getAnnotation(controller).crdName(); + } - public static Class> - getCustomResourceDoneableClass(ResourceController controller) { - final Class customResourceClass = getCustomResourceClass(controller); - final Class> doneableClass = (Class>) resourceToDoneableMappings.get(customResourceClass); - if (doneableClass == null) { - throw new RuntimeException(String.format("No matching Doneable class found for %s", customResourceClass)); - } - return doneableClass; + public static + Class> getCustomResourceDoneableClass( + ResourceController controller) { + final Class customResourceClass = getCustomResourceClass(controller); + final Class> doneableClass = + (Class>) + resourceToDoneableMappings.get(customResourceClass); + if (doneableClass == null) { + throw new RuntimeException( + String.format("No matching Doneable class found for %s", customResourceClass)); } + return doneableClass; + } - private static Controller getAnnotation(ResourceController controller) { - return controller.getClass().getAnnotation(Controller.class); - } + private static Controller getAnnotation(ResourceController controller) { + return controller.getClass().getAnnotation(Controller.class); + } - public static boolean hasGivenFinalizer(CustomResource resource, String finalizer) { - return resource.getMetadata().getFinalizers() != null && resource.getMetadata().getFinalizers().contains(finalizer); - } + public static boolean hasGivenFinalizer(CustomResource resource, String finalizer) { + return resource.getMetadata().getFinalizers() != null + && resource.getMetadata().getFinalizers().contains(finalizer); + } } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/EventListUtils.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/EventListUtils.java index a55142618a..19a976c38e 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/EventListUtils.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/EventListUtils.java @@ -1,21 +1,21 @@ package io.javaoperatorsdk.operator; -import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.client.Watcher; import io.javaoperatorsdk.operator.processing.event.Event; import io.javaoperatorsdk.operator.processing.event.internal.CustomResourceEvent; - import java.util.List; public class EventListUtils { - public static boolean containsCustomResourceDeletedEvent(List events) { - return events.stream().anyMatch(e -> { - if (e instanceof CustomResourceEvent) { + public static boolean containsCustomResourceDeletedEvent(List events) { + return events.stream() + .anyMatch( + e -> { + if (e instanceof CustomResourceEvent) { return ((CustomResourceEvent) e).getAction() == Watcher.Action.DELETED; - } else { + } else { return false; - } - }); - } + } + }); + } } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/Operator.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/Operator.java index 7eb63b18f9..6f94656521 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/Operator.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/Operator.java @@ -1,5 +1,7 @@ package io.javaoperatorsdk.operator; +import static io.javaoperatorsdk.operator.ControllerUtils.*; + import io.fabric8.kubernetes.api.model.apiextensions.v1beta1.CustomResourceDefinition; import io.fabric8.kubernetes.client.CustomResource; import io.fabric8.kubernetes.client.CustomResourceDoneable; @@ -10,105 +12,132 @@ import io.fabric8.kubernetes.client.dsl.internal.CustomResourceOperationsImpl; import io.fabric8.kubernetes.internal.KubernetesDeserializer; import io.javaoperatorsdk.operator.api.ResourceController; -import io.javaoperatorsdk.operator.processing.EventDispatcher; -import io.javaoperatorsdk.operator.processing.DefaultEventHandler; import io.javaoperatorsdk.operator.processing.CustomResourceCache; +import io.javaoperatorsdk.operator.processing.DefaultEventHandler; +import io.javaoperatorsdk.operator.processing.EventDispatcher; import io.javaoperatorsdk.operator.processing.event.DefaultEventSourceManager; import io.javaoperatorsdk.operator.processing.event.internal.CustomResourceEventSource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.util.Arrays; import java.util.HashMap; import java.util.Map; - -import static io.javaoperatorsdk.operator.ControllerUtils.*; - +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; @SuppressWarnings("rawtypes") public class Operator { - private final static Logger log = LoggerFactory.getLogger(Operator.class); - private final KubernetesClient k8sClient; - private Map, CustomResourceOperationsImpl> customResourceClients = new HashMap<>(); - - public Operator(KubernetesClient k8sClient) { - this.k8sClient = k8sClient; - } - - - public void registerControllerForAllNamespaces(ResourceController controller) throws OperatorException { - registerController(controller, true); - } - - public void registerController(ResourceController controller, String... targetNamespaces) throws OperatorException { - registerController(controller, false, targetNamespaces); + private static final Logger log = LoggerFactory.getLogger(Operator.class); + private final KubernetesClient k8sClient; + private Map, CustomResourceOperationsImpl> customResourceClients = + new HashMap<>(); + + public Operator(KubernetesClient k8sClient) { + this.k8sClient = k8sClient; + } + + public void registerControllerForAllNamespaces( + ResourceController controller) throws OperatorException { + registerController(controller, true); + } + + public void registerController( + ResourceController controller, String... targetNamespaces) throws OperatorException { + registerController(controller, false, targetNamespaces); + } + + @SuppressWarnings("rawtypes") + private void registerController( + ResourceController controller, boolean watchAllNamespaces, String... targetNamespaces) + throws OperatorException { + Class resClass = getCustomResourceClass(controller); + CustomResourceDefinitionContext crd = getCustomResourceDefinitionForController(controller); + KubernetesDeserializer.registerCustomKind(crd.getVersion(), crd.getKind(), resClass); + String finalizer = ControllerUtils.getFinalizer(controller); + MixedOperation client = + k8sClient.customResources( + crd, + resClass, + CustomResourceList.class, + ControllerUtils.getCustomResourceDoneableClass(controller)); + EventDispatcher eventDispatcher = + new EventDispatcher( + controller, finalizer, new EventDispatcher.CustomResourceFacade(client)); + + CustomResourceCache customResourceCache = new CustomResourceCache(); + DefaultEventHandler defaultEventHandler = + new DefaultEventHandler( + customResourceCache, eventDispatcher, controller.getClass().getName()); + DefaultEventSourceManager eventSourceManager = + new DefaultEventSourceManager(defaultEventHandler); + defaultEventHandler.setDefaultEventSourceManager(eventSourceManager); + eventDispatcher.setEventSourceManager(eventSourceManager); + + customResourceClients.put(resClass, (CustomResourceOperationsImpl) client); + + controller.init(eventSourceManager); + CustomResourceEventSource customResourceEventSource = + createCustomResourceEventSource( + client, + customResourceCache, + watchAllNamespaces, + targetNamespaces, + defaultEventHandler, + ControllerUtils.getGenerationEventProcessing(controller), + finalizer); + eventSourceManager.registerCustomResourceEventSource(customResourceEventSource); + + log.info( + "Registered Controller: '{}' for CRD: '{}' for namespaces: {}", + controller.getClass().getSimpleName(), + resClass, + targetNamespaces.length == 0 + ? "[all/client namespace]" + : Arrays.toString(targetNamespaces)); + } + + private CustomResourceEventSource createCustomResourceEventSource( + MixedOperation client, + CustomResourceCache customResourceCache, + boolean watchAllNamespaces, + String[] targetNamespaces, + DefaultEventHandler defaultEventHandler, + boolean generationAware, + String finalizer) { + CustomResourceEventSource customResourceEventSource = + watchAllNamespaces + ? CustomResourceEventSource.customResourceEventSourceForAllNamespaces( + customResourceCache, client, generationAware, finalizer) + : CustomResourceEventSource.customResourceEventSourceForTargetNamespaces( + customResourceCache, client, targetNamespaces, generationAware, finalizer); + + customResourceEventSource.setEventHandler(defaultEventHandler); + + return customResourceEventSource; + } + + private CustomResourceDefinitionContext getCustomResourceDefinitionForController( + ResourceController controller) { + String crdName = getCrdName(controller); + CustomResourceDefinition customResourceDefinition = + k8sClient.customResourceDefinitions().withName(crdName).get(); + if (customResourceDefinition == null) { + throw new OperatorException("Cannot find Custom Resource Definition with name: " + crdName); } - - @SuppressWarnings("rawtypes") - private void registerController(ResourceController controller, - boolean watchAllNamespaces, String... targetNamespaces) throws OperatorException { - Class resClass = getCustomResourceClass(controller); - CustomResourceDefinitionContext crd = getCustomResourceDefinitionForController(controller); - KubernetesDeserializer.registerCustomKind(crd.getVersion(), crd.getKind(), resClass); - String finalizer = ControllerUtils.getFinalizer(controller); - MixedOperation client = k8sClient.customResources(crd, resClass, CustomResourceList.class, ControllerUtils.getCustomResourceDoneableClass(controller)); - EventDispatcher eventDispatcher = new EventDispatcher(controller, - finalizer, new EventDispatcher.CustomResourceFacade(client)); - - - CustomResourceCache customResourceCache = new CustomResourceCache(); - DefaultEventHandler defaultEventHandler = new DefaultEventHandler(customResourceCache, eventDispatcher, controller.getClass().getName()); - DefaultEventSourceManager eventSourceManager = new DefaultEventSourceManager(defaultEventHandler); - defaultEventHandler.setDefaultEventSourceManager(eventSourceManager); - eventDispatcher.setEventSourceManager(eventSourceManager); - - customResourceClients.put(resClass, (CustomResourceOperationsImpl) client); - - controller.init(eventSourceManager); - CustomResourceEventSource customResourceEventSource - = createCustomResourceEventSource(client, customResourceCache, watchAllNamespaces, targetNamespaces, - defaultEventHandler, ControllerUtils.getGenerationEventProcessing(controller), finalizer); - eventSourceManager.registerCustomResourceEventSource(customResourceEventSource); - - - log.info("Registered Controller: '{}' for CRD: '{}' for namespaces: {}", controller.getClass().getSimpleName(), - resClass, targetNamespaces.length == 0 ? "[all/client namespace]" : Arrays.toString(targetNamespaces)); - } - - private CustomResourceEventSource createCustomResourceEventSource(MixedOperation client, - CustomResourceCache customResourceCache, - boolean watchAllNamespaces, - String[] targetNamespaces, - DefaultEventHandler defaultEventHandler, - boolean generationAware, - String finalizer) { - CustomResourceEventSource customResourceEventSource = watchAllNamespaces ? - CustomResourceEventSource.customResourceEventSourceForAllNamespaces(customResourceCache, client, generationAware, finalizer) : - CustomResourceEventSource.customResourceEventSourceForTargetNamespaces(customResourceCache, client, targetNamespaces, generationAware, finalizer); - - customResourceEventSource.setEventHandler(defaultEventHandler); - - return customResourceEventSource; - } - - private CustomResourceDefinitionContext getCustomResourceDefinitionForController(ResourceController controller) { - String crdName = getCrdName(controller); - CustomResourceDefinition customResourceDefinition = k8sClient.customResourceDefinitions().withName(crdName).get(); - if (customResourceDefinition == null) { - throw new OperatorException("Cannot find Custom Resource Definition with name: " + crdName); - } - CustomResourceDefinitionContext context = CustomResourceDefinitionContext.fromCrd(customResourceDefinition); - return context; - } - - public Map, CustomResourceOperationsImpl> getCustomResourceClients() { - return customResourceClients; - } - - public , D extends CustomResourceDoneable> CustomResourceOperationsImpl - getCustomResourceClients(Class customResourceClass) { - return customResourceClients.get(customResourceClass); - } - + CustomResourceDefinitionContext context = + CustomResourceDefinitionContext.fromCrd(customResourceDefinition); + return context; + } + + public Map, CustomResourceOperationsImpl> + getCustomResourceClients() { + return customResourceClients; + } + + public < + T extends CustomResource, + L extends CustomResourceList, + D extends CustomResourceDoneable> + CustomResourceOperationsImpl getCustomResourceClients(Class customResourceClass) { + return customResourceClients.get(customResourceClass); + } } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/OperatorException.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/OperatorException.java index da1040f3ab..b11ff0a61a 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/OperatorException.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/OperatorException.java @@ -2,14 +2,13 @@ public class OperatorException extends RuntimeException { - public OperatorException() { - } + public OperatorException() {} - public OperatorException(String message) { - super(message); - } + public OperatorException(String message) { + super(message); + } - public OperatorException(String message, Throwable cause) { - super(message, cause); - } + public OperatorException(String message, Throwable cause) { + super(message, cause); + } } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/api/Context.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/api/Context.java index 02da09cd66..2b79a9d6ae 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/api/Context.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/api/Context.java @@ -1,16 +1,12 @@ package io.javaoperatorsdk.operator.api; import io.fabric8.kubernetes.client.CustomResource; -import io.javaoperatorsdk.operator.processing.event.Event; import io.javaoperatorsdk.operator.processing.event.EventList; import io.javaoperatorsdk.operator.processing.event.EventSourceManager; -import java.util.List; - public interface Context { - EventSourceManager getEventSourceManager(); - - EventList getEvents(); + EventSourceManager getEventSourceManager(); + EventList getEvents(); } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/api/Controller.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/api/Controller.java index 87bca2e15a..61f8bd9d22 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/api/Controller.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/api/Controller.java @@ -1,6 +1,5 @@ package io.javaoperatorsdk.operator.api; - import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -9,21 +8,20 @@ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) public @interface Controller { - String NULL = ""; + String NULL = ""; - String crdName(); + String crdName(); - /** - * Optional finalizer name, if it is not, - * the crdName will be used as the name of the finalizer too. - */ - String finalizerName() default NULL; + /** + * Optional finalizer name, if it is not, the crdName will be used as the name of the finalizer + * too. + */ + String finalizerName() default NULL; - /** - * If true, will dispatch new event to the controller if generation increased since the last processing, otherwise will - * process all events. - * See generation meta attribute - * here - */ - boolean generationAwareEventProcessing() default true; + /** + * If true, will dispatch new event to the controller if generation increased since the last + * processing, otherwise will process all events. See generation meta attribute here + */ + boolean generationAwareEventProcessing() default true; } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/api/DefaultContext.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/api/DefaultContext.java index 475f183ad5..7e472324d6 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/api/DefaultContext.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/api/DefaultContext.java @@ -1,30 +1,26 @@ package io.javaoperatorsdk.operator.api; import io.fabric8.kubernetes.client.CustomResource; -import io.javaoperatorsdk.operator.processing.event.Event; import io.javaoperatorsdk.operator.processing.event.EventList; import io.javaoperatorsdk.operator.processing.event.EventSourceManager; -import java.util.List; - public class DefaultContext implements Context { - private final EventList events; - private final EventSourceManager eventSourceManager; - - public DefaultContext(EventSourceManager eventSourceManager, EventList events) { - this.events = events; - this.eventSourceManager = eventSourceManager; - } + private final EventList events; + private final EventSourceManager eventSourceManager; - @Override - public EventSourceManager getEventSourceManager() { - return eventSourceManager; - } + public DefaultContext(EventSourceManager eventSourceManager, EventList events) { + this.events = events; + this.eventSourceManager = eventSourceManager; + } - @Override - public EventList getEvents() { - return events; - } + @Override + public EventSourceManager getEventSourceManager() { + return eventSourceManager; + } + @Override + public EventList getEvents() { + return events; + } } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/api/DeleteControl.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/api/DeleteControl.java index 1c7ad04f1a..bd019de348 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/api/DeleteControl.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/api/DeleteControl.java @@ -1,8 +1,6 @@ package io.javaoperatorsdk.operator.api; public enum DeleteControl { - - DEFAULT_DELETE, - NO_FINALIZER_REMOVAL - + DEFAULT_DELETE, + NO_FINALIZER_REMOVAL } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/api/ResourceController.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/api/ResourceController.java index 3153d5f295..85fd03a21b 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/api/ResourceController.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/api/ResourceController.java @@ -5,35 +5,36 @@ public interface ResourceController { - /** - * The implementation should delete the associated component(s). Note that this is method is called when an object - * is marked for deletion. After its executed the custom resource finalizer is automatically removed by the framework; - * unless the return value is false - note that this is almost never the case. - * Its important to have the implementation also idempotent, in the current implementation to cover all edge cases - * actually will be executed mostly twice. - * - * @param resource - * @return true - so the finalizer is automatically removed after the call. - * false if you don't want to remove the finalizer. Note that this is ALMOST NEVER the case. - */ - DeleteControl deleteResource(R resource, Context context); + /** + * The implementation should delete the associated component(s). Note that this is method is + * called when an object is marked for deletion. After its executed the custom resource finalizer + * is automatically removed by the framework; unless the return value is false - note that this is + * almost never the case. Its important to have the implementation also idempotent, in the current + * implementation to cover all edge cases actually will be executed mostly twice. + * + * @param resource + * @return true - so the finalizer is automatically removed after the call. false if you don't + * want to remove the finalizer. Note that this is ALMOST NEVER the case. + */ + DeleteControl deleteResource(R resource, Context context); - /** - * The implementation of this operation is required to be idempotent. - * Always use the UpdateControl object to make updates on custom resource if possible. - * Also always use the custom resource parameter (not the custom resource that might be in the events) - * - * @return The resource is updated in api server if the return value is present - * within Optional. This the common use cases. However in cases, for example the operator is restarted, - * and we don't want to have an update call to k8s api to be made unnecessarily, by returning an empty Optional - * this update can be skipped. - * However we will always call an update if there is no finalizer on object and its not marked for deletion. - */ - UpdateControl createOrUpdateResource(R resource, Context context); + /** + * The implementation of this operation is required to be idempotent. Always use the UpdateControl + * object to make updates on custom resource if possible. Also always use the custom resource + * parameter (not the custom resource that might be in the events) + * + * @return The resource is updated in api server if the return value is present within Optional. + * This the common use cases. However in cases, for example the operator is restarted, and we + * don't want to have an update call to k8s api to be made unnecessarily, by returning an + * empty Optional this update can be skipped. However we will always call an update if + * there is no finalizer on object and its not marked for deletion. + */ + UpdateControl createOrUpdateResource(R resource, Context context); - /** - * In init typically you might want to register event sources. - * @param eventSourceManager - */ - default void init(EventSourceManager eventSourceManager) {} + /** + * In init typically you might want to register event sources. + * + * @param eventSourceManager + */ + default void init(EventSourceManager eventSourceManager) {} } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/api/RetryInfo.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/api/RetryInfo.java index a1b385a51a..9c3b3c05cc 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/api/RetryInfo.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/api/RetryInfo.java @@ -2,19 +2,19 @@ public class RetryInfo { - private int retryNumber; - private boolean lastAttempt; + private int retryNumber; + private boolean lastAttempt; - public RetryInfo(int retryNumber, boolean lastAttempt) { - this.retryNumber = retryNumber; - this.lastAttempt = lastAttempt; - } + public RetryInfo(int retryNumber, boolean lastAttempt) { + this.retryNumber = retryNumber; + this.lastAttempt = lastAttempt; + } - public int getRetryNumber() { - return retryNumber; - } + public int getRetryNumber() { + return retryNumber; + } - public boolean isLastAttempt() { - return lastAttempt; - } + public boolean isLastAttempt() { + return lastAttempt; + } } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/api/UpdateControl.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/api/UpdateControl.java index 0c152b67e6..fa02e845a2 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/api/UpdateControl.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/api/UpdateControl.java @@ -4,41 +4,42 @@ public class UpdateControl { - private final T customResource; - private final boolean updateStatusSubResource; - private final boolean updateCustomResource; - - private UpdateControl(T customResource, boolean updateStatusSubResource, boolean updateCustomResource) { - if ((updateCustomResource || updateStatusSubResource) && customResource == null) { - throw new IllegalArgumentException("CustomResource cannot be null in case of update"); - } - this.customResource = customResource; - this.updateStatusSubResource = updateStatusSubResource; - this.updateCustomResource = updateCustomResource; + private final T customResource; + private final boolean updateStatusSubResource; + private final boolean updateCustomResource; + + private UpdateControl( + T customResource, boolean updateStatusSubResource, boolean updateCustomResource) { + if ((updateCustomResource || updateStatusSubResource) && customResource == null) { + throw new IllegalArgumentException("CustomResource cannot be null in case of update"); } - - public static UpdateControl updateCustomResource(T customResource) { - return new UpdateControl<>(customResource, false, true); - } - - public static UpdateControl updateStatusSubResource(T customResource) { - return new UpdateControl<>(customResource, true, false); - } - - public static UpdateControl noUpdate() { - return new UpdateControl<>(null, false, false); - } - - public T getCustomResource() { - return customResource; - } - - public boolean isUpdateStatusSubResource() { - return updateStatusSubResource; - } - - public boolean isUpdateCustomResource() { - return updateCustomResource; - } - + this.customResource = customResource; + this.updateStatusSubResource = updateStatusSubResource; + this.updateCustomResource = updateCustomResource; + } + + public static UpdateControl updateCustomResource(T customResource) { + return new UpdateControl<>(customResource, false, true); + } + + public static UpdateControl updateStatusSubResource( + T customResource) { + return new UpdateControl<>(customResource, true, false); + } + + public static UpdateControl noUpdate() { + return new UpdateControl<>(null, false, false); + } + + public T getCustomResource() { + return customResource; + } + + public boolean isUpdateStatusSubResource() { + return updateStatusSubResource; + } + + public boolean isUpdateCustomResource() { + return updateCustomResource; + } } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/CustomResourceCache.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/CustomResourceCache.java index ef6ddd186b..ecba9ed788 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/CustomResourceCache.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/CustomResourceCache.java @@ -1,49 +1,47 @@ package io.javaoperatorsdk.operator.processing; import io.fabric8.kubernetes.client.CustomResource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Predicate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class CustomResourceCache { - private static final Logger log = LoggerFactory.getLogger(CustomResourceCache.class); + private static final Logger log = LoggerFactory.getLogger(CustomResourceCache.class); - private final Map resources = new ConcurrentHashMap<>(); - private final Lock lock = new ReentrantLock(); + private final Map resources = new ConcurrentHashMap<>(); + private final Lock lock = new ReentrantLock(); - public void cacheResource(CustomResource resource) { - try { - lock.lock(); - resources.put(KubernetesResourceUtils.getUID(resource), resource); - } finally { - lock.unlock(); - } + public void cacheResource(CustomResource resource) { + try { + lock.lock(); + resources.put(KubernetesResourceUtils.getUID(resource), resource); + } finally { + lock.unlock(); } - - public void cacheResource(CustomResource resource, Predicate predicate) { - try { - lock.lock(); - if (predicate.test(resources.get(KubernetesResourceUtils.getUID(resource)))) { - log.trace("Update cache after condition is true: {}", resource); - resources.put(resource.getMetadata().getUid(), resource); - } - } finally { - lock.unlock(); - } + } + + public void cacheResource(CustomResource resource, Predicate predicate) { + try { + lock.lock(); + if (predicate.test(resources.get(KubernetesResourceUtils.getUID(resource)))) { + log.trace("Update cache after condition is true: {}", resource); + resources.put(resource.getMetadata().getUid(), resource); + } + } finally { + lock.unlock(); } + } - public Optional getLatestResource(String uuid) { - return Optional.ofNullable(resources.get(uuid)); - } + public Optional getLatestResource(String uuid) { + return Optional.ofNullable(resources.get(uuid)); + } - public CustomResource cleanup(String customResourceUid) { - return resources.remove(customResourceUid); - } + public CustomResource cleanup(String customResourceUid) { + return resources.remove(customResourceUid); + } } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/DefaultEventHandler.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/DefaultEventHandler.java index fb225aae65..e852e546f1 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/DefaultEventHandler.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/DefaultEventHandler.java @@ -1,150 +1,165 @@ package io.javaoperatorsdk.operator.processing; +import static io.javaoperatorsdk.operator.EventListUtils.containsCustomResourceDeletedEvent; +import static io.javaoperatorsdk.operator.processing.KubernetesResourceUtils.getUID; +import static io.javaoperatorsdk.operator.processing.KubernetesResourceUtils.getVersion; import io.fabric8.kubernetes.client.CustomResource; import io.javaoperatorsdk.operator.processing.event.DefaultEventSourceManager; import io.javaoperatorsdk.operator.processing.event.Event; import io.javaoperatorsdk.operator.processing.event.EventHandler; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.util.HashSet; import java.util.Optional; import java.util.Set; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadFactory; import java.util.concurrent.locks.ReentrantLock; -import java.util.function.Predicate; - -import static io.javaoperatorsdk.operator.EventListUtils.containsCustomResourceDeletedEvent; -import static io.javaoperatorsdk.operator.processing.KubernetesResourceUtils.getUID; -import static io.javaoperatorsdk.operator.processing.KubernetesResourceUtils.getVersion; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** - * Event handler that makes sure that events are processed in a "single threaded" way per resource UID, while buffering - * events which are received during an execution. + * Event handler that makes sure that events are processed in a "single threaded" way per resource + * UID, while buffering events which are received during an execution. */ - public class DefaultEventHandler implements EventHandler { - private final static Logger log = LoggerFactory.getLogger(DefaultEventHandler.class); - - private final CustomResourceCache customResourceCache; - private final EventBuffer eventBuffer; - private final Set underProcessing = new HashSet<>(); - private final ScheduledThreadPoolExecutor executor; - private final EventDispatcher eventDispatcher; - private DefaultEventSourceManager defaultEventSourceManager; - - private final ReentrantLock lock = new ReentrantLock(); - - public DefaultEventHandler(CustomResourceCache customResourceCache, EventDispatcher eventDispatcher, String relatedControllerName) { - this.customResourceCache = customResourceCache; - this.eventDispatcher = eventDispatcher; - eventBuffer = new EventBuffer(); - executor = new ScheduledThreadPoolExecutor(5, new ThreadFactory() { - @Override - public Thread newThread(Runnable runnable) { + private static final Logger log = LoggerFactory.getLogger(DefaultEventHandler.class); + + private final CustomResourceCache customResourceCache; + private final EventBuffer eventBuffer; + private final Set underProcessing = new HashSet<>(); + private final ScheduledThreadPoolExecutor executor; + private final EventDispatcher eventDispatcher; + private DefaultEventSourceManager defaultEventSourceManager; + + private final ReentrantLock lock = new ReentrantLock(); + + public DefaultEventHandler( + CustomResourceCache customResourceCache, + EventDispatcher eventDispatcher, + String relatedControllerName) { + this.customResourceCache = customResourceCache; + this.eventDispatcher = eventDispatcher; + eventBuffer = new EventBuffer(); + executor = + new ScheduledThreadPoolExecutor( + 5, + new ThreadFactory() { + @Override + public Thread newThread(Runnable runnable) { return new Thread(runnable, "EventHandler-" + relatedControllerName); - } - }); - } - - public void setDefaultEventSourceManager(DefaultEventSourceManager defaultEventSourceManager) { - this.defaultEventSourceManager = defaultEventSourceManager; + } + }); + } + + public void setDefaultEventSourceManager(DefaultEventSourceManager defaultEventSourceManager) { + this.defaultEventSourceManager = defaultEventSourceManager; + } + + @Override + public void handleEvent(Event event) { + try { + lock.lock(); + log.debug("Received event: {}", event); + eventBuffer.addEvent(event); + executeBufferedEvents(event.getRelatedCustomResourceUid()); + } finally { + lock.unlock(); } - - @Override - public void handleEvent(Event event) { - try { - lock.lock(); - log.debug("Received event: {}", event); - eventBuffer.addEvent(event); - executeBufferedEvents(event.getRelatedCustomResourceUid()); - } finally { - lock.unlock(); - } - } - - private void executeBufferedEvents(String customResourceUid) { - boolean newEventForResourceId = eventBuffer.containsEvents(customResourceUid); - boolean controllerUnderExecution = isControllerUnderExecution(customResourceUid); - Optional latestCustomResource = customResourceCache.getLatestResource(customResourceUid); - - if (!controllerUnderExecution && newEventForResourceId && latestCustomResource.isPresent()) { - setUnderExecutionProcessing(customResourceUid); - ExecutionScope executionScope = new ExecutionScope( - eventBuffer.getAndRemoveEventsForExecution(customResourceUid), - latestCustomResource.get()); - log.debug("Executing events for custom resource. Scope: {}", executionScope); - executor.execute(new ExecutionConsumer(executionScope, eventDispatcher, this)); - } else { - log.debug("Skipping executing controller for resource id: {}. Events in queue: {}." + - " Controller in execution: {}. Latest CustomResource present: {}" - , customResourceUid, newEventForResourceId, controllerUnderExecution, latestCustomResource.isPresent()); - } + } + + private void executeBufferedEvents(String customResourceUid) { + boolean newEventForResourceId = eventBuffer.containsEvents(customResourceUid); + boolean controllerUnderExecution = isControllerUnderExecution(customResourceUid); + Optional latestCustomResource = + customResourceCache.getLatestResource(customResourceUid); + + if (!controllerUnderExecution && newEventForResourceId && latestCustomResource.isPresent()) { + setUnderExecutionProcessing(customResourceUid); + ExecutionScope executionScope = + new ExecutionScope( + eventBuffer.getAndRemoveEventsForExecution(customResourceUid), + latestCustomResource.get()); + log.debug("Executing events for custom resource. Scope: {}", executionScope); + executor.execute(new ExecutionConsumer(executionScope, eventDispatcher, this)); + } else { + log.debug( + "Skipping executing controller for resource id: {}. Events in queue: {}." + + " Controller in execution: {}. Latest CustomResource present: {}", + customResourceUid, + newEventForResourceId, + controllerUnderExecution, + latestCustomResource.isPresent()); } - - void eventProcessingFinished(ExecutionScope executionScope, PostExecutionControl postExecutionControl) { - try { - lock.lock(); - log.debug("Event processing finished. Scope: {}", executionScope); - unsetUnderExecution(executionScope.getCustomResourceUid()); - if (containsCustomResourceDeletedEvent(executionScope.getEvents())) { - cleanupAfterDeletedEvent(executionScope.getCustomResourceUid()); - } else { - cacheUpdatedResourceIfChanged(executionScope, postExecutionControl); - executeBufferedEvents(executionScope.getCustomResourceUid()); - } - } finally { - lock.unlock(); - } + } + + void eventProcessingFinished( + ExecutionScope executionScope, PostExecutionControl postExecutionControl) { + try { + lock.lock(); + log.debug("Event processing finished. Scope: {}", executionScope); + unsetUnderExecution(executionScope.getCustomResourceUid()); + if (containsCustomResourceDeletedEvent(executionScope.getEvents())) { + cleanupAfterDeletedEvent(executionScope.getCustomResourceUid()); + } else { + cacheUpdatedResourceIfChanged(executionScope, postExecutionControl); + executeBufferedEvents(executionScope.getCustomResourceUid()); + } + } finally { + lock.unlock(); } - - /** - * Here we try to cache the latest resource after an update. The goal is to solve a concurrency issue we've seen: - * If an execution is finished, where we updated a custom resource, but there are other events already buffered for next - * execution, we might not get the newest custom resource from CustomResource event source in time. Thus we execute - * the next batch of events but with a non up to date CR. Here we cache the latest CustomResource from the update - * execution so we make sure its already used in the up-coming execution. - * - * Note that this is an improvement, not a bug fix. This situation can happen naturally, we just make the execution more - * efficient, and avoid questions about conflicts. - * - * Note that without the conditional locking in the cache, there is a very minor chance that we would override an - * additional change coming from a different client. - */ - private void cacheUpdatedResourceIfChanged(ExecutionScope executionScope, PostExecutionControl postExecutionControl) { - if (postExecutionControl.customResourceUpdatedDuringExecution()) { - CustomResource originalCustomResource = executionScope.getCustomResource(); - CustomResource customResourceAfterExecution = postExecutionControl.getUpdatedCustomResource().get(); - String originalResourceVersion = getVersion(originalCustomResource); - - log.debug("Trying to update resource cache from update response for resource uid: {} new version: {} old version: {}", - getUID(originalCustomResource), getVersion(customResourceAfterExecution), getVersion(originalCustomResource)); - this.customResourceCache.cacheResource(customResourceAfterExecution, customResource -> - getVersion(customResource).equals(originalResourceVersion) - && !originalResourceVersion.equals(getVersion(customResourceAfterExecution)) - ); - } + } + + /** + * Here we try to cache the latest resource after an update. The goal is to solve a concurrency + * issue we've seen: If an execution is finished, where we updated a custom resource, but there + * are other events already buffered for next execution, we might not get the newest custom + * resource from CustomResource event source in time. Thus we execute the next batch of events but + * with a non up to date CR. Here we cache the latest CustomResource from the update execution so + * we make sure its already used in the up-coming execution. + * + *

Note that this is an improvement, not a bug fix. This situation can happen naturally, we + * just make the execution more efficient, and avoid questions about conflicts. + * + *

Note that without the conditional locking in the cache, there is a very minor chance that we + * would override an additional change coming from a different client. + */ + private void cacheUpdatedResourceIfChanged( + ExecutionScope executionScope, PostExecutionControl postExecutionControl) { + if (postExecutionControl.customResourceUpdatedDuringExecution()) { + CustomResource originalCustomResource = executionScope.getCustomResource(); + CustomResource customResourceAfterExecution = + postExecutionControl.getUpdatedCustomResource().get(); + String originalResourceVersion = getVersion(originalCustomResource); + + log.debug( + "Trying to update resource cache from update response for resource uid: {} new version: {} old version: {}", + getUID(originalCustomResource), + getVersion(customResourceAfterExecution), + getVersion(originalCustomResource)); + this.customResourceCache.cacheResource( + customResourceAfterExecution, + customResource -> + getVersion(customResource).equals(originalResourceVersion) + && !originalResourceVersion.equals(getVersion(customResourceAfterExecution))); } + } - private void cleanupAfterDeletedEvent(String customResourceUid) { - defaultEventSourceManager.cleanup(customResourceUid); - eventBuffer.cleanup(customResourceUid); - customResourceCache.cleanup(customResourceUid); - } + private void cleanupAfterDeletedEvent(String customResourceUid) { + defaultEventSourceManager.cleanup(customResourceUid); + eventBuffer.cleanup(customResourceUid); + customResourceCache.cleanup(customResourceUid); + } - private boolean isControllerUnderExecution(String customResourceUid) { - return underProcessing.contains(customResourceUid); - } + private boolean isControllerUnderExecution(String customResourceUid) { + return underProcessing.contains(customResourceUid); + } - private void setUnderExecutionProcessing(String customResourceUid) { - underProcessing.add(customResourceUid); - } + private void setUnderExecutionProcessing(String customResourceUid) { + underProcessing.add(customResourceUid); + } - private void unsetUnderExecution(String customResourceUid) { - underProcessing.remove(customResourceUid); - } + private void unsetUnderExecution(String customResourceUid) { + underProcessing.remove(customResourceUid); + } } - diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/EventBuffer.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/EventBuffer.java index a7be15a934..3e8b63a36c 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/EventBuffer.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/EventBuffer.java @@ -1,30 +1,28 @@ package io.javaoperatorsdk.operator.processing; import io.javaoperatorsdk.operator.processing.event.Event; -import io.javaoperatorsdk.operator.processing.event.internal.CustomResourceEvent; - import java.util.*; class EventBuffer { - private final Map> events = new HashMap<>(); + private final Map> events = new HashMap<>(); - public void addEvent(Event event) { - String uid = event.getRelatedCustomResourceUid(); - List crEvents = events.computeIfAbsent(uid, (id) -> new ArrayList<>(1)); - crEvents.add(event); - } + public void addEvent(Event event) { + String uid = event.getRelatedCustomResourceUid(); + List crEvents = events.computeIfAbsent(uid, (id) -> new ArrayList<>(1)); + crEvents.add(event); + } - public boolean containsEvents(String customResourceId) { - return events.get(customResourceId) != null; - } + public boolean containsEvents(String customResourceId) { + return events.get(customResourceId) != null; + } - public List getAndRemoveEventsForExecution(String resourceUid) { - List crEvents = events.remove(resourceUid); - return crEvents == null ? Collections.emptyList() : crEvents; - } + public List getAndRemoveEventsForExecution(String resourceUid) { + List crEvents = events.remove(resourceUid); + return crEvents == null ? Collections.emptyList() : crEvents; + } - public void cleanup(String resourceUid) { - events.remove(resourceUid); - } + public void cleanup(String resourceUid) { + events.remove(resourceUid); + } } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/EventDispatcher.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/EventDispatcher.java index f73777df60..3246be7f47 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/EventDispatcher.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/EventDispatcher.java @@ -1,177 +1,199 @@ package io.javaoperatorsdk.operator.processing; -import io.javaoperatorsdk.operator.ControllerUtils; +import static io.javaoperatorsdk.operator.EventListUtils.containsCustomResourceDeletedEvent; +import static io.javaoperatorsdk.operator.processing.KubernetesResourceUtils.*; + import io.fabric8.kubernetes.client.CustomResource; import io.fabric8.kubernetes.client.dsl.MixedOperation; import io.fabric8.kubernetes.client.dsl.Resource; +import io.javaoperatorsdk.operator.ControllerUtils; import io.javaoperatorsdk.operator.api.*; -import io.javaoperatorsdk.operator.processing.event.Event; import io.javaoperatorsdk.operator.processing.event.EventList; import io.javaoperatorsdk.operator.processing.event.EventSourceManager; -import io.javaoperatorsdk.operator.processing.event.internal.CustomResourceEvent; +import java.util.ArrayList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.ArrayList; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import static io.javaoperatorsdk.operator.EventListUtils.containsCustomResourceDeletedEvent; -import static io.javaoperatorsdk.operator.processing.KubernetesResourceUtils.*; - /** * Dispatches events to the Controller and handles Finalizers for a single type of Custom Resource. */ public class EventDispatcher { - private final static Logger log = LoggerFactory.getLogger(EventDispatcher.class); - - private final ResourceController controller; - private final String resourceFinalizer; - private final CustomResourceFacade customResourceFacade; - private EventSourceManager eventSourceManager; - - public EventDispatcher(ResourceController controller, - String finalizer, - CustomResourceFacade customResourceFacade) { - this.controller = controller; - this.customResourceFacade = customResourceFacade; - this.resourceFinalizer = finalizer; + private static final Logger log = LoggerFactory.getLogger(EventDispatcher.class); + + private final ResourceController controller; + private final String resourceFinalizer; + private final CustomResourceFacade customResourceFacade; + private EventSourceManager eventSourceManager; + + public EventDispatcher( + ResourceController controller, String finalizer, CustomResourceFacade customResourceFacade) { + this.controller = controller; + this.customResourceFacade = customResourceFacade; + this.resourceFinalizer = finalizer; + } + + public void setEventSourceManager(EventSourceManager eventSourceManager) { + this.eventSourceManager = eventSourceManager; + } + + public PostExecutionControl handleEvent(ExecutionScope event) { + try { + return handDispatch(event); + } catch (RuntimeException e) { + log.error("Error during event processing {} failed.", event, e); + return PostExecutionControl.defaultDispatch(); } - - public void setEventSourceManager(EventSourceManager eventSourceManager) { - this.eventSourceManager = eventSourceManager; + } + + private PostExecutionControl handDispatch(ExecutionScope executionScope) { + CustomResource resource = executionScope.getCustomResource(); + log.debug( + "Handling events: {} for resource {}", executionScope.getEvents(), resource.getMetadata()); + + if (containsCustomResourceDeletedEvent(executionScope.getEvents())) { + log.debug( + "Skipping dispatch processing because of a Delete event: {} with version: {}", + getUID(resource), + getVersion(resource)); + return PostExecutionControl.defaultDispatch(); } - - public PostExecutionControl handleEvent(ExecutionScope event) { - try { - return handDispatch(event); - } catch (RuntimeException e) { - log.error("Error during event processing {} failed.", event, e); - return PostExecutionControl.defaultDispatch(); - } + if ((markedForDeletion(resource) + && !ControllerUtils.hasGivenFinalizer(resource, resourceFinalizer))) { + log.debug( + "Skipping event dispatching since its marked for deletion but has no finalizer: {}", + executionScope); + return PostExecutionControl.defaultDispatch(); } - - private PostExecutionControl handDispatch(ExecutionScope executionScope) { - CustomResource resource = executionScope.getCustomResource(); - log.debug("Handling events: {} for resource {}", executionScope.getEvents(), resource.getMetadata()); - - if (containsCustomResourceDeletedEvent(executionScope.getEvents())) { - log.debug("Skipping dispatch processing because of a Delete event: {} with version: {}", - getUID(resource), getVersion(resource)); - return PostExecutionControl.defaultDispatch(); - } - if ((markedForDeletion(resource) && !ControllerUtils.hasGivenFinalizer(resource, resourceFinalizer))) { - log.debug("Skipping event dispatching since its marked for deletion but has no finalizer: {}", executionScope); - return PostExecutionControl.defaultDispatch(); - } - Context context = new DefaultContext(eventSourceManager, new EventList(executionScope.getEvents())); - if (markedForDeletion(resource)) { - return handleDelete(resource, context); - } else { - return handleCreateOrUpdate(executionScope, resource, context); - } + Context context = + new DefaultContext(eventSourceManager, new EventList(executionScope.getEvents())); + if (markedForDeletion(resource)) { + return handleDelete(resource, context); + } else { + return handleCreateOrUpdate(executionScope, resource, context); } - - private PostExecutionControl handleCreateOrUpdate(ExecutionScope executionScope, CustomResource resource, Context context) { - if (!ControllerUtils.hasGivenFinalizer(resource, resourceFinalizer) && !markedForDeletion(resource)) { - /* We always add the finalizer if missing and not marked for deletion. - We execute the controller processing only for processing the event sent as a results - of the finalizer add. This will make sure that the resources are not created before - there is a finalizer. - */ - updateCustomResourceWithFinalizer(resource); - return PostExecutionControl.onlyFinalizerAdded(); - } else { - log.debug("Executing createOrUpdate for resource {} with version: {} with execution scope: {}", - getUID(resource), getVersion(resource), executionScope); - UpdateControl updateControl = controller.createOrUpdateResource(resource, context); - CustomResource updatedCustomResource = null; - if (updateControl.isUpdateStatusSubResource()) { - updatedCustomResource = customResourceFacade.updateStatus(updateControl.getCustomResource()); - } else if (updateControl.isUpdateCustomResource()) { - updatedCustomResource = updateCustomResource(updateControl.getCustomResource()); - } - if (updatedCustomResource != null) { - return PostExecutionControl.customResourceUpdated(updatedCustomResource); - } else { - return PostExecutionControl.defaultDispatch(); - } - } + } + + private PostExecutionControl handleCreateOrUpdate( + ExecutionScope executionScope, CustomResource resource, Context context) { + if (!ControllerUtils.hasGivenFinalizer(resource, resourceFinalizer) + && !markedForDeletion(resource)) { + /* We always add the finalizer if missing and not marked for deletion. + We execute the controller processing only for processing the event sent as a results + of the finalizer add. This will make sure that the resources are not created before + there is a finalizer. + */ + updateCustomResourceWithFinalizer(resource); + return PostExecutionControl.onlyFinalizerAdded(); + } else { + log.debug( + "Executing createOrUpdate for resource {} with version: {} with execution scope: {}", + getUID(resource), + getVersion(resource), + executionScope); + UpdateControl updateControl = + controller.createOrUpdateResource(resource, context); + CustomResource updatedCustomResource = null; + if (updateControl.isUpdateStatusSubResource()) { + updatedCustomResource = + customResourceFacade.updateStatus(updateControl.getCustomResource()); + } else if (updateControl.isUpdateCustomResource()) { + updatedCustomResource = updateCustomResource(updateControl.getCustomResource()); + } + if (updatedCustomResource != null) { + return PostExecutionControl.customResourceUpdated(updatedCustomResource); + } else { + return PostExecutionControl.defaultDispatch(); + } } - - - private PostExecutionControl handleDelete(CustomResource resource, Context context) { - log.debug("Executing delete for resource: {} with version: {}", getUID(resource), getVersion(resource)); - DeleteControl deleteControl = controller.deleteResource(resource, context); - boolean hasFinalizer = ControllerUtils.hasGivenFinalizer(resource, resourceFinalizer); - if (deleteControl == DeleteControl.DEFAULT_DELETE && hasFinalizer) { - CustomResource customResource = removeFinalizer(resource); - return PostExecutionControl.customResourceUpdated(customResource); - } else { - log.debug("Skipping finalizer remove for resource: {} with version: {}. delete control: {}, hasFinalizer: {} ", - getUID(resource), getVersion(resource), deleteControl, hasFinalizer); - return PostExecutionControl.defaultDispatch(); - } + } + + private PostExecutionControl handleDelete(CustomResource resource, Context context) { + log.debug( + "Executing delete for resource: {} with version: {}", + getUID(resource), + getVersion(resource)); + DeleteControl deleteControl = controller.deleteResource(resource, context); + boolean hasFinalizer = ControllerUtils.hasGivenFinalizer(resource, resourceFinalizer); + if (deleteControl == DeleteControl.DEFAULT_DELETE && hasFinalizer) { + CustomResource customResource = removeFinalizer(resource); + return PostExecutionControl.customResourceUpdated(customResource); + } else { + log.debug( + "Skipping finalizer remove for resource: {} with version: {}. delete control: {}, hasFinalizer: {} ", + getUID(resource), + getVersion(resource), + deleteControl, + hasFinalizer); + return PostExecutionControl.defaultDispatch(); } - - private void updateCustomResourceWithFinalizer(CustomResource resource) { - log.debug("Adding finalizer for resource: {} version: {}", getUID(resource), - getVersion(resource)); - addFinalizerIfNotPresent(resource); - replace(resource); + } + + private void updateCustomResourceWithFinalizer(CustomResource resource) { + log.debug( + "Adding finalizer for resource: {} version: {}", getUID(resource), getVersion(resource)); + addFinalizerIfNotPresent(resource); + replace(resource); + } + + private CustomResource updateCustomResource(CustomResource resource) { + log.debug("Updating resource: {} with version: {}", getUID(resource), getVersion(resource)); + log.trace("Resource before update: {}", resource); + return replace(resource); + } + + private CustomResource removeFinalizer(CustomResource resource) { + log.debug( + "Removing finalizer on resource: {} with version: {}", + getUID(resource), + getVersion(resource)); + resource.getMetadata().getFinalizers().remove(resourceFinalizer); + return customResourceFacade.replaceWithLock(resource); + } + + private CustomResource replace(CustomResource resource) { + log.debug( + "Trying to replace resource {}, version: {}", + resource.getMetadata().getName(), + resource.getMetadata().getResourceVersion()); + return customResourceFacade.replaceWithLock(resource); + } + + private void addFinalizerIfNotPresent(CustomResource resource) { + if (!ControllerUtils.hasGivenFinalizer(resource, resourceFinalizer) + && !markedForDeletion(resource)) { + log.info("Adding finalizer to {}", resource.getMetadata()); + if (resource.getMetadata().getFinalizers() == null) { + resource.getMetadata().setFinalizers(new ArrayList<>(1)); + } + resource.getMetadata().getFinalizers().add(resourceFinalizer); } + } - private CustomResource updateCustomResource(CustomResource resource) { - log.debug("Updating resource: {} with version: {}", getUID(resource), - getVersion(resource)); - log.trace("Resource before update: {}", resource); - return replace(resource); - } + // created to support unit testing + public static class CustomResourceFacade { + private final MixedOperation> resourceOperation; - private CustomResource removeFinalizer(CustomResource resource) { - log.debug("Removing finalizer on resource: {} with version: {}", getUID(resource), getVersion(resource)); - resource.getMetadata().getFinalizers().remove(resourceFinalizer); - return customResourceFacade.replaceWithLock(resource); + public CustomResourceFacade( + MixedOperation> resourceOperation) { + this.resourceOperation = resourceOperation; } - private CustomResource replace(CustomResource resource) { - log.debug("Trying to replace resource {}, version: {}", resource.getMetadata().getName(), resource.getMetadata().getResourceVersion()); - return customResourceFacade.replaceWithLock(resource); + public CustomResource updateStatus(CustomResource resource) { + log.trace("Updating status for resource: {}", resource); + return resourceOperation + .inNamespace(resource.getMetadata().getNamespace()) + .withName(resource.getMetadata().getName()) + .updateStatus(resource); } - private void addFinalizerIfNotPresent(CustomResource resource) { - if (!ControllerUtils.hasGivenFinalizer(resource, resourceFinalizer) && !markedForDeletion(resource)) { - log.info("Adding finalizer to {}", resource.getMetadata()); - if (resource.getMetadata().getFinalizers() == null) { - resource.getMetadata().setFinalizers(new ArrayList<>(1)); - } - resource.getMetadata().getFinalizers().add(resourceFinalizer); - } - } - - // created to support unit testing - public static class CustomResourceFacade { - - private final MixedOperation> resourceOperation; - - public CustomResourceFacade(MixedOperation> resourceOperation) { - this.resourceOperation = resourceOperation; - } - - public CustomResource updateStatus(CustomResource resource) { - log.trace("Updating status for resource: {}", resource); - return resourceOperation.inNamespace(resource.getMetadata().getNamespace()) - .withName(resource.getMetadata().getName()) - .updateStatus(resource); - } - - public CustomResource replaceWithLock(CustomResource resource) { - return resourceOperation.inNamespace(resource.getMetadata().getNamespace()) - .withName(resource.getMetadata().getName()) - .lockResourceVersion(resource.getMetadata().getResourceVersion()) - .replace(resource); - } + public CustomResource replaceWithLock(CustomResource resource) { + return resourceOperation + .inNamespace(resource.getMetadata().getNamespace()) + .withName(resource.getMetadata().getName()) + .lockResourceVersion(resource.getMetadata().getResourceVersion()) + .replace(resource); } + } } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/ExecutionConsumer.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/ExecutionConsumer.java index 58c5767432..45aa4b32d0 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/ExecutionConsumer.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/ExecutionConsumer.java @@ -4,23 +4,25 @@ import org.slf4j.LoggerFactory; class ExecutionConsumer implements Runnable { - - private final static Logger log = LoggerFactory.getLogger(ExecutionConsumer.class); - private final ExecutionScope executionScope; - private final EventDispatcher eventDispatcher; - private final DefaultEventHandler defaultEventHandler; + private static final Logger log = LoggerFactory.getLogger(ExecutionConsumer.class); - ExecutionConsumer(ExecutionScope executionScope, EventDispatcher eventDispatcher, DefaultEventHandler defaultEventHandler) { - this.executionScope = executionScope; - this.eventDispatcher = eventDispatcher; - this.defaultEventHandler = defaultEventHandler; - } + private final ExecutionScope executionScope; + private final EventDispatcher eventDispatcher; + private final DefaultEventHandler defaultEventHandler; - @Override - public void run() { - PostExecutionControl postExecutionControl = eventDispatcher.handleEvent(executionScope); - defaultEventHandler.eventProcessingFinished(executionScope, postExecutionControl); - } + ExecutionConsumer( + ExecutionScope executionScope, + EventDispatcher eventDispatcher, + DefaultEventHandler defaultEventHandler) { + this.executionScope = executionScope; + this.eventDispatcher = eventDispatcher; + this.defaultEventHandler = defaultEventHandler; + } + @Override + public void run() { + PostExecutionControl postExecutionControl = eventDispatcher.handleEvent(executionScope); + defaultEventHandler.eventProcessingFinished(executionScope, postExecutionControl); + } } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/ExecutionScope.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/ExecutionScope.java index c557daa999..a4b65f5f95 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/ExecutionScope.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/ExecutionScope.java @@ -1,40 +1,41 @@ package io.javaoperatorsdk.operator.processing; - import io.fabric8.kubernetes.client.CustomResource; import io.javaoperatorsdk.operator.processing.event.Event; - import java.util.List; public class ExecutionScope { - private List events; - // the latest custom resource from cache - private CustomResource customResource; - - public ExecutionScope(List list, CustomResource customResource) { - this.events = list; - this.customResource = customResource; - } - - public List getEvents() { - return events; - } - - public CustomResource getCustomResource() { - return customResource; - } - - public String getCustomResourceUid() { - return customResource.getMetadata().getUid(); - } - - @Override - public String toString() { - return "ExecutionScope{" + - "events=" + events + - ", customResource uid: " + customResource.getMetadata().getUid() + - ", version: " + customResource.getMetadata().getResourceVersion() + - '}'; - } + private List events; + // the latest custom resource from cache + private CustomResource customResource; + + public ExecutionScope(List list, CustomResource customResource) { + this.events = list; + this.customResource = customResource; + } + + public List getEvents() { + return events; + } + + public CustomResource getCustomResource() { + return customResource; + } + + public String getCustomResourceUid() { + return customResource.getMetadata().getUid(); + } + + @Override + public String toString() { + return "ExecutionScope{" + + "events=" + + events + + ", customResource uid: " + + customResource.getMetadata().getUid() + + ", version: " + + customResource.getMetadata().getResourceVersion() + + '}'; + } } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/KubernetesResourceUtils.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/KubernetesResourceUtils.java index 4be0dca630..2fd434ce5f 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/KubernetesResourceUtils.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/KubernetesResourceUtils.java @@ -5,16 +5,16 @@ public class KubernetesResourceUtils { - public static String getUID(HasMetadata customResource) { - return customResource.getMetadata().getUid(); - } + public static String getUID(HasMetadata customResource) { + return customResource.getMetadata().getUid(); + } - public static String getVersion(HasMetadata customResource) { - return customResource.getMetadata().getResourceVersion(); - } + public static String getVersion(HasMetadata customResource) { + return customResource.getMetadata().getResourceVersion(); + } - - public static boolean markedForDeletion(CustomResource resource) { - return resource.getMetadata().getDeletionTimestamp() != null && !resource.getMetadata().getDeletionTimestamp().isEmpty(); - } + public static boolean markedForDeletion(CustomResource resource) { + return resource.getMetadata().getDeletionTimestamp() != null + && !resource.getMetadata().getDeletionTimestamp().isEmpty(); + } } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/PostExecutionControl.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/PostExecutionControl.java index aa4dc6df93..0e60933a81 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/PostExecutionControl.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/PostExecutionControl.java @@ -1,42 +1,40 @@ package io.javaoperatorsdk.operator.processing; import io.fabric8.kubernetes.client.CustomResource; -import io.javaoperatorsdk.operator.api.UpdateControl; - import java.util.Optional; public final class PostExecutionControl { - private final boolean onlyFinalizerHandled; + private final boolean onlyFinalizerHandled; - private final CustomResource updatedCustomResource; + private final CustomResource updatedCustomResource; - private PostExecutionControl(boolean onlyFinalizerHandled, CustomResource updatedCustomResource) { - this.onlyFinalizerHandled = onlyFinalizerHandled; - this.updatedCustomResource = updatedCustomResource; - } + private PostExecutionControl(boolean onlyFinalizerHandled, CustomResource updatedCustomResource) { + this.onlyFinalizerHandled = onlyFinalizerHandled; + this.updatedCustomResource = updatedCustomResource; + } - public static PostExecutionControl onlyFinalizerAdded() { - return new PostExecutionControl(true, null); - } + public static PostExecutionControl onlyFinalizerAdded() { + return new PostExecutionControl(true, null); + } - public static PostExecutionControl defaultDispatch() { - return new PostExecutionControl(false, null); - } + public static PostExecutionControl defaultDispatch() { + return new PostExecutionControl(false, null); + } - public static PostExecutionControl customResourceUpdated(CustomResource updatedCustomResource) { - return new PostExecutionControl(false, updatedCustomResource); - } + public static PostExecutionControl customResourceUpdated(CustomResource updatedCustomResource) { + return new PostExecutionControl(false, updatedCustomResource); + } - public boolean isOnlyFinalizerHandled() { - return onlyFinalizerHandled; - } + public boolean isOnlyFinalizerHandled() { + return onlyFinalizerHandled; + } - public Optional getUpdatedCustomResource() { - return Optional.ofNullable(updatedCustomResource); - } + public Optional getUpdatedCustomResource() { + return Optional.ofNullable(updatedCustomResource); + } - public boolean customResourceUpdatedDuringExecution() { - return updatedCustomResource != null; - } + public boolean customResourceUpdatedDuringExecution() { + return updatedCustomResource != null; + } } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/annotation/AccumulativeMappingWriter.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/annotation/AccumulativeMappingWriter.java index 0a035f2d82..2242dcb40a 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/annotation/AccumulativeMappingWriter.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/annotation/AccumulativeMappingWriter.java @@ -1,7 +1,5 @@ package io.javaoperatorsdk.operator.processing.annotation; -import javax.annotation.processing.ProcessingEnvironment; -import javax.tools.StandardLocation; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; @@ -9,58 +7,64 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; +import javax.annotation.processing.ProcessingEnvironment; +import javax.tools.StandardLocation; class AccumulativeMappingWriter { - private Map mappings = new ConcurrentHashMap<>(); - private final String resourcePath; - private final ProcessingEnvironment processingEnvironment; + private Map mappings = new ConcurrentHashMap<>(); + private final String resourcePath; + private final ProcessingEnvironment processingEnvironment; - public AccumulativeMappingWriter(String resourcePath, ProcessingEnvironment processingEnvironment) { - this.resourcePath = resourcePath; - this.processingEnvironment = processingEnvironment; - } - - public AccumulativeMappingWriter loadExistingMappings() { - try { - final var readonlyResource = processingEnvironment - .getFiler() - .getResource(StandardLocation.CLASS_OUTPUT, "", resourcePath); + public AccumulativeMappingWriter( + String resourcePath, ProcessingEnvironment processingEnvironment) { + this.resourcePath = resourcePath; + this.processingEnvironment = processingEnvironment; + } - final var bufferedReader = new BufferedReader(new InputStreamReader(readonlyResource.openInputStream())); - final var existingLines = bufferedReader - .lines() - .map(l -> l.split(",")) - .collect(Collectors.toMap(parts -> parts[0], parts -> parts[1])); - mappings.putAll(existingLines); - } catch (IOException e) { - } - return this; - } + public AccumulativeMappingWriter loadExistingMappings() { + try { + final var readonlyResource = + processingEnvironment + .getFiler() + .getResource(StandardLocation.CLASS_OUTPUT, "", resourcePath); - public AccumulativeMappingWriter add(String key, String value) { - this.mappings.put(key, value); - return this; + final var bufferedReader = + new BufferedReader(new InputStreamReader(readonlyResource.openInputStream())); + final var existingLines = + bufferedReader + .lines() + .map(l -> l.split(",")) + .collect(Collectors.toMap(parts -> parts[0], parts -> parts[1])); + mappings.putAll(existingLines); + } catch (IOException e) { } + return this; + } - public void flush() { - PrintWriter printWriter = null; - try { - final var resource = processingEnvironment - .getFiler() - .createResource(StandardLocation.CLASS_OUTPUT, "", resourcePath); - printWriter = new PrintWriter(resource.openOutputStream()); + public AccumulativeMappingWriter add(String key, String value) { + this.mappings.put(key, value); + return this; + } + public void flush() { + PrintWriter printWriter = null; + try { + final var resource = + processingEnvironment + .getFiler() + .createResource(StandardLocation.CLASS_OUTPUT, "", resourcePath); + printWriter = new PrintWriter(resource.openOutputStream()); - for (Map.Entry entry : mappings.entrySet()) { - printWriter.println(entry.getKey() + "," + entry.getValue()); - } - } catch (IOException e) { - e.printStackTrace(); - throw new RuntimeException(e); - } finally { - if (printWriter != null) { - printWriter.close(); - } - } + for (Map.Entry entry : mappings.entrySet()) { + printWriter.println(entry.getKey() + "," + entry.getValue()); + } + } catch (IOException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } finally { + if (printWriter != null) { + printWriter.close(); + } } + } } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/annotation/ControllerAnnotationProcessor.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/annotation/ControllerAnnotationProcessor.java index eaa9cc9a92..bddf800ecb 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/annotation/ControllerAnnotationProcessor.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/annotation/ControllerAnnotationProcessor.java @@ -1,11 +1,19 @@ package io.javaoperatorsdk.operator.processing.annotation; +import static io.javaoperatorsdk.operator.ControllerUtils.CONTROLLERS_RESOURCE_PATH; +import static io.javaoperatorsdk.operator.ControllerUtils.DONEABLES_RESOURCE_PATH; + import com.google.auto.service.AutoService; import com.squareup.javapoet.*; import io.fabric8.kubernetes.api.builder.Function; import io.fabric8.kubernetes.client.CustomResourceDoneable; import io.javaoperatorsdk.operator.api.ResourceController; - +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; import javax.annotation.processing.*; import javax.lang.model.SourceVersion; import javax.lang.model.element.*; @@ -14,141 +22,148 @@ import javax.lang.model.type.TypeMirror; import javax.tools.Diagnostic; import javax.tools.JavaFileObject; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; -import static io.javaoperatorsdk.operator.ControllerUtils.CONTROLLERS_RESOURCE_PATH; -import static io.javaoperatorsdk.operator.ControllerUtils.DONEABLES_RESOURCE_PATH; - -@SupportedAnnotationTypes( - "io.javaoperatorsdk.operator.api.Controller") +@SupportedAnnotationTypes("io.javaoperatorsdk.operator.api.Controller") @SupportedSourceVersion(SourceVersion.RELEASE_8) @AutoService(Processor.class) public class ControllerAnnotationProcessor extends AbstractProcessor { - private AccumulativeMappingWriter controllersResourceWriter; - private AccumulativeMappingWriter doneablesResourceWriter; - private Set generatedDoneableClassFiles = new HashSet<>(); - - @Override - public synchronized void init(ProcessingEnvironment processingEnv) { - super.init(processingEnv); - controllersResourceWriter = new AccumulativeMappingWriter(CONTROLLERS_RESOURCE_PATH, processingEnv) - .loadExistingMappings(); - doneablesResourceWriter = new AccumulativeMappingWriter(DONEABLES_RESOURCE_PATH, processingEnv) - .loadExistingMappings(); + private AccumulativeMappingWriter controllersResourceWriter; + private AccumulativeMappingWriter doneablesResourceWriter; + private Set generatedDoneableClassFiles = new HashSet<>(); + + @Override + public synchronized void init(ProcessingEnvironment processingEnv) { + super.init(processingEnv); + controllersResourceWriter = + new AccumulativeMappingWriter(CONTROLLERS_RESOURCE_PATH, processingEnv) + .loadExistingMappings(); + doneablesResourceWriter = + new AccumulativeMappingWriter(DONEABLES_RESOURCE_PATH, processingEnv) + .loadExistingMappings(); + } + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + try { + for (TypeElement annotation : annotations) { + Set annotatedElements = roundEnv.getElementsAnnotatedWith(annotation); + annotatedElements.stream() + .filter(element -> element.getKind().equals(ElementKind.CLASS)) + .map(e -> (TypeElement) e) + .forEach(e -> this.generateDoneableClass(e)); + } + } finally { + if (roundEnv.processingOver()) { + controllersResourceWriter.flush(); + doneablesResourceWriter.flush(); + } } - - @Override - public boolean process(Set annotations, RoundEnvironment roundEnv) { - try { - for (TypeElement annotation : annotations) { - Set annotatedElements - = roundEnv.getElementsAnnotatedWith(annotation); - annotatedElements.stream().filter(element -> element.getKind().equals(ElementKind.CLASS)) - .map(e -> (TypeElement) e) - .forEach(e -> this.generateDoneableClass(e)); - } - } finally { - if (roundEnv.processingOver()) { - controllersResourceWriter.flush(); - doneablesResourceWriter.flush(); - } - } - return true; - } - - private void generateDoneableClass(TypeElement controllerClassSymbol) { - try { - final TypeMirror resourceType = findResourceType(controllerClassSymbol); - - TypeElement customerResourceTypeElement = processingEnv - .getElementUtils() - .getTypeElement(resourceType.toString()); - - final String doneableClassName = customerResourceTypeElement.getSimpleName() + "Doneable"; - final String destinationClassFileName = customerResourceTypeElement.getQualifiedName() + "Doneable"; - final TypeName customResourceType = TypeName.get(resourceType); - - if (!generatedDoneableClassFiles.add(destinationClassFileName)) { - processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, - String.format( - "%s already exists! adding the mapping to the %s", - destinationClassFileName, - CONTROLLERS_RESOURCE_PATH) - ); - controllersResourceWriter.add(controllerClassSymbol.getQualifiedName().toString(), customResourceType.toString()); - return; - } - JavaFileObject builderFile = processingEnv.getFiler() - .createSourceFile(destinationClassFileName); - - try (PrintWriter out = new PrintWriter(builderFile.openWriter())) { - controllersResourceWriter.add(controllerClassSymbol.getQualifiedName().toString(), customResourceType.toString()); - final MethodSpec constructor = MethodSpec.constructorBuilder() - .addModifiers(Modifier.PUBLIC) - .addParameter(customResourceType, "resource") - .addParameter(Function.class, "function") - .addStatement("super(resource,function)") - .build(); - - - final TypeSpec typeSpec = TypeSpec.classBuilder(doneableClassName) - .superclass(ParameterizedTypeName.get(ClassName.get(CustomResourceDoneable.class), customResourceType)) - .addModifiers(Modifier.PUBLIC) - .addMethod(constructor) - .build(); - - final PackageElement packageElement = processingEnv.getElementUtils().getPackageOf(customerResourceTypeElement); - final var packageQualifiedName = packageElement.getQualifiedName().toString(); - JavaFile file = JavaFile.builder(packageQualifiedName, typeSpec) - .build(); - file.writeTo(out); - doneablesResourceWriter.add(customResourceType.toString(), makeQualifiedClassName(packageQualifiedName, doneableClassName)); - } - } catch (Exception ioException) { - ioException.printStackTrace(); - } + return true; + } + + private void generateDoneableClass(TypeElement controllerClassSymbol) { + try { + final TypeMirror resourceType = findResourceType(controllerClassSymbol); + + TypeElement customerResourceTypeElement = + processingEnv.getElementUtils().getTypeElement(resourceType.toString()); + + final String doneableClassName = customerResourceTypeElement.getSimpleName() + "Doneable"; + final String destinationClassFileName = + customerResourceTypeElement.getQualifiedName() + "Doneable"; + final TypeName customResourceType = TypeName.get(resourceType); + + if (!generatedDoneableClassFiles.add(destinationClassFileName)) { + processingEnv + .getMessager() + .printMessage( + Diagnostic.Kind.NOTE, + String.format( + "%s already exists! adding the mapping to the %s", + destinationClassFileName, CONTROLLERS_RESOURCE_PATH)); + controllersResourceWriter.add( + controllerClassSymbol.getQualifiedName().toString(), customResourceType.toString()); + return; + } + JavaFileObject builderFile = + processingEnv.getFiler().createSourceFile(destinationClassFileName); + + try (PrintWriter out = new PrintWriter(builderFile.openWriter())) { + controllersResourceWriter.add( + controllerClassSymbol.getQualifiedName().toString(), customResourceType.toString()); + final MethodSpec constructor = + MethodSpec.constructorBuilder() + .addModifiers(Modifier.PUBLIC) + .addParameter(customResourceType, "resource") + .addParameter(Function.class, "function") + .addStatement("super(resource,function)") + .build(); + + final TypeSpec typeSpec = + TypeSpec.classBuilder(doneableClassName) + .superclass( + ParameterizedTypeName.get( + ClassName.get(CustomResourceDoneable.class), customResourceType)) + .addModifiers(Modifier.PUBLIC) + .addMethod(constructor) + .build(); + + final PackageElement packageElement = + processingEnv.getElementUtils().getPackageOf(customerResourceTypeElement); + final var packageQualifiedName = packageElement.getQualifiedName().toString(); + JavaFile file = JavaFile.builder(packageQualifiedName, typeSpec).build(); + file.writeTo(out); + doneablesResourceWriter.add( + customResourceType.toString(), + makeQualifiedClassName(packageQualifiedName, doneableClassName)); + } + } catch (Exception ioException) { + ioException.printStackTrace(); } - - private TypeMirror findResourceType(TypeElement controllerClassSymbol) throws Exception { - try { - final DeclaredType controllerType = collectAllInterfaces(controllerClassSymbol) - .stream() - .filter(i -> i.toString() - .startsWith(ResourceController.class.getCanonicalName()) - ) - .findFirst() - .orElseThrow(() -> new Exception("ResourceController is not implemented by " + controllerClassSymbol.toString())); - return controllerType.getTypeArguments().get(0); - } catch (Exception e) { - e.printStackTrace(); - return null; - } + } + + private TypeMirror findResourceType(TypeElement controllerClassSymbol) throws Exception { + try { + final DeclaredType controllerType = + collectAllInterfaces(controllerClassSymbol).stream() + .filter(i -> i.toString().startsWith(ResourceController.class.getCanonicalName())) + .findFirst() + .orElseThrow( + () -> + new Exception( + "ResourceController is not implemented by " + + controllerClassSymbol.toString())); + return controllerType.getTypeArguments().get(0); + } catch (Exception e) { + e.printStackTrace(); + return null; } - - private List collectAllInterfaces(TypeElement element) { - try { - List interfaces = new ArrayList<>(element.getInterfaces()).stream().map(t -> (DeclaredType) t).collect(Collectors.toList()); - TypeElement superclass = ((TypeElement) ((DeclaredType) element.getSuperclass()).asElement()); - while (superclass.getSuperclass().getKind() != TypeKind.NONE) { - interfaces.addAll(superclass.getInterfaces().stream().map(t -> (DeclaredType) t).collect(Collectors.toList())); - superclass = ((TypeElement) ((DeclaredType) superclass.getSuperclass()).asElement()); - } - return interfaces; - } catch (Exception e) { - e.printStackTrace(); - return null; - } + } + + private List collectAllInterfaces(TypeElement element) { + try { + List interfaces = + new ArrayList<>(element.getInterfaces()) + .stream().map(t -> (DeclaredType) t).collect(Collectors.toList()); + TypeElement superclass = ((TypeElement) ((DeclaredType) element.getSuperclass()).asElement()); + while (superclass.getSuperclass().getKind() != TypeKind.NONE) { + interfaces.addAll( + superclass.getInterfaces().stream() + .map(t -> (DeclaredType) t) + .collect(Collectors.toList())); + superclass = ((TypeElement) ((DeclaredType) superclass.getSuperclass()).asElement()); + } + return interfaces; + } catch (Exception e) { + e.printStackTrace(); + return null; } + } - private String makeQualifiedClassName(String packageName, String className) { - if (packageName.equals("")) { - return className; - } - return packageName + "." + className; + private String makeQualifiedClassName(String packageName, String className) { + if (packageName.equals("")) { + return className; } + return packageName + "." + className; + } } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/AbstractEvent.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/AbstractEvent.java index 31a2aebd60..79bd68e4d5 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/AbstractEvent.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/AbstractEvent.java @@ -1,31 +1,34 @@ package io.javaoperatorsdk.operator.processing.event; -public abstract class AbstractEvent implements Event{ - - private final String relatedCustomResourceUid; - - private final EventSource eventSource; - - public AbstractEvent(String relatedCustomResourceUid, EventSource eventSource) { - this.relatedCustomResourceUid = relatedCustomResourceUid; - this.eventSource = eventSource; - } - - @Override - public String getRelatedCustomResourceUid() { - return relatedCustomResourceUid; - } - - @Override - public EventSource getEventSource() { - return eventSource; - } - - @Override - public String toString() { - return "{ class=" + this.getClass().getName() + - ", relatedCustomResourceUid=" + relatedCustomResourceUid + - ", eventSource=" + eventSource + " }"; - - } +public abstract class AbstractEvent implements Event { + + private final String relatedCustomResourceUid; + + private final EventSource eventSource; + + public AbstractEvent(String relatedCustomResourceUid, EventSource eventSource) { + this.relatedCustomResourceUid = relatedCustomResourceUid; + this.eventSource = eventSource; + } + + @Override + public String getRelatedCustomResourceUid() { + return relatedCustomResourceUid; + } + + @Override + public EventSource getEventSource() { + return eventSource; + } + + @Override + public String toString() { + return "{ class=" + + this.getClass().getName() + + ", relatedCustomResourceUid=" + + relatedCustomResourceUid + + ", eventSource=" + + eventSource + + " }"; + } } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/AbstractEventSource.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/AbstractEventSource.java index 8dfbbb91d3..da3d4866c8 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/AbstractEventSource.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/AbstractEventSource.java @@ -1,18 +1,14 @@ package io.javaoperatorsdk.operator.processing.event; -import io.fabric8.kubernetes.client.CustomResource; - public abstract class AbstractEventSource implements EventSource { - protected EventHandler eventHandler; - - @Override - public void setEventHandler(EventHandler eventHandler) { - this.eventHandler = eventHandler; - } + protected EventHandler eventHandler; - @Override - public void eventSourceDeRegisteredForResource(String customResourceUid) { - } + @Override + public void setEventHandler(EventHandler eventHandler) { + this.eventHandler = eventHandler; + } + @Override + public void eventSourceDeRegisteredForResource(String customResourceUid) {} } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/DefaultEventSourceManager.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/DefaultEventSourceManager.java index b1babc7f70..abd321f579 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/DefaultEventSourceManager.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/DefaultEventSourceManager.java @@ -2,72 +2,77 @@ import io.javaoperatorsdk.operator.processing.DefaultEventHandler; import io.javaoperatorsdk.operator.processing.event.internal.CustomResourceEventSource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantLock; -import java.util.function.Supplier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class DefaultEventSourceManager implements EventSourceManager { - private static final Logger log = LoggerFactory.getLogger(DefaultEventSourceManager.class); + private static final Logger log = LoggerFactory.getLogger(DefaultEventSourceManager.class); - private final ReentrantLock lock = new ReentrantLock(); - private Map eventSources = new ConcurrentHashMap<>(); - private CustomResourceEventSource customResourceEventSource; - private DefaultEventHandler defaultEventHandler; + private final ReentrantLock lock = new ReentrantLock(); + private Map eventSources = new ConcurrentHashMap<>(); + private CustomResourceEventSource customResourceEventSource; + private DefaultEventHandler defaultEventHandler; - public DefaultEventSourceManager(DefaultEventHandler defaultEventHandler) { - this.defaultEventHandler = defaultEventHandler; - } + public DefaultEventSourceManager(DefaultEventHandler defaultEventHandler) { + this.defaultEventHandler = defaultEventHandler; + } - public void registerCustomResourceEventSource(CustomResourceEventSource customResourceEventSource) { - this.customResourceEventSource = customResourceEventSource; - this.customResourceEventSource.addedToEventManager(); - } + public void registerCustomResourceEventSource( + CustomResourceEventSource customResourceEventSource) { + this.customResourceEventSource = customResourceEventSource; + this.customResourceEventSource.addedToEventManager(); + } - @Override - public void registerEventSource(String name, T eventSource) { - try { - lock.lock(); - EventSource currentEventSource = eventSources.get(name); - if (currentEventSource != null) { - throw new IllegalStateException("Event source with name already registered. Event source name: " + name); - } - eventSources.put(name,eventSource); - eventSource.setEventHandler(defaultEventHandler); - } finally { - lock.unlock(); - } + @Override + public void registerEventSource(String name, T eventSource) { + try { + lock.lock(); + EventSource currentEventSource = eventSources.get(name); + if (currentEventSource != null) { + throw new IllegalStateException( + "Event source with name already registered. Event source name: " + name); + } + eventSources.put(name, eventSource); + eventSource.setEventHandler(defaultEventHandler); + } finally { + lock.unlock(); } + } - @Override - public Optional deRegisterCustomResourceFromEventSource(String eventSourceName,String customResourceUid) { - try { - lock.lock(); - EventSource eventSource = this.eventSources.get(eventSourceName); - if (eventSource == null) { - log.warn("Event producer: {} not found for custom resource: {}", eventSourceName, customResourceUid); - return Optional.empty(); - } else { - eventSource.eventSourceDeRegisteredForResource(customResourceUid); - return Optional.of(eventSource); - } - } finally { - lock.unlock(); - } + @Override + public Optional deRegisterCustomResourceFromEventSource( + String eventSourceName, String customResourceUid) { + try { + lock.lock(); + EventSource eventSource = this.eventSources.get(eventSourceName); + if (eventSource == null) { + log.warn( + "Event producer: {} not found for custom resource: {}", + eventSourceName, + customResourceUid); + return Optional.empty(); + } else { + eventSource.eventSourceDeRegisteredForResource(customResourceUid); + return Optional.of(eventSource); + } + } finally { + lock.unlock(); } + } - @Override - public Map getRegisteredEventSources() { - return Collections.unmodifiableMap(eventSources); - } - - public void cleanup(String customResourceUid) { - getRegisteredEventSources().keySet().forEach(k -> deRegisterCustomResourceFromEventSource(k,customResourceUid)); - eventSources.remove(customResourceUid); - } + @Override + public Map getRegisteredEventSources() { + return Collections.unmodifiableMap(eventSources); + } + public void cleanup(String customResourceUid) { + getRegisteredEventSources() + .keySet() + .forEach(k -> deRegisterCustomResourceFromEventSource(k, customResourceUid)); + eventSources.remove(customResourceUid); + } } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/Event.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/Event.java index f95f4833cc..5af3b47eb6 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/Event.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/Event.java @@ -2,7 +2,7 @@ public interface Event { - String getRelatedCustomResourceUid(); + String getRelatedCustomResourceUid(); - EventSource getEventSource(); + EventSource getEventSource(); } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/EventHandler.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/EventHandler.java index 97a6c776aa..064b566220 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/EventHandler.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/EventHandler.java @@ -2,6 +2,5 @@ public interface EventHandler { - void handleEvent(Event event); - + void handleEvent(Event event); } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/EventList.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/EventList.java index 69909b3b01..d9560f6f1c 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/EventList.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/EventList.java @@ -5,23 +5,23 @@ public class EventList { - private final List eventList; + private final List eventList; - public EventList(List eventList) { - this.eventList = eventList; - } + public EventList(List eventList) { + this.eventList = eventList; + } - public List getList() { - return eventList; - } + public List getList() { + return eventList; + } - public Optional getLatestOfType(Class eventType) { - for (int i = eventList.size() - 1; i >= 0; i--){ - Event event = eventList.get(i); - if (event.getClass().isAssignableFrom(eventType)) { - return (Optional) Optional.of(event); - } - } - return Optional.empty(); + public Optional getLatestOfType(Class eventType) { + for (int i = eventList.size() - 1; i >= 0; i--) { + Event event = eventList.get(i); + if (event.getClass().isAssignableFrom(eventType)) { + return (Optional) Optional.of(event); + } } + return Optional.empty(); + } } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSource.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSource.java index 57add67ef9..bd992b71de 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSource.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSource.java @@ -2,8 +2,7 @@ public interface EventSource { - void setEventHandler(EventHandler eventHandler); - - void eventSourceDeRegisteredForResource(String customResourceUid); + void setEventHandler(EventHandler eventHandler); + void eventSourceDeRegisteredForResource(String customResourceUid); } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceManager.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceManager.java index fe58f66344..cc8e5660a8 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceManager.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceManager.java @@ -1,18 +1,14 @@ package io.javaoperatorsdk.operator.processing.event; -import io.fabric8.kubernetes.client.CustomResource; - -import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.function.Supplier; public interface EventSourceManager { - void registerEventSource(String name, T eventSource); - - Optional deRegisterCustomResourceFromEventSource(String name, String customResourceUid); + void registerEventSource(String name, T eventSource); - Map getRegisteredEventSources(); + Optional deRegisterCustomResourceFromEventSource( + String name, String customResourceUid); + Map getRegisteredEventSources(); } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/ExecutionDescriptor.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/ExecutionDescriptor.java index 42f1db41c6..676b355412 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/ExecutionDescriptor.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/ExecutionDescriptor.java @@ -2,31 +2,33 @@ import io.javaoperatorsdk.operator.processing.ExecutionScope; import io.javaoperatorsdk.operator.processing.PostExecutionControl; - import java.time.LocalDateTime; public class ExecutionDescriptor { - private final ExecutionScope executionScope; - private final PostExecutionControl postExecutionControl; - private final LocalDateTime executionFinishedAt; + private final ExecutionScope executionScope; + private final PostExecutionControl postExecutionControl; + private final LocalDateTime executionFinishedAt; - public ExecutionDescriptor(ExecutionScope executionScope, PostExecutionControl postExecutionControl, LocalDateTime executionFinishedAt) { - this.executionScope = executionScope; - this.postExecutionControl = postExecutionControl; + public ExecutionDescriptor( + ExecutionScope executionScope, + PostExecutionControl postExecutionControl, + LocalDateTime executionFinishedAt) { + this.executionScope = executionScope; + this.postExecutionControl = postExecutionControl; - this.executionFinishedAt = executionFinishedAt; - } + this.executionFinishedAt = executionFinishedAt; + } - public ExecutionScope getExecutionScope() { - return executionScope; - } + public ExecutionScope getExecutionScope() { + return executionScope; + } - public PostExecutionControl getPostExecutionControl() { - return postExecutionControl; - } + public PostExecutionControl getPostExecutionControl() { + return postExecutionControl; + } - public String getCustomResourceUid() { - return executionScope.getCustomResourceUid(); - } + public String getCustomResourceUid() { + return executionScope.getCustomResourceUid(); + } } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/internal/CustomResourceEvent.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/internal/CustomResourceEvent.java index fb9a7ca888..0c9f79f3ed 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/internal/CustomResourceEvent.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/internal/CustomResourceEvent.java @@ -7,36 +7,46 @@ public class CustomResourceEvent extends AbstractEvent { - private final Watcher.Action action; - private final CustomResource customResource; - - public CustomResourceEvent(Watcher.Action action, CustomResource resource, - CustomResourceEventSource customResourceEventSource) { - super(KubernetesResourceUtils.getUID(resource), customResourceEventSource); - this.action = action; - this.customResource = resource; - } - - public Watcher.Action getAction() { - return action; - } - - public String resourceUid() { - return getCustomResource().getMetadata().getUid(); - } - - @Override - public String toString() { - return "CustomResourceEvent{" + - "action=" + action + - ", resource=[ name=" + getCustomResource().getMetadata().getName() + ", kind=" + getCustomResource().getKind() + - ", apiVersion=" + getCustomResource().getApiVersion() + " ,resourceVersion=" + getCustomResource().getMetadata().getResourceVersion() + - ", markedForDeletion: " + (getCustomResource().getMetadata().getDeletionTimestamp() != null - && !getCustomResource().getMetadata().getDeletionTimestamp().isEmpty()) + - " ]}"; - } - - public CustomResource getCustomResource() { - return customResource; - } + private final Watcher.Action action; + private final CustomResource customResource; + + public CustomResourceEvent( + Watcher.Action action, + CustomResource resource, + CustomResourceEventSource customResourceEventSource) { + super(KubernetesResourceUtils.getUID(resource), customResourceEventSource); + this.action = action; + this.customResource = resource; + } + + public Watcher.Action getAction() { + return action; + } + + public String resourceUid() { + return getCustomResource().getMetadata().getUid(); + } + + @Override + public String toString() { + return "CustomResourceEvent{" + + "action=" + + action + + ", resource=[ name=" + + getCustomResource().getMetadata().getName() + + ", kind=" + + getCustomResource().getKind() + + ", apiVersion=" + + getCustomResource().getApiVersion() + + " ,resourceVersion=" + + getCustomResource().getMetadata().getResourceVersion() + + ", markedForDeletion: " + + (getCustomResource().getMetadata().getDeletionTimestamp() != null + && !getCustomResource().getMetadata().getDeletionTimestamp().isEmpty()) + + " ]}"; + } + + public CustomResource getCustomResource() { + return customResource; + } } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/internal/CustomResourceEventSource.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/internal/CustomResourceEventSource.java index d03ff7942f..93bc3ff6f5 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/internal/CustomResourceEventSource.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/internal/CustomResourceEventSource.java @@ -1,5 +1,8 @@ package io.javaoperatorsdk.operator.processing.event.internal; +import static io.javaoperatorsdk.operator.processing.KubernetesResourceUtils.*; +import static java.net.HttpURLConnection.HTTP_GONE; + import io.fabric8.kubernetes.client.CustomResource; import io.fabric8.kubernetes.client.KubernetesClientException; import io.fabric8.kubernetes.client.Watcher; @@ -9,139 +12,155 @@ import io.javaoperatorsdk.operator.processing.CustomResourceCache; import io.javaoperatorsdk.operator.processing.KubernetesResourceUtils; import io.javaoperatorsdk.operator.processing.event.AbstractEventSource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import static io.javaoperatorsdk.operator.processing.KubernetesResourceUtils.*; -import static java.net.HttpURLConnection.HTTP_GONE; - -/** - * This is a special case since is not bound to a single custom resource - */ -public class CustomResourceEventSource extends AbstractEventSource implements Watcher { - - private final static Logger log = LoggerFactory.getLogger(CustomResourceEventSource.class); - - private final CustomResourceCache resourceCache; - private MixedOperation client; - private final String[] targetNamespaces; - private final boolean generationAware; - private final String resourceFinalizer; - private final Map lastGenerationProcessedSuccessfully = new ConcurrentHashMap<>(); - - public static CustomResourceEventSource customResourceEventSourceForAllNamespaces(CustomResourceCache customResourceCache, - MixedOperation client, boolean generationAware, - String resourceFinalizer) { - return new CustomResourceEventSource(customResourceCache, client, null, generationAware, resourceFinalizer); +/** This is a special case since is not bound to a single custom resource */ +public class CustomResourceEventSource extends AbstractEventSource + implements Watcher { + + private static final Logger log = LoggerFactory.getLogger(CustomResourceEventSource.class); + + private final CustomResourceCache resourceCache; + private MixedOperation client; + private final String[] targetNamespaces; + private final boolean generationAware; + private final String resourceFinalizer; + private final Map lastGenerationProcessedSuccessfully = new ConcurrentHashMap<>(); + + public static CustomResourceEventSource customResourceEventSourceForAllNamespaces( + CustomResourceCache customResourceCache, + MixedOperation client, + boolean generationAware, + String resourceFinalizer) { + return new CustomResourceEventSource( + customResourceCache, client, null, generationAware, resourceFinalizer); + } + + public static CustomResourceEventSource customResourceEventSourceForTargetNamespaces( + CustomResourceCache customResourceCache, + MixedOperation client, + String[] namespaces, + boolean generationAware, + String resourceFinalizer) { + return new CustomResourceEventSource( + customResourceCache, client, namespaces, generationAware, resourceFinalizer); + } + + private CustomResourceEventSource( + CustomResourceCache customResourceCache, + MixedOperation client, + String[] targetNamespaces, + boolean generationAware, + String resourceFinalizer) { + this.resourceCache = customResourceCache; + this.client = client; + this.targetNamespaces = targetNamespaces; + this.generationAware = generationAware; + this.resourceFinalizer = resourceFinalizer; + } + + private boolean isWatchAllNamespaces() { + return targetNamespaces == null; + } + + public void addedToEventManager() { + registerWatch(); + } + + private void registerWatch() { + CustomResourceOperationsImpl crClient = (CustomResourceOperationsImpl) client; + if (isWatchAllNamespaces()) { + crClient.inAnyNamespace().watch(this); + } else if (targetNamespaces.length == 0) { + client.watch(this); + } else { + for (String targetNamespace : targetNamespaces) { + crClient.inNamespace(targetNamespace).watch(this); + log.debug("Registered controller for namespace: {}", targetNamespace); + } } - - public static CustomResourceEventSource customResourceEventSourceForTargetNamespaces(CustomResourceCache customResourceCache, - MixedOperation client, - String[] namespaces, boolean generationAware, - String resourceFinalizer) { - return new CustomResourceEventSource(customResourceCache, client, namespaces, generationAware, resourceFinalizer); + } + + @Override + public void eventReceived(Watcher.Action action, CustomResource customResource) { + log.debug( + "Event received for action: {}, resource: {}", + action.name(), + customResource.getMetadata().getName()); + + resourceCache.cacheResource( + customResource); // always store the latest event. Outside the sync block is intentional. + if (action == Action.ERROR) { + log.debug( + "Skipping {} event for custom resource uid: {}, version: {}", + action, + getUID(customResource), + getVersion(customResource)); + return; } - private CustomResourceEventSource(CustomResourceCache customResourceCache, MixedOperation client, String[] targetNamespaces, boolean generationAware, String resourceFinalizer) { - this.resourceCache = customResourceCache; - this.client = client; - this.targetNamespaces = targetNamespaces; - this.generationAware = generationAware; - this.resourceFinalizer = resourceFinalizer; + if (!skipBecauseOfGenerations(customResource)) { + eventHandler.handleEvent(new CustomResourceEvent(action, customResource, this)); + markLastGenerationProcessed(customResource); + } else { + log.debug( + "Skipping event handling resource {} with version: {}", + getUID(customResource), + getVersion(customResource)); } + } - private boolean isWatchAllNamespaces() { - return targetNamespaces == null; + private void markLastGenerationProcessed(CustomResource resource) { + if (generationAware && ControllerUtils.hasGivenFinalizer(resource, resourceFinalizer)) { + lastGenerationProcessedSuccessfully.put( + KubernetesResourceUtils.getUID(resource), resource.getMetadata().getGeneration()); } + } - public void addedToEventManager() { - registerWatch(); + private boolean skipBecauseOfGenerations(CustomResource customResource) { + if (!generationAware) { + return false; } - - private void registerWatch() { - CustomResourceOperationsImpl crClient = (CustomResourceOperationsImpl) client; - if (isWatchAllNamespaces()) { - crClient.inAnyNamespace().watch(this); - } else if (targetNamespaces.length == 0) { - client.watch(this); - } else { - for (String targetNamespace : targetNamespaces) { - crClient.inNamespace(targetNamespace).watch(this); - log.debug("Registered controller for namespace: {}", targetNamespace); - } - } + // if CR being deleted generation is naturally not changing, so we process all the events + if (markedForDeletion(customResource)) { + return false; } - - @Override - public void eventReceived(Watcher.Action action, CustomResource customResource) { - log.debug("Event received for action: {}, resource: {}", action.name(), customResource.getMetadata().getName()); - - resourceCache.cacheResource(customResource); // always store the latest event. Outside the sync block is intentional. - if (action == Action.ERROR) { - log.debug("Skipping {} event for custom resource uid: {}, version: {}", action, - getUID(customResource), getVersion(customResource)); - return; - } - - if (!skipBecauseOfGenerations(customResource)) { - eventHandler.handleEvent(new CustomResourceEvent(action, customResource, this)); - markLastGenerationProcessed(customResource); - } else { - log.debug("Skipping event handling resource {} with version: {}", getUID(customResource), - getVersion(customResource)); - } + if (!largerGenerationThenProcessedBefore(customResource)) { + return true; } - - private void markLastGenerationProcessed(CustomResource resource) { - if (generationAware && ControllerUtils.hasGivenFinalizer(resource, resourceFinalizer)) { - lastGenerationProcessedSuccessfully.put(KubernetesResourceUtils.getUID(resource), resource.getMetadata().getGeneration()); - } + return false; + } + + public boolean largerGenerationThenProcessedBefore(CustomResource resource) { + Long lastGeneration = lastGenerationProcessedSuccessfully.get(resource.getMetadata().getUid()); + if (lastGeneration == null) { + return true; + } else { + return resource.getMetadata().getGeneration() > lastGeneration; } + } - private boolean skipBecauseOfGenerations(CustomResource customResource) { - if (!generationAware) { - return false; - } - // if CR being deleted generation is naturally not changing, so we process all the events - if (markedForDeletion(customResource)) { - return false; - } - if (!largerGenerationThenProcessedBefore(customResource)) { - return true; - } - return false; - } + @Override + public void eventSourceDeRegisteredForResource(String customResourceUid) { + lastGenerationProcessedSuccessfully.remove(customResourceUid); + } - public boolean largerGenerationThenProcessedBefore(CustomResource resource) { - Long lastGeneration = lastGenerationProcessedSuccessfully.get(resource.getMetadata().getUid()); - if (lastGeneration == null) { - return true; - } else { - return resource.getMetadata().getGeneration() > lastGeneration; - } + @Override + public void onClose(KubernetesClientException e) { + if (e == null) { + return; } - - @Override - public void eventSourceDeRegisteredForResource(String customResourceUid) { - lastGenerationProcessedSuccessfully.remove(customResourceUid); - } - - @Override - public void onClose(KubernetesClientException e) { - if (e == null) { - return; - } - if (e.getCode() == HTTP_GONE) { - log.warn("Received error for watch, will try to reconnect.", e); - registerWatch(); - } else { - // Note that this should not happen normally, since fabric8 client handles reconnect. - // In case it tries to reconnect this method is not called. - log.error("Unexpected error happened with watch. Will exit.", e); - System.exit(1); - } + if (e.getCode() == HTTP_GONE) { + log.warn("Received error for watch, will try to reconnect.", e); + registerWatch(); + } else { + // Note that this should not happen normally, since fabric8 client handles reconnect. + // In case it tries to reconnect this method is not called. + log.error("Unexpected error happened with watch. Will exit.", e); + System.exit(1); } + } } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/internal/TimerEvent.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/internal/TimerEvent.java index eaa6eb2792..a14cdda9a8 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/internal/TimerEvent.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/internal/TimerEvent.java @@ -4,8 +4,7 @@ public class TimerEvent extends AbstractEvent { - public TimerEvent(String relatedCustomResourceUid, TimerEventSource eventSource) { - super(relatedCustomResourceUid, eventSource); - } - + public TimerEvent(String relatedCustomResourceUid, TimerEventSource eventSource) { + super(relatedCustomResourceUid, eventSource); + } } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/internal/TimerEventSource.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/internal/TimerEventSource.java index 8446945b96..e45e854cc6 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/internal/TimerEventSource.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/event/internal/TimerEventSource.java @@ -3,75 +3,71 @@ import io.fabric8.kubernetes.client.CustomResource; import io.javaoperatorsdk.operator.processing.KubernetesResourceUtils; import io.javaoperatorsdk.operator.processing.event.AbstractEventSource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.util.*; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.locks.ReentrantLock; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class TimerEventSource extends AbstractEventSource { - private Logger log = LoggerFactory.getLogger(TimerEventSource.class); - - private final Timer timer = new Timer(); + private Logger log = LoggerFactory.getLogger(TimerEventSource.class); - private final Map onceTasks = new ConcurrentHashMap<>(); - private final Map timerTasks = new ConcurrentHashMap<>(); + private final Timer timer = new Timer(); - public void schedule(CustomResource customResource, long delay, long period) { - String resourceUid = KubernetesResourceUtils.getUID(customResource); - if (timerTasks.containsKey(resourceUid)) { - return; - } - EventProducerTimeTask task = new EventProducerTimeTask(resourceUid); - timerTasks.put(resourceUid, task); - timer.schedule(task, delay, period); - } + private final Map onceTasks = new ConcurrentHashMap<>(); + private final Map timerTasks = new ConcurrentHashMap<>(); - public void scheduleOnce(CustomResource customResource, long delay) { - String resourceUid = KubernetesResourceUtils.getUID(customResource); - if (onceTasks.containsKey(resourceUid)) { - cancelOnceSchedule(resourceUid); - } - EventProducerTimeTask task = new EventProducerTimeTask(resourceUid); - onceTasks.put(resourceUid, task); - timer.schedule(task, delay); + public void schedule(CustomResource customResource, long delay, long period) { + String resourceUid = KubernetesResourceUtils.getUID(customResource); + if (timerTasks.containsKey(resourceUid)) { + return; } - - @Override - public void eventSourceDeRegisteredForResource(String customResourceUid) { - cancelSchedule(customResourceUid); - cancelOnceSchedule(customResourceUid); + EventProducerTimeTask task = new EventProducerTimeTask(resourceUid); + timerTasks.put(resourceUid, task); + timer.schedule(task, delay, period); + } + + public void scheduleOnce(CustomResource customResource, long delay) { + String resourceUid = KubernetesResourceUtils.getUID(customResource); + if (onceTasks.containsKey(resourceUid)) { + cancelOnceSchedule(resourceUid); } - - public void cancelSchedule(String customResourceUid) { - TimerTask timerTask = timerTasks.remove(customResourceUid); - if (timerTask != null) { - timerTask.cancel(); - } + EventProducerTimeTask task = new EventProducerTimeTask(resourceUid); + onceTasks.put(resourceUid, task); + timer.schedule(task, delay); + } + + @Override + public void eventSourceDeRegisteredForResource(String customResourceUid) { + cancelSchedule(customResourceUid); + cancelOnceSchedule(customResourceUid); + } + + public void cancelSchedule(String customResourceUid) { + TimerTask timerTask = timerTasks.remove(customResourceUid); + if (timerTask != null) { + timerTask.cancel(); } + } - public void cancelOnceSchedule(String customResourceUid) { - TimerTask timerTask = onceTasks.remove(customResourceUid); - if (timerTask != null) { - timerTask.cancel(); - } + public void cancelOnceSchedule(String customResourceUid) { + TimerTask timerTask = onceTasks.remove(customResourceUid); + if (timerTask != null) { + timerTask.cancel(); } + } - public class EventProducerTimeTask extends TimerTask { - protected final String customResourceUid; - - public EventProducerTimeTask(String customResourceUid) { - this.customResourceUid = customResourceUid; - } + public class EventProducerTimeTask extends TimerTask { + protected final String customResourceUid; - @Override - public void run() { - log.debug("Producing event for custom resource id: {}", customResourceUid); - eventHandler.handleEvent(new TimerEvent(customResourceUid, TimerEventSource.this)); - } + public EventProducerTimeTask(String customResourceUid) { + this.customResourceUid = customResourceUid; } - + @Override + public void run() { + log.debug("Producing event for custom resource id: {}", customResourceUid); + eventHandler.handleEvent(new TimerEvent(customResourceUid, TimerEventSource.this)); + } + } } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/retry/GenericRetry.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/retry/GenericRetry.java index 31f65bf21e..ca8d47aa84 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/retry/GenericRetry.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/retry/GenericRetry.java @@ -2,82 +2,79 @@ public class GenericRetry implements Retry { - public static final int DEFAULT_MAX_ATTEMPTS = 5; - public static final long DEFAULT_INITIAL_INTERVAL = 2000L; - public static final double DEFAULT_MULTIPLIER = 1.5D; - - private int maxAttempts = DEFAULT_MAX_ATTEMPTS; - private long initialInterval = DEFAULT_INITIAL_INTERVAL; - private double intervalMultiplier = DEFAULT_MULTIPLIER; - private long maxInterval = -1; - - public static GenericRetry defaultLimitedExponentialRetry() { - return new GenericRetry(); - } - - public static GenericRetry noRetry() { - return new GenericRetry().setMaxAttempts(1); - } - - public static GenericRetry every10second10TimesRetry() { - return new GenericRetry() - .withLinearRetry() - .setMaxAttempts(10) - .setInitialInterval(10000); - } - - @Override - public GenericRetryExecution initExecution() { - return new GenericRetryExecution(this); - } - - public int getMaxAttempts() { - return maxAttempts; - } - - public GenericRetry setMaxAttempts(int maxRetryAttempts) { - this.maxAttempts = maxRetryAttempts; - return this; - } - - public long getInitialInterval() { - return initialInterval; - } - - public GenericRetry setInitialInterval(long initialInterval) { - this.initialInterval = initialInterval; - return this; - } - - public double getIntervalMultiplier() { - return intervalMultiplier; - } - - public GenericRetry setIntervalMultiplier(double intervalMultiplier) { - this.intervalMultiplier = intervalMultiplier; - return this; - } - - public long getMaxInterval() { - return maxInterval; - } - - public GenericRetry setMaxInterval(long maxInterval) { - this.maxInterval = maxInterval; - return this; - } - - public GenericRetry withoutMaxInterval() { - this.maxInterval = -1; - return this; - } - - public GenericRetry withoutMaxAttempts() { - return this.setMaxAttempts(-1); - } - - public GenericRetry withLinearRetry() { - this.intervalMultiplier = 1; - return this; - } + public static final int DEFAULT_MAX_ATTEMPTS = 5; + public static final long DEFAULT_INITIAL_INTERVAL = 2000L; + public static final double DEFAULT_MULTIPLIER = 1.5D; + + private int maxAttempts = DEFAULT_MAX_ATTEMPTS; + private long initialInterval = DEFAULT_INITIAL_INTERVAL; + private double intervalMultiplier = DEFAULT_MULTIPLIER; + private long maxInterval = -1; + + public static GenericRetry defaultLimitedExponentialRetry() { + return new GenericRetry(); + } + + public static GenericRetry noRetry() { + return new GenericRetry().setMaxAttempts(1); + } + + public static GenericRetry every10second10TimesRetry() { + return new GenericRetry().withLinearRetry().setMaxAttempts(10).setInitialInterval(10000); + } + + @Override + public GenericRetryExecution initExecution() { + return new GenericRetryExecution(this); + } + + public int getMaxAttempts() { + return maxAttempts; + } + + public GenericRetry setMaxAttempts(int maxRetryAttempts) { + this.maxAttempts = maxRetryAttempts; + return this; + } + + public long getInitialInterval() { + return initialInterval; + } + + public GenericRetry setInitialInterval(long initialInterval) { + this.initialInterval = initialInterval; + return this; + } + + public double getIntervalMultiplier() { + return intervalMultiplier; + } + + public GenericRetry setIntervalMultiplier(double intervalMultiplier) { + this.intervalMultiplier = intervalMultiplier; + return this; + } + + public long getMaxInterval() { + return maxInterval; + } + + public GenericRetry setMaxInterval(long maxInterval) { + this.maxInterval = maxInterval; + return this; + } + + public GenericRetry withoutMaxInterval() { + this.maxInterval = -1; + return this; + } + + public GenericRetry withoutMaxAttempts() { + return this.setMaxAttempts(-1); + } + + public GenericRetry withLinearRetry() { + this.intervalMultiplier = 1; + return this; + } } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/retry/GenericRetryExecution.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/retry/GenericRetryExecution.java index c39ae93709..b15144f536 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/retry/GenericRetryExecution.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/retry/GenericRetryExecution.java @@ -2,42 +2,42 @@ import java.util.Optional; - public class GenericRetryExecution implements RetryExecution { - private final GenericRetry genericRetry; + private final GenericRetry genericRetry; - private int lastAttemptIndex = 0; - private long currentInterval; + private int lastAttemptIndex = 0; + private long currentInterval; - public GenericRetryExecution(GenericRetry genericRetry) { - this.genericRetry = genericRetry; - this.currentInterval = genericRetry.getInitialInterval(); - } + public GenericRetryExecution(GenericRetry genericRetry) { + this.genericRetry = genericRetry; + this.currentInterval = genericRetry.getInitialInterval(); + } - /** - * Note that first attempt is always 0. Since this implementation is tailored for event scheduling. - */ - public Optional nextDelay() { - if (lastAttemptIndex == 0) { - lastAttemptIndex++; - return Optional.of(0L); - } - if (genericRetry.getMaxAttempts() > -1 && lastAttemptIndex >= genericRetry.getMaxAttempts()) { - return Optional.empty(); - } - if (lastAttemptIndex > 1) { - currentInterval = (long) (currentInterval * genericRetry.getIntervalMultiplier()); - if (genericRetry.getMaxInterval() > -1 && currentInterval > genericRetry.getMaxInterval()) { - currentInterval = genericRetry.getMaxInterval(); - } - } - lastAttemptIndex++; - return Optional.of(currentInterval); + /** + * Note that first attempt is always 0. Since this implementation is tailored for event + * scheduling. + */ + public Optional nextDelay() { + if (lastAttemptIndex == 0) { + lastAttemptIndex++; + return Optional.of(0L); } - - @Override - public boolean isLastExecution() { - return genericRetry.getMaxAttempts() > -1 && lastAttemptIndex >= genericRetry.getMaxAttempts(); + if (genericRetry.getMaxAttempts() > -1 && lastAttemptIndex >= genericRetry.getMaxAttempts()) { + return Optional.empty(); + } + if (lastAttemptIndex > 1) { + currentInterval = (long) (currentInterval * genericRetry.getIntervalMultiplier()); + if (genericRetry.getMaxInterval() > -1 && currentInterval > genericRetry.getMaxInterval()) { + currentInterval = genericRetry.getMaxInterval(); + } } + lastAttemptIndex++; + return Optional.of(currentInterval); + } + + @Override + public boolean isLastExecution() { + return genericRetry.getMaxAttempts() > -1 && lastAttemptIndex >= genericRetry.getMaxAttempts(); + } } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/retry/Retry.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/retry/Retry.java index cd71bd57f4..dd7a34722a 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/retry/Retry.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/retry/Retry.java @@ -2,6 +2,5 @@ public interface Retry { - RetryExecution initExecution(); - + RetryExecution initExecution(); } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/retry/RetryExecution.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/retry/RetryExecution.java index d365b3d4de..b14a5966fb 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/retry/RetryExecution.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/processing/retry/RetryExecution.java @@ -4,15 +4,17 @@ public interface RetryExecution { - /** - * Calculates the delay for the next execution. This method should return 0, when called first time; - * - * @return - */ - Optional nextDelay(); + /** + * Calculates the delay for the next execution. This method should return 0, when called first + * time; + * + * @return + */ + Optional nextDelay(); - /** - * @return true, if the last returned delay is, the last returned values, thus there will be no further retry - */ - boolean isLastExecution(); + /** + * @return true, if the last returned delay is, the last returned values, thus there will be no + * further retry + */ + boolean isLastExecution(); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/ConcurrencyIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/ConcurrencyIT.java index 84daca554e..ca72b3a741 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/ConcurrencyIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/ConcurrencyIT.java @@ -1,10 +1,15 @@ package io.javaoperatorsdk.operator; -import io.javaoperatorsdk.operator.sample.simple.TestCustomResource; +import static org.assertj.core.api.Assertions.assertThat; + import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.client.DefaultKubernetesClient; import io.fabric8.kubernetes.client.KubernetesClient; +import io.javaoperatorsdk.operator.sample.simple.TestCustomResource; import io.javaoperatorsdk.operator.sample.simple.TestCustomResourceController; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import org.awaitility.Awaitility; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -13,88 +18,116 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.List; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -import static org.assertj.core.api.Assertions.assertThat; - @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class ConcurrencyIT { - public static final int NUMBER_OF_RESOURCES_CREATED = 50; - public static final int NUMBER_OF_RESOURCES_DELETED = 30; - public static final int NUMBER_OF_RESOURCES_UPDATED = 20; - private static final Logger log = LoggerFactory.getLogger(ConcurrencyIT.class); - public static final String UPDATED_SUFFIX = "_updated"; - private IntegrationTestSupport integrationTest = new IntegrationTestSupport(); + public static final int NUMBER_OF_RESOURCES_CREATED = 50; + public static final int NUMBER_OF_RESOURCES_DELETED = 30; + public static final int NUMBER_OF_RESOURCES_UPDATED = 20; + private static final Logger log = LoggerFactory.getLogger(ConcurrencyIT.class); + public static final String UPDATED_SUFFIX = "_updated"; + private IntegrationTestSupport integrationTest = new IntegrationTestSupport(); - @BeforeAll - public void setup() { - KubernetesClient k8sClient = new DefaultKubernetesClient(); - integrationTest.initialize(k8sClient, new TestCustomResourceController(k8sClient, true), - "test-crd.yaml"); - } + @BeforeAll + public void setup() { + KubernetesClient k8sClient = new DefaultKubernetesClient(); + integrationTest.initialize( + k8sClient, new TestCustomResourceController(k8sClient, true), "test-crd.yaml"); + } - @BeforeEach - public void cleanup() { - integrationTest.cleanup(); - } + @BeforeEach + public void cleanup() { + integrationTest.cleanup(); + } - @Test - public void manyResourcesGetCreatedUpdatedAndDeleted() { - integrationTest.teardownIfSuccess(() -> { - log.info("Creating {} new resources", NUMBER_OF_RESOURCES_CREATED); - for (int i = 0; i < NUMBER_OF_RESOURCES_CREATED; i++) { - TestCustomResource tcr = integrationTest.createTestCustomResource(String.valueOf(i)); - integrationTest.getCrOperations().inNamespace(IntegrationTestSupport.TEST_NAMESPACE).create(tcr); - } + @Test + public void manyResourcesGetCreatedUpdatedAndDeleted() { + integrationTest.teardownIfSuccess( + () -> { + log.info("Creating {} new resources", NUMBER_OF_RESOURCES_CREATED); + for (int i = 0; i < NUMBER_OF_RESOURCES_CREATED; i++) { + TestCustomResource tcr = integrationTest.createTestCustomResource(String.valueOf(i)); + integrationTest + .getCrOperations() + .inNamespace(IntegrationTestSupport.TEST_NAMESPACE) + .create(tcr); + } - Awaitility.await().atMost(1, TimeUnit.MINUTES) - .untilAsserted(() -> { - List items = integrationTest.getK8sClient().configMaps() - .inNamespace(IntegrationTestSupport.TEST_NAMESPACE) - .withLabel("managedBy", TestCustomResourceController.class.getSimpleName()) - .list().getItems(); - assertThat(items).hasSize(NUMBER_OF_RESOURCES_CREATED); - }); + Awaitility.await() + .atMost(1, TimeUnit.MINUTES) + .untilAsserted( + () -> { + List items = + integrationTest + .getK8sClient() + .configMaps() + .inNamespace(IntegrationTestSupport.TEST_NAMESPACE) + .withLabel( + "managedBy", TestCustomResourceController.class.getSimpleName()) + .list() + .getItems(); + assertThat(items).hasSize(NUMBER_OF_RESOURCES_CREATED); + }); - log.info("Updating {} resources", NUMBER_OF_RESOURCES_UPDATED); - // update some resources - for (int i = 0; i < NUMBER_OF_RESOURCES_UPDATED; i++) { - TestCustomResource tcr = (TestCustomResource) integrationTest.getCrOperations() + log.info("Updating {} resources", NUMBER_OF_RESOURCES_UPDATED); + // update some resources + for (int i = 0; i < NUMBER_OF_RESOURCES_UPDATED; i++) { + TestCustomResource tcr = + (TestCustomResource) + integrationTest + .getCrOperations() .inNamespace(IntegrationTestSupport.TEST_NAMESPACE) .withName(IntegrationTestSupport.TEST_CUSTOM_RESOURCE_PREFIX + i) .get(); - tcr.getSpec().setValue(i + UPDATED_SUFFIX); - integrationTest.getCrOperations().inNamespace(IntegrationTestSupport.TEST_NAMESPACE).createOrReplace(tcr); - } - // sleep for a short time to make variability to the test, so some updates are not executed before delete - Thread.sleep(300); + tcr.getSpec().setValue(i + UPDATED_SUFFIX); + integrationTest + .getCrOperations() + .inNamespace(IntegrationTestSupport.TEST_NAMESPACE) + .createOrReplace(tcr); + } + // sleep for a short time to make variability to the test, so some updates are not + // executed before delete + Thread.sleep(300); - log.info("Deleting {} resources", NUMBER_OF_RESOURCES_DELETED); - for (int i = 0; i < NUMBER_OF_RESOURCES_DELETED; i++) { - TestCustomResource tcr = integrationTest.createTestCustomResource(String.valueOf(i)); - integrationTest.getCrOperations().inNamespace(IntegrationTestSupport.TEST_NAMESPACE).delete(tcr); - } + log.info("Deleting {} resources", NUMBER_OF_RESOURCES_DELETED); + for (int i = 0; i < NUMBER_OF_RESOURCES_DELETED; i++) { + TestCustomResource tcr = integrationTest.createTestCustomResource(String.valueOf(i)); + integrationTest + .getCrOperations() + .inNamespace(IntegrationTestSupport.TEST_NAMESPACE) + .delete(tcr); + } - Awaitility.await().atMost(1, TimeUnit.MINUTES) - .untilAsserted(() -> { - List items = integrationTest.getK8sClient().configMaps() - .inNamespace(IntegrationTestSupport.TEST_NAMESPACE) - .withLabel("managedBy", TestCustomResourceController.class.getSimpleName()) - .list().getItems(); - //reducing configmaps to names only - better for debugging - List itemDescs = items.stream().map(configMap -> configMap.getMetadata().getName()).collect(Collectors.toList()); - assertThat(itemDescs).hasSize(NUMBER_OF_RESOURCES_CREATED - NUMBER_OF_RESOURCES_DELETED); + Awaitility.await() + .atMost(1, TimeUnit.MINUTES) + .untilAsserted( + () -> { + List items = + integrationTest + .getK8sClient() + .configMaps() + .inNamespace(IntegrationTestSupport.TEST_NAMESPACE) + .withLabel( + "managedBy", TestCustomResourceController.class.getSimpleName()) + .list() + .getItems(); + // reducing configmaps to names only - better for debugging + List itemDescs = + items.stream() + .map(configMap -> configMap.getMetadata().getName()) + .collect(Collectors.toList()); + assertThat(itemDescs) + .hasSize(NUMBER_OF_RESOURCES_CREATED - NUMBER_OF_RESOURCES_DELETED); - List crs = integrationTest.getCrOperations() - .inNamespace(IntegrationTestSupport.TEST_NAMESPACE) - .list().getItems(); - assertThat(crs).hasSize(NUMBER_OF_RESOURCES_CREATED - NUMBER_OF_RESOURCES_DELETED); - }); + List crs = + integrationTest + .getCrOperations() + .inNamespace(IntegrationTestSupport.TEST_NAMESPACE) + .list() + .getItems(); + assertThat(crs) + .hasSize(NUMBER_OF_RESOURCES_CREATED - NUMBER_OF_RESOURCES_DELETED); + }); }); - } - - + } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/ControllerExecutionIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/ControllerExecutionIT.java index e64d965930..4caa97de91 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/ControllerExecutionIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/ControllerExecutionIT.java @@ -1,83 +1,104 @@ package io.javaoperatorsdk.operator; -import io.javaoperatorsdk.operator.sample.simple.TestCustomResource; -import io.javaoperatorsdk.operator.sample.simple.TestCustomResourceController; +import static io.javaoperatorsdk.operator.TestUtils.TEST_CUSTOM_RESOURCE_NAME; +import static io.javaoperatorsdk.operator.TestUtils.testCustomResource; +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.client.DefaultKubernetesClient; import io.fabric8.kubernetes.client.KubernetesClient; +import io.javaoperatorsdk.operator.sample.simple.TestCustomResource; +import io.javaoperatorsdk.operator.sample.simple.TestCustomResourceController; +import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; -import java.util.concurrent.TimeUnit; - -import static io.javaoperatorsdk.operator.TestUtils.TEST_CUSTOM_RESOURCE_NAME; -import static io.javaoperatorsdk.operator.TestUtils.testCustomResource; -import static org.assertj.core.api.Assertions.assertThat; -import static org.awaitility.Awaitility.await; - @TestInstance(TestInstance.Lifecycle.PER_METHOD) public class ControllerExecutionIT { - private IntegrationTestSupport integrationTestSupport = new IntegrationTestSupport(); - - public void initAndCleanup(boolean controllerStatusUpdate) { - KubernetesClient k8sClient = new DefaultKubernetesClient(); - integrationTestSupport.initialize(k8sClient, new TestCustomResourceController(k8sClient, controllerStatusUpdate), "test-crd.yaml"); - integrationTestSupport.cleanup(); - } - - @Test - public void configMapGetsCreatedForTestCustomResource() { - initAndCleanup(true); - integrationTestSupport.teardownIfSuccess(() -> { - TestCustomResource resource = testCustomResource(); - - integrationTestSupport.getCrOperations().inNamespace(IntegrationTestSupport.TEST_NAMESPACE).create(resource); - - awaitResourcesCreatedOrUpdated(); - awaitStatusUpdated(); - assertThat(integrationTestSupport.numberOfControllerExecutions()).isEqualTo(2); + private IntegrationTestSupport integrationTestSupport = new IntegrationTestSupport(); + + public void initAndCleanup(boolean controllerStatusUpdate) { + KubernetesClient k8sClient = new DefaultKubernetesClient(); + integrationTestSupport.initialize( + k8sClient, + new TestCustomResourceController(k8sClient, controllerStatusUpdate), + "test-crd.yaml"); + integrationTestSupport.cleanup(); + } + + @Test + public void configMapGetsCreatedForTestCustomResource() { + initAndCleanup(true); + integrationTestSupport.teardownIfSuccess( + () -> { + TestCustomResource resource = testCustomResource(); + + integrationTestSupport + .getCrOperations() + .inNamespace(IntegrationTestSupport.TEST_NAMESPACE) + .create(resource); + + awaitResourcesCreatedOrUpdated(); + awaitStatusUpdated(); + assertThat(integrationTestSupport.numberOfControllerExecutions()).isEqualTo(2); }); - } - - @Test - public void eventIsSkippedChangedOnMetadataOnlyUpdate() { - initAndCleanup(false); - integrationTestSupport.teardownIfSuccess(() -> { - TestCustomResource resource = testCustomResource(); - - integrationTestSupport.getCrOperations().inNamespace(IntegrationTestSupport.TEST_NAMESPACE).create(resource); - - awaitResourcesCreatedOrUpdated(); - assertThat(integrationTestSupport.numberOfControllerExecutions()).isEqualTo(1); + } + + @Test + public void eventIsSkippedChangedOnMetadataOnlyUpdate() { + initAndCleanup(false); + integrationTestSupport.teardownIfSuccess( + () -> { + TestCustomResource resource = testCustomResource(); + + integrationTestSupport + .getCrOperations() + .inNamespace(IntegrationTestSupport.TEST_NAMESPACE) + .create(resource); + + awaitResourcesCreatedOrUpdated(); + assertThat(integrationTestSupport.numberOfControllerExecutions()).isEqualTo(1); }); - } - - void awaitResourcesCreatedOrUpdated() { - await("config map created").atMost(5, TimeUnit.SECONDS) - .untilAsserted(() -> { - ConfigMap configMap = integrationTestSupport.getK8sClient().configMaps().inNamespace(IntegrationTestSupport.TEST_NAMESPACE) - .withName("test-config-map").get(); - assertThat(configMap).isNotNull(); - assertThat(configMap.getData().get("test-key")).isEqualTo("test-value"); - }); - } - - void awaitStatusUpdated() { - awaitStatusUpdated(5); - } - - void awaitStatusUpdated(int timeout) { - await("cr status updated").atMost(timeout, TimeUnit.SECONDS) - .untilAsserted(() -> { - TestCustomResource cr = (TestCustomResource) integrationTestSupport.getCrOperations() - .inNamespace(IntegrationTestSupport.TEST_NAMESPACE).withName(TEST_CUSTOM_RESOURCE_NAME).get(); - assertThat(cr).isNotNull(); - assertThat(cr.getStatus()).isNotNull(); - assertThat(cr.getStatus().getConfigMapStatus()).isEqualTo("ConfigMap Ready"); - }); - } - - - + } + + void awaitResourcesCreatedOrUpdated() { + await("config map created") + .atMost(5, TimeUnit.SECONDS) + .untilAsserted( + () -> { + ConfigMap configMap = + integrationTestSupport + .getK8sClient() + .configMaps() + .inNamespace(IntegrationTestSupport.TEST_NAMESPACE) + .withName("test-config-map") + .get(); + assertThat(configMap).isNotNull(); + assertThat(configMap.getData().get("test-key")).isEqualTo("test-value"); + }); + } + + void awaitStatusUpdated() { + awaitStatusUpdated(5); + } + + void awaitStatusUpdated(int timeout) { + await("cr status updated") + .atMost(timeout, TimeUnit.SECONDS) + .untilAsserted( + () -> { + TestCustomResource cr = + (TestCustomResource) + integrationTestSupport + .getCrOperations() + .inNamespace(IntegrationTestSupport.TEST_NAMESPACE) + .withName(TEST_CUSTOM_RESOURCE_NAME) + .get(); + assertThat(cr).isNotNull(); + assertThat(cr.getStatus()).isNotNull(); + assertThat(cr.getStatus().getConfigMapStatus()).isEqualTo("ConfigMap Ready"); + }); + } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/ControllerUtilsTest.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/ControllerUtilsTest.java index 9ab13d99f8..543d5ba812 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/ControllerUtilsTest.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/ControllerUtilsTest.java @@ -1,5 +1,7 @@ package io.javaoperatorsdk.operator; +import static org.junit.jupiter.api.Assertions.*; + import io.fabric8.kubernetes.client.CustomResource; import io.fabric8.kubernetes.client.CustomResourceDoneable; import io.javaoperatorsdk.operator.api.*; @@ -8,47 +10,59 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; - class ControllerUtilsTest { - public static final String CUSTOM_FINALIZER_NAME = "a.custom/finalizer"; + public static final String CUSTOM_FINALIZER_NAME = "a.custom/finalizer"; - @Test - public void returnsValuesFromControllerAnnotationFinalizer() { - Assertions.assertEquals(TestCustomResourceController.CRD_NAME + "/finalizer", ControllerUtils.getFinalizer(new TestCustomResourceController(null))); - assertEquals(TestCustomResource.class, ControllerUtils.getCustomResourceClass(new TestCustomResourceController(null))); - Assertions.assertEquals(TestCustomResourceController.CRD_NAME, ControllerUtils.getCrdName(new TestCustomResourceController(null))); - assertFalse(ControllerUtils.getGenerationEventProcessing(new TestCustomResourceController(null))); - assertTrue(CustomResourceDoneable.class.isAssignableFrom(ControllerUtils.getCustomResourceDoneableClass(new TestCustomResourceController(null)))); - } + @Test + public void returnsValuesFromControllerAnnotationFinalizer() { + Assertions.assertEquals( + TestCustomResourceController.CRD_NAME + "/finalizer", + ControllerUtils.getFinalizer(new TestCustomResourceController(null))); + assertEquals( + TestCustomResource.class, + ControllerUtils.getCustomResourceClass(new TestCustomResourceController(null))); + Assertions.assertEquals( + TestCustomResourceController.CRD_NAME, + ControllerUtils.getCrdName(new TestCustomResourceController(null))); + assertFalse( + ControllerUtils.getGenerationEventProcessing(new TestCustomResourceController(null))); + assertTrue( + CustomResourceDoneable.class.isAssignableFrom( + ControllerUtils.getCustomResourceDoneableClass( + new TestCustomResourceController(null)))); + } + + @Controller(crdName = "test.crd", finalizerName = CUSTOM_FINALIZER_NAME) + static class TestCustomFinalizerController + implements ResourceController { + public class InnerCustomResource extends CustomResource {} - @Controller(crdName = "test.crd", finalizerName = CUSTOM_FINALIZER_NAME) - static class TestCustomFinalizerController implements ResourceController { - public class InnerCustomResource extends CustomResource { - } - - @Override - public DeleteControl deleteResource(TestCustomFinalizerController.InnerCustomResource resource, Context context) { - return DeleteControl.DEFAULT_DELETE; - } - - @Override - public UpdateControl createOrUpdateResource(InnerCustomResource resource, - Context context) { - return null; - } + @Override + public DeleteControl deleteResource( + TestCustomFinalizerController.InnerCustomResource resource, + Context context) { + return DeleteControl.DEFAULT_DELETE; } - @Test - public void returnCustomerFinalizerNameIfSet() { - assertEquals(CUSTOM_FINALIZER_NAME, ControllerUtils.getFinalizer(new TestCustomFinalizerController())); + @Override + public UpdateControl createOrUpdateResource( + InnerCustomResource resource, Context context) { + return null; } + } - @Test - public void supportsInnerClassCustomResources() { - assertDoesNotThrow(() -> { - ControllerUtils.getCustomResourceDoneableClass(new TestCustomFinalizerController()); + @Test + public void returnCustomerFinalizerNameIfSet() { + assertEquals( + CUSTOM_FINALIZER_NAME, ControllerUtils.getFinalizer(new TestCustomFinalizerController())); + } + + @Test + public void supportsInnerClassCustomResources() { + assertDoesNotThrow( + () -> { + ControllerUtils.getCustomResourceDoneableClass(new TestCustomFinalizerController()); }); - } + } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/EventDispatcherTest.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/EventDispatcherTest.java index fba1240f38..3d06b271f9 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/EventDispatcherTest.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/EventDispatcherTest.java @@ -1,5 +1,8 @@ package io.javaoperatorsdk.operator; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.*; + import io.fabric8.kubernetes.client.CustomResource; import io.fabric8.kubernetes.client.Watcher; import io.javaoperatorsdk.operator.api.DeleteControl; @@ -9,169 +12,185 @@ import io.javaoperatorsdk.operator.processing.ExecutionScope; import io.javaoperatorsdk.operator.processing.event.Event; import io.javaoperatorsdk.operator.processing.event.internal.CustomResourceEvent; -import io.javaoperatorsdk.operator.processing.event.internal.TimerEvent; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentMatchers; - import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; - -import static io.javaoperatorsdk.operator.processing.KubernetesResourceUtils.getUID; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.Mockito.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentMatchers; class EventDispatcherTest { - private static final String DEFAULT_FINALIZER = "finalizer"; - private CustomResource testCustomResource; - private EventDispatcher eventDispatcher; - private ResourceController controller = mock(ResourceController.class); - private EventDispatcher.CustomResourceFacade customResourceFacade = mock(EventDispatcher.CustomResourceFacade.class); - - @BeforeEach - void setup() { - eventDispatcher = new EventDispatcher(controller, - DEFAULT_FINALIZER, customResourceFacade); - - testCustomResource = TestUtils.testCustomResource(); - testCustomResource.getMetadata().setFinalizers(new ArrayList<>(Collections.singletonList(DEFAULT_FINALIZER))); - - when(controller.createOrUpdateResource(eq(testCustomResource), any())).thenReturn(UpdateControl.updateCustomResource(testCustomResource)); - when(controller.deleteResource(eq(testCustomResource), any())).thenReturn(DeleteControl.DEFAULT_DELETE); - when(customResourceFacade.replaceWithLock(any())).thenReturn(null); - } - - @Test - void callCreateOrUpdateOnNewResource() { - eventDispatcher.handleEvent(executionScopeWithCREvent(Watcher.Action.ADDED, testCustomResource)); - verify(controller, times(1)).createOrUpdateResource(ArgumentMatchers.eq(testCustomResource), any()); - } - - @Test - void updatesOnlyStatusSubResource() { - when(controller.createOrUpdateResource(eq(testCustomResource), any())) - .thenReturn(UpdateControl.updateStatusSubResource(testCustomResource)); - - eventDispatcher.handleEvent(executionScopeWithCREvent(Watcher.Action.ADDED, testCustomResource)); - - verify(customResourceFacade, times(1)).updateStatus(testCustomResource); - verify(customResourceFacade, never()).replaceWithLock(any()); - } - - - @Test - void callCreateOrUpdateOnModifiedResource() { - eventDispatcher.handleEvent(executionScopeWithCREvent(Watcher.Action.MODIFIED, testCustomResource)); - verify(controller, times(1)).createOrUpdateResource(ArgumentMatchers.eq(testCustomResource), any()); - } - - @Test - void adsDefaultFinalizerOnCreateIfNotThere() { - eventDispatcher.handleEvent(executionScopeWithCREvent(Watcher.Action.MODIFIED, testCustomResource)); - verify(controller, times(1)) - .createOrUpdateResource(argThat(testCustomResource -> - testCustomResource.getMetadata().getFinalizers().contains(DEFAULT_FINALIZER)), any()); - } - - @Test - void callsDeleteIfObjectHasFinalizerAndMarkedForDelete() { - testCustomResource.getMetadata().setDeletionTimestamp("2019-8-10"); - testCustomResource.getMetadata().getFinalizers().add(DEFAULT_FINALIZER); - - eventDispatcher.handleEvent(executionScopeWithCREvent(Watcher.Action.MODIFIED, testCustomResource)); - - verify(controller, times(1)).deleteResource(eq(testCustomResource), any()); - } - - /** - * Note that there could be more finalizers. Out of our control. - */ - @Test - void callDeleteOnControllerIfMarkedForDeletionButThereIsNoDefaultFinalizer() { - markForDeletion(testCustomResource); - - eventDispatcher.handleEvent(executionScopeWithCREvent(Watcher.Action.MODIFIED, testCustomResource)); - - verify(controller).deleteResource(eq(testCustomResource), any()); - } - - @Test - void removesDefaultFinalizerOnDelete() { - markForDeletion(testCustomResource); - - eventDispatcher.handleEvent(executionScopeWithCREvent(Watcher.Action.MODIFIED, testCustomResource)); - - assertEquals(0, testCustomResource.getMetadata().getFinalizers().size()); - verify(customResourceFacade, times(1)).replaceWithLock(any()); - } - - @Test - void doesNotRemovesTheFinalizerIfTheDeleteNotMethodInstructsIt() { - when(controller.deleteResource(eq(testCustomResource), any())).thenReturn(DeleteControl.NO_FINALIZER_REMOVAL); - markForDeletion(testCustomResource); - - eventDispatcher.handleEvent(executionScopeWithCREvent(Watcher.Action.MODIFIED, testCustomResource)); - - assertEquals(1, testCustomResource.getMetadata().getFinalizers().size()); - verify(customResourceFacade, never()).replaceWithLock(any()); - } - - @Test - void doesNotUpdateTheResourceIfNoUpdateUpdateControl() { - when(controller.createOrUpdateResource(eq(testCustomResource), any())).thenReturn(UpdateControl.noUpdate()); - - eventDispatcher.handleEvent(executionScopeWithCREvent(Watcher.Action.MODIFIED, testCustomResource)); - verify(customResourceFacade, never()).replaceWithLock(any()); - verify(customResourceFacade, never()).updateStatus(testCustomResource); - } - - @Test - void addsFinalizerIfNotMarkedForDeletionAndEmptyCustomResourceReturned() { - removeFinalizers(testCustomResource); - when(controller.createOrUpdateResource(eq(testCustomResource), any())).thenReturn(UpdateControl.noUpdate()); - - eventDispatcher.handleEvent(executionScopeWithCREvent(Watcher.Action.MODIFIED, testCustomResource)); - - assertEquals(1, testCustomResource.getMetadata().getFinalizers().size()); - verify(customResourceFacade, times(1)).replaceWithLock(any()); - } - - @Test - void doesNotCallDeleteIfMarkedForDeletionButNotOurFinalizer() { - removeFinalizers(testCustomResource); - markForDeletion(testCustomResource); - - eventDispatcher.handleEvent(executionScopeWithCREvent(Watcher.Action.MODIFIED, testCustomResource)); - - verify(customResourceFacade, never()).replaceWithLock(any()); - verify(controller, never()).deleteResource(eq(testCustomResource), any()); - } - - @Test - void executeControllerRegardlessGenerationInNonGenerationAwareMode() { - eventDispatcher.handleEvent(executionScopeWithCREvent(Watcher.Action.MODIFIED, testCustomResource)); - eventDispatcher.handleEvent(executionScopeWithCREvent(Watcher.Action.MODIFIED, testCustomResource)); - - verify(controller, times(2)).createOrUpdateResource(eq(testCustomResource), any()); - } - - private void markForDeletion(CustomResource customResource) { - customResource.getMetadata().setDeletionTimestamp("2019-8-10"); - } - - private void removeFinalizers(CustomResource customResource) { - customResource.getMetadata().getFinalizers().clear(); - } - - public ExecutionScope executionScopeWithCREvent(Watcher.Action action, CustomResource resource, Event... otherEvents) { - CustomResourceEvent event = new CustomResourceEvent(action, resource, null); - List eventList = new ArrayList<>(1 + otherEvents.length); - eventList.add(event); - eventList.addAll(Arrays.asList(otherEvents)); - return new ExecutionScope(eventList, resource); - } - + private static final String DEFAULT_FINALIZER = "finalizer"; + private CustomResource testCustomResource; + private EventDispatcher eventDispatcher; + private ResourceController controller = mock(ResourceController.class); + private EventDispatcher.CustomResourceFacade customResourceFacade = + mock(EventDispatcher.CustomResourceFacade.class); + + @BeforeEach + void setup() { + eventDispatcher = new EventDispatcher(controller, DEFAULT_FINALIZER, customResourceFacade); + + testCustomResource = TestUtils.testCustomResource(); + testCustomResource + .getMetadata() + .setFinalizers(new ArrayList<>(Collections.singletonList(DEFAULT_FINALIZER))); + + when(controller.createOrUpdateResource(eq(testCustomResource), any())) + .thenReturn(UpdateControl.updateCustomResource(testCustomResource)); + when(controller.deleteResource(eq(testCustomResource), any())) + .thenReturn(DeleteControl.DEFAULT_DELETE); + when(customResourceFacade.replaceWithLock(any())).thenReturn(null); + } + + @Test + void callCreateOrUpdateOnNewResource() { + eventDispatcher.handleEvent( + executionScopeWithCREvent(Watcher.Action.ADDED, testCustomResource)); + verify(controller, times(1)) + .createOrUpdateResource(ArgumentMatchers.eq(testCustomResource), any()); + } + + @Test + void updatesOnlyStatusSubResource() { + when(controller.createOrUpdateResource(eq(testCustomResource), any())) + .thenReturn(UpdateControl.updateStatusSubResource(testCustomResource)); + + eventDispatcher.handleEvent( + executionScopeWithCREvent(Watcher.Action.ADDED, testCustomResource)); + + verify(customResourceFacade, times(1)).updateStatus(testCustomResource); + verify(customResourceFacade, never()).replaceWithLock(any()); + } + + @Test + void callCreateOrUpdateOnModifiedResource() { + eventDispatcher.handleEvent( + executionScopeWithCREvent(Watcher.Action.MODIFIED, testCustomResource)); + verify(controller, times(1)) + .createOrUpdateResource(ArgumentMatchers.eq(testCustomResource), any()); + } + + @Test + void adsDefaultFinalizerOnCreateIfNotThere() { + eventDispatcher.handleEvent( + executionScopeWithCREvent(Watcher.Action.MODIFIED, testCustomResource)); + verify(controller, times(1)) + .createOrUpdateResource( + argThat( + testCustomResource -> + testCustomResource.getMetadata().getFinalizers().contains(DEFAULT_FINALIZER)), + any()); + } + + @Test + void callsDeleteIfObjectHasFinalizerAndMarkedForDelete() { + testCustomResource.getMetadata().setDeletionTimestamp("2019-8-10"); + testCustomResource.getMetadata().getFinalizers().add(DEFAULT_FINALIZER); + + eventDispatcher.handleEvent( + executionScopeWithCREvent(Watcher.Action.MODIFIED, testCustomResource)); + + verify(controller, times(1)).deleteResource(eq(testCustomResource), any()); + } + + /** Note that there could be more finalizers. Out of our control. */ + @Test + void callDeleteOnControllerIfMarkedForDeletionButThereIsNoDefaultFinalizer() { + markForDeletion(testCustomResource); + + eventDispatcher.handleEvent( + executionScopeWithCREvent(Watcher.Action.MODIFIED, testCustomResource)); + + verify(controller).deleteResource(eq(testCustomResource), any()); + } + + @Test + void removesDefaultFinalizerOnDelete() { + markForDeletion(testCustomResource); + + eventDispatcher.handleEvent( + executionScopeWithCREvent(Watcher.Action.MODIFIED, testCustomResource)); + + assertEquals(0, testCustomResource.getMetadata().getFinalizers().size()); + verify(customResourceFacade, times(1)).replaceWithLock(any()); + } + + @Test + void doesNotRemovesTheFinalizerIfTheDeleteNotMethodInstructsIt() { + when(controller.deleteResource(eq(testCustomResource), any())) + .thenReturn(DeleteControl.NO_FINALIZER_REMOVAL); + markForDeletion(testCustomResource); + + eventDispatcher.handleEvent( + executionScopeWithCREvent(Watcher.Action.MODIFIED, testCustomResource)); + + assertEquals(1, testCustomResource.getMetadata().getFinalizers().size()); + verify(customResourceFacade, never()).replaceWithLock(any()); + } + + @Test + void doesNotUpdateTheResourceIfNoUpdateUpdateControl() { + when(controller.createOrUpdateResource(eq(testCustomResource), any())) + .thenReturn(UpdateControl.noUpdate()); + + eventDispatcher.handleEvent( + executionScopeWithCREvent(Watcher.Action.MODIFIED, testCustomResource)); + verify(customResourceFacade, never()).replaceWithLock(any()); + verify(customResourceFacade, never()).updateStatus(testCustomResource); + } + + @Test + void addsFinalizerIfNotMarkedForDeletionAndEmptyCustomResourceReturned() { + removeFinalizers(testCustomResource); + when(controller.createOrUpdateResource(eq(testCustomResource), any())) + .thenReturn(UpdateControl.noUpdate()); + + eventDispatcher.handleEvent( + executionScopeWithCREvent(Watcher.Action.MODIFIED, testCustomResource)); + + assertEquals(1, testCustomResource.getMetadata().getFinalizers().size()); + verify(customResourceFacade, times(1)).replaceWithLock(any()); + } + + @Test + void doesNotCallDeleteIfMarkedForDeletionButNotOurFinalizer() { + removeFinalizers(testCustomResource); + markForDeletion(testCustomResource); + + eventDispatcher.handleEvent( + executionScopeWithCREvent(Watcher.Action.MODIFIED, testCustomResource)); + + verify(customResourceFacade, never()).replaceWithLock(any()); + verify(controller, never()).deleteResource(eq(testCustomResource), any()); + } + + @Test + void executeControllerRegardlessGenerationInNonGenerationAwareMode() { + eventDispatcher.handleEvent( + executionScopeWithCREvent(Watcher.Action.MODIFIED, testCustomResource)); + eventDispatcher.handleEvent( + executionScopeWithCREvent(Watcher.Action.MODIFIED, testCustomResource)); + + verify(controller, times(2)).createOrUpdateResource(eq(testCustomResource), any()); + } + + private void markForDeletion(CustomResource customResource) { + customResource.getMetadata().setDeletionTimestamp("2019-8-10"); + } + + private void removeFinalizers(CustomResource customResource) { + customResource.getMetadata().getFinalizers().clear(); + } + + public ExecutionScope executionScopeWithCREvent( + Watcher.Action action, CustomResource resource, Event... otherEvents) { + CustomResourceEvent event = new CustomResourceEvent(action, resource, null); + List eventList = new ArrayList<>(1 + otherEvents.length); + eventList.add(event); + eventList.addAll(Arrays.asList(otherEvents)); + return new ExecutionScope(eventList, resource); + } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/EventSourceIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/EventSourceIT.java index 53b5adadaa..4086ef758a 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/EventSourceIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/EventSourceIT.java @@ -1,5 +1,9 @@ package io.javaoperatorsdk.operator; +import static io.javaoperatorsdk.operator.IntegrationTestSupport.TEST_NAMESPACE; +import static io.javaoperatorsdk.operator.sample.event.EventSourceTestCustomResourceController.*; +import static org.assertj.core.api.Assertions.assertThat; + import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.client.DefaultKubernetesClient; import io.fabric8.kubernetes.client.KubernetesClient; @@ -12,48 +16,46 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static io.javaoperatorsdk.operator.IntegrationTestSupport.TEST_NAMESPACE; -import static io.javaoperatorsdk.operator.sample.event.EventSourceTestCustomResourceController.*; -import static org.assertj.core.api.Assertions.assertThat; - @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class EventSourceIT { - private static final Logger log = LoggerFactory.getLogger(EventSourceIT.class); - - public static final int EXPECTED_TIMER_EVENT_COUNT = 3; - private IntegrationTestSupport integrationTestSupport = new IntegrationTestSupport(); - - @BeforeEach - public void initAndCleanup() { - KubernetesClient k8sClient = new DefaultKubernetesClient(); - integrationTestSupport.initialize(k8sClient, new EventSourceTestCustomResourceController(), - "eventsource-test-crd.yaml"); - integrationTestSupport.cleanup(); - } - - @Test - public void receivingPeriodicEvents() { - integrationTestSupport.teardownIfSuccess(() -> { - EventSourceTestCustomResource resource = createTestCustomResource("1"); - integrationTestSupport.getCrOperations().inNamespace(TEST_NAMESPACE).create(resource); - - Thread.sleep(TIMER_DELAY + EXPECTED_TIMER_EVENT_COUNT * TIMER_PERIOD); - - assertThat(integrationTestSupport.numberOfControllerExecutions()).isGreaterThanOrEqualTo(EXPECTED_TIMER_EVENT_COUNT + 1); + private static final Logger log = LoggerFactory.getLogger(EventSourceIT.class); + + public static final int EXPECTED_TIMER_EVENT_COUNT = 3; + private IntegrationTestSupport integrationTestSupport = new IntegrationTestSupport(); + + @BeforeEach + public void initAndCleanup() { + KubernetesClient k8sClient = new DefaultKubernetesClient(); + integrationTestSupport.initialize( + k8sClient, new EventSourceTestCustomResourceController(), "eventsource-test-crd.yaml"); + integrationTestSupport.cleanup(); + } + + @Test + public void receivingPeriodicEvents() { + integrationTestSupport.teardownIfSuccess( + () -> { + EventSourceTestCustomResource resource = createTestCustomResource("1"); + integrationTestSupport.getCrOperations().inNamespace(TEST_NAMESPACE).create(resource); + + Thread.sleep(TIMER_DELAY + EXPECTED_TIMER_EVENT_COUNT * TIMER_PERIOD); + + assertThat(integrationTestSupport.numberOfControllerExecutions()) + .isGreaterThanOrEqualTo(EXPECTED_TIMER_EVENT_COUNT + 1); }); - } - - public EventSourceTestCustomResource createTestCustomResource(String id) { - EventSourceTestCustomResource resource = new EventSourceTestCustomResource(); - resource.setMetadata(new ObjectMetaBuilder() - .withName("eventsource-" + id) - .withNamespace(TEST_NAMESPACE) - .withFinalizers(FINALIZER_NAME) - .build()); - resource.setKind("Eventsourcesample"); - resource.setSpec(new EventSourceTestCustomResourceSpec()); - resource.getSpec().setValue(id); - return resource; - } - + } + + public EventSourceTestCustomResource createTestCustomResource(String id) { + EventSourceTestCustomResource resource = new EventSourceTestCustomResource(); + resource.setMetadata( + new ObjectMetaBuilder() + .withName("eventsource-" + id) + .withNamespace(TEST_NAMESPACE) + .withFinalizers(FINALIZER_NAME) + .build()); + resource.setKind("Eventsourcesample"); + resource.setSpec(new EventSourceTestCustomResourceSpec()); + resource.getSpec().setValue(id); + return resource; + } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/IntegrationTestSupport.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/IntegrationTestSupport.java index a8330efebb..bc6ef2db01 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/IntegrationTestSupport.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/IntegrationTestSupport.java @@ -1,8 +1,8 @@ package io.javaoperatorsdk.operator; -import io.javaoperatorsdk.operator.api.ResourceController; -import io.javaoperatorsdk.operator.sample.simple.TestCustomResource; -import io.javaoperatorsdk.operator.sample.simple.TestCustomResourceSpec; +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + import io.fabric8.kubernetes.api.model.Namespace; import io.fabric8.kubernetes.api.model.NamespaceBuilder; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; @@ -15,146 +15,173 @@ import io.fabric8.kubernetes.client.dsl.Resource; import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext; import io.fabric8.kubernetes.client.utils.Serialization; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - +import io.javaoperatorsdk.operator.api.ResourceController; +import io.javaoperatorsdk.operator.sample.simple.TestCustomResource; +import io.javaoperatorsdk.operator.sample.simple.TestCustomResourceSpec; import java.io.IOException; import java.io.InputStream; import java.util.concurrent.TimeUnit; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.awaitility.Awaitility.await; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class IntegrationTestSupport { - public static final String TEST_NAMESPACE = "java-operator-sdk-int-test"; - public static final String TEST_CUSTOM_RESOURCE_PREFIX = "test-custom-resource-"; - private final static Logger log = LoggerFactory.getLogger(IntegrationTestSupport.class); - private KubernetesClient k8sClient; - private MixedOperation> crOperations; - private Operator operator; - private ResourceController controller; - - public void initialize(KubernetesClient k8sClient, ResourceController controller, String crdPath) { - log.info("Initializing integration test in namespace {}", TEST_NAMESPACE); - this.k8sClient = k8sClient; - CustomResourceDefinition crd = loadCRDAndApplyToCluster(crdPath); - CustomResourceDefinitionContext crdContext = CustomResourceDefinitionContext.fromCrd(crd); - this.controller = controller; - - Class doneableClass = ControllerUtils.getCustomResourceDoneableClass(controller); - Class customResourceClass = ControllerUtils.getCustomResourceClass(controller); - crOperations = k8sClient.customResources(crdContext, customResourceClass, CustomResourceList.class, doneableClass); - - if (k8sClient.namespaces().withName(TEST_NAMESPACE).get() == null) { - k8sClient.namespaces().create(new NamespaceBuilder() - .withMetadata(new ObjectMetaBuilder().withName(TEST_NAMESPACE).build()).build()); - } - operator = new Operator(k8sClient); - operator.registerController(controller, TEST_NAMESPACE); - log.info("Operator is running with {}", controller.getClass().getCanonicalName()); + public static final String TEST_NAMESPACE = "java-operator-sdk-int-test"; + public static final String TEST_CUSTOM_RESOURCE_PREFIX = "test-custom-resource-"; + private static final Logger log = LoggerFactory.getLogger(IntegrationTestSupport.class); + private KubernetesClient k8sClient; + private MixedOperation< + CustomResource, + CustomResourceList, + CustomResourceDoneable, + Resource> + crOperations; + private Operator operator; + private ResourceController controller; + + public void initialize( + KubernetesClient k8sClient, ResourceController controller, String crdPath) { + log.info("Initializing integration test in namespace {}", TEST_NAMESPACE); + this.k8sClient = k8sClient; + CustomResourceDefinition crd = loadCRDAndApplyToCluster(crdPath); + CustomResourceDefinitionContext crdContext = CustomResourceDefinitionContext.fromCrd(crd); + this.controller = controller; + + Class doneableClass = ControllerUtils.getCustomResourceDoneableClass(controller); + Class customResourceClass = ControllerUtils.getCustomResourceClass(controller); + crOperations = + k8sClient.customResources( + crdContext, customResourceClass, CustomResourceList.class, doneableClass); + + if (k8sClient.namespaces().withName(TEST_NAMESPACE).get() == null) { + k8sClient + .namespaces() + .create( + new NamespaceBuilder() + .withMetadata(new ObjectMetaBuilder().withName(TEST_NAMESPACE).build()) + .build()); } - - public CustomResourceDefinition loadCRDAndApplyToCluster(String classPathYaml) { - CustomResourceDefinition crd = loadYaml(CustomResourceDefinition.class, classPathYaml); - k8sClient.customResourceDefinitions().createOrReplace(crd); - return crd; + operator = new Operator(k8sClient); + operator.registerController(controller, TEST_NAMESPACE); + log.info("Operator is running with {}", controller.getClass().getCanonicalName()); + } + + public CustomResourceDefinition loadCRDAndApplyToCluster(String classPathYaml) { + CustomResourceDefinition crd = loadYaml(CustomResourceDefinition.class, classPathYaml); + k8sClient.customResourceDefinitions().createOrReplace(crd); + return crd; + } + + public void cleanup() { + log.info("Cleaning up namespace {}", TEST_NAMESPACE); + + // we depend on the actual operator from the startup to handle the finalizers and clean up + // resources from previous test runs + crOperations.inNamespace(TEST_NAMESPACE).delete(crOperations.list().getItems()); + + await("all CRs cleaned up") + .atMost(60, TimeUnit.SECONDS) + .untilAsserted( + () -> { + assertThat(crOperations.inNamespace(TEST_NAMESPACE).list().getItems()).isEmpty(); + }); + + k8sClient + .configMaps() + .inNamespace(TEST_NAMESPACE) + .withLabel("managedBy", controller.getClass().getSimpleName()) + .delete(); + + await("all config maps cleaned up") + .atMost(60, TimeUnit.SECONDS) + .untilAsserted( + () -> { + assertThat( + k8sClient + .configMaps() + .inNamespace(TEST_NAMESPACE) + .withLabel("managedBy", controller.getClass().getSimpleName()) + .list() + .getItems() + .isEmpty()); + }); + + log.info("Cleaned up namespace " + TEST_NAMESPACE); + } + + /** + * Use this method to execute the cleanup of the integration test namespace only in case the test + * was successful. This is useful to keep the Kubernetes resources around to debug a failed test + * run. Unfortunately I couldn't make this work with standard JUnit methods as the @AfterAll + * method doesn't know if the tests succeeded or not. + * + * @param test The code of the actual test. + * @throws Exception if the test threw an exception. + */ + public void teardownIfSuccess(TestRun test) { + try { + test.run(); + + log.info("Deleting namespace {} and stopping operator", TEST_NAMESPACE); + Namespace namespace = k8sClient.namespaces().withName(TEST_NAMESPACE).get(); + if (namespace.getStatus().getPhase().equals("Active")) { + k8sClient.namespaces().withName(TEST_NAMESPACE).delete(); + } + await("namespace deleted") + .atMost(45, TimeUnit.SECONDS) + .until(() -> k8sClient.namespaces().withName(TEST_NAMESPACE).get() == null); + } catch (Exception e) { + throw new IllegalStateException(e); + } finally { + k8sClient.close(); } + } - public void cleanup() { - log.info("Cleaning up namespace {}", TEST_NAMESPACE); - - //we depend on the actual operator from the startup to handle the finalizers and clean up - //resources from previous test runs - crOperations.inNamespace(TEST_NAMESPACE).delete(crOperations.list().getItems()); - - await("all CRs cleaned up").atMost(60, TimeUnit.SECONDS) - .untilAsserted(() -> { - assertThat(crOperations.inNamespace(TEST_NAMESPACE).list().getItems()).isEmpty(); - - }); - - k8sClient.configMaps().inNamespace(TEST_NAMESPACE) - .withLabel("managedBy", controller.getClass().getSimpleName()) - .delete(); - - await("all config maps cleaned up").atMost(60, TimeUnit.SECONDS) - .untilAsserted(() -> { - assertThat(k8sClient.configMaps().inNamespace(TEST_NAMESPACE) - .withLabel("managedBy", controller.getClass().getSimpleName()) - .list().getItems().isEmpty()); - }); - - log.info("Cleaned up namespace " + TEST_NAMESPACE); - } - - /** - * Use this method to execute the cleanup of the integration test namespace only in case the test - * was successful. This is useful to keep the Kubernetes resources around to debug a failed test run. - * Unfortunately I couldn't make this work with standard JUnit methods as the @AfterAll method doesn't know - * if the tests succeeded or not. - * - * @param test The code of the actual test. - * @throws Exception if the test threw an exception. - */ - public void teardownIfSuccess(TestRun test) { - try { - test.run(); - - log.info("Deleting namespace {} and stopping operator", TEST_NAMESPACE); - Namespace namespace = k8sClient.namespaces().withName(TEST_NAMESPACE).get(); - if (namespace.getStatus().getPhase().equals("Active")) { - k8sClient.namespaces().withName(TEST_NAMESPACE).delete(); - } - await("namespace deleted").atMost(45, TimeUnit.SECONDS) - .until(() -> k8sClient.namespaces().withName(TEST_NAMESPACE).get() == null); - } catch (Exception e) { - throw new IllegalStateException(e); - } finally { - k8sClient.close(); - } - } - - public int numberOfControllerExecutions() { - return ((TestExecutionInfoProvider) controller).getNumberOfExecutions(); - } - - private T loadYaml(Class clazz, String yaml) { - try (InputStream is = getClass().getResourceAsStream(yaml)) { - return Serialization.unmarshal(is, clazz); - } catch (IOException ex) { - throw new IllegalStateException("Cannot find yaml on classpath: " + yaml); - } - } - - public TestCustomResource createTestCustomResource(String id) { - TestCustomResource resource = new TestCustomResource(); - resource.setMetadata(new ObjectMetaBuilder() - .withName(TEST_CUSTOM_RESOURCE_PREFIX + id) - .withNamespace(TEST_NAMESPACE) - .build()); - resource.setKind("CustomService"); - resource.setSpec(new TestCustomResourceSpec()); - resource.getSpec().setConfigMapName("test-config-map-" + id); - resource.getSpec().setKey("test-key"); - resource.getSpec().setValue(id); - return resource; - } - - public KubernetesClient getK8sClient() { - return k8sClient; - } - - public MixedOperation> getCrOperations() { - return crOperations; - } - - public Operator getOperator() { - return operator; - } + public int numberOfControllerExecutions() { + return ((TestExecutionInfoProvider) controller).getNumberOfExecutions(); + } - public interface TestRun { - void run() throws Exception; + private T loadYaml(Class clazz, String yaml) { + try (InputStream is = getClass().getResourceAsStream(yaml)) { + return Serialization.unmarshal(is, clazz); + } catch (IOException ex) { + throw new IllegalStateException("Cannot find yaml on classpath: " + yaml); } + } + + public TestCustomResource createTestCustomResource(String id) { + TestCustomResource resource = new TestCustomResource(); + resource.setMetadata( + new ObjectMetaBuilder() + .withName(TEST_CUSTOM_RESOURCE_PREFIX + id) + .withNamespace(TEST_NAMESPACE) + .build()); + resource.setKind("CustomService"); + resource.setSpec(new TestCustomResourceSpec()); + resource.getSpec().setConfigMapName("test-config-map-" + id); + resource.getSpec().setKey("test-key"); + resource.getSpec().setValue(id); + return resource; + } + + public KubernetesClient getK8sClient() { + return k8sClient; + } + + public MixedOperation< + CustomResource, + CustomResourceList, + CustomResourceDoneable, + Resource> + getCrOperations() { + return crOperations; + } + + public Operator getOperator() { + return operator; + } + + public interface TestRun { + void run() throws Exception; + } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/SubResourceUpdateIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/SubResourceUpdateIT.java index 9d64645105..5961201faf 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/SubResourceUpdateIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/SubResourceUpdateIT.java @@ -1,139 +1,151 @@ package io.javaoperatorsdk.operator; -import io.javaoperatorsdk.operator.sample.subresource.SubResourceTestCustomResource; -import io.javaoperatorsdk.operator.sample.subresource.SubResourceTestCustomResourceController; -import io.javaoperatorsdk.operator.sample.subresource.SubResourceTestCustomResourceSpec; +import static io.javaoperatorsdk.operator.IntegrationTestSupport.TEST_NAMESPACE; +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.client.DefaultKubernetesClient; import io.fabric8.kubernetes.client.KubernetesClient; -import io.javaoperatorsdk.operator.api.Controller; +import io.javaoperatorsdk.operator.sample.subresource.SubResourceTestCustomResource; +import io.javaoperatorsdk.operator.sample.subresource.SubResourceTestCustomResourceController; +import io.javaoperatorsdk.operator.sample.subresource.SubResourceTestCustomResourceSpec; import io.javaoperatorsdk.operator.sample.subresource.SubResourceTestCustomResourceStatus; +import java.util.Collections; +import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; -import java.util.Collections; -import java.util.concurrent.TimeUnit; - -import static io.javaoperatorsdk.operator.IntegrationTestSupport.TEST_NAMESPACE; -import static org.assertj.core.api.Assertions.assertThat; -import static org.awaitility.Awaitility.await; - @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class SubResourceUpdateIT { - private IntegrationTestSupport integrationTestSupport = new IntegrationTestSupport(); - - @BeforeEach - public void initAndCleanup() { - KubernetesClient k8sClient = new DefaultKubernetesClient(); - integrationTestSupport.initialize(k8sClient, new SubResourceTestCustomResourceController(), - "subresource-test-crd.yaml"); - integrationTestSupport.cleanup(); - } - - @Test - public void updatesSubResourceStatus() { - integrationTestSupport.teardownIfSuccess(() -> { - SubResourceTestCustomResource resource = createTestCustomResource("1"); - integrationTestSupport.getCrOperations().inNamespace(TEST_NAMESPACE).create(resource); - - awaitStatusUpdated(resource.getMetadata().getName()); - // wait for sure, there are no more events - waitXms(200); - // there is no event on status update processed - assertThat(integrationTestSupport.numberOfControllerExecutions()).isEqualTo(2); + private IntegrationTestSupport integrationTestSupport = new IntegrationTestSupport(); + + @BeforeEach + public void initAndCleanup() { + KubernetesClient k8sClient = new DefaultKubernetesClient(); + integrationTestSupport.initialize( + k8sClient, new SubResourceTestCustomResourceController(), "subresource-test-crd.yaml"); + integrationTestSupport.cleanup(); + } + + @Test + public void updatesSubResourceStatus() { + integrationTestSupport.teardownIfSuccess( + () -> { + SubResourceTestCustomResource resource = createTestCustomResource("1"); + integrationTestSupport.getCrOperations().inNamespace(TEST_NAMESPACE).create(resource); + + awaitStatusUpdated(resource.getMetadata().getName()); + // wait for sure, there are no more events + waitXms(200); + // there is no event on status update processed + assertThat(integrationTestSupport.numberOfControllerExecutions()).isEqualTo(2); }); - } - - @Test - public void updatesSubResourceStatusNoFinalizer() { - integrationTestSupport.teardownIfSuccess(() -> { - SubResourceTestCustomResource resource = createTestCustomResource("1"); - resource.getMetadata().setFinalizers(Collections.EMPTY_LIST); - - integrationTestSupport.getCrOperations().inNamespace(TEST_NAMESPACE).create(resource); - - awaitStatusUpdated(resource.getMetadata().getName()); - // wait for sure, there are no more events - waitXms(200); - // there is no event on status update processed - assertThat(integrationTestSupport.numberOfControllerExecutions()).isEqualTo(2); + } + + @Test + public void updatesSubResourceStatusNoFinalizer() { + integrationTestSupport.teardownIfSuccess( + () -> { + SubResourceTestCustomResource resource = createTestCustomResource("1"); + resource.getMetadata().setFinalizers(Collections.EMPTY_LIST); + + integrationTestSupport.getCrOperations().inNamespace(TEST_NAMESPACE).create(resource); + + awaitStatusUpdated(resource.getMetadata().getName()); + // wait for sure, there are no more events + waitXms(200); + // there is no event on status update processed + assertThat(integrationTestSupport.numberOfControllerExecutions()).isEqualTo(2); }); - } - - /** - * Note that we check on controller impl if there is finalizer on execution. - */ - @Test - public void ifNoFinalizerPresentFirstAddsTheFinalizerThenExecutesControllerAgain() { - integrationTestSupport.teardownIfSuccess(() -> { - SubResourceTestCustomResource resource = createTestCustomResource("1"); - resource.getMetadata().getFinalizers().clear(); - integrationTestSupport.getCrOperations().inNamespace(TEST_NAMESPACE).create(resource); - - awaitStatusUpdated(resource.getMetadata().getName()); - // wait for sure, there are no more events - waitXms(200); - // there is no event on status update processed - assertThat(integrationTestSupport.numberOfControllerExecutions()).isEqualTo(2); + } + + /** Note that we check on controller impl if there is finalizer on execution. */ + @Test + public void ifNoFinalizerPresentFirstAddsTheFinalizerThenExecutesControllerAgain() { + integrationTestSupport.teardownIfSuccess( + () -> { + SubResourceTestCustomResource resource = createTestCustomResource("1"); + resource.getMetadata().getFinalizers().clear(); + integrationTestSupport.getCrOperations().inNamespace(TEST_NAMESPACE).create(resource); + + awaitStatusUpdated(resource.getMetadata().getName()); + // wait for sure, there are no more events + waitXms(200); + // there is no event on status update processed + assertThat(integrationTestSupport.numberOfControllerExecutions()).isEqualTo(2); }); - } - - /** - * Not that here status sub-resource update will fail on optimistic locking. This solves a tricky situation: - * If this would not happen (no optimistic locking on status sub-resource) we could receive and store an event - * while processing the controller method. But this event would always fail since its resource version is outdated - * already. - */ - @Test - public void updateCustomResourceAfterSubResourceChange() { - integrationTestSupport.teardownIfSuccess(() -> { - SubResourceTestCustomResource resource = createTestCustomResource("1"); - integrationTestSupport.getCrOperations().inNamespace(TEST_NAMESPACE).create(resource); - - resource.getSpec().setValue("new value"); - integrationTestSupport.getCrOperations().inNamespace(TEST_NAMESPACE).createOrReplace(resource); - - awaitStatusUpdated(resource.getMetadata().getName()); - - // wait for sure, there are no more events - waitXms(200); - // there is no event on status update processed - assertThat(integrationTestSupport.numberOfControllerExecutions()).isEqualTo(3); + } + + /** + * Not that here status sub-resource update will fail on optimistic locking. This solves a tricky + * situation: If this would not happen (no optimistic locking on status sub-resource) we could + * receive and store an event while processing the controller method. But this event would always + * fail since its resource version is outdated already. + */ + @Test + public void updateCustomResourceAfterSubResourceChange() { + integrationTestSupport.teardownIfSuccess( + () -> { + SubResourceTestCustomResource resource = createTestCustomResource("1"); + integrationTestSupport.getCrOperations().inNamespace(TEST_NAMESPACE).create(resource); + + resource.getSpec().setValue("new value"); + integrationTestSupport + .getCrOperations() + .inNamespace(TEST_NAMESPACE) + .createOrReplace(resource); + + awaitStatusUpdated(resource.getMetadata().getName()); + + // wait for sure, there are no more events + waitXms(200); + // there is no event on status update processed + assertThat(integrationTestSupport.numberOfControllerExecutions()).isEqualTo(3); }); - - } - - void awaitStatusUpdated(String name) { - await("cr status updated").atMost(5, TimeUnit.SECONDS) - .untilAsserted(() -> { - SubResourceTestCustomResource cr = (SubResourceTestCustomResource) integrationTestSupport.getCrOperations().inNamespace(TEST_NAMESPACE).withName(name).get(); - assertThat(cr.getMetadata().getFinalizers()).hasSize(1); - assertThat(cr).isNotNull(); - assertThat(cr.getStatus()).isNotNull(); - assertThat(cr.getStatus().getState()).isEqualTo(SubResourceTestCustomResourceStatus.State.SUCCESS); - }); - } - - public SubResourceTestCustomResource createTestCustomResource(String id) { - SubResourceTestCustomResource resource = new SubResourceTestCustomResource(); - resource.setMetadata(new ObjectMetaBuilder() - .withName("subresource-" + id) - .withNamespace(TEST_NAMESPACE) - .withFinalizers(SubResourceTestCustomResourceController.FINALIZER_NAME) - .build()); - resource.setKind("SubresourceSample"); - resource.setSpec(new SubResourceTestCustomResourceSpec()); - resource.getSpec().setValue(id); - return resource; - } - - private void waitXms(int x) { - try { - Thread.sleep(x); - } catch (InterruptedException e) { - throw new IllegalStateException(e); - } + } + + void awaitStatusUpdated(String name) { + await("cr status updated") + .atMost(5, TimeUnit.SECONDS) + .untilAsserted( + () -> { + SubResourceTestCustomResource cr = + (SubResourceTestCustomResource) + integrationTestSupport + .getCrOperations() + .inNamespace(TEST_NAMESPACE) + .withName(name) + .get(); + assertThat(cr.getMetadata().getFinalizers()).hasSize(1); + assertThat(cr).isNotNull(); + assertThat(cr.getStatus()).isNotNull(); + assertThat(cr.getStatus().getState()) + .isEqualTo(SubResourceTestCustomResourceStatus.State.SUCCESS); + }); + } + + public SubResourceTestCustomResource createTestCustomResource(String id) { + SubResourceTestCustomResource resource = new SubResourceTestCustomResource(); + resource.setMetadata( + new ObjectMetaBuilder() + .withName("subresource-" + id) + .withNamespace(TEST_NAMESPACE) + .withFinalizers(SubResourceTestCustomResourceController.FINALIZER_NAME) + .build()); + resource.setKind("SubresourceSample"); + resource.setSpec(new SubResourceTestCustomResourceSpec()); + resource.getSpec().setValue(id); + return resource; + } + + private void waitXms(int x) { + try { + Thread.sleep(x); + } catch (InterruptedException e) { + throw new IllegalStateException(e); } + } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/TestExecutionInfoProvider.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/TestExecutionInfoProvider.java index 4a6548c727..caf0211df4 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/TestExecutionInfoProvider.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/TestExecutionInfoProvider.java @@ -2,6 +2,5 @@ public interface TestExecutionInfoProvider { - int getNumberOfExecutions(); - + int getNumberOfExecutions(); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/TestUtils.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/TestUtils.java index c03d764187..8189b9c574 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/TestUtils.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/TestUtils.java @@ -3,32 +3,32 @@ import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.javaoperatorsdk.operator.sample.simple.TestCustomResource; import io.javaoperatorsdk.operator.sample.simple.TestCustomResourceSpec; - import java.util.HashMap; import java.util.UUID; public class TestUtils { - public static final String TEST_CUSTOM_RESOURCE_NAME = "test-custom-resource"; + public static final String TEST_CUSTOM_RESOURCE_NAME = "test-custom-resource"; - public static TestCustomResource testCustomResource() { - return testCustomResource(UUID.randomUUID().toString()); - } + public static TestCustomResource testCustomResource() { + return testCustomResource(UUID.randomUUID().toString()); + } - public static TestCustomResource testCustomResource(String uid) { - TestCustomResource resource = new TestCustomResource(); - resource.setMetadata(new ObjectMetaBuilder() - .withName(TEST_CUSTOM_RESOURCE_NAME) - .withUid(uid) - .withGeneration(1L) - .withNamespace(IntegrationTestSupport.TEST_NAMESPACE) - .build()); - resource.getMetadata().setAnnotations(new HashMap<>()); - resource.setKind("CustomService"); - resource.setSpec(new TestCustomResourceSpec()); - resource.getSpec().setConfigMapName("test-config-map"); - resource.getSpec().setKey("test-key"); - resource.getSpec().setValue("test-value"); - return resource; - } + public static TestCustomResource testCustomResource(String uid) { + TestCustomResource resource = new TestCustomResource(); + resource.setMetadata( + new ObjectMetaBuilder() + .withName(TEST_CUSTOM_RESOURCE_NAME) + .withUid(uid) + .withGeneration(1L) + .withNamespace(IntegrationTestSupport.TEST_NAMESPACE) + .build()); + resource.getMetadata().setAnnotations(new HashMap<>()); + resource.setKind("CustomService"); + resource.setSpec(new TestCustomResourceSpec()); + resource.getSpec().setConfigMapName("test-config-map"); + resource.getSpec().setKey("test-key"); + resource.getSpec().setValue("test-value"); + return resource; + } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/processing/DefaultEventHandlerTest.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/processing/DefaultEventHandlerTest.java index 7f8d9e9291..46318a973d 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/processing/DefaultEventHandlerTest.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/processing/DefaultEventHandlerTest.java @@ -1,123 +1,129 @@ package io.javaoperatorsdk.operator.processing; +import static io.javaoperatorsdk.operator.TestUtils.testCustomResource; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; + import io.fabric8.kubernetes.client.Watcher; import io.javaoperatorsdk.operator.processing.event.DefaultEventSourceManager; import io.javaoperatorsdk.operator.processing.event.Event; import io.javaoperatorsdk.operator.processing.event.internal.CustomResourceEvent; import io.javaoperatorsdk.operator.processing.event.internal.TimerEvent; import io.javaoperatorsdk.operator.sample.simple.TestCustomResource; +import java.util.List; +import java.util.UUID; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.stubbing.Answer; -import java.util.List; -import java.util.UUID; - -import static io.javaoperatorsdk.operator.TestUtils.testCustomResource; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.*; - class DefaultEventHandlerTest { - public static final int FAKE_CONTROLLER_EXECUTION_DURATION = 250; - public static final int SEPARATE_EXECUTION_TIMEOUT = 450; - private EventDispatcher eventDispatcherMock = mock(EventDispatcher.class); - private CustomResourceCache customResourceCache = new CustomResourceCache(); - private DefaultEventHandler defaultEventHandler = new DefaultEventHandler(customResourceCache, eventDispatcherMock, "Test"); - private DefaultEventSourceManager defaultEventSourceManagerMock = mock(DefaultEventSourceManager.class); - - @BeforeEach - public void setup() { - defaultEventHandler.setDefaultEventSourceManager(defaultEventSourceManagerMock); - } - - @Test - public void dispatchesEventsIfNoExecutionInProgress() { - defaultEventHandler.handleEvent(prepareCREvent()); - - verify(eventDispatcherMock, timeout(50).times(1)).handleEvent(any()); - } - - @Test - public void skipProcessingIfLatestCustomResourceNotInCache() { - Event event = prepareCREvent(); - customResourceCache.cleanup(event.getRelatedCustomResourceUid()); - - defaultEventHandler.handleEvent(event); - - verify(eventDispatcherMock, timeout(50).times(0)).handleEvent(any()); - } - - @Test - public void ifExecutionInProgressWaitsUntilItsFinished() throws InterruptedException { - String resourceUid = eventAlreadyUnderProcessing(); - - defaultEventHandler.handleEvent(nonCREvent(resourceUid)); - - verify(eventDispatcherMock, timeout(SEPARATE_EXECUTION_TIMEOUT).times(1)).handleEvent(any()); - } - - @Test - public void buffersAllIncomingEventsWhileControllerInExecution() { - String resourceUid = eventAlreadyUnderProcessing(); - - defaultEventHandler.handleEvent(nonCREvent(resourceUid)); - defaultEventHandler.handleEvent(prepareCREvent(resourceUid)); - - ArgumentCaptor captor = ArgumentCaptor.forClass(ExecutionScope.class); - verify(eventDispatcherMock, timeout(SEPARATE_EXECUTION_TIMEOUT).times(2)).handleEvent(captor.capture()); - List events = captor.getAllValues().get(1).getEvents(); - assertThat(events).hasSize(2); - assertThat(events.get(0)).isInstanceOf(TimerEvent.class); - assertThat(events.get(1)).isInstanceOf(CustomResourceEvent.class); - } - - @Test - public void cleanUpAfterDeleteEvent() { - TestCustomResource customResource = testCustomResource(); - customResourceCache.cacheResource(customResource); - CustomResourceEvent event = new CustomResourceEvent(Watcher.Action.DELETED, customResource, null); - String uid = customResource.getMetadata().getUid(); - - defaultEventHandler.handleEvent(event); - // todo awaitility? - waitMinimalTime(); - - verify(defaultEventSourceManagerMock, times(1)).cleanup(uid); - assertThat(customResourceCache.getLatestResource(uid)).isNotPresent(); - } - - private void waitMinimalTime() { - try { - Thread.sleep(50); - } catch (InterruptedException e) { - throw new IllegalStateException(e); - } - } - - private String eventAlreadyUnderProcessing() { - when(eventDispatcherMock.handleEvent(any())).then((Answer) invocationOnMock -> { - Thread.sleep(FAKE_CONTROLLER_EXECUTION_DURATION); - return PostExecutionControl.defaultDispatch(); - }); - Event event = prepareCREvent(); - defaultEventHandler.handleEvent(event); - return event.getRelatedCustomResourceUid(); - } - - private CustomResourceEvent prepareCREvent() { - return prepareCREvent(UUID.randomUUID().toString()); - } - - private CustomResourceEvent prepareCREvent(String uid) { - TestCustomResource customResource = testCustomResource(uid); - customResourceCache.cacheResource(customResource); - return new CustomResourceEvent(Watcher.Action.MODIFIED, customResource, null); - } - - private Event nonCREvent(String relatedCustomResourceUid) { - TimerEvent timerEvent = new TimerEvent(relatedCustomResourceUid, null); - return timerEvent; + public static final int FAKE_CONTROLLER_EXECUTION_DURATION = 250; + public static final int SEPARATE_EXECUTION_TIMEOUT = 450; + private EventDispatcher eventDispatcherMock = mock(EventDispatcher.class); + private CustomResourceCache customResourceCache = new CustomResourceCache(); + private DefaultEventHandler defaultEventHandler = + new DefaultEventHandler(customResourceCache, eventDispatcherMock, "Test"); + private DefaultEventSourceManager defaultEventSourceManagerMock = + mock(DefaultEventSourceManager.class); + + @BeforeEach + public void setup() { + defaultEventHandler.setDefaultEventSourceManager(defaultEventSourceManagerMock); + } + + @Test + public void dispatchesEventsIfNoExecutionInProgress() { + defaultEventHandler.handleEvent(prepareCREvent()); + + verify(eventDispatcherMock, timeout(50).times(1)).handleEvent(any()); + } + + @Test + public void skipProcessingIfLatestCustomResourceNotInCache() { + Event event = prepareCREvent(); + customResourceCache.cleanup(event.getRelatedCustomResourceUid()); + + defaultEventHandler.handleEvent(event); + + verify(eventDispatcherMock, timeout(50).times(0)).handleEvent(any()); + } + + @Test + public void ifExecutionInProgressWaitsUntilItsFinished() throws InterruptedException { + String resourceUid = eventAlreadyUnderProcessing(); + + defaultEventHandler.handleEvent(nonCREvent(resourceUid)); + + verify(eventDispatcherMock, timeout(SEPARATE_EXECUTION_TIMEOUT).times(1)).handleEvent(any()); + } + + @Test + public void buffersAllIncomingEventsWhileControllerInExecution() { + String resourceUid = eventAlreadyUnderProcessing(); + + defaultEventHandler.handleEvent(nonCREvent(resourceUid)); + defaultEventHandler.handleEvent(prepareCREvent(resourceUid)); + + ArgumentCaptor captor = ArgumentCaptor.forClass(ExecutionScope.class); + verify(eventDispatcherMock, timeout(SEPARATE_EXECUTION_TIMEOUT).times(2)) + .handleEvent(captor.capture()); + List events = captor.getAllValues().get(1).getEvents(); + assertThat(events).hasSize(2); + assertThat(events.get(0)).isInstanceOf(TimerEvent.class); + assertThat(events.get(1)).isInstanceOf(CustomResourceEvent.class); + } + + @Test + public void cleanUpAfterDeleteEvent() { + TestCustomResource customResource = testCustomResource(); + customResourceCache.cacheResource(customResource); + CustomResourceEvent event = + new CustomResourceEvent(Watcher.Action.DELETED, customResource, null); + String uid = customResource.getMetadata().getUid(); + + defaultEventHandler.handleEvent(event); + // todo awaitility? + waitMinimalTime(); + + verify(defaultEventSourceManagerMock, times(1)).cleanup(uid); + assertThat(customResourceCache.getLatestResource(uid)).isNotPresent(); + } + + private void waitMinimalTime() { + try { + Thread.sleep(50); + } catch (InterruptedException e) { + throw new IllegalStateException(e); } -} \ No newline at end of file + } + + private String eventAlreadyUnderProcessing() { + when(eventDispatcherMock.handleEvent(any())) + .then( + (Answer) + invocationOnMock -> { + Thread.sleep(FAKE_CONTROLLER_EXECUTION_DURATION); + return PostExecutionControl.defaultDispatch(); + }); + Event event = prepareCREvent(); + defaultEventHandler.handleEvent(event); + return event.getRelatedCustomResourceUid(); + } + + private CustomResourceEvent prepareCREvent() { + return prepareCREvent(UUID.randomUUID().toString()); + } + + private CustomResourceEvent prepareCREvent(String uid) { + TestCustomResource customResource = testCustomResource(uid); + customResourceCache.cacheResource(customResource); + return new CustomResourceEvent(Watcher.Action.MODIFIED, customResource, null); + } + + private Event nonCREvent(String relatedCustomResourceUid) { + TimerEvent timerEvent = new TimerEvent(relatedCustomResourceUid, null); + return timerEvent; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/processing/EventBufferTest.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/processing/EventBufferTest.java index 59e0f3cb1d..1351236c74 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/processing/EventBufferTest.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/processing/EventBufferTest.java @@ -1,59 +1,57 @@ package io.javaoperatorsdk.operator.processing; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; + import io.javaoperatorsdk.operator.processing.event.Event; import io.javaoperatorsdk.operator.processing.event.internal.TimerEvent; -import org.junit.jupiter.api.Test; - import java.util.List; import java.util.UUID; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; class EventBufferTest { - private EventBuffer eventBuffer = new EventBuffer(); - - String uid = UUID.randomUUID().toString(); - Event testEvent1 = new TimerEvent(uid, null); - Event testEvent2 = new TimerEvent(uid, null); + private EventBuffer eventBuffer = new EventBuffer(); - @Test - public void storesEvents() { - eventBuffer.addEvent(testEvent1); - eventBuffer.addEvent(testEvent2); + String uid = UUID.randomUUID().toString(); + Event testEvent1 = new TimerEvent(uid, null); + Event testEvent2 = new TimerEvent(uid, null); - assertThat(eventBuffer.containsEvents(testEvent1.getRelatedCustomResourceUid())).isTrue(); - List events = eventBuffer.getAndRemoveEventsForExecution(uid); - assertThat(events).hasSize(2); - } + @Test + public void storesEvents() { + eventBuffer.addEvent(testEvent1); + eventBuffer.addEvent(testEvent2); - @Test - public void getsAndRemovesEvents() { - eventBuffer.addEvent(testEvent1); - eventBuffer.addEvent(testEvent2); + assertThat(eventBuffer.containsEvents(testEvent1.getRelatedCustomResourceUid())).isTrue(); + List events = eventBuffer.getAndRemoveEventsForExecution(uid); + assertThat(events).hasSize(2); + } - List events = eventBuffer.getAndRemoveEventsForExecution(uid); - assertThat(events).hasSize(2); - assertThat(events).contains(testEvent1,testEvent2); - } + @Test + public void getsAndRemovesEvents() { + eventBuffer.addEvent(testEvent1); + eventBuffer.addEvent(testEvent2); - @Test - public void checksIfThereAreStoredEvents() { - eventBuffer.addEvent(testEvent1); - eventBuffer.addEvent(testEvent2); + List events = eventBuffer.getAndRemoveEventsForExecution(uid); + assertThat(events).hasSize(2); + assertThat(events).contains(testEvent1, testEvent2); + } - assertThat(eventBuffer.containsEvents(testEvent1.getRelatedCustomResourceUid())).isTrue(); - } + @Test + public void checksIfThereAreStoredEvents() { + eventBuffer.addEvent(testEvent1); + eventBuffer.addEvent(testEvent2); - @Test - public void canClearEvents() { - eventBuffer.addEvent(testEvent1); - eventBuffer.addEvent(testEvent2); + assertThat(eventBuffer.containsEvents(testEvent1.getRelatedCustomResourceUid())).isTrue(); + } - eventBuffer.cleanup(uid); + @Test + public void canClearEvents() { + eventBuffer.addEvent(testEvent1); + eventBuffer.addEvent(testEvent2); - assertThat(eventBuffer.containsEvents(testEvent1.getRelatedCustomResourceUid())).isFalse(); - } + eventBuffer.cleanup(uid); -} \ No newline at end of file + assertThat(eventBuffer.containsEvents(testEvent1.getRelatedCustomResourceUid())).isFalse(); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/processing/annotation/ControllerAnnotationProcessorTest.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/processing/annotation/ControllerAnnotationProcessorTest.java index 3e748270e2..a8f22fd26d 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/processing/annotation/ControllerAnnotationProcessorTest.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/processing/annotation/ControllerAnnotationProcessorTest.java @@ -2,35 +2,38 @@ import com.google.testing.compile.*; import com.google.testing.compile.Compiler; -import org.junit.jupiter.api.Test; - import javax.tools.JavaFileObject; +import org.junit.jupiter.api.Test; class ControllerAnnotationProcessorTest { - @Test - public void generateCorrectDoneableClassIfInterfaceIsSecond() { - Compilation compilation = Compiler.javac() - .withProcessors(new ControllerAnnotationProcessor()) - .compile(JavaFileObjects.forResource("ControllerImplemented2Interfaces.java")); - CompilationSubject.assertThat(compilation).succeeded(); - - final JavaFileObject expectedResource = JavaFileObjects.forResource("ControllerImplemented2InterfacesExpected.java"); - JavaFileObjectSubject.assertThat(compilation.generatedSourceFiles().get(0)).hasSourceEquivalentTo(expectedResource); - } - - @Test - public void generateCorrectDoneableClassIfThereIsAbstractBaseController() { - - Compilation compilation = Compiler.javac() - .withProcessors(new ControllerAnnotationProcessor()) - .compile( - JavaFileObjects.forResource("AbstractController.java"), - JavaFileObjects.forResource("ControllerImplementedIntermediateAbstractClass.java") - ); - CompilationSubject.assertThat(compilation).succeeded(); - - final JavaFileObject expectedResource = JavaFileObjects.forResource("ControllerImplementedIntermediateAbstractClassExpected.java"); - JavaFileObjectSubject.assertThat(compilation.generatedSourceFiles().get(0)).hasSourceEquivalentTo(expectedResource); - - } + @Test + public void generateCorrectDoneableClassIfInterfaceIsSecond() { + Compilation compilation = + Compiler.javac() + .withProcessors(new ControllerAnnotationProcessor()) + .compile(JavaFileObjects.forResource("ControllerImplemented2Interfaces.java")); + CompilationSubject.assertThat(compilation).succeeded(); + + final JavaFileObject expectedResource = + JavaFileObjects.forResource("ControllerImplemented2InterfacesExpected.java"); + JavaFileObjectSubject.assertThat(compilation.generatedSourceFiles().get(0)) + .hasSourceEquivalentTo(expectedResource); + } + + @Test + public void generateCorrectDoneableClassIfThereIsAbstractBaseController() { + + Compilation compilation = + Compiler.javac() + .withProcessors(new ControllerAnnotationProcessor()) + .compile( + JavaFileObjects.forResource("AbstractController.java"), + JavaFileObjects.forResource("ControllerImplementedIntermediateAbstractClass.java")); + CompilationSubject.assertThat(compilation).succeeded(); + + final JavaFileObject expectedResource = + JavaFileObjects.forResource("ControllerImplementedIntermediateAbstractClassExpected.java"); + JavaFileObjectSubject.assertThat(compilation.generatedSourceFiles().get(0)) + .hasSourceEquivalentTo(expectedResource); + } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/processing/event/DefaultEventSourceManagerTest.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/processing/event/DefaultEventSourceManagerTest.java index 0a9074c5d0..5f82a0ace8 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/processing/event/DefaultEventSourceManagerTest.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/processing/event/DefaultEventSourceManagerTest.java @@ -1,58 +1,61 @@ package io.javaoperatorsdk.operator.processing.event; +import static io.javaoperatorsdk.operator.processing.KubernetesResourceUtils.getUID; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.Mockito.*; + import io.fabric8.kubernetes.client.CustomResource; import io.javaoperatorsdk.operator.TestUtils; import io.javaoperatorsdk.operator.processing.DefaultEventHandler; import io.javaoperatorsdk.operator.processing.KubernetesResourceUtils; -import org.junit.jupiter.api.Test; - import java.util.Map; - -import static io.javaoperatorsdk.operator.processing.KubernetesResourceUtils.getUID; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.Mockito.*; +import org.junit.jupiter.api.Test; class DefaultEventSourceManagerTest { - public static final String CUSTOM_EVENT_SOURCE_NAME = "CustomEventSource"; - - private DefaultEventHandler defaultEventHandlerMock = mock(DefaultEventHandler.class); - private DefaultEventSourceManager defaultEventSourceManager = new DefaultEventSourceManager(defaultEventHandlerMock); - - @Test - public void registersEventSource() { - EventSource eventSource = mock(EventSource.class); - - defaultEventSourceManager.registerEventSource(CUSTOM_EVENT_SOURCE_NAME, eventSource); - - Map registeredSources = - defaultEventSourceManager.getRegisteredEventSources(); - assertThat(registeredSources.entrySet()).hasSize(1); - assertThat(registeredSources.get(CUSTOM_EVENT_SOURCE_NAME)).isEqualTo(eventSource); - verify(eventSource, times(1)).setEventHandler(eq(defaultEventHandlerMock)); - } - - @Test - public void throwExceptionIfRegisteringEventSourceWithSameName() { - EventSource eventSource = mock(EventSource.class); - EventSource eventSource2 = mock(EventSource.class); - - defaultEventSourceManager.registerEventSource(CUSTOM_EVENT_SOURCE_NAME, eventSource); - assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> { - defaultEventSourceManager.registerEventSource(CUSTOM_EVENT_SOURCE_NAME, eventSource2); - }); - } - - @Test - public void deRegistersEventSources() { - CustomResource customResource = TestUtils.testCustomResource(); - EventSource eventSource = mock(EventSource.class); - defaultEventSourceManager.registerEventSource(CUSTOM_EVENT_SOURCE_NAME, eventSource); - - defaultEventSourceManager.deRegisterCustomResourceFromEventSource(CUSTOM_EVENT_SOURCE_NAME, getUID(customResource)); - - verify(eventSource, times(1)).eventSourceDeRegisteredForResource(eq(KubernetesResourceUtils.getUID(customResource))); - } - -} \ No newline at end of file + public static final String CUSTOM_EVENT_SOURCE_NAME = "CustomEventSource"; + + private DefaultEventHandler defaultEventHandlerMock = mock(DefaultEventHandler.class); + private DefaultEventSourceManager defaultEventSourceManager = + new DefaultEventSourceManager(defaultEventHandlerMock); + + @Test + public void registersEventSource() { + EventSource eventSource = mock(EventSource.class); + + defaultEventSourceManager.registerEventSource(CUSTOM_EVENT_SOURCE_NAME, eventSource); + + Map registeredSources = + defaultEventSourceManager.getRegisteredEventSources(); + assertThat(registeredSources.entrySet()).hasSize(1); + assertThat(registeredSources.get(CUSTOM_EVENT_SOURCE_NAME)).isEqualTo(eventSource); + verify(eventSource, times(1)).setEventHandler(eq(defaultEventHandlerMock)); + } + + @Test + public void throwExceptionIfRegisteringEventSourceWithSameName() { + EventSource eventSource = mock(EventSource.class); + EventSource eventSource2 = mock(EventSource.class); + + defaultEventSourceManager.registerEventSource(CUSTOM_EVENT_SOURCE_NAME, eventSource); + assertThatExceptionOfType(IllegalStateException.class) + .isThrownBy( + () -> { + defaultEventSourceManager.registerEventSource(CUSTOM_EVENT_SOURCE_NAME, eventSource2); + }); + } + + @Test + public void deRegistersEventSources() { + CustomResource customResource = TestUtils.testCustomResource(); + EventSource eventSource = mock(EventSource.class); + defaultEventSourceManager.registerEventSource(CUSTOM_EVENT_SOURCE_NAME, eventSource); + + defaultEventSourceManager.deRegisterCustomResourceFromEventSource( + CUSTOM_EVENT_SOURCE_NAME, getUID(customResource)); + + verify(eventSource, times(1)) + .eventSourceDeRegisteredForResource(eq(KubernetesResourceUtils.getUID(customResource))); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/processing/event/EventListTest.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/processing/event/EventListTest.java index 1a34ad201d..131f205b0c 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/processing/event/EventListTest.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/processing/event/EventListTest.java @@ -1,24 +1,21 @@ package io.javaoperatorsdk.operator.processing.event; -import io.javaoperatorsdk.operator.processing.event.internal.TimerEvent; -import org.junit.jupiter.api.Test; - -import java.util.Arrays; - import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; +import io.javaoperatorsdk.operator.processing.event.internal.TimerEvent; +import java.util.Arrays; +import org.junit.jupiter.api.Test; + class EventListTest { - @Test - public void returnsLatestOfEventType() { - TimerEvent event2 = new TimerEvent("1", null); - EventList eventList = new EventList(Arrays.asList( - mock(Event.class), - new TimerEvent("2", null), - event2, - mock(Event.class))); + @Test + public void returnsLatestOfEventType() { + TimerEvent event2 = new TimerEvent("1", null); + EventList eventList = + new EventList( + Arrays.asList(mock(Event.class), new TimerEvent("2", null), event2, mock(Event.class))); - assertThat(eventList.getLatestOfType(TimerEvent.class).get()).isSameInstanceAs(event2); - } -} \ No newline at end of file + assertThat(eventList.getLatestOfType(TimerEvent.class).get()).isSameInstanceAs(event2); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/processing/event/internal/CustomResourceEventSourceTest.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/processing/event/internal/CustomResourceEventSourceTest.java index 0176945084..7080eeb3a3 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/processing/event/internal/CustomResourceEventSourceTest.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/processing/event/internal/CustomResourceEventSourceTest.java @@ -1,99 +1,96 @@ package io.javaoperatorsdk.operator.processing.event.internal; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + import io.fabric8.kubernetes.client.Watcher; import io.fabric8.kubernetes.client.dsl.MixedOperation; import io.javaoperatorsdk.operator.TestUtils; import io.javaoperatorsdk.operator.processing.CustomResourceCache; import io.javaoperatorsdk.operator.processing.event.EventHandler; import io.javaoperatorsdk.operator.sample.simple.TestCustomResource; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.time.LocalDate; import java.time.LocalDateTime; import java.util.Arrays; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; class CustomResourceEventSourceTest { - public static final String FINALIZER = "finalizer"; - CustomResourceCache customResourceCache = new CustomResourceCache(); - MixedOperation mixedOperation = mock(MixedOperation.class); - EventHandler eventHandler = mock(EventHandler.class); - - private CustomResourceEventSource customResourceEventSource = - CustomResourceEventSource.customResourceEventSourceForAllNamespaces(customResourceCache, mixedOperation, true, FINALIZER); - - @BeforeEach - public void setup() { - customResourceEventSource.setEventHandler(eventHandler); - } + public static final String FINALIZER = "finalizer"; + CustomResourceCache customResourceCache = new CustomResourceCache(); + MixedOperation mixedOperation = mock(MixedOperation.class); + EventHandler eventHandler = mock(EventHandler.class); - @Test - public void skipsEventHandlingIfGenerationNotIncreased() { - TestCustomResource customResource1 = TestUtils.testCustomResource(); - customResource1.getMetadata().setFinalizers(Arrays.asList(FINALIZER)); + private CustomResourceEventSource customResourceEventSource = + CustomResourceEventSource.customResourceEventSourceForAllNamespaces( + customResourceCache, mixedOperation, true, FINALIZER); - customResourceEventSource.eventReceived(Watcher.Action.MODIFIED, customResource1); - verify(eventHandler, times(1)).handleEvent(any()); + @BeforeEach + public void setup() { + customResourceEventSource.setEventHandler(eventHandler); + } - customResourceEventSource.eventReceived(Watcher.Action.MODIFIED, customResource1); - verify(eventHandler, times(1)).handleEvent(any()); - } + @Test + public void skipsEventHandlingIfGenerationNotIncreased() { + TestCustomResource customResource1 = TestUtils.testCustomResource(); + customResource1.getMetadata().setFinalizers(Arrays.asList(FINALIZER)); - @Test - public void dontSkipEventHandlingIfMarkedForDeletion() { - TestCustomResource customResource1 = TestUtils.testCustomResource(); + customResourceEventSource.eventReceived(Watcher.Action.MODIFIED, customResource1); + verify(eventHandler, times(1)).handleEvent(any()); - customResourceEventSource.eventReceived(Watcher.Action.MODIFIED, customResource1); - verify(eventHandler, times(1)).handleEvent(any()); + customResourceEventSource.eventReceived(Watcher.Action.MODIFIED, customResource1); + verify(eventHandler, times(1)).handleEvent(any()); + } - // mark for deletion - customResource1.getMetadata().setDeletionTimestamp(LocalDateTime.now().toString()); - customResourceEventSource.eventReceived(Watcher.Action.MODIFIED, customResource1); - verify(eventHandler, times(2)).handleEvent(any()); - } + @Test + public void dontSkipEventHandlingIfMarkedForDeletion() { + TestCustomResource customResource1 = TestUtils.testCustomResource(); + customResourceEventSource.eventReceived(Watcher.Action.MODIFIED, customResource1); + verify(eventHandler, times(1)).handleEvent(any()); - @Test - public void normalExecutionIfGenerationChanges() { - TestCustomResource customResource1 = TestUtils.testCustomResource(); + // mark for deletion + customResource1.getMetadata().setDeletionTimestamp(LocalDateTime.now().toString()); + customResourceEventSource.eventReceived(Watcher.Action.MODIFIED, customResource1); + verify(eventHandler, times(2)).handleEvent(any()); + } - customResourceEventSource.eventReceived(Watcher.Action.MODIFIED, customResource1); - verify(eventHandler, times(1)).handleEvent(any()); + @Test + public void normalExecutionIfGenerationChanges() { + TestCustomResource customResource1 = TestUtils.testCustomResource(); - customResource1.getMetadata().setGeneration(2L); - customResourceEventSource.eventReceived(Watcher.Action.MODIFIED, customResource1); - verify(eventHandler, times(2)).handleEvent(any()); - } + customResourceEventSource.eventReceived(Watcher.Action.MODIFIED, customResource1); + verify(eventHandler, times(1)).handleEvent(any()); - @Test - public void handlesAllEventIfNotGenerationAware() { - customResourceEventSource = - CustomResourceEventSource.customResourceEventSourceForAllNamespaces(customResourceCache, - mixedOperation, false, FINALIZER); - setup(); + customResource1.getMetadata().setGeneration(2L); + customResourceEventSource.eventReceived(Watcher.Action.MODIFIED, customResource1); + verify(eventHandler, times(2)).handleEvent(any()); + } - TestCustomResource customResource1 = TestUtils.testCustomResource(); + @Test + public void handlesAllEventIfNotGenerationAware() { + customResourceEventSource = + CustomResourceEventSource.customResourceEventSourceForAllNamespaces( + customResourceCache, mixedOperation, false, FINALIZER); + setup(); - customResourceEventSource.eventReceived(Watcher.Action.MODIFIED, customResource1); - verify(eventHandler, times(1)).handleEvent(any()); + TestCustomResource customResource1 = TestUtils.testCustomResource(); - customResourceEventSource.eventReceived(Watcher.Action.MODIFIED, customResource1); - verify(eventHandler, times(2)).handleEvent(any()); - } + customResourceEventSource.eventReceived(Watcher.Action.MODIFIED, customResource1); + verify(eventHandler, times(1)).handleEvent(any()); - @Test - public void eventNotMarkedForLastGenerationIfNoFinalizer() { - TestCustomResource customResource1 = TestUtils.testCustomResource(); + customResourceEventSource.eventReceived(Watcher.Action.MODIFIED, customResource1); + verify(eventHandler, times(2)).handleEvent(any()); + } - customResourceEventSource.eventReceived(Watcher.Action.MODIFIED, customResource1); - verify(eventHandler, times(1)).handleEvent(any()); + @Test + public void eventNotMarkedForLastGenerationIfNoFinalizer() { + TestCustomResource customResource1 = TestUtils.testCustomResource(); - customResourceEventSource.eventReceived(Watcher.Action.MODIFIED, customResource1); - verify(eventHandler, times(2)).handleEvent(any()); - } + customResourceEventSource.eventReceived(Watcher.Action.MODIFIED, customResource1); + verify(eventHandler, times(1)).handleEvent(any()); -} \ No newline at end of file + customResourceEventSource.eventReceived(Watcher.Action.MODIFIED, customResource1); + verify(eventHandler, times(2)).handleEvent(any()); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/processing/event/internal/TimerEventSourceTest.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/processing/event/internal/TimerEventSourceTest.java index 6247017d70..ddb1c6dca6 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/processing/event/internal/TimerEventSourceTest.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/processing/event/internal/TimerEventSourceTest.java @@ -1,109 +1,102 @@ package io.javaoperatorsdk.operator.processing.event.internal; +import static io.javaoperatorsdk.operator.processing.KubernetesResourceUtils.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; + import io.fabric8.kubernetes.client.CustomResource; import io.javaoperatorsdk.operator.TestUtils; import io.javaoperatorsdk.operator.processing.KubernetesResourceUtils; import io.javaoperatorsdk.operator.processing.event.EventHandler; -import io.javaoperatorsdk.operator.processing.event.EventSourceManager; +import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; -import java.util.List; +class TimerEventSourceTest { -import static io.javaoperatorsdk.operator.processing.KubernetesResourceUtils.*; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.*; + public static final int INITIAL_DELAY = 50; + public static final int PERIOD = 50; + public static final int TESTING_TIME_SLACK = 20; -class TimerEventSourceTest { + private TimerEventSource timerEventSource; + private EventHandler eventHandlerMock = mock(EventHandler.class); + + @BeforeEach + public void setup() { + timerEventSource = new TimerEventSource(); + timerEventSource.setEventHandler(eventHandlerMock); + } + + @Test + public void producesEventsPeriodically() { + CustomResource customResource = TestUtils.testCustomResource(); + + timerEventSource.schedule(customResource, INITIAL_DELAY, PERIOD); + + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(TimerEvent.class); + verify(eventHandlerMock, timeout(INITIAL_DELAY + PERIOD + TESTING_TIME_SLACK).times(2)) + .handleEvent(argumentCaptor.capture()); + List events = argumentCaptor.getAllValues(); + assertThat(events) + .allMatch(e -> e.getRelatedCustomResourceUid().equals(getUID(customResource))); + assertThat(events).allMatch(e -> e.getEventSource().equals(timerEventSource)); + } + + @Test + public void deRegistersPeriodicalEventSources() throws InterruptedException { + CustomResource customResource = TestUtils.testCustomResource(); + + timerEventSource.schedule(customResource, INITIAL_DELAY, PERIOD); + Thread.sleep(INITIAL_DELAY + PERIOD + TESTING_TIME_SLACK); + timerEventSource.eventSourceDeRegisteredForResource(getUID(customResource)); + Thread.sleep(PERIOD + TESTING_TIME_SLACK); + + verify(eventHandlerMock, times(2)).handleEvent(any()); + } + + @Test + public void schedulesOnce() throws InterruptedException { + CustomResource customResource = TestUtils.testCustomResource(); + + timerEventSource.scheduleOnce(customResource, PERIOD); + + Thread.sleep(2 * PERIOD + TESTING_TIME_SLACK); + verify(eventHandlerMock, times(1)).handleEvent(any()); + } + + @Test + public void canCancelOnce() throws InterruptedException { + CustomResource customResource = TestUtils.testCustomResource(); + + timerEventSource.scheduleOnce(customResource, PERIOD); + timerEventSource.cancelOnceSchedule(KubernetesResourceUtils.getUID(customResource)); + + Thread.sleep(PERIOD + TESTING_TIME_SLACK); + verify(eventHandlerMock, never()).handleEvent(any()); + } + + @Test + public void canRescheduleOnceEvent() throws InterruptedException { + CustomResource customResource = TestUtils.testCustomResource(); + + timerEventSource.scheduleOnce(customResource, PERIOD); + timerEventSource.scheduleOnce(customResource, 2 * PERIOD); + + Thread.sleep(PERIOD + TESTING_TIME_SLACK); + verify(eventHandlerMock, never()).handleEvent(any()); + Thread.sleep(PERIOD + TESTING_TIME_SLACK); + verify(eventHandlerMock, times(1)).handleEvent(any()); + } + + @Test + public void deRegistersOnceEventSources() throws InterruptedException { + CustomResource customResource = TestUtils.testCustomResource(); + + timerEventSource.scheduleOnce(customResource, PERIOD); + timerEventSource.eventSourceDeRegisteredForResource(getUID(customResource)); + Thread.sleep(PERIOD + TESTING_TIME_SLACK); - public static final int INITIAL_DELAY = 50; - public static final int PERIOD = 50; - public static final int TESTING_TIME_SLACK = 20; - - private TimerEventSource timerEventSource; - private EventHandler eventHandlerMock = mock(EventHandler.class); - - @BeforeEach - public void setup() { - timerEventSource = new TimerEventSource(); - timerEventSource.setEventHandler(eventHandlerMock); - } - - @Test - public void producesEventsPeriodically() { - CustomResource customResource = TestUtils.testCustomResource(); - - timerEventSource.schedule(customResource, INITIAL_DELAY, PERIOD); - - ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(TimerEvent.class); - verify(eventHandlerMock, timeout(INITIAL_DELAY + PERIOD + TESTING_TIME_SLACK).times(2)) - .handleEvent(argumentCaptor.capture()); - List events = argumentCaptor.getAllValues(); - assertThat(events).allMatch(e -> e.getRelatedCustomResourceUid().equals(getUID(customResource))); - assertThat(events).allMatch(e -> e.getEventSource().equals(timerEventSource)); - } - - @Test - public void deRegistersPeriodicalEventSources() throws InterruptedException { - CustomResource customResource = TestUtils.testCustomResource(); - - timerEventSource.schedule(customResource, INITIAL_DELAY, PERIOD); - Thread.sleep(INITIAL_DELAY + PERIOD + TESTING_TIME_SLACK); - timerEventSource.eventSourceDeRegisteredForResource(getUID(customResource)); - Thread.sleep(PERIOD + TESTING_TIME_SLACK); - - verify(eventHandlerMock, times(2)) - .handleEvent(any()); - } - - @Test - public void schedulesOnce() throws InterruptedException { - CustomResource customResource = TestUtils.testCustomResource(); - - timerEventSource.scheduleOnce(customResource, PERIOD); - - Thread.sleep(2 * PERIOD + TESTING_TIME_SLACK); - verify(eventHandlerMock, times(1)) - .handleEvent(any()); - } - - @Test - public void canCancelOnce() throws InterruptedException { - CustomResource customResource = TestUtils.testCustomResource(); - - timerEventSource.scheduleOnce(customResource, PERIOD); - timerEventSource.cancelOnceSchedule(KubernetesResourceUtils.getUID(customResource)); - - Thread.sleep(PERIOD + TESTING_TIME_SLACK); - verify(eventHandlerMock, never()) - .handleEvent(any()); - } - - @Test - public void canRescheduleOnceEvent() throws InterruptedException { - CustomResource customResource = TestUtils.testCustomResource(); - - timerEventSource.scheduleOnce(customResource, PERIOD); - timerEventSource.scheduleOnce(customResource, 2 * PERIOD); - - Thread.sleep(PERIOD + TESTING_TIME_SLACK); - verify(eventHandlerMock, never()) - .handleEvent(any()); - Thread.sleep(PERIOD + TESTING_TIME_SLACK); - verify(eventHandlerMock, times(1)) - .handleEvent(any()); - } - - @Test - public void deRegistersOnceEventSources() throws InterruptedException { - CustomResource customResource = TestUtils.testCustomResource(); - - timerEventSource.scheduleOnce(customResource, PERIOD); - timerEventSource.eventSourceDeRegisteredForResource(getUID(customResource)); - Thread.sleep(PERIOD + TESTING_TIME_SLACK); - - verify(eventHandlerMock, never()) - .handleEvent(any()); - } -} \ No newline at end of file + verify(eventHandlerMock, never()).handleEvent(any()); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/processing/retry/GenericRetryExecutionTest.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/processing/retry/GenericRetryExecutionTest.java index ffc5238998..ab7aa98fd3 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/processing/retry/GenericRetryExecutionTest.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/processing/retry/GenericRetryExecutionTest.java @@ -1,81 +1,88 @@ package io.javaoperatorsdk.operator.processing.retry; -import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.assertThat; import java.util.Optional; - -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.Test; public class GenericRetryExecutionTest { - @Test - public void forFirstBackOffAlwaysReturnsZero() { - assertThat(getDefaultRetryExecution().nextDelay().get()).isEqualTo(0); - } - - @Test - public void delayIsMultipliedEveryNextDelayCall() { - RetryExecution retryExecution = getDefaultRetryExecution(); - - Optional res = callNextDelayNTimes(retryExecution, 2); - assertThat(res.get()).isEqualTo(GenericRetry.DEFAULT_INITIAL_INTERVAL); - - res = retryExecution.nextDelay(); - assertThat(res.get()).isEqualTo((long) (GenericRetry.DEFAULT_INITIAL_INTERVAL * GenericRetry.DEFAULT_MULTIPLIER)); - - res = retryExecution.nextDelay(); - assertThat(res.get()).isEqualTo((long) (GenericRetry.DEFAULT_INITIAL_INTERVAL * GenericRetry.DEFAULT_MULTIPLIER * GenericRetry.DEFAULT_MULTIPLIER)); + @Test + public void forFirstBackOffAlwaysReturnsZero() { + assertThat(getDefaultRetryExecution().nextDelay().get()).isEqualTo(0); + } + + @Test + public void delayIsMultipliedEveryNextDelayCall() { + RetryExecution retryExecution = getDefaultRetryExecution(); + + Optional res = callNextDelayNTimes(retryExecution, 2); + assertThat(res.get()).isEqualTo(GenericRetry.DEFAULT_INITIAL_INTERVAL); + + res = retryExecution.nextDelay(); + assertThat(res.get()) + .isEqualTo( + (long) (GenericRetry.DEFAULT_INITIAL_INTERVAL * GenericRetry.DEFAULT_MULTIPLIER)); + + res = retryExecution.nextDelay(); + assertThat(res.get()) + .isEqualTo( + (long) + (GenericRetry.DEFAULT_INITIAL_INTERVAL + * GenericRetry.DEFAULT_MULTIPLIER + * GenericRetry.DEFAULT_MULTIPLIER)); + } + + @Test + public void noNextDelayIfMaxAttemptLimitReached() { + RetryExecution retryExecution = + GenericRetry.defaultLimitedExponentialRetry().setMaxAttempts(3).initExecution(); + Optional res = callNextDelayNTimes(retryExecution, 3); + assertThat(res).isNotEmpty(); + + res = retryExecution.nextDelay(); + assertThat(res).isEmpty(); + } + + @Test + public void canLimitMaxIntervalLength() { + RetryExecution retryExecution = + GenericRetry.defaultLimitedExponentialRetry() + .setInitialInterval(2000) + .setMaxInterval(4500) + .setIntervalMultiplier(2) + .initExecution(); + + Optional res = callNextDelayNTimes(retryExecution, 4); + + assertThat(res.get()).isEqualTo(4500); + } + + @Test + public void supportsNoRetry() { + RetryExecution retryExecution = GenericRetry.noRetry().initExecution(); + assertThat(retryExecution.nextDelay().get()).isZero(); + assertThat(retryExecution.nextDelay()).isEmpty(); + } + + @Test + public void supportsIsLastExecution() { + GenericRetryExecution execution = new GenericRetry().setMaxAttempts(2).initExecution(); + assertThat(execution.isLastExecution()).isFalse(); + + execution.nextDelay(); + execution.nextDelay(); + assertThat(execution.isLastExecution()).isTrue(); + } + + private RetryExecution getDefaultRetryExecution() { + return GenericRetry.defaultLimitedExponentialRetry().initExecution(); + } + + public Optional callNextDelayNTimes(RetryExecution retryExecution, int n) { + for (int i = 0; i < n - 1; i++) { + retryExecution.nextDelay(); } - - @Test - public void noNextDelayIfMaxAttemptLimitReached() { - RetryExecution retryExecution = GenericRetry.defaultLimitedExponentialRetry().setMaxAttempts(3).initExecution(); - Optional res = callNextDelayNTimes(retryExecution, 3); - assertThat(res).isNotEmpty(); - - res = retryExecution.nextDelay(); - assertThat(res).isEmpty(); - } - - @Test - public void canLimitMaxIntervalLength() { - RetryExecution retryExecution = GenericRetry.defaultLimitedExponentialRetry() - .setInitialInterval(2000) - .setMaxInterval(4500) - .setIntervalMultiplier(2) - .initExecution(); - - Optional res = callNextDelayNTimes(retryExecution, 4); - - assertThat(res.get()).isEqualTo(4500); - } - - @Test - public void supportsNoRetry() { - RetryExecution retryExecution = GenericRetry.noRetry().initExecution(); - assertThat(retryExecution.nextDelay().get()).isZero(); - assertThat(retryExecution.nextDelay()).isEmpty(); - } - - @Test - public void supportsIsLastExecution() { - GenericRetryExecution execution = new GenericRetry().setMaxAttempts(2).initExecution(); - assertThat(execution.isLastExecution()).isFalse(); - - execution.nextDelay(); - execution.nextDelay(); - assertThat(execution.isLastExecution()).isTrue(); - } - - private RetryExecution getDefaultRetryExecution() { - return GenericRetry.defaultLimitedExponentialRetry().initExecution(); - } - - public Optional callNextDelayNTimes(RetryExecution retryExecution, int n) { - for (int i = 0; i < n - 1; i++) { - retryExecution.nextDelay(); - } - return retryExecution.nextDelay(); - } - + return retryExecution.nextDelay(); + } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/event/EventSourceTestCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/event/EventSourceTestCustomResource.java index 15d96a5304..fcd968ab53 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/event/EventSourceTestCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/event/EventSourceTestCustomResource.java @@ -4,32 +4,35 @@ public class EventSourceTestCustomResource extends CustomResource { - private EventSourceTestCustomResourceSpec spec; - - private EventSourceTestCustomResourceStatus status; - - public EventSourceTestCustomResourceSpec getSpec() { - return spec; - } - - public void setSpec(EventSourceTestCustomResourceSpec spec) { - this.spec = spec; - } - - public EventSourceTestCustomResourceStatus getStatus() { - return status; - } - - public void setStatus(EventSourceTestCustomResourceStatus status) { - this.status = status; - } - - @Override - public String toString() { - return "TestCustomResource{" + - "spec=" + spec + - ", status=" + status + - ", extendedFrom=" + super.toString() + - '}'; - } + private EventSourceTestCustomResourceSpec spec; + + private EventSourceTestCustomResourceStatus status; + + public EventSourceTestCustomResourceSpec getSpec() { + return spec; + } + + public void setSpec(EventSourceTestCustomResourceSpec spec) { + this.spec = spec; + } + + public EventSourceTestCustomResourceStatus getStatus() { + return status; + } + + public void setStatus(EventSourceTestCustomResourceStatus status) { + this.status = status; + } + + @Override + public String toString() { + return "TestCustomResource{" + + "spec=" + + spec + + ", status=" + + status + + ", extendedFrom=" + + super.toString() + + '}'; + } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/event/EventSourceTestCustomResourceController.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/event/EventSourceTestCustomResourceController.java index ebd2fe5da6..cdc466bc16 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/event/EventSourceTestCustomResourceController.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/event/EventSourceTestCustomResourceController.java @@ -4,57 +4,57 @@ import io.javaoperatorsdk.operator.api.*; import io.javaoperatorsdk.operator.processing.event.EventSourceManager; import io.javaoperatorsdk.operator.processing.event.internal.TimerEventSource; +import java.util.concurrent.atomic.AtomicInteger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.concurrent.atomic.AtomicInteger; - -@Controller( - crdName = EventSourceTestCustomResourceController.CRD_NAME) -public class EventSourceTestCustomResourceController implements ResourceController, - TestExecutionInfoProvider { - - public static final String CRD_NAME = "eventsourcesamples.sample.javaoperatorsdk"; - public static final String FINALIZER_NAME = CRD_NAME + "/finalizer"; - private static final Logger log = LoggerFactory.getLogger(EventSourceTestCustomResourceController.class); - public static final int TIMER_DELAY = 300; - public static final int TIMER_PERIOD = 500; - private final AtomicInteger numberOfExecutions = new AtomicInteger(0); - private final TimerEventSource timerEventSource = new TimerEventSource(); - - @Override - public void init(EventSourceManager eventSourceManager) { - eventSourceManager.registerEventSource("Timer", timerEventSource); +@Controller(crdName = EventSourceTestCustomResourceController.CRD_NAME) +public class EventSourceTestCustomResourceController + implements ResourceController, TestExecutionInfoProvider { + + public static final String CRD_NAME = "eventsourcesamples.sample.javaoperatorsdk"; + public static final String FINALIZER_NAME = CRD_NAME + "/finalizer"; + private static final Logger log = + LoggerFactory.getLogger(EventSourceTestCustomResourceController.class); + public static final int TIMER_DELAY = 300; + public static final int TIMER_PERIOD = 500; + private final AtomicInteger numberOfExecutions = new AtomicInteger(0); + private final TimerEventSource timerEventSource = new TimerEventSource(); + + @Override + public void init(EventSourceManager eventSourceManager) { + eventSourceManager.registerEventSource("Timer", timerEventSource); + } + + @Override + public DeleteControl deleteResource( + EventSourceTestCustomResource resource, Context context) { + return DeleteControl.DEFAULT_DELETE; + } + + @Override + public UpdateControl createOrUpdateResource( + EventSourceTestCustomResource resource, Context context) { + + timerEventSource.schedule(resource, TIMER_DELAY, TIMER_PERIOD); + + log.info("Events:: " + context.getEvents()); + numberOfExecutions.addAndGet(1); + ensureStatusExists(resource); + resource.getStatus().setState(EventSourceTestCustomResourceStatus.State.SUCCESS); + + return UpdateControl.updateStatusSubResource(resource); + } + + private void ensureStatusExists(EventSourceTestCustomResource resource) { + EventSourceTestCustomResourceStatus status = resource.getStatus(); + if (status == null) { + status = new EventSourceTestCustomResourceStatus(); + resource.setStatus(status); } + } - @Override - public DeleteControl deleteResource(EventSourceTestCustomResource resource, Context context) { - return DeleteControl.DEFAULT_DELETE; - } - - @Override - public UpdateControl createOrUpdateResource(EventSourceTestCustomResource resource, - Context context) { - - timerEventSource.schedule(resource, TIMER_DELAY, TIMER_PERIOD); - - log.info("Events:: " + context.getEvents()); - numberOfExecutions.addAndGet(1); - ensureStatusExists(resource); - resource.getStatus().setState(EventSourceTestCustomResourceStatus.State.SUCCESS); - - return UpdateControl.updateStatusSubResource(resource); - } - - private void ensureStatusExists(EventSourceTestCustomResource resource) { - EventSourceTestCustomResourceStatus status = resource.getStatus(); - if (status == null) { - status = new EventSourceTestCustomResourceStatus(); - resource.setStatus(status); - } - } - - public int getNumberOfExecutions() { - return numberOfExecutions.get(); - } + public int getNumberOfExecutions() { + return numberOfExecutions.get(); + } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/event/EventSourceTestCustomResourceSpec.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/event/EventSourceTestCustomResourceSpec.java index 97411fbff4..d22f34ce97 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/event/EventSourceTestCustomResourceSpec.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/event/EventSourceTestCustomResourceSpec.java @@ -2,14 +2,14 @@ public class EventSourceTestCustomResourceSpec { - private String value; + private String value; - public String getValue() { - return value; - } + public String getValue() { + return value; + } - public EventSourceTestCustomResourceSpec setValue(String value) { - this.value = value; - return this; - } + public EventSourceTestCustomResourceSpec setValue(String value) { + this.value = value; + return this; + } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/event/EventSourceTestCustomResourceStatus.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/event/EventSourceTestCustomResourceStatus.java index 615fbdd8b3..e06c44cb1c 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/event/EventSourceTestCustomResourceStatus.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/event/EventSourceTestCustomResourceStatus.java @@ -2,20 +2,19 @@ public class EventSourceTestCustomResourceStatus { - private State state; + private State state; - public State getState() { - return state; - } + public State getState() { + return state; + } - public EventSourceTestCustomResourceStatus setState(State state) { - this.state = state; - return this; - } - - public enum State { - SUCCESS, - ERROR - } + public EventSourceTestCustomResourceStatus setState(State state) { + this.state = state; + return this; + } + public enum State { + SUCCESS, + ERROR + } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomResource.java index b0ac877bae..dd6ed3ab86 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomResource.java @@ -4,32 +4,35 @@ public class TestCustomResource extends CustomResource { - private TestCustomResourceSpec spec; - - private TestCustomResourceStatus status; - - public TestCustomResourceSpec getSpec() { - return spec; - } - - public void setSpec(TestCustomResourceSpec spec) { - this.spec = spec; - } - - public TestCustomResourceStatus getStatus() { - return status; - } - - public void setStatus(TestCustomResourceStatus status) { - this.status = status; - } - - @Override - public String toString() { - return "TestCustomResource{" + - "spec=" + spec + - ", status=" + status + - ", extendedFrom=" + super.toString() + - '}'; - } + private TestCustomResourceSpec spec; + + private TestCustomResourceStatus status; + + public TestCustomResourceSpec getSpec() { + return spec; + } + + public void setSpec(TestCustomResourceSpec spec) { + this.spec = spec; + } + + public TestCustomResourceStatus getStatus() { + return status; + } + + public void setStatus(TestCustomResourceStatus status) { + this.status = status; + } + + @Override + public String toString() { + return "TestCustomResource{" + + "spec=" + + spec + + ", status=" + + status + + ", extendedFrom=" + + super.toString() + + '}'; + } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomResourceController.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomResourceController.java index 89b215f46c..f719566c0b 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomResourceController.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomResourceController.java @@ -6,94 +6,114 @@ import io.fabric8.kubernetes.client.KubernetesClient; import io.javaoperatorsdk.operator.TestExecutionInfoProvider; import io.javaoperatorsdk.operator.api.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -@Controller( - generationAwareEventProcessing = false, - crdName = TestCustomResourceController.CRD_NAME) -public class TestCustomResourceController implements ResourceController, TestExecutionInfoProvider { +@Controller(generationAwareEventProcessing = false, crdName = TestCustomResourceController.CRD_NAME) +public class TestCustomResourceController + implements ResourceController, TestExecutionInfoProvider { - private static final Logger log = LoggerFactory.getLogger(TestCustomResourceController.class); + private static final Logger log = LoggerFactory.getLogger(TestCustomResourceController.class); - public static final String CRD_NAME = "customservices.sample.javaoperatorsdk"; - public static final String FINALIZER_NAME = CRD_NAME + "/finalizer"; + public static final String CRD_NAME = "customservices.sample.javaoperatorsdk"; + public static final String FINALIZER_NAME = CRD_NAME + "/finalizer"; - private final KubernetesClient kubernetesClient; - private final boolean updateStatus; - private final AtomicInteger numberOfExecutions = new AtomicInteger(0); + private final KubernetesClient kubernetesClient; + private final boolean updateStatus; + private final AtomicInteger numberOfExecutions = new AtomicInteger(0); - public TestCustomResourceController(KubernetesClient kubernetesClient) { - this(kubernetesClient, true); - } + public TestCustomResourceController(KubernetesClient kubernetesClient) { + this(kubernetesClient, true); + } - public TestCustomResourceController(KubernetesClient kubernetesClient, boolean updateStatus) { - this.kubernetesClient = kubernetesClient; - this.updateStatus = updateStatus; - } + public TestCustomResourceController(KubernetesClient kubernetesClient, boolean updateStatus) { + this.kubernetesClient = kubernetesClient; + this.updateStatus = updateStatus; + } - @Override - public DeleteControl deleteResource(TestCustomResource resource, Context context) { - Boolean delete = kubernetesClient.configMaps().inNamespace(resource.getMetadata().getNamespace()) - .withName(resource.getSpec().getConfigMapName()).delete(); - if (delete) { - log.info("Deleted ConfigMap {} for resource: {}", resource.getSpec().getConfigMapName(), resource.getMetadata().getName()); - } else { - log.error("Failed to delete ConfigMap {} for resource: {}", resource.getSpec().getConfigMapName(), resource.getMetadata().getName()); - } - return DeleteControl.DEFAULT_DELETE; + @Override + public DeleteControl deleteResource( + TestCustomResource resource, Context context) { + Boolean delete = + kubernetesClient + .configMaps() + .inNamespace(resource.getMetadata().getNamespace()) + .withName(resource.getSpec().getConfigMapName()) + .delete(); + if (delete) { + log.info( + "Deleted ConfigMap {} for resource: {}", + resource.getSpec().getConfigMapName(), + resource.getMetadata().getName()); + } else { + log.error( + "Failed to delete ConfigMap {} for resource: {}", + resource.getSpec().getConfigMapName(), + resource.getMetadata().getName()); } + return DeleteControl.DEFAULT_DELETE; + } - @Override - public UpdateControl createOrUpdateResource(TestCustomResource resource, - Context context) { - numberOfExecutions.addAndGet(1); - if (!resource.getMetadata().getFinalizers().contains(FINALIZER_NAME)) { - throw new IllegalStateException("Finalizer is not present."); - } + @Override + public UpdateControl createOrUpdateResource( + TestCustomResource resource, Context context) { + numberOfExecutions.addAndGet(1); + if (!resource.getMetadata().getFinalizers().contains(FINALIZER_NAME)) { + throw new IllegalStateException("Finalizer is not present."); + } - ConfigMap existingConfigMap = kubernetesClient - .configMaps().inNamespace(resource.getMetadata().getNamespace()) - .withName(resource.getSpec().getConfigMapName()).get(); + ConfigMap existingConfigMap = + kubernetesClient + .configMaps() + .inNamespace(resource.getMetadata().getNamespace()) + .withName(resource.getSpec().getConfigMapName()) + .get(); - if (existingConfigMap != null) { - existingConfigMap.setData(configMapData(resource)); -// existingConfigMap.getMetadata().setResourceVersion(null); - kubernetesClient.configMaps().inNamespace(resource.getMetadata().getNamespace()) - .withName(existingConfigMap.getMetadata().getName()).createOrReplace(existingConfigMap); - } else { - Map labels = new HashMap<>(); - labels.put("managedBy", TestCustomResourceController.class.getSimpleName()); - ConfigMap newConfigMap = new ConfigMapBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName(resource.getSpec().getConfigMapName()) - .withNamespace(resource.getMetadata().getNamespace()) - .withLabels(labels) - .build()) - .withData(configMapData(resource)).build(); - kubernetesClient.configMaps().inNamespace(resource.getMetadata().getNamespace()) - .createOrReplace(newConfigMap); - } - if (updateStatus) { - if (resource.getStatus() == null) { - resource.setStatus(new TestCustomResourceStatus()); - } - resource.getStatus().setConfigMapStatus("ConfigMap Ready"); - } - return UpdateControl.updateCustomResource(resource); + if (existingConfigMap != null) { + existingConfigMap.setData(configMapData(resource)); + // existingConfigMap.getMetadata().setResourceVersion(null); + kubernetesClient + .configMaps() + .inNamespace(resource.getMetadata().getNamespace()) + .withName(existingConfigMap.getMetadata().getName()) + .createOrReplace(existingConfigMap); + } else { + Map labels = new HashMap<>(); + labels.put("managedBy", TestCustomResourceController.class.getSimpleName()); + ConfigMap newConfigMap = + new ConfigMapBuilder() + .withMetadata( + new ObjectMetaBuilder() + .withName(resource.getSpec().getConfigMapName()) + .withNamespace(resource.getMetadata().getNamespace()) + .withLabels(labels) + .build()) + .withData(configMapData(resource)) + .build(); + kubernetesClient + .configMaps() + .inNamespace(resource.getMetadata().getNamespace()) + .createOrReplace(newConfigMap); } - - private Map configMapData(TestCustomResource resource) { - Map data = new HashMap<>(); - data.put(resource.getSpec().getKey(), resource.getSpec().getValue()); - return data; + if (updateStatus) { + if (resource.getStatus() == null) { + resource.setStatus(new TestCustomResourceStatus()); + } + resource.getStatus().setConfigMapStatus("ConfigMap Ready"); } + return UpdateControl.updateCustomResource(resource); + } - public int getNumberOfExecutions() { - return numberOfExecutions.get(); - } + private Map configMapData(TestCustomResource resource) { + Map data = new HashMap<>(); + data.put(resource.getSpec().getKey(), resource.getSpec().getValue()); + return data; + } + + public int getNumberOfExecutions() { + return numberOfExecutions.get(); + } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomResourceSpec.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomResourceSpec.java index fc5b2fc500..5fd9f49084 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomResourceSpec.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomResourceSpec.java @@ -2,42 +2,48 @@ public class TestCustomResourceSpec { - private String configMapName; - - private String key; - - private String value; - - public String getConfigMapName() { - return configMapName; - } - - public void setConfigMapName(String configMapName) { - this.configMapName = configMapName; - } - - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } - - @Override - public String toString() { - return "TestCustomResourceSpec{" + - "configMapName='" + configMapName + '\'' + - ", key='" + key + '\'' + - ", value='" + value + '\'' + - '}'; - } + private String configMapName; + + private String key; + + private String value; + + public String getConfigMapName() { + return configMapName; + } + + public void setConfigMapName(String configMapName) { + this.configMapName = configMapName; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public String toString() { + return "TestCustomResourceSpec{" + + "configMapName='" + + configMapName + + '\'' + + ", key='" + + key + + '\'' + + ", value='" + + value + + '\'' + + '}'; + } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomResourceStatus.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomResourceStatus.java index f08b794ca2..620bbaabd8 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomResourceStatus.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomResourceStatus.java @@ -2,20 +2,18 @@ public class TestCustomResourceStatus { - private String configMapStatus; + private String configMapStatus; - public String getConfigMapStatus() { - return configMapStatus; - } + public String getConfigMapStatus() { + return configMapStatus; + } - public void setConfigMapStatus(String configMapStatus) { - this.configMapStatus = configMapStatus; - } + public void setConfigMapStatus(String configMapStatus) { + this.configMapStatus = configMapStatus; + } - @Override - public String toString() { - return "TestCustomResourceStatus{" + - "configMapStatus='" + configMapStatus + '\'' + - '}'; - } + @Override + public String toString() { + return "TestCustomResourceStatus{" + "configMapStatus='" + configMapStatus + '\'' + '}'; + } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/subresource/SubResourceTestCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/subresource/SubResourceTestCustomResource.java index caab91bfe6..8c0beb1947 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/subresource/SubResourceTestCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/subresource/SubResourceTestCustomResource.java @@ -4,32 +4,35 @@ public class SubResourceTestCustomResource extends CustomResource { - private SubResourceTestCustomResourceSpec spec; - - private SubResourceTestCustomResourceStatus status; - - public SubResourceTestCustomResourceSpec getSpec() { - return spec; - } - - public void setSpec(SubResourceTestCustomResourceSpec spec) { - this.spec = spec; - } - - public SubResourceTestCustomResourceStatus getStatus() { - return status; - } - - public void setStatus(SubResourceTestCustomResourceStatus status) { - this.status = status; - } - - @Override - public String toString() { - return "TestCustomResource{" + - "spec=" + spec + - ", status=" + status + - ", extendedFrom=" + super.toString() + - '}'; - } + private SubResourceTestCustomResourceSpec spec; + + private SubResourceTestCustomResourceStatus status; + + public SubResourceTestCustomResourceSpec getSpec() { + return spec; + } + + public void setSpec(SubResourceTestCustomResourceSpec spec) { + this.spec = spec; + } + + public SubResourceTestCustomResourceStatus getStatus() { + return status; + } + + public void setStatus(SubResourceTestCustomResourceStatus status) { + this.status = status; + } + + @Override + public String toString() { + return "TestCustomResource{" + + "spec=" + + spec + + ", status=" + + status + + ", extendedFrom=" + + super.toString() + + '}'; + } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/subresource/SubResourceTestCustomResourceController.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/subresource/SubResourceTestCustomResourceController.java index 76526688e9..0756b508f7 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/subresource/SubResourceTestCustomResourceController.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/subresource/SubResourceTestCustomResourceController.java @@ -2,51 +2,52 @@ import io.javaoperatorsdk.operator.TestExecutionInfoProvider; import io.javaoperatorsdk.operator.api.*; +import java.util.concurrent.atomic.AtomicInteger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.concurrent.atomic.AtomicInteger; - @Controller( - crdName = SubResourceTestCustomResourceController.CRD_NAME, - generationAwareEventProcessing = false) -public class SubResourceTestCustomResourceController implements ResourceController, - TestExecutionInfoProvider { - - public static final String CRD_NAME = "subresourcesample.sample.javaoperatorsdk"; - public static final String FINALIZER_NAME = CRD_NAME + "/finalizer"; - private static final Logger log = LoggerFactory.getLogger(SubResourceTestCustomResourceController.class); - private final AtomicInteger numberOfExecutions = new AtomicInteger(0); - - @Override - public DeleteControl deleteResource(SubResourceTestCustomResource resource, Context context) { - return DeleteControl.DEFAULT_DELETE; + crdName = SubResourceTestCustomResourceController.CRD_NAME, + generationAwareEventProcessing = false) +public class SubResourceTestCustomResourceController + implements ResourceController, TestExecutionInfoProvider { + + public static final String CRD_NAME = "subresourcesample.sample.javaoperatorsdk"; + public static final String FINALIZER_NAME = CRD_NAME + "/finalizer"; + private static final Logger log = + LoggerFactory.getLogger(SubResourceTestCustomResourceController.class); + private final AtomicInteger numberOfExecutions = new AtomicInteger(0); + + @Override + public DeleteControl deleteResource( + SubResourceTestCustomResource resource, Context context) { + return DeleteControl.DEFAULT_DELETE; + } + + @Override + public UpdateControl createOrUpdateResource( + SubResourceTestCustomResource resource, Context context) { + numberOfExecutions.addAndGet(1); + if (!resource.getMetadata().getFinalizers().contains(FINALIZER_NAME)) { + throw new IllegalStateException("Finalizer is not present."); } + log.info("Value: " + resource.getSpec().getValue()); - @Override - public UpdateControl createOrUpdateResource(SubResourceTestCustomResource resource, - Context context) { - numberOfExecutions.addAndGet(1); - if (!resource.getMetadata().getFinalizers().contains(FINALIZER_NAME)) { - throw new IllegalStateException("Finalizer is not present."); - } - log.info("Value: " + resource.getSpec().getValue()); + ensureStatusExists(resource); + resource.getStatus().setState(SubResourceTestCustomResourceStatus.State.SUCCESS); - ensureStatusExists(resource); - resource.getStatus().setState(SubResourceTestCustomResourceStatus.State.SUCCESS); + return UpdateControl.updateStatusSubResource(resource); + } - return UpdateControl.updateStatusSubResource(resource); + private void ensureStatusExists(SubResourceTestCustomResource resource) { + SubResourceTestCustomResourceStatus status = resource.getStatus(); + if (status == null) { + status = new SubResourceTestCustomResourceStatus(); + resource.setStatus(status); } + } - private void ensureStatusExists(SubResourceTestCustomResource resource) { - SubResourceTestCustomResourceStatus status = resource.getStatus(); - if (status == null) { - status = new SubResourceTestCustomResourceStatus(); - resource.setStatus(status); - } - } - - public int getNumberOfExecutions() { - return numberOfExecutions.get(); - } + public int getNumberOfExecutions() { + return numberOfExecutions.get(); + } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/subresource/SubResourceTestCustomResourceSpec.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/subresource/SubResourceTestCustomResourceSpec.java index b9bc226a33..21b8e2bc84 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/subresource/SubResourceTestCustomResourceSpec.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/subresource/SubResourceTestCustomResourceSpec.java @@ -2,14 +2,14 @@ public class SubResourceTestCustomResourceSpec { - private String value; + private String value; - public String getValue() { - return value; - } + public String getValue() { + return value; + } - public SubResourceTestCustomResourceSpec setValue(String value) { - this.value = value; - return this; - } + public SubResourceTestCustomResourceSpec setValue(String value) { + this.value = value; + return this; + } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/subresource/SubResourceTestCustomResourceStatus.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/subresource/SubResourceTestCustomResourceStatus.java index 4ca0de5fe7..64bc786b2f 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/subresource/SubResourceTestCustomResourceStatus.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/subresource/SubResourceTestCustomResourceStatus.java @@ -2,20 +2,19 @@ public class SubResourceTestCustomResourceStatus { - private State state; + private State state; - public State getState() { - return state; - } + public State getState() { + return state; + } - public SubResourceTestCustomResourceStatus setState(State state) { - this.state = state; - return this; - } - - public enum State { - SUCCESS, - ERROR - } + public SubResourceTestCustomResourceStatus setState(State state) { + this.state = state; + return this; + } + public enum State { + SUCCESS, + ERROR + } } diff --git a/pom.xml b/pom.xml index 3da5f45ab9..3aafbabfad 100644 --- a/pom.xml +++ b/pom.xml @@ -128,6 +128,22 @@ + + com.coveo + fmt-maven-plugin + 2.10 + + + + + + + format + + compile + + + diff --git a/samples/common/src/main/java/io/javaoperatorsdk/operator/sample/CustomService.java b/samples/common/src/main/java/io/javaoperatorsdk/operator/sample/CustomService.java index 404a07c672..bfd16de585 100644 --- a/samples/common/src/main/java/io/javaoperatorsdk/operator/sample/CustomService.java +++ b/samples/common/src/main/java/io/javaoperatorsdk/operator/sample/CustomService.java @@ -4,13 +4,13 @@ public class CustomService extends CustomResource { - private ServiceSpec spec; + private ServiceSpec spec; - public ServiceSpec getSpec() { - return spec; - } + public ServiceSpec getSpec() { + return spec; + } - public void setSpec(ServiceSpec spec) { - this.spec = spec; - } + public void setSpec(ServiceSpec spec) { + this.spec = spec; + } } diff --git a/samples/common/src/main/java/io/javaoperatorsdk/operator/sample/CustomServiceController.java b/samples/common/src/main/java/io/javaoperatorsdk/operator/sample/CustomServiceController.java index eb444e092d..2b07bcabd3 100644 --- a/samples/common/src/main/java/io/javaoperatorsdk/operator/sample/CustomServiceController.java +++ b/samples/common/src/main/java/io/javaoperatorsdk/operator/sample/CustomServiceController.java @@ -4,51 +4,54 @@ import io.fabric8.kubernetes.api.model.ServiceSpec; import io.fabric8.kubernetes.client.KubernetesClient; import io.javaoperatorsdk.operator.api.*; +import java.util.Collections; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Collections; - -/** - * A very simple sample controller that creates a service with a label. - */ -@Controller( - crdName = "customservices.sample.javaoperatorsdk") +/** A very simple sample controller that creates a service with a label. */ +@Controller(crdName = "customservices.sample.javaoperatorsdk") public class CustomServiceController implements ResourceController { - public static final String KIND = "CustomService"; - private final static Logger log = LoggerFactory.getLogger(CustomServiceController.class); - - private final KubernetesClient kubernetesClient; - - public CustomServiceController(KubernetesClient kubernetesClient) { - this.kubernetesClient = kubernetesClient; - } - - @Override - public DeleteControl deleteResource(CustomService resource, Context context) { - log.info("Execution deleteResource for: {}", resource.getMetadata().getName()); - kubernetesClient.services().inNamespace(resource.getMetadata().getNamespace()) - .withName(resource.getSpec().getName()).delete(); - return DeleteControl.DEFAULT_DELETE; - } - - @Override - public UpdateControl createOrUpdateResource(CustomService resource, Context context) { - log.info("Execution createOrUpdateResource for: {}", resource.getMetadata().getName()); - - ServicePort servicePort = new ServicePort(); - servicePort.setPort(8080); - ServiceSpec serviceSpec = new ServiceSpec(); - serviceSpec.setPorts(Collections.singletonList(servicePort)); - - kubernetesClient.services().inNamespace(resource.getMetadata().getNamespace()).createOrReplaceWithNew() - .withNewMetadata() - .withName(resource.getSpec().getName()) - .addToLabels("testLabel", resource.getSpec().getLabel()) - .endMetadata() - .withSpec(serviceSpec) - .done(); - return UpdateControl.updateCustomResource(resource); - } + public static final String KIND = "CustomService"; + private static final Logger log = LoggerFactory.getLogger(CustomServiceController.class); + + private final KubernetesClient kubernetesClient; + + public CustomServiceController(KubernetesClient kubernetesClient) { + this.kubernetesClient = kubernetesClient; + } + + @Override + public DeleteControl deleteResource(CustomService resource, Context context) { + log.info("Execution deleteResource for: {}", resource.getMetadata().getName()); + kubernetesClient + .services() + .inNamespace(resource.getMetadata().getNamespace()) + .withName(resource.getSpec().getName()) + .delete(); + return DeleteControl.DEFAULT_DELETE; + } + + @Override + public UpdateControl createOrUpdateResource( + CustomService resource, Context context) { + log.info("Execution createOrUpdateResource for: {}", resource.getMetadata().getName()); + + ServicePort servicePort = new ServicePort(); + servicePort.setPort(8080); + ServiceSpec serviceSpec = new ServiceSpec(); + serviceSpec.setPorts(Collections.singletonList(servicePort)); + + kubernetesClient + .services() + .inNamespace(resource.getMetadata().getNamespace()) + .createOrReplaceWithNew() + .withNewMetadata() + .withName(resource.getSpec().getName()) + .addToLabels("testLabel", resource.getSpec().getLabel()) + .endMetadata() + .withSpec(serviceSpec) + .done(); + return UpdateControl.updateCustomResource(resource); + } } diff --git a/samples/common/src/main/java/io/javaoperatorsdk/operator/sample/ServiceSpec.java b/samples/common/src/main/java/io/javaoperatorsdk/operator/sample/ServiceSpec.java index 840cbf581f..f4a3452b35 100644 --- a/samples/common/src/main/java/io/javaoperatorsdk/operator/sample/ServiceSpec.java +++ b/samples/common/src/main/java/io/javaoperatorsdk/operator/sample/ServiceSpec.java @@ -2,22 +2,22 @@ public class ServiceSpec { - private String name; - private String label; + private String name; + private String label; - public String getName() { - return name; - } + public String getName() { + return name; + } - public void setName(String name) { - this.name = name; - } + public void setName(String name) { + this.name = name; + } - public String getLabel() { - return label; - } + public String getLabel() { + return label; + } - public void setLabel(String label) { - this.label = label; - } + public void setLabel(String label) { + this.label = label; + } } diff --git a/samples/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchemaOperator.java b/samples/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchemaOperator.java index 0b9aee41e0..fd48eee3d8 100644 --- a/samples/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchemaOperator.java +++ b/samples/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchemaOperator.java @@ -1,10 +1,11 @@ package io.javaoperatorsdk.operator.sample; -import io.javaoperatorsdk.operator.Operator; import io.fabric8.kubernetes.client.Config; import io.fabric8.kubernetes.client.ConfigBuilder; import io.fabric8.kubernetes.client.DefaultKubernetesClient; import io.fabric8.kubernetes.client.KubernetesClient; +import io.javaoperatorsdk.operator.Operator; +import java.io.IOException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.takes.facets.fork.FkRegex; @@ -12,22 +13,18 @@ import org.takes.http.Exit; import org.takes.http.FtBasic; -import java.io.IOException; - public class MySQLSchemaOperator { - private static final Logger log = LoggerFactory.getLogger(MySQLSchemaOperator.class); + private static final Logger log = LoggerFactory.getLogger(MySQLSchemaOperator.class); - public static void main(String[] args) throws IOException { - log.info("MySQL Schema Operator starting"); + public static void main(String[] args) throws IOException { + log.info("MySQL Schema Operator starting"); - Config config = new ConfigBuilder().withNamespace(null).build(); - KubernetesClient client = new DefaultKubernetesClient(config); - Operator operator = new Operator(client); - operator.registerControllerForAllNamespaces(new SchemaController(client)); + Config config = new ConfigBuilder().withNamespace(null).build(); + KubernetesClient client = new DefaultKubernetesClient(config); + Operator operator = new Operator(client); + operator.registerControllerForAllNamespaces(new SchemaController(client)); - new FtBasic( - new TkFork(new FkRegex("/health", "ALL GOOD!")), 8080 - ).start(Exit.NEVER); - } + new FtBasic(new TkFork(new FkRegex("/health", "ALL GOOD!")), 8080).start(Exit.NEVER); + } } diff --git a/samples/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/Schema.java b/samples/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/Schema.java index 93e028b716..17bfe0c745 100644 --- a/samples/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/Schema.java +++ b/samples/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/Schema.java @@ -4,23 +4,23 @@ public class Schema extends CustomResource { - private SchemaSpec spec; + private SchemaSpec spec; - private SchemaStatus status; + private SchemaStatus status; - public SchemaSpec getSpec() { - return spec; - } + public SchemaSpec getSpec() { + return spec; + } - public void setSpec(SchemaSpec spec) { - this.spec = spec; - } + public void setSpec(SchemaSpec spec) { + this.spec = spec; + } - public SchemaStatus getStatus() { - return status; - } + public SchemaStatus getStatus() { + return status; + } - public void setStatus(SchemaStatus status) { - this.status = status; - } + public void setStatus(SchemaStatus status) { + this.status = status; + } } diff --git a/samples/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/SchemaController.java b/samples/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/SchemaController.java index 26f6fcca3f..f93217a894 100644 --- a/samples/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/SchemaController.java +++ b/samples/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/SchemaController.java @@ -1,13 +1,11 @@ package io.javaoperatorsdk.operator.sample; +import static java.lang.String.format; + import io.fabric8.kubernetes.api.model.Secret; import io.fabric8.kubernetes.api.model.SecretBuilder; import io.fabric8.kubernetes.client.KubernetesClient; import io.javaoperatorsdk.operator.api.*; -import org.apache.commons.lang3.RandomStringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; @@ -15,143 +13,150 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.Base64; - -import static java.lang.String.format; +import org.apache.commons.lang3.RandomStringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; @Controller(crdName = "schemas.mysql.sample.javaoperatorsdk") public class SchemaController implements ResourceController { - static final String USERNAME_FORMAT = "%s-user"; - static final String SECRET_FORMAT = "%s-secret"; - - private final Logger log = LoggerFactory.getLogger(getClass()); - - private final KubernetesClient kubernetesClient; - - public SchemaController(KubernetesClient kubernetesClient) { - this.kubernetesClient = kubernetesClient; - } - - @Override - public UpdateControl createOrUpdateResource(Schema schema, Context context) { - try (Connection connection = getConnection()) { - if (!schemaExists(connection, schema.getMetadata().getName())) { - try (Statement statement = connection.createStatement()) { - statement.execute(format("CREATE SCHEMA `%1$s` DEFAULT CHARACTER SET %2$s", - schema.getMetadata().getName(), - schema.getSpec().getEncoding())); - } - - String password = RandomStringUtils.randomAlphanumeric(16); - String userName = String.format(USERNAME_FORMAT, - schema.getMetadata().getName()); - String secretName = String.format(SECRET_FORMAT, - schema.getMetadata().getName()); - try (Statement statement = connection.createStatement()) { - statement.execute(format("CREATE USER '%1$s' IDENTIFIED BY '%2$s'", - userName, - password)); - } - try (Statement statement = connection.createStatement()) { - statement.execute(format("GRANT ALL ON `%1$s`.* TO '%2$s'", - schema.getMetadata().getName(), - userName)); - } - Secret credentialsSecret = new SecretBuilder() - .withNewMetadata().withName(secretName).endMetadata() - .addToData("MYSQL_USERNAME", Base64.getEncoder().encodeToString(userName.getBytes())) - .addToData("MYSQL_PASSWORD", Base64.getEncoder().encodeToString(password.getBytes())) - .build(); - this.kubernetesClient.secrets() - .inNamespace(schema.getMetadata().getNamespace()) - .create(credentialsSecret); - - SchemaStatus status = new SchemaStatus(); - status.setUrl(format("jdbc:mysql://%1$s/%2$s", - System.getenv("MYSQL_HOST"), - schema.getMetadata().getName())); - status.setUserName(userName); - status.setSecretName(secretName); - status.setStatus("CREATED"); - schema.setStatus(status); - log.info("Schema {} created - updating CR status", schema.getMetadata().getName()); - - return UpdateControl.updateStatusSubResource(schema); - } - return UpdateControl.noUpdate(); - } catch (SQLException e) { - log.error("Error while creating Schema", e); - - SchemaStatus status = new SchemaStatus(); - status.setUrl(null); - status.setUserName(null); - status.setSecretName(null); - status.setStatus("ERROR"); - schema.setStatus(status); - - return UpdateControl.updateCustomResource(schema); + static final String USERNAME_FORMAT = "%s-user"; + static final String SECRET_FORMAT = "%s-secret"; + + private final Logger log = LoggerFactory.getLogger(getClass()); + + private final KubernetesClient kubernetesClient; + + public SchemaController(KubernetesClient kubernetesClient) { + this.kubernetesClient = kubernetesClient; + } + + @Override + public UpdateControl createOrUpdateResource(Schema schema, Context context) { + try (Connection connection = getConnection()) { + if (!schemaExists(connection, schema.getMetadata().getName())) { + try (Statement statement = connection.createStatement()) { + statement.execute( + format( + "CREATE SCHEMA `%1$s` DEFAULT CHARACTER SET %2$s", + schema.getMetadata().getName(), schema.getSpec().getEncoding())); } - } - @Override - public DeleteControl deleteResource(Schema schema, Context context) { - log.info("Execution deleteResource for: {}", schema.getMetadata().getName()); - - try (Connection connection = getConnection()) { - if (schemaExists(connection, schema.getMetadata().getName())) { - try (Statement statement = connection.createStatement()) { - statement.execute(format("DROP DATABASE `%1$s`", - schema.getMetadata().getName())); - } - log.info("Deleted Schema '{}'", schema.getMetadata().getName()); - - if (userExists(connection, schema.getStatus().getUserName())) { - try (Statement statement = connection.createStatement()) { - statement.execute(format("DROP USER '%1$s'", - schema.getStatus().getUserName())); - } - log.info("Deleted User '{}'", schema.getStatus().getUserName()); - } - - this.kubernetesClient.secrets() - .inNamespace(schema.getMetadata().getNamespace()) - .withName(schema.getStatus().getSecretName()) - .delete(); - } else { - log.info("Delete event ignored for schema '{}', real schema doesn't exist", - schema.getMetadata().getName()); - } - return DeleteControl.DEFAULT_DELETE; - } catch (SQLException e) { - log.error("Error while trying to delete Schema", e); - return DeleteControl.NO_FINALIZER_REMOVAL; + String password = RandomStringUtils.randomAlphanumeric(16); + String userName = String.format(USERNAME_FORMAT, schema.getMetadata().getName()); + String secretName = String.format(SECRET_FORMAT, schema.getMetadata().getName()); + try (Statement statement = connection.createStatement()) { + statement.execute(format("CREATE USER '%1$s' IDENTIFIED BY '%2$s'", userName, password)); + } + try (Statement statement = connection.createStatement()) { + statement.execute( + format("GRANT ALL ON `%1$s`.* TO '%2$s'", schema.getMetadata().getName(), userName)); } + Secret credentialsSecret = + new SecretBuilder() + .withNewMetadata() + .withName(secretName) + .endMetadata() + .addToData( + "MYSQL_USERNAME", Base64.getEncoder().encodeToString(userName.getBytes())) + .addToData( + "MYSQL_PASSWORD", Base64.getEncoder().encodeToString(password.getBytes())) + .build(); + this.kubernetesClient + .secrets() + .inNamespace(schema.getMetadata().getNamespace()) + .create(credentialsSecret); + + SchemaStatus status = new SchemaStatus(); + status.setUrl( + format( + "jdbc:mysql://%1$s/%2$s", + System.getenv("MYSQL_HOST"), schema.getMetadata().getName())); + status.setUserName(userName); + status.setSecretName(secretName); + status.setStatus("CREATED"); + schema.setStatus(status); + log.info("Schema {} created - updating CR status", schema.getMetadata().getName()); + + return UpdateControl.updateStatusSubResource(schema); + } + return UpdateControl.noUpdate(); + } catch (SQLException e) { + log.error("Error while creating Schema", e); + + SchemaStatus status = new SchemaStatus(); + status.setUrl(null); + status.setUserName(null); + status.setSecretName(null); + status.setStatus("ERROR"); + schema.setStatus(status); + + return UpdateControl.updateCustomResource(schema); } + } - private Connection getConnection() throws SQLException { - return DriverManager.getConnection(format("jdbc:mysql://%1$s:%2$s?user=%3$s&password=%4$s", - System.getenv("MYSQL_HOST"), - System.getenv("MYSQL_PORT") != null ? System.getenv("MYSQL_PORT") : "3306", - System.getenv("MYSQL_USER"), - System.getenv("MYSQL_PASSWORD"))); - } + @Override + public DeleteControl deleteResource(Schema schema, Context context) { + log.info("Execution deleteResource for: {}", schema.getMetadata().getName()); - private boolean schemaExists(Connection connection, String schemaName) throws SQLException { - try (PreparedStatement ps = - connection.prepareStatement("SELECT schema_name FROM information_schema.schemata WHERE schema_name = ?")) { - ps.setString(1, schemaName); - try (ResultSet resultSet = ps.executeQuery()) { - return resultSet.first(); - } + try (Connection connection = getConnection()) { + if (schemaExists(connection, schema.getMetadata().getName())) { + try (Statement statement = connection.createStatement()) { + statement.execute(format("DROP DATABASE `%1$s`", schema.getMetadata().getName())); } - } + log.info("Deleted Schema '{}'", schema.getMetadata().getName()); - private boolean userExists(Connection connection, String userName) throws SQLException { - try (PreparedStatement ps = - connection.prepareStatement("SELECT User FROM mysql.user WHERE User = ?")) { - ps.setString(1, userName); - try (ResultSet resultSet = ps.executeQuery()) { - return resultSet.first(); - } + if (userExists(connection, schema.getStatus().getUserName())) { + try (Statement statement = connection.createStatement()) { + statement.execute(format("DROP USER '%1$s'", schema.getStatus().getUserName())); + } + log.info("Deleted User '{}'", schema.getStatus().getUserName()); } + + this.kubernetesClient + .secrets() + .inNamespace(schema.getMetadata().getNamespace()) + .withName(schema.getStatus().getSecretName()) + .delete(); + } else { + log.info( + "Delete event ignored for schema '{}', real schema doesn't exist", + schema.getMetadata().getName()); + } + return DeleteControl.DEFAULT_DELETE; + } catch (SQLException e) { + log.error("Error while trying to delete Schema", e); + return DeleteControl.NO_FINALIZER_REMOVAL; + } + } + + private Connection getConnection() throws SQLException { + return DriverManager.getConnection( + format( + "jdbc:mysql://%1$s:%2$s?user=%3$s&password=%4$s", + System.getenv("MYSQL_HOST"), + System.getenv("MYSQL_PORT") != null ? System.getenv("MYSQL_PORT") : "3306", + System.getenv("MYSQL_USER"), + System.getenv("MYSQL_PASSWORD"))); + } + + private boolean schemaExists(Connection connection, String schemaName) throws SQLException { + try (PreparedStatement ps = + connection.prepareStatement( + "SELECT schema_name FROM information_schema.schemata WHERE schema_name = ?")) { + ps.setString(1, schemaName); + try (ResultSet resultSet = ps.executeQuery()) { + return resultSet.first(); + } + } + } + + private boolean userExists(Connection connection, String userName) throws SQLException { + try (PreparedStatement ps = + connection.prepareStatement("SELECT User FROM mysql.user WHERE User = ?")) { + ps.setString(1, userName); + try (ResultSet resultSet = ps.executeQuery()) { + return resultSet.first(); + } } + } } diff --git a/samples/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/SchemaSpec.java b/samples/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/SchemaSpec.java index 3bf564f797..19101c328a 100644 --- a/samples/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/SchemaSpec.java +++ b/samples/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/SchemaSpec.java @@ -2,14 +2,13 @@ public class SchemaSpec { - private String encoding; + private String encoding; - public String getEncoding() { - return encoding; - } - - public void setEncoding(String encoding) { - this.encoding = encoding; - } + public String getEncoding() { + return encoding; + } + public void setEncoding(String encoding) { + this.encoding = encoding; + } } diff --git a/samples/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/SchemaStatus.java b/samples/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/SchemaStatus.java index 41f18ab49f..168cd8db15 100644 --- a/samples/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/SchemaStatus.java +++ b/samples/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/SchemaStatus.java @@ -2,35 +2,43 @@ public class SchemaStatus { - private String url; + private String url; - private String status; + private String status; - private String userName; + private String userName; - private String secretName; + private String secretName; - public String getUrl() { - return url; - } + public String getUrl() { + return url; + } - public void setUrl(String url) { - this.url = url; - } + public void setUrl(String url) { + this.url = url; + } - public String getStatus() { - return status; - } + public String getStatus() { + return status; + } - public void setStatus(String status) { - this.status = status; - } + public void setStatus(String status) { + this.status = status; + } - public String getUserName() { return userName; } + public String getUserName() { + return userName; + } - public void setUserName(String userName) { this.userName = userName; } + public void setUserName(String userName) { + this.userName = userName; + } - public String getSecretName() { return secretName; } + public String getSecretName() { + return secretName; + } - public void setSecretName(String secretName) { this.secretName = secretName; } + public void setSecretName(String secretName) { + this.secretName = secretName; + } } diff --git a/samples/pure-java/src/main/java/io/javaoperatorsdk/operator/sample/PureJavaApplicationRunner.java b/samples/pure-java/src/main/java/io/javaoperatorsdk/operator/sample/PureJavaApplicationRunner.java index 673dbe4727..fe700db0d9 100644 --- a/samples/pure-java/src/main/java/io/javaoperatorsdk/operator/sample/PureJavaApplicationRunner.java +++ b/samples/pure-java/src/main/java/io/javaoperatorsdk/operator/sample/PureJavaApplicationRunner.java @@ -1,14 +1,14 @@ package io.javaoperatorsdk.operator.sample; -import io.javaoperatorsdk.operator.Operator; import io.fabric8.kubernetes.client.DefaultKubernetesClient; import io.fabric8.kubernetes.client.KubernetesClient; +import io.javaoperatorsdk.operator.Operator; public class PureJavaApplicationRunner { - public static void main(String[] args) { - KubernetesClient client = new DefaultKubernetesClient(); - Operator operator = new Operator(client); - operator.registerController(new CustomServiceController(client)); - } + public static void main(String[] args) { + KubernetesClient client = new DefaultKubernetesClient(); + Operator operator = new Operator(client); + operator.registerController(new CustomServiceController(client)); + } } diff --git a/samples/spring-boot-auto-config/src/main/java/io/javaoperatorsdk/operator/sample/SampleComponent.java b/samples/spring-boot-auto-config/src/main/java/io/javaoperatorsdk/operator/sample/SampleComponent.java index fa38106cd7..ff846507b3 100644 --- a/samples/spring-boot-auto-config/src/main/java/io/javaoperatorsdk/operator/sample/SampleComponent.java +++ b/samples/spring-boot-auto-config/src/main/java/io/javaoperatorsdk/operator/sample/SampleComponent.java @@ -1,25 +1,25 @@ package io.javaoperatorsdk.operator.sample; -import io.javaoperatorsdk.operator.Operator; import io.fabric8.kubernetes.client.KubernetesClient; +import io.javaoperatorsdk.operator.Operator; import org.springframework.stereotype.Component; -/** - * This component just showcases what beans are registered. - */ +/** This component just showcases what beans are registered. */ @Component public class SampleComponent { - private final Operator operator; + private final Operator operator; - private final KubernetesClient kubernetesClient; + private final KubernetesClient kubernetesClient; - private final CustomServiceController customServiceController; + private final CustomServiceController customServiceController; - public SampleComponent(Operator operator, KubernetesClient kubernetesClient, - CustomServiceController customServiceController) { - this.operator = operator; - this.kubernetesClient = kubernetesClient; - this.customServiceController = customServiceController; - } + public SampleComponent( + Operator operator, + KubernetesClient kubernetesClient, + CustomServiceController customServiceController) { + this.operator = operator; + this.kubernetesClient = kubernetesClient; + this.customServiceController = customServiceController; + } } diff --git a/samples/spring-boot-auto-config/src/main/java/io/javaoperatorsdk/operator/sample/SpringBootStarterSampleApplication.java b/samples/spring-boot-auto-config/src/main/java/io/javaoperatorsdk/operator/sample/SpringBootStarterSampleApplication.java index 818a642fe7..822429cbdb 100644 --- a/samples/spring-boot-auto-config/src/main/java/io/javaoperatorsdk/operator/sample/SpringBootStarterSampleApplication.java +++ b/samples/spring-boot-auto-config/src/main/java/io/javaoperatorsdk/operator/sample/SpringBootStarterSampleApplication.java @@ -7,15 +7,18 @@ import org.springframework.context.annotation.FilterType; /** - * Note that we have multiple options here either we can add this component scan as seen below. Or annotate controllers - * with @Component or @Service annotation or just register the bean within a spring "@Configuration". + * Note that we have multiple options here either we can add this component scan as seen below. Or + * annotate controllers with @Component or @Service annotation or just register the bean within a + * spring "@Configuration". */ -@ComponentScan(includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class)}) +@ComponentScan( + includeFilters = { + @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class) + }) @SpringBootApplication public class SpringBootStarterSampleApplication { - public static void main(String[] args) { - SpringApplication.run(SpringBootStarterSampleApplication.class, args); - } - + public static void main(String[] args) { + SpringApplication.run(SpringBootStarterSampleApplication.class, args); + } } diff --git a/samples/spring-boot-auto-config/src/test/java/io/javaoperatorsdk/operator/sample/SpringBootStarterSampleApplicationIT.java b/samples/spring-boot-auto-config/src/test/java/io/javaoperatorsdk/operator/sample/SpringBootStarterSampleApplicationIT.java index fa103be82f..cc87ca0464 100644 --- a/samples/spring-boot-auto-config/src/test/java/io/javaoperatorsdk/operator/sample/SpringBootStarterSampleApplicationIT.java +++ b/samples/spring-boot-auto-config/src/test/java/io/javaoperatorsdk/operator/sample/SpringBootStarterSampleApplicationIT.java @@ -6,7 +6,6 @@ @SpringBootTest public class SpringBootStarterSampleApplicationIT { - @Test - void contextLoads() { - } + @Test + void contextLoads() {} } diff --git a/samples/spring-boot-plain/src/main/java/io/javaoperatorsdk/operator/sample/Config.java b/samples/spring-boot-plain/src/main/java/io/javaoperatorsdk/operator/sample/Config.java index 88e9293939..a6f13c5a26 100644 --- a/samples/spring-boot-plain/src/main/java/io/javaoperatorsdk/operator/sample/Config.java +++ b/samples/spring-boot-plain/src/main/java/io/javaoperatorsdk/operator/sample/Config.java @@ -1,34 +1,31 @@ package io.javaoperatorsdk.operator.sample; -import io.javaoperatorsdk.operator.Operator; -import io.javaoperatorsdk.operator.api.ResourceController; -import io.javaoperatorsdk.operator.processing.retry.GenericRetry; import io.fabric8.kubernetes.client.DefaultKubernetesClient; import io.fabric8.kubernetes.client.KubernetesClient; +import io.javaoperatorsdk.operator.Operator; +import io.javaoperatorsdk.operator.api.ResourceController; +import java.util.List; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import java.util.List; - @Configuration public class Config { - @Bean - public KubernetesClient kubernetesClient() { - return new DefaultKubernetesClient(); - } - - @Bean - public CustomServiceController customServiceController(KubernetesClient client) { - return new CustomServiceController(client); - } + @Bean + public KubernetesClient kubernetesClient() { + return new DefaultKubernetesClient(); + } - // Register all controller beans - @Bean - public Operator operator(KubernetesClient client, List controllers) { - Operator operator = new Operator(client); - controllers.forEach(c -> operator.registerControllerForAllNamespaces(c)); - return operator; - } + @Bean + public CustomServiceController customServiceController(KubernetesClient client) { + return new CustomServiceController(client); + } + // Register all controller beans + @Bean + public Operator operator(KubernetesClient client, List controllers) { + Operator operator = new Operator(client); + controllers.forEach(c -> operator.registerControllerForAllNamespaces(c)); + return operator; + } } diff --git a/samples/spring-boot-plain/src/main/java/io/javaoperatorsdk/operator/sample/SpringBootStarterSampleApplication.java b/samples/spring-boot-plain/src/main/java/io/javaoperatorsdk/operator/sample/SpringBootStarterSampleApplication.java index 85ab957e78..97533f858a 100644 --- a/samples/spring-boot-plain/src/main/java/io/javaoperatorsdk/operator/sample/SpringBootStarterSampleApplication.java +++ b/samples/spring-boot-plain/src/main/java/io/javaoperatorsdk/operator/sample/SpringBootStarterSampleApplication.java @@ -6,8 +6,7 @@ @SpringBootApplication public class SpringBootStarterSampleApplication { - public static void main(String[] args) { - SpringApplication.run(SpringBootStarterSampleApplication.class, args); - } - + public static void main(String[] args) { + SpringApplication.run(SpringBootStarterSampleApplication.class, args); + } } diff --git a/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/Tomcat.java b/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/Tomcat.java index 749048945f..6c192ab846 100644 --- a/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/Tomcat.java +++ b/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/Tomcat.java @@ -4,29 +4,29 @@ public class Tomcat extends CustomResource { - private TomcatSpec spec; + private TomcatSpec spec; - private TomcatStatus status; + private TomcatStatus status; - public TomcatSpec getSpec() { - if (spec == null) { - spec = new TomcatSpec(); - } - return spec; + public TomcatSpec getSpec() { + if (spec == null) { + spec = new TomcatSpec(); } + return spec; + } - public void setSpec(TomcatSpec spec) { - this.spec = spec; - } + public void setSpec(TomcatSpec spec) { + this.spec = spec; + } - public TomcatStatus getStatus() { - if (status == null) { - status = new TomcatStatus(); - } - return status; + public TomcatStatus getStatus() { + if (status == null) { + status = new TomcatStatus(); } + return status; + } - public void setStatus(TomcatStatus status) { - this.status = status; - } -} \ No newline at end of file + public void setStatus(TomcatStatus status) { + this.status = status; + } +} diff --git a/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/TomcatController.java b/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/TomcatController.java index 614ae67f43..28f1161c58 100644 --- a/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/TomcatController.java +++ b/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/TomcatController.java @@ -12,189 +12,236 @@ import io.fabric8.kubernetes.client.dsl.ServiceResource; import io.fabric8.kubernetes.client.utils.Serialization; import io.javaoperatorsdk.operator.api.*; -import org.apache.commons.lang3.builder.EqualsBuilder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.Optional; +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; @Controller(crdName = "tomcats.tomcatoperator.io") public class TomcatController implements ResourceController { - private final Logger log = LoggerFactory.getLogger(getClass()); - - private final KubernetesClient kubernetesClient; - - private MixedOperation, CustomResourceDoneable, Resource>> tomcatOperations; - - private final List watchedResources = new ArrayList<>(); - - public TomcatController(KubernetesClient client) { - this.kubernetesClient = client; - } - - private void updateTomcatStatus(Context context, Tomcat tomcat, Deployment deployment) { - int readyReplicas = Optional.ofNullable(deployment.getStatus().getReadyReplicas()).orElse(0); - // Java 9+ - // int readyReplicas = Objects.requireNonNullElse(deployment.getStatus().getReadyReplicas(), 0); - log.info("Updating status of Tomcat {} in namespace {} to {} ready replicas", tomcat.getMetadata().getName(), - tomcat.getMetadata().getNamespace(), readyReplicas); - - tomcatOperations - .inNamespace(tomcat.getMetadata().getNamespace()) - .withName(tomcat.getMetadata().getName()) - .replace(tomcat); - } - - @Override - public UpdateControl createOrUpdateResource(Tomcat tomcat, Context context) { - Deployment deployment = createOrUpdateDeployment(tomcat); - createOrUpdateService(tomcat); - - if (!watchedResources.contains(WatchedResource.fromResource(deployment))) { - log.info("Attaching Watch to Deployment {}", deployment.getMetadata().getName()); - kubernetesClient.apps().deployments().withName(deployment.getMetadata().getName()).watch(new Watcher() { + private final Logger log = LoggerFactory.getLogger(getClass()); + + private final KubernetesClient kubernetesClient; + + private MixedOperation< + Tomcat, + CustomResourceList, + CustomResourceDoneable, + Resource>> + tomcatOperations; + + private final List watchedResources = new ArrayList<>(); + + public TomcatController(KubernetesClient client) { + this.kubernetesClient = client; + } + + private void updateTomcatStatus(Context context, Tomcat tomcat, Deployment deployment) { + int readyReplicas = Optional.ofNullable(deployment.getStatus().getReadyReplicas()).orElse(0); + // Java 9+ + // int readyReplicas = Objects.requireNonNullElse(deployment.getStatus().getReadyReplicas(), 0); + log.info( + "Updating status of Tomcat {} in namespace {} to {} ready replicas", + tomcat.getMetadata().getName(), + tomcat.getMetadata().getNamespace(), + readyReplicas); + + tomcatOperations + .inNamespace(tomcat.getMetadata().getNamespace()) + .withName(tomcat.getMetadata().getName()) + .replace(tomcat); + } + + @Override + public UpdateControl createOrUpdateResource(Tomcat tomcat, Context context) { + Deployment deployment = createOrUpdateDeployment(tomcat); + createOrUpdateService(tomcat); + + if (!watchedResources.contains(WatchedResource.fromResource(deployment))) { + log.info("Attaching Watch to Deployment {}", deployment.getMetadata().getName()); + kubernetesClient + .apps() + .deployments() + .withName(deployment.getMetadata().getName()) + .watch( + new Watcher() { @Override public void eventReceived(Action action, Deployment deployment) { - try { - Tomcat tomcat = tomcatOperations.inNamespace(deployment.getMetadata().getNamespace()) - .withName(deployment.getMetadata().getLabels().get("created-by")).get(); - updateTomcatStatus(context, tomcat, deployment); - } catch (Exception ex) { - log.error(ex.getMessage()); - } + try { + Tomcat tomcat = + tomcatOperations + .inNamespace(deployment.getMetadata().getNamespace()) + .withName(deployment.getMetadata().getLabels().get("created-by")) + .get(); + updateTomcatStatus(context, tomcat, deployment); + } catch (Exception ex) { + log.error(ex.getMessage()); + } } @Override - public void onClose(KubernetesClientException cause) { - } - }); - watchedResources.add(WatchedResource.fromResource(deployment)); - } - - - return UpdateControl.noUpdate(); + public void onClose(KubernetesClientException cause) {} + }); + watchedResources.add(WatchedResource.fromResource(deployment)); } - @Override - public DeleteControl deleteResource(Tomcat tomcat, Context context) { - deleteDeployment(tomcat); - deleteService(tomcat); - return DeleteControl.DEFAULT_DELETE; + return UpdateControl.noUpdate(); + } + + @Override + public DeleteControl deleteResource(Tomcat tomcat, Context context) { + deleteDeployment(tomcat); + deleteService(tomcat); + return DeleteControl.DEFAULT_DELETE; + } + + private Deployment createOrUpdateDeployment(Tomcat tomcat) { + String ns = tomcat.getMetadata().getNamespace(); + Deployment existingDeployment = + kubernetesClient + .apps() + .deployments() + .inNamespace(ns) + .withName(tomcat.getMetadata().getName()) + .get(); + if (existingDeployment == null) { + Deployment deployment = loadYaml(Deployment.class, "deployment.yaml"); + deployment.getMetadata().setName(tomcat.getMetadata().getName()); + deployment.getMetadata().setNamespace(ns); + deployment.getMetadata().getLabels().put("created-by", tomcat.getMetadata().getName()); + // set tomcat version + deployment + .getSpec() + .getTemplate() + .getSpec() + .getContainers() + .get(0) + .setImage("tomcat:" + tomcat.getSpec().getVersion()); + deployment.getSpec().setReplicas(tomcat.getSpec().getReplicas()); + + // make sure label selector matches label (which has to be matched by service selector too) + deployment + .getSpec() + .getTemplate() + .getMetadata() + .getLabels() + .put("app", tomcat.getMetadata().getName()); + deployment + .getSpec() + .getSelector() + .getMatchLabels() + .put("app", tomcat.getMetadata().getName()); + + log.info("Creating or updating Deployment {} in {}", deployment.getMetadata().getName(), ns); + return kubernetesClient.apps().deployments().inNamespace(ns).create(deployment); + } else { + existingDeployment + .getSpec() + .getTemplate() + .getSpec() + .getContainers() + .get(0) + .setImage("tomcat:" + tomcat.getSpec().getVersion()); + existingDeployment.getSpec().setReplicas(tomcat.getSpec().getReplicas()); + return kubernetesClient + .apps() + .deployments() + .inNamespace(ns) + .createOrReplace(existingDeployment); } - - private Deployment createOrUpdateDeployment(Tomcat tomcat) { - String ns = tomcat.getMetadata().getNamespace(); - Deployment existingDeployment = kubernetesClient.apps().deployments() - .inNamespace(ns).withName(tomcat.getMetadata().getName()) - .get(); - if (existingDeployment == null) { - Deployment deployment = loadYaml(Deployment.class, "deployment.yaml"); - deployment.getMetadata().setName(tomcat.getMetadata().getName()); - deployment.getMetadata().setNamespace(ns); - deployment.getMetadata().getLabels().put("created-by", tomcat.getMetadata().getName()); - // set tomcat version - deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage("tomcat:" + tomcat.getSpec().getVersion()); - deployment.getSpec().setReplicas(tomcat.getSpec().getReplicas()); - - //make sure label selector matches label (which has to be matched by service selector too) - deployment.getSpec().getTemplate().getMetadata().getLabels().put("app", tomcat.getMetadata().getName()); - deployment.getSpec().getSelector().getMatchLabels().put("app", tomcat.getMetadata().getName()); - - log.info("Creating or updating Deployment {} in {}", deployment.getMetadata().getName(), ns); - return kubernetesClient.apps().deployments().inNamespace(ns).create(deployment); - } else { - existingDeployment.getSpec().getTemplate().getSpec().getContainers().get(0).setImage("tomcat:" + tomcat.getSpec().getVersion()); - existingDeployment.getSpec().setReplicas(tomcat.getSpec().getReplicas()); - return kubernetesClient.apps().deployments().inNamespace(ns).createOrReplace(existingDeployment); - } + } + + private void deleteDeployment(Tomcat tomcat) { + log.info("Deleting Deployment {}", tomcat.getMetadata().getName()); + RollableScalableResource deployment = + kubernetesClient + .apps() + .deployments() + .inNamespace(tomcat.getMetadata().getNamespace()) + .withName(tomcat.getMetadata().getName()); + if (deployment.get() != null) { + deployment.delete(); } - - private void deleteDeployment(Tomcat tomcat) { - log.info("Deleting Deployment {}", tomcat.getMetadata().getName()); - RollableScalableResource deployment = kubernetesClient.apps().deployments() - .inNamespace(tomcat.getMetadata().getNamespace()) - .withName(tomcat.getMetadata().getName()); - if (deployment.get() != null) { - deployment.delete(); - } + } + + private void createOrUpdateService(Tomcat tomcat) { + Service service = loadYaml(Service.class, "service.yaml"); + service.getMetadata().setName(tomcat.getMetadata().getName()); + String ns = tomcat.getMetadata().getNamespace(); + service.getMetadata().setNamespace(ns); + service.getSpec().getSelector().put("app", tomcat.getMetadata().getName()); + log.info("Creating or updating Service {} in {}", service.getMetadata().getName(), ns); + kubernetesClient.services().inNamespace(ns).createOrReplace(service); + } + + private void deleteService(Tomcat tomcat) { + log.info("Deleting Service {}", tomcat.getMetadata().getName()); + ServiceResource service = + kubernetesClient + .services() + .inNamespace(tomcat.getMetadata().getNamespace()) + .withName(tomcat.getMetadata().getName()); + if (service.get() != null) { + service.delete(); } + } - private void createOrUpdateService(Tomcat tomcat) { - Service service = loadYaml(Service.class, "service.yaml"); - service.getMetadata().setName(tomcat.getMetadata().getName()); - String ns = tomcat.getMetadata().getNamespace(); - service.getMetadata().setNamespace(ns); - service.getSpec().getSelector().put("app", tomcat.getMetadata().getName()); - log.info("Creating or updating Service {} in {}", service.getMetadata().getName(), ns); - kubernetesClient.services().inNamespace(ns).createOrReplace(service); + private T loadYaml(Class clazz, String yaml) { + try (InputStream is = getClass().getResourceAsStream(yaml)) { + return Serialization.unmarshal(is, clazz); + } catch (IOException ex) { + throw new IllegalStateException("Cannot find yaml on classpath: " + yaml); + } + } + + public void setTomcatOperations( + MixedOperation< + Tomcat, + CustomResourceList, + CustomResourceDoneable, + Resource>> + tomcatOperations) { + this.tomcatOperations = tomcatOperations; + } + + private static class WatchedResource { + private final String type; + private final String name; + + public WatchedResource(String type, String name) { + this.type = type; + this.name = name; } - private void deleteService(Tomcat tomcat) { - log.info("Deleting Service {}", tomcat.getMetadata().getName()); - ServiceResource service = kubernetesClient.services() - .inNamespace(tomcat.getMetadata().getNamespace()) - .withName(tomcat.getMetadata().getName()); - if (service.get() != null) { - service.delete(); - } + public static WatchedResource fromResource(HasMetadata resource) { + return new WatchedResource(resource.getKind(), resource.getMetadata().getName()); } - private T loadYaml(Class clazz, String yaml) { - try (InputStream is = getClass().getResourceAsStream(yaml)) { - return Serialization.unmarshal(is, clazz); - } catch (IOException ex) { - throw new IllegalStateException("Cannot find yaml on classpath: " + yaml); - } + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || getClass() != o.getClass()) return false; + + WatchedResource that = (WatchedResource) o; + + return new EqualsBuilder().append(type, that.type).append(name, that.name).isEquals(); } - public void setTomcatOperations(MixedOperation, CustomResourceDoneable, Resource>> tomcatOperations) { - this.tomcatOperations = tomcatOperations; + @Override + public int hashCode() { + return Objects.hash(type, name); } - private static class WatchedResource { - private final String type; - private final String name; - - public WatchedResource(String type, String name) { - this.type = type; - this.name = name; - } - - public static WatchedResource fromResource(HasMetadata resource) { - return new WatchedResource(resource.getKind(), resource.getMetadata().getName()); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - - if (o == null || getClass() != o.getClass()) return false; - - WatchedResource that = (WatchedResource) o; - - return new EqualsBuilder() - .append(type, that.type) - .append(name, that.name) - .isEquals(); - } - - @Override - public int hashCode() { - return Objects.hash(type, name); - } - - @Override - public String toString() { - return "WatchedResource{" + - "type='" + type + '\'' + - ", name='" + name + '\'' + - '}'; - } + @Override + public String toString() { + return "WatchedResource{" + "type='" + type + '\'' + ", name='" + name + '\'' + '}'; } + } } diff --git a/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/TomcatOperator.java b/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/TomcatOperator.java index fe1efe418f..27872e9c72 100644 --- a/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/TomcatOperator.java +++ b/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/TomcatOperator.java @@ -1,10 +1,11 @@ package io.javaoperatorsdk.operator.sample; -import io.javaoperatorsdk.operator.Operator; import io.fabric8.kubernetes.client.Config; import io.fabric8.kubernetes.client.ConfigBuilder; import io.fabric8.kubernetes.client.DefaultKubernetesClient; import io.fabric8.kubernetes.client.KubernetesClient; +import io.javaoperatorsdk.operator.Operator; +import java.io.IOException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.takes.facets.fork.FkRegex; @@ -12,27 +13,22 @@ import org.takes.http.Exit; import org.takes.http.FtBasic; -import java.io.IOException; - public class TomcatOperator { - private static final Logger log = LoggerFactory.getLogger(TomcatOperator.class); - - public static void main(String[] args) throws IOException { + private static final Logger log = LoggerFactory.getLogger(TomcatOperator.class); - Config config = new ConfigBuilder().withNamespace(null).build(); - KubernetesClient client = new DefaultKubernetesClient(config); - Operator operator = new Operator(client); + public static void main(String[] args) throws IOException { - TomcatController tomcatController = new TomcatController(client); - operator.registerControllerForAllNamespaces(tomcatController); - tomcatController.setTomcatOperations(operator.getCustomResourceClients(Tomcat.class)); + Config config = new ConfigBuilder().withNamespace(null).build(); + KubernetesClient client = new DefaultKubernetesClient(config); + Operator operator = new Operator(client); - operator.registerControllerForAllNamespaces(new WebappController(client)); + TomcatController tomcatController = new TomcatController(client); + operator.registerControllerForAllNamespaces(tomcatController); + tomcatController.setTomcatOperations(operator.getCustomResourceClients(Tomcat.class)); + operator.registerControllerForAllNamespaces(new WebappController(client)); - new FtBasic( - new TkFork(new FkRegex("/health", "ALL GOOD.")), 8080 - ).start(Exit.NEVER); - } + new FtBasic(new TkFork(new FkRegex("/health", "ALL GOOD.")), 8080).start(Exit.NEVER); + } } diff --git a/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/TomcatSpec.java b/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/TomcatSpec.java index 317ce022ad..fbd22f30f9 100644 --- a/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/TomcatSpec.java +++ b/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/TomcatSpec.java @@ -2,18 +2,22 @@ public class TomcatSpec { - private Integer version; - private Integer replicas; + private Integer version; + private Integer replicas; - public Integer getVersion() { return version; } + public Integer getVersion() { + return version; + } - public void setVersion(Integer version) { - this.version = version; - } + public void setVersion(Integer version) { + this.version = version; + } - public Integer getReplicas() { return replicas; } + public Integer getReplicas() { + return replicas; + } - public void setReplicas(Integer replicas) { - this.replicas = replicas; - } -} \ No newline at end of file + public void setReplicas(Integer replicas) { + this.replicas = replicas; + } +} diff --git a/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/TomcatStatus.java b/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/TomcatStatus.java index 66984e73f5..3bf3d2ab4b 100644 --- a/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/TomcatStatus.java +++ b/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/TomcatStatus.java @@ -2,13 +2,13 @@ public class TomcatStatus { - private Integer readyReplicas = 0; + private Integer readyReplicas = 0; - public Integer getReadyReplicas() { - return readyReplicas; - } + public Integer getReadyReplicas() { + return readyReplicas; + } - public void setReadyReplicas(Integer readyReplicas) { - this.readyReplicas = readyReplicas; - } + public void setReadyReplicas(Integer readyReplicas) { + this.readyReplicas = readyReplicas; + } } diff --git a/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/Webapp.java b/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/Webapp.java index 124a2ed454..5c3efa7a40 100644 --- a/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/Webapp.java +++ b/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/Webapp.java @@ -4,26 +4,26 @@ public class Webapp extends CustomResource { - private WebappSpec spec; + private WebappSpec spec; - private WebappStatus status; + private WebappStatus status; - public WebappSpec getSpec() { - return spec; - } + public WebappSpec getSpec() { + return spec; + } - public void setSpec(WebappSpec spec) { - this.spec = spec; - } + public void setSpec(WebappSpec spec) { + this.spec = spec; + } - public WebappStatus getStatus() { - if (status == null) { - status = new WebappStatus(); - } - return status; + public WebappStatus getStatus() { + if (status == null) { + status = new WebappStatus(); } + return status; + } - public void setStatus(WebappStatus status) { - this.status = status; - } -} \ No newline at end of file + public void setStatus(WebappStatus status) { + this.status = status; + } +} diff --git a/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/WebappController.java b/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/WebappController.java index 333033471a..bc74c4282b 100644 --- a/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/WebappController.java +++ b/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/WebappController.java @@ -4,78 +4,92 @@ import io.fabric8.kubernetes.api.model.apps.Deployment; import io.fabric8.kubernetes.client.KubernetesClient; import io.javaoperatorsdk.operator.api.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.ByteArrayOutputStream; import java.util.List; import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -@Controller( - crdName = "webapps.tomcatoperator.io") +@Controller(crdName = "webapps.tomcatoperator.io") public class WebappController implements ResourceController { - private KubernetesClient kubernetesClient; + private KubernetesClient kubernetesClient; - private final Logger log = LoggerFactory.getLogger(getClass()); + private final Logger log = LoggerFactory.getLogger(getClass()); - public WebappController(KubernetesClient kubernetesClient) { - this.kubernetesClient = kubernetesClient; - } + public WebappController(KubernetesClient kubernetesClient) { + this.kubernetesClient = kubernetesClient; + } - @Override - public UpdateControl createOrUpdateResource(Webapp webapp, Context context) { - if (Objects.equals(webapp.getSpec().getUrl(), webapp.getStatus().getDeployedArtifact())) { - return UpdateControl.noUpdate(); - } + @Override + public UpdateControl createOrUpdateResource(Webapp webapp, Context context) { + if (Objects.equals(webapp.getSpec().getUrl(), webapp.getStatus().getDeployedArtifact())) { + return UpdateControl.noUpdate(); + } - String fileName = fileNameFromWebapp(webapp); - String[] command = new String[]{"wget", "-O", "/data/" + fileName, webapp.getSpec().getUrl()}; + String fileName = fileNameFromWebapp(webapp); + String[] command = new String[] {"wget", "-O", "/data/" + fileName, webapp.getSpec().getUrl()}; - executeCommandInAllPods(kubernetesClient, webapp, command); + executeCommandInAllPods(kubernetesClient, webapp, command); - webapp.getStatus().setDeployedArtifact(webapp.getSpec().getUrl()); - return UpdateControl.updateStatusSubResource(webapp); - } + webapp.getStatus().setDeployedArtifact(webapp.getSpec().getUrl()); + return UpdateControl.updateStatusSubResource(webapp); + } - @Override - public DeleteControl deleteResource(Webapp webapp, Context context) { - String fileName = fileNameFromWebapp(webapp); - String[] command = new String[]{"rm", "/data/" + fileName}; - executeCommandInAllPods(kubernetesClient, webapp, command); - return DeleteControl.DEFAULT_DELETE; - } + @Override + public DeleteControl deleteResource(Webapp webapp, Context context) { + String fileName = fileNameFromWebapp(webapp); + String[] command = new String[] {"rm", "/data/" + fileName}; + executeCommandInAllPods(kubernetesClient, webapp, command); + return DeleteControl.DEFAULT_DELETE; + } - private void executeCommandInAllPods(KubernetesClient kubernetesClient, Webapp webapp, String[] command) { - Deployment deployment = kubernetesClient.apps().deployments().inNamespace(webapp.getMetadata().getNamespace()) - .withName(webapp.getSpec().getTomcat()).get(); + private void executeCommandInAllPods( + KubernetesClient kubernetesClient, Webapp webapp, String[] command) { + Deployment deployment = + kubernetesClient + .apps() + .deployments() + .inNamespace(webapp.getMetadata().getNamespace()) + .withName(webapp.getSpec().getTomcat()) + .get(); - if (deployment != null) { - List pods = kubernetesClient.pods().inNamespace(webapp.getMetadata().getNamespace()) - .withLabels(deployment.getSpec().getSelector().getMatchLabels()).list().getItems(); - for (Pod pod : pods) { - log.info("Executing command {} in Pod {}", String.join(" ", command), pod.getMetadata().getName()); - kubernetesClient.pods().inNamespace(deployment.getMetadata().getNamespace()) - .withName(pod.getMetadata().getName()) - .inContainer("war-downloader") - .writingOutput(new ByteArrayOutputStream()) - .writingError(new ByteArrayOutputStream()) - .exec(command); - } - } + if (deployment != null) { + List pods = + kubernetesClient + .pods() + .inNamespace(webapp.getMetadata().getNamespace()) + .withLabels(deployment.getSpec().getSelector().getMatchLabels()) + .list() + .getItems(); + for (Pod pod : pods) { + log.info( + "Executing command {} in Pod {}", + String.join(" ", command), + pod.getMetadata().getName()); + kubernetesClient + .pods() + .inNamespace(deployment.getMetadata().getNamespace()) + .withName(pod.getMetadata().getName()) + .inContainer("war-downloader") + .writingOutput(new ByteArrayOutputStream()) + .writingError(new ByteArrayOutputStream()) + .exec(command); + } } + } - private String fileNameFromWebapp(Webapp webapp) { - try { - Pattern regexpPattern = Pattern.compile("([^\\/]+$)"); - Matcher regexpMatcher = regexpPattern.matcher(webapp.getSpec().getUrl()); - regexpMatcher.find(); - return regexpMatcher.group(); - } catch (RuntimeException ex) { - log.error("Failed to parse file name from URL {}", webapp.getSpec().getUrl()); - throw ex; - } + private String fileNameFromWebapp(Webapp webapp) { + try { + Pattern regexpPattern = Pattern.compile("([^\\/]+$)"); + Matcher regexpMatcher = regexpPattern.matcher(webapp.getSpec().getUrl()); + regexpMatcher.find(); + return regexpMatcher.group(); + } catch (RuntimeException ex) { + log.error("Failed to parse file name from URL {}", webapp.getSpec().getUrl()); + throw ex; } + } } diff --git a/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/WebappSpec.java b/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/WebappSpec.java index c652e2f713..a34621c35b 100644 --- a/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/WebappSpec.java +++ b/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/WebappSpec.java @@ -2,33 +2,33 @@ public class WebappSpec { - private String url; + private String url; - private String contextPath; + private String contextPath; - private String tomcat; + private String tomcat; - public String getUrl() { - return url; - } + public String getUrl() { + return url; + } - public void setUrl(String url) { - this.url = url; - } + public void setUrl(String url) { + this.url = url; + } - public String getContextPath() { - return contextPath; - } + public String getContextPath() { + return contextPath; + } - public void setContextPath(String contextPath) { - this.contextPath = contextPath; - } + public void setContextPath(String contextPath) { + this.contextPath = contextPath; + } - public String getTomcat() { - return tomcat; - } + public String getTomcat() { + return tomcat; + } - public void setTomcat(String tomcat) { - this.tomcat = tomcat; - } + public void setTomcat(String tomcat) { + this.tomcat = tomcat; + } } diff --git a/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/WebappStatus.java b/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/WebappStatus.java index 2eba99a93b..53e71fe20c 100644 --- a/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/WebappStatus.java +++ b/samples/tomcat/src/main/java/io/javaoperatorsdk/operator/sample/WebappStatus.java @@ -2,13 +2,13 @@ public class WebappStatus { - private String deployedArtifact; + private String deployedArtifact; - public String getDeployedArtifact() { - return deployedArtifact; - } + public String getDeployedArtifact() { + return deployedArtifact; + } - public void setDeployedArtifact(String deployedArtifact) { - this.deployedArtifact = deployedArtifact; - } + public void setDeployedArtifact(String deployedArtifact) { + this.deployedArtifact = deployedArtifact; + } } diff --git a/samples/webserver/src/main/java/io/javaoperatorsdk/operator/sample/ErrorSimulationException.java b/samples/webserver/src/main/java/io/javaoperatorsdk/operator/sample/ErrorSimulationException.java index f87cb9898e..e2d3f3c1dd 100644 --- a/samples/webserver/src/main/java/io/javaoperatorsdk/operator/sample/ErrorSimulationException.java +++ b/samples/webserver/src/main/java/io/javaoperatorsdk/operator/sample/ErrorSimulationException.java @@ -2,7 +2,7 @@ public class ErrorSimulationException extends RuntimeException { - public ErrorSimulationException(String message) { - super(message); - } + public ErrorSimulationException(String message) { + super(message); + } } diff --git a/samples/webserver/src/main/java/io/javaoperatorsdk/operator/sample/WebServer.java b/samples/webserver/src/main/java/io/javaoperatorsdk/operator/sample/WebServer.java index 9ec51c6954..cbef68eb80 100644 --- a/samples/webserver/src/main/java/io/javaoperatorsdk/operator/sample/WebServer.java +++ b/samples/webserver/src/main/java/io/javaoperatorsdk/operator/sample/WebServer.java @@ -4,23 +4,23 @@ public class WebServer extends CustomResource { - private WebServerSpec spec; + private WebServerSpec spec; - private WebServerStatus status; + private WebServerStatus status; - public WebServerSpec getSpec() { - return spec; - } + public WebServerSpec getSpec() { + return spec; + } - public void setSpec(WebServerSpec spec) { - this.spec = spec; - } + public void setSpec(WebServerSpec spec) { + this.spec = spec; + } - public WebServerStatus getStatus() { - return status; - } + public WebServerStatus getStatus() { + return status; + } - public void setStatus(WebServerStatus status) { - this.status = status; - } + public void setStatus(WebServerStatus status) { + this.status = status; + } } diff --git a/samples/webserver/src/main/java/io/javaoperatorsdk/operator/sample/WebServerController.java b/samples/webserver/src/main/java/io/javaoperatorsdk/operator/sample/WebServerController.java index 57f1152524..cb066417cc 100644 --- a/samples/webserver/src/main/java/io/javaoperatorsdk/operator/sample/WebServerController.java +++ b/samples/webserver/src/main/java/io/javaoperatorsdk/operator/sample/WebServerController.java @@ -10,135 +10,165 @@ import io.fabric8.kubernetes.client.utils.Serialization; import io.javaoperatorsdk.operator.api.*; import io.javaoperatorsdk.operator.api.Context; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -@Controller( - crdName = "webservers.sample.javaoperatorsdk") +@Controller(crdName = "webservers.sample.javaoperatorsdk") public class WebServerController implements ResourceController { - private final Logger log = LoggerFactory.getLogger(getClass()); + private final Logger log = LoggerFactory.getLogger(getClass()); - private final KubernetesClient kubernetesClient; + private final KubernetesClient kubernetesClient; - public WebServerController(KubernetesClient kubernetesClient) { - this.kubernetesClient = kubernetesClient; - } + public WebServerController(KubernetesClient kubernetesClient) { + this.kubernetesClient = kubernetesClient; + } - @Override - public UpdateControl createOrUpdateResource(WebServer webServer, Context context) { - if (webServer.getSpec().getHtml().contains("error")) { - throw new ErrorSimulationException("Simulating error"); - } - - String ns = webServer.getMetadata().getNamespace(); - - Map data = new HashMap<>(); - data.put("index.html", webServer.getSpec().getHtml()); - - ConfigMap htmlConfigMap = new ConfigMapBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName(configMapName(webServer)) - .withNamespace(ns) - .build()) - .withData(data) - .build(); - - Deployment deployment = loadYaml(Deployment.class, "deployment.yaml"); - deployment.getMetadata().setName(deploymentName(webServer)); - deployment.getMetadata().setNamespace(ns); - deployment.getSpec().getSelector().getMatchLabels().put("app", deploymentName(webServer)); - deployment.getSpec().getTemplate().getMetadata().getLabels().put("app", deploymentName(webServer)); - deployment.getSpec().getTemplate().getSpec().getVolumes().get(0).setConfigMap( - new ConfigMapVolumeSourceBuilder().withName(configMapName(webServer)).build()); - - Service service = loadYaml(Service.class, "service.yaml"); - service.getMetadata().setName(serviceName(webServer)); - service.getMetadata().setNamespace(ns); - service.getSpec().setSelector(deployment.getSpec().getTemplate().getMetadata().getLabels()); - - ConfigMap existingConfigMap = kubernetesClient.configMaps() - .inNamespace(htmlConfigMap.getMetadata().getNamespace()) - .withName(htmlConfigMap.getMetadata().getName()).get(); - - log.info("Creating or updating ConfigMap {} in {}", htmlConfigMap.getMetadata().getName(), ns); - kubernetesClient.configMaps().inNamespace(ns).createOrReplace(htmlConfigMap); - log.info("Creating or updating Deployment {} in {}", deployment.getMetadata().getName(), ns); - kubernetesClient.apps().deployments().inNamespace(ns).createOrReplace(deployment); - - if (kubernetesClient.services().inNamespace(ns).withName(service.getMetadata().getName()).get() == null) { - log.info("Creating Service {} in {}", service.getMetadata().getName(), ns); - kubernetesClient.services().inNamespace(ns).createOrReplace(service); - } - - if (existingConfigMap != null) { - if (!StringUtils.equals(existingConfigMap.getData().get("index.html"), htmlConfigMap.getData().get("index.html"))) { - log.info("Restarting pods because HTML has changed in {}", ns); - kubernetesClient.pods().inNamespace(ns).withLabel("app", deploymentName(webServer)).delete(); - } - } - - WebServerStatus status = new WebServerStatus(); - status.setHtmlConfigMap(htmlConfigMap.getMetadata().getName()); - status.setAreWeGood("Yes!"); - webServer.setStatus(status); -// throw new RuntimeException("Creating object failed, because it failed"); - return UpdateControl.updateCustomResource(webServer); + @Override + public UpdateControl createOrUpdateResource( + WebServer webServer, Context context) { + if (webServer.getSpec().getHtml().contains("error")) { + throw new ErrorSimulationException("Simulating error"); } - @Override - public DeleteControl deleteResource(WebServer nginx, io.javaoperatorsdk.operator.api.Context context) { - log.info("Execution deleteResource for: {}", nginx.getMetadata().getName()); - - log.info("Deleting ConfigMap {}", configMapName(nginx)); - Resource configMap = kubernetesClient.configMaps() - .inNamespace(nginx.getMetadata().getNamespace()) - .withName(configMapName(nginx)); - if (configMap.get() != null) { - configMap.delete(); - } - - log.info("Deleting Deployment {}", deploymentName(nginx)); - RollableScalableResource deployment = kubernetesClient.apps().deployments() - .inNamespace(nginx.getMetadata().getNamespace()) - .withName(deploymentName(nginx)); - if (deployment.get() != null) { - deployment.cascading(true).delete(); - } - - log.info("Deleting Service {}", serviceName(nginx)); - ServiceResource service = kubernetesClient.services() - .inNamespace(nginx.getMetadata().getNamespace()) - .withName(serviceName(nginx)); - if (service.get() != null) { - service.delete(); - } - return DeleteControl.DEFAULT_DELETE; + String ns = webServer.getMetadata().getNamespace(); + + Map data = new HashMap<>(); + data.put("index.html", webServer.getSpec().getHtml()); + + ConfigMap htmlConfigMap = + new ConfigMapBuilder() + .withMetadata( + new ObjectMetaBuilder() + .withName(configMapName(webServer)) + .withNamespace(ns) + .build()) + .withData(data) + .build(); + + Deployment deployment = loadYaml(Deployment.class, "deployment.yaml"); + deployment.getMetadata().setName(deploymentName(webServer)); + deployment.getMetadata().setNamespace(ns); + deployment.getSpec().getSelector().getMatchLabels().put("app", deploymentName(webServer)); + deployment + .getSpec() + .getTemplate() + .getMetadata() + .getLabels() + .put("app", deploymentName(webServer)); + deployment + .getSpec() + .getTemplate() + .getSpec() + .getVolumes() + .get(0) + .setConfigMap( + new ConfigMapVolumeSourceBuilder().withName(configMapName(webServer)).build()); + + Service service = loadYaml(Service.class, "service.yaml"); + service.getMetadata().setName(serviceName(webServer)); + service.getMetadata().setNamespace(ns); + service.getSpec().setSelector(deployment.getSpec().getTemplate().getMetadata().getLabels()); + + ConfigMap existingConfigMap = + kubernetesClient + .configMaps() + .inNamespace(htmlConfigMap.getMetadata().getNamespace()) + .withName(htmlConfigMap.getMetadata().getName()) + .get(); + + log.info("Creating or updating ConfigMap {} in {}", htmlConfigMap.getMetadata().getName(), ns); + kubernetesClient.configMaps().inNamespace(ns).createOrReplace(htmlConfigMap); + log.info("Creating or updating Deployment {} in {}", deployment.getMetadata().getName(), ns); + kubernetesClient.apps().deployments().inNamespace(ns).createOrReplace(deployment); + + if (kubernetesClient.services().inNamespace(ns).withName(service.getMetadata().getName()).get() + == null) { + log.info("Creating Service {} in {}", service.getMetadata().getName(), ns); + kubernetesClient.services().inNamespace(ns).createOrReplace(service); } - private static String configMapName(WebServer nginx) { - return nginx.getMetadata().getName() + "-html"; + if (existingConfigMap != null) { + if (!StringUtils.equals( + existingConfigMap.getData().get("index.html"), + htmlConfigMap.getData().get("index.html"))) { + log.info("Restarting pods because HTML has changed in {}", ns); + kubernetesClient + .pods() + .inNamespace(ns) + .withLabel("app", deploymentName(webServer)) + .delete(); + } } - private static String deploymentName(WebServer nginx) { - return nginx.getMetadata().getName(); + WebServerStatus status = new WebServerStatus(); + status.setHtmlConfigMap(htmlConfigMap.getMetadata().getName()); + status.setAreWeGood("Yes!"); + webServer.setStatus(status); + // throw new RuntimeException("Creating object failed, because it failed"); + return UpdateControl.updateCustomResource(webServer); + } + + @Override + public DeleteControl deleteResource( + WebServer nginx, io.javaoperatorsdk.operator.api.Context context) { + log.info("Execution deleteResource for: {}", nginx.getMetadata().getName()); + + log.info("Deleting ConfigMap {}", configMapName(nginx)); + Resource configMap = + kubernetesClient + .configMaps() + .inNamespace(nginx.getMetadata().getNamespace()) + .withName(configMapName(nginx)); + if (configMap.get() != null) { + configMap.delete(); } - private static String serviceName(WebServer nginx) { - return nginx.getMetadata().getName(); + log.info("Deleting Deployment {}", deploymentName(nginx)); + RollableScalableResource deployment = + kubernetesClient + .apps() + .deployments() + .inNamespace(nginx.getMetadata().getNamespace()) + .withName(deploymentName(nginx)); + if (deployment.get() != null) { + deployment.cascading(true).delete(); } - private T loadYaml(Class clazz, String yaml) { - try (InputStream is = getClass().getResourceAsStream(yaml)) { - return Serialization.unmarshal(is, clazz); - } catch (IOException ex) { - throw new IllegalStateException("Cannot find yaml on classpath: " + yaml); - } + log.info("Deleting Service {}", serviceName(nginx)); + ServiceResource service = + kubernetesClient + .services() + .inNamespace(nginx.getMetadata().getNamespace()) + .withName(serviceName(nginx)); + if (service.get() != null) { + service.delete(); + } + return DeleteControl.DEFAULT_DELETE; + } + + private static String configMapName(WebServer nginx) { + return nginx.getMetadata().getName() + "-html"; + } + + private static String deploymentName(WebServer nginx) { + return nginx.getMetadata().getName(); + } + + private static String serviceName(WebServer nginx) { + return nginx.getMetadata().getName(); + } + + private T loadYaml(Class clazz, String yaml) { + try (InputStream is = getClass().getResourceAsStream(yaml)) { + return Serialization.unmarshal(is, clazz); + } catch (IOException ex) { + throw new IllegalStateException("Cannot find yaml on classpath: " + yaml); } + } } diff --git a/samples/webserver/src/main/java/io/javaoperatorsdk/operator/sample/WebServerOperator.java b/samples/webserver/src/main/java/io/javaoperatorsdk/operator/sample/WebServerOperator.java index 5bedebeac9..d0deb00c58 100644 --- a/samples/webserver/src/main/java/io/javaoperatorsdk/operator/sample/WebServerOperator.java +++ b/samples/webserver/src/main/java/io/javaoperatorsdk/operator/sample/WebServerOperator.java @@ -1,10 +1,11 @@ package io.javaoperatorsdk.operator.sample; -import io.javaoperatorsdk.operator.Operator; import io.fabric8.kubernetes.client.Config; import io.fabric8.kubernetes.client.ConfigBuilder; import io.fabric8.kubernetes.client.DefaultKubernetesClient; import io.fabric8.kubernetes.client.KubernetesClient; +import io.javaoperatorsdk.operator.Operator; +import java.io.IOException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.takes.facets.fork.FkRegex; @@ -12,22 +13,18 @@ import org.takes.http.Exit; import org.takes.http.FtBasic; -import java.io.IOException; - public class WebServerOperator { - private static final Logger log = LoggerFactory.getLogger(WebServerOperator.class); + private static final Logger log = LoggerFactory.getLogger(WebServerOperator.class); - public static void main(String[] args) throws IOException { - log.info("WebServer Operator starting!"); + public static void main(String[] args) throws IOException { + log.info("WebServer Operator starting!"); - Config config = new ConfigBuilder().withNamespace(null).build(); - KubernetesClient client = new DefaultKubernetesClient(config); - Operator operator = new Operator(client); - operator.registerControllerForAllNamespaces(new WebServerController(client)); + Config config = new ConfigBuilder().withNamespace(null).build(); + KubernetesClient client = new DefaultKubernetesClient(config); + Operator operator = new Operator(client); + operator.registerControllerForAllNamespaces(new WebServerController(client)); - new FtBasic( - new TkFork(new FkRegex("/health", "ALL GOOD!")), 8080 - ).start(Exit.NEVER); - } + new FtBasic(new TkFork(new FkRegex("/health", "ALL GOOD!")), 8080).start(Exit.NEVER); + } } diff --git a/samples/webserver/src/main/java/io/javaoperatorsdk/operator/sample/WebServerSpec.java b/samples/webserver/src/main/java/io/javaoperatorsdk/operator/sample/WebServerSpec.java index 284004cdc1..57ca97cf47 100644 --- a/samples/webserver/src/main/java/io/javaoperatorsdk/operator/sample/WebServerSpec.java +++ b/samples/webserver/src/main/java/io/javaoperatorsdk/operator/sample/WebServerSpec.java @@ -2,13 +2,13 @@ public class WebServerSpec { - private String html; + private String html; - public String getHtml() { - return html; - } + public String getHtml() { + return html; + } - public void setHtml(String html) { - this.html = html; - } + public void setHtml(String html) { + this.html = html; + } } diff --git a/samples/webserver/src/main/java/io/javaoperatorsdk/operator/sample/WebServerStatus.java b/samples/webserver/src/main/java/io/javaoperatorsdk/operator/sample/WebServerStatus.java index d1d0d9367a..21e0140e00 100644 --- a/samples/webserver/src/main/java/io/javaoperatorsdk/operator/sample/WebServerStatus.java +++ b/samples/webserver/src/main/java/io/javaoperatorsdk/operator/sample/WebServerStatus.java @@ -2,33 +2,33 @@ public class WebServerStatus { - private String htmlConfigMap; + private String htmlConfigMap; - private String areWeGood; + private String areWeGood; - private String url; + private String url; - public String getHtmlConfigMap() { - return htmlConfigMap; - } + public String getHtmlConfigMap() { + return htmlConfigMap; + } - public void setHtmlConfigMap(String htmlConfigMap) { - this.htmlConfigMap = htmlConfigMap; - } + public void setHtmlConfigMap(String htmlConfigMap) { + this.htmlConfigMap = htmlConfigMap; + } - public String getAreWeGood() { - return areWeGood; - } + public String getAreWeGood() { + return areWeGood; + } - public void setAreWeGood(String areWeGood) { - this.areWeGood = areWeGood; - } + public void setAreWeGood(String areWeGood) { + this.areWeGood = areWeGood; + } - public String getUrl() { - return url; - } + public String getUrl() { + return url; + } - public void setUrl(String url) { - this.url = url; - } + public void setUrl(String url) { + this.url = url; + } } diff --git a/spring-boot-starter/src/main/java/io/javaoperatorsdk/operator/springboot/starter/OperatorAutoConfiguration.java b/spring-boot-starter/src/main/java/io/javaoperatorsdk/operator/springboot/starter/OperatorAutoConfiguration.java index dadcb3e251..9bb52c456e 100644 --- a/spring-boot-starter/src/main/java/io/javaoperatorsdk/operator/springboot/starter/OperatorAutoConfiguration.java +++ b/spring-boot-starter/src/main/java/io/javaoperatorsdk/operator/springboot/starter/OperatorAutoConfiguration.java @@ -1,7 +1,5 @@ package io.javaoperatorsdk.operator.springboot.starter; -import java.util.List; - import io.fabric8.kubernetes.client.ConfigBuilder; import io.fabric8.kubernetes.client.DefaultKubernetesClient; import io.fabric8.kubernetes.client.KubernetesClient; @@ -10,6 +8,7 @@ import io.javaoperatorsdk.operator.api.ResourceController; import io.javaoperatorsdk.operator.processing.retry.GenericRetry; import io.javaoperatorsdk.operator.processing.retry.Retry; +import java.util.List; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -21,49 +20,52 @@ @Configuration @EnableConfigurationProperties({OperatorProperties.class, RetryProperties.class}) public class OperatorAutoConfiguration { - private static final Logger log = LoggerFactory.getLogger(OperatorAutoConfiguration.class); + private static final Logger log = LoggerFactory.getLogger(OperatorAutoConfiguration.class); - @Bean - @ConditionalOnMissingBean - public KubernetesClient kubernetesClient(OperatorProperties operatorProperties) { - ConfigBuilder config = new ConfigBuilder(); - config.withTrustCerts(operatorProperties.isTrustSelfSignedCertificates()); - if (StringUtils.isNotBlank(operatorProperties.getUsername())) { - config.withUsername(operatorProperties.getUsername()); - } - if (StringUtils.isNotBlank(operatorProperties.getPassword())) { - config.withUsername(operatorProperties.getPassword()); - } - if (StringUtils.isNotBlank(operatorProperties.getMasterUrl())) { - config.withMasterUrl(operatorProperties.getMasterUrl()); - } - return operatorProperties.isOpenshift() ? new DefaultOpenShiftClient(config.build()) : new DefaultKubernetesClient(config.build()); + @Bean + @ConditionalOnMissingBean + public KubernetesClient kubernetesClient(OperatorProperties operatorProperties) { + ConfigBuilder config = new ConfigBuilder(); + config.withTrustCerts(operatorProperties.isTrustSelfSignedCertificates()); + if (StringUtils.isNotBlank(operatorProperties.getUsername())) { + config.withUsername(operatorProperties.getUsername()); } - - @Bean - @ConditionalOnMissingBean(Operator.class) - public Operator operator(KubernetesClient kubernetesClient, List resourceControllers) { - Operator operator = new Operator(kubernetesClient); - resourceControllers.forEach(r -> operator.registerControllerForAllNamespaces(r)); - return operator; + if (StringUtils.isNotBlank(operatorProperties.getPassword())) { + config.withUsername(operatorProperties.getPassword()); + } + if (StringUtils.isNotBlank(operatorProperties.getMasterUrl())) { + config.withMasterUrl(operatorProperties.getMasterUrl()); } + return operatorProperties.isOpenshift() + ? new DefaultOpenShiftClient(config.build()) + : new DefaultKubernetesClient(config.build()); + } - @Bean - @ConditionalOnMissingBean - public Retry retry(RetryProperties retryProperties) { - GenericRetry retry = new GenericRetry(); - if (retryProperties.getInitialInterval() != null) { - retry.setInitialInterval(retryProperties.getInitialInterval()); - } - if (retryProperties.getIntervalMultiplier() != null) { - retry.setIntervalMultiplier(retryProperties.getIntervalMultiplier()); - } - if (retryProperties.getMaxAttempts() != null) { - retry.setMaxAttempts(retryProperties.getMaxAttempts()); - } - if (retryProperties.getMaxInterval() != null) { - retry.setInitialInterval(retryProperties.getMaxInterval()); - } - return retry; + @Bean + @ConditionalOnMissingBean(Operator.class) + public Operator operator( + KubernetesClient kubernetesClient, List resourceControllers) { + Operator operator = new Operator(kubernetesClient); + resourceControllers.forEach(r -> operator.registerControllerForAllNamespaces(r)); + return operator; + } + + @Bean + @ConditionalOnMissingBean + public Retry retry(RetryProperties retryProperties) { + GenericRetry retry = new GenericRetry(); + if (retryProperties.getInitialInterval() != null) { + retry.setInitialInterval(retryProperties.getInitialInterval()); + } + if (retryProperties.getIntervalMultiplier() != null) { + retry.setIntervalMultiplier(retryProperties.getIntervalMultiplier()); + } + if (retryProperties.getMaxAttempts() != null) { + retry.setMaxAttempts(retryProperties.getMaxAttempts()); + } + if (retryProperties.getMaxInterval() != null) { + retry.setInitialInterval(retryProperties.getMaxInterval()); } + return retry; + } } diff --git a/spring-boot-starter/src/main/java/io/javaoperatorsdk/operator/springboot/starter/OperatorProperties.java b/spring-boot-starter/src/main/java/io/javaoperatorsdk/operator/springboot/starter/OperatorProperties.java index bb70414888..5cc4d7d437 100644 --- a/spring-boot-starter/src/main/java/io/javaoperatorsdk/operator/springboot/starter/OperatorProperties.java +++ b/spring-boot-starter/src/main/java/io/javaoperatorsdk/operator/springboot/starter/OperatorProperties.java @@ -5,54 +5,54 @@ @ConfigurationProperties(prefix = "operator.kubernetes.client") public class OperatorProperties { - private boolean openshift = false; - private String username; - private String password; - private String masterUrl; - private boolean trustSelfSignedCertificates = false; - - public boolean isOpenshift() { - return openshift; - } - - public OperatorProperties setOpenshift(boolean openshift) { - this.openshift = openshift; - return this; - } - - public String getUsername() { - return username; - } - - public OperatorProperties setUsername(String username) { - this.username = username; - return this; - } - - public String getPassword() { - return password; - } - - public OperatorProperties setPassword(String password) { - this.password = password; - return this; - } - - public String getMasterUrl() { - return masterUrl; - } - - public OperatorProperties setMasterUrl(String masterUrl) { - this.masterUrl = masterUrl; - return this; - } - - public boolean isTrustSelfSignedCertificates() { - return trustSelfSignedCertificates; - } - - public OperatorProperties setTrustSelfSignedCertificates(boolean trustSelfSignedCertificates) { - this.trustSelfSignedCertificates = trustSelfSignedCertificates; - return this; - } + private boolean openshift = false; + private String username; + private String password; + private String masterUrl; + private boolean trustSelfSignedCertificates = false; + + public boolean isOpenshift() { + return openshift; + } + + public OperatorProperties setOpenshift(boolean openshift) { + this.openshift = openshift; + return this; + } + + public String getUsername() { + return username; + } + + public OperatorProperties setUsername(String username) { + this.username = username; + return this; + } + + public String getPassword() { + return password; + } + + public OperatorProperties setPassword(String password) { + this.password = password; + return this; + } + + public String getMasterUrl() { + return masterUrl; + } + + public OperatorProperties setMasterUrl(String masterUrl) { + this.masterUrl = masterUrl; + return this; + } + + public boolean isTrustSelfSignedCertificates() { + return trustSelfSignedCertificates; + } + + public OperatorProperties setTrustSelfSignedCertificates(boolean trustSelfSignedCertificates) { + this.trustSelfSignedCertificates = trustSelfSignedCertificates; + return this; + } } diff --git a/spring-boot-starter/src/main/java/io/javaoperatorsdk/operator/springboot/starter/RetryProperties.java b/spring-boot-starter/src/main/java/io/javaoperatorsdk/operator/springboot/starter/RetryProperties.java index fe89df99a7..e68b31f38c 100644 --- a/spring-boot-starter/src/main/java/io/javaoperatorsdk/operator/springboot/starter/RetryProperties.java +++ b/spring-boot-starter/src/main/java/io/javaoperatorsdk/operator/springboot/starter/RetryProperties.java @@ -5,54 +5,54 @@ @ConfigurationProperties(prefix = "operator.controller.retry") public class RetryProperties { - private Integer maxAttempts; - private Long initialInterval; - private Double intervalMultiplier; - private Long maxInterval; - private Long maxElapsedTime; - - public Integer getMaxAttempts() { - return maxAttempts; - } - - public RetryProperties setMaxAttempts(Integer maxAttempts) { - this.maxAttempts = maxAttempts; - return this; - } - - public Long getInitialInterval() { - return initialInterval; - } - - public RetryProperties setInitialInterval(Long initialInterval) { - this.initialInterval = initialInterval; - return this; - } - - public Double getIntervalMultiplier() { - return intervalMultiplier; - } - - public RetryProperties setIntervalMultiplier(Double intervalMultiplier) { - this.intervalMultiplier = intervalMultiplier; - return this; - } - - public Long getMaxInterval() { - return maxInterval; - } - - public RetryProperties setMaxInterval(Long maxInterval) { - this.maxInterval = maxInterval; - return this; - } - - public Long getMaxElapsedTime() { - return maxElapsedTime; - } - - public RetryProperties setMaxElapsedTime(Long maxElapsedTime) { - this.maxElapsedTime = maxElapsedTime; - return this; - } + private Integer maxAttempts; + private Long initialInterval; + private Double intervalMultiplier; + private Long maxInterval; + private Long maxElapsedTime; + + public Integer getMaxAttempts() { + return maxAttempts; + } + + public RetryProperties setMaxAttempts(Integer maxAttempts) { + this.maxAttempts = maxAttempts; + return this; + } + + public Long getInitialInterval() { + return initialInterval; + } + + public RetryProperties setInitialInterval(Long initialInterval) { + this.initialInterval = initialInterval; + return this; + } + + public Double getIntervalMultiplier() { + return intervalMultiplier; + } + + public RetryProperties setIntervalMultiplier(Double intervalMultiplier) { + this.intervalMultiplier = intervalMultiplier; + return this; + } + + public Long getMaxInterval() { + return maxInterval; + } + + public RetryProperties setMaxInterval(Long maxInterval) { + this.maxInterval = maxInterval; + return this; + } + + public Long getMaxElapsedTime() { + return maxElapsedTime; + } + + public RetryProperties setMaxElapsedTime(Long maxElapsedTime) { + this.maxElapsedTime = maxElapsedTime; + return this; + } } diff --git a/spring-boot-starter/src/test/java/io/javaoperatorsdk/operator/springboot/starter/AutoConfigurationTest.java b/spring-boot-starter/src/test/java/io/javaoperatorsdk/operator/springboot/starter/AutoConfigurationTest.java index d8b4a5eeb6..1e1f044a44 100644 --- a/spring-boot-starter/src/test/java/io/javaoperatorsdk/operator/springboot/starter/AutoConfigurationTest.java +++ b/spring-boot-starter/src/test/java/io/javaoperatorsdk/operator/springboot/starter/AutoConfigurationTest.java @@ -1,10 +1,13 @@ package io.javaoperatorsdk.operator.springboot.starter; -import java.util.List; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import io.fabric8.kubernetes.client.KubernetesClient; import io.javaoperatorsdk.operator.Operator; import io.javaoperatorsdk.operator.api.ResourceController; +import java.util.List; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; @@ -12,55 +15,44 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.junit.jupiter.SpringExtension; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - @ExtendWith(SpringExtension.class) @SpringBootTest public class AutoConfigurationTest { + @Autowired private RetryProperties retryProperties; - @Autowired - private RetryProperties retryProperties; - - @Autowired - private OperatorProperties operatorProperties; - - @MockBean - private Operator operator; + @Autowired private OperatorProperties operatorProperties; - @Autowired - private KubernetesClient kubernetesClient; + @MockBean private Operator operator; - @Autowired - private List resourceControllers; + @Autowired private KubernetesClient kubernetesClient; - @Test - public void loadsKubernetesClientPropertiesProperly() { - assertEquals("user", operatorProperties.getUsername()); - assertEquals("password", operatorProperties.getPassword()); - assertEquals("http://master.url", operatorProperties.getMasterUrl()); - } + @Autowired private List resourceControllers; - @Test - public void loadsRetryPropertiesProperly() { - assertEquals(3, retryProperties.getMaxAttempts().intValue()); - assertEquals(1000, retryProperties.getInitialInterval().intValue()); - assertEquals(1.5, retryProperties.getIntervalMultiplier().doubleValue()); - assertEquals(50000, retryProperties.getMaxInterval().intValue()); - assertEquals(100000, retryProperties.getMaxElapsedTime().intValue()); - } + @Test + public void loadsKubernetesClientPropertiesProperly() { + assertEquals("user", operatorProperties.getUsername()); + assertEquals("password", operatorProperties.getPassword()); + assertEquals("http://master.url", operatorProperties.getMasterUrl()); + } - @Test - public void beansCreated() { - assertNotNull(kubernetesClient); - } + @Test + public void loadsRetryPropertiesProperly() { + assertEquals(3, retryProperties.getMaxAttempts().intValue()); + assertEquals(1000, retryProperties.getInitialInterval().intValue()); + assertEquals(1.5, retryProperties.getIntervalMultiplier().doubleValue()); + assertEquals(50000, retryProperties.getMaxInterval().intValue()); + assertEquals(100000, retryProperties.getMaxElapsedTime().intValue()); + } - @Test - public void resourceControllersAreDiscovered() { - assertEquals(1, resourceControllers.size()); - assertTrue(resourceControllers.get(0) instanceof TestController); - } + @Test + public void beansCreated() { + assertNotNull(kubernetesClient); + } + @Test + public void resourceControllersAreDiscovered() { + assertEquals(1, resourceControllers.size()); + assertTrue(resourceControllers.get(0) instanceof TestController); + } } diff --git a/spring-boot-starter/src/test/java/io/javaoperatorsdk/operator/springboot/starter/TestApplication.java b/spring-boot-starter/src/test/java/io/javaoperatorsdk/operator/springboot/starter/TestApplication.java index 8b10204d3e..0ac9754396 100644 --- a/spring-boot-starter/src/test/java/io/javaoperatorsdk/operator/springboot/starter/TestApplication.java +++ b/spring-boot-starter/src/test/java/io/javaoperatorsdk/operator/springboot/starter/TestApplication.java @@ -6,8 +6,7 @@ @SpringBootApplication public class TestApplication { - public static void main(String[] args) { - SpringApplication.run(TestApplication.class, args); - } - + public static void main(String[] args) { + SpringApplication.run(TestApplication.class, args); + } } diff --git a/spring-boot-starter/src/test/java/io/javaoperatorsdk/operator/springboot/starter/TestController.java b/spring-boot-starter/src/test/java/io/javaoperatorsdk/operator/springboot/starter/TestController.java index dc53b2bff2..ea8442d8d9 100644 --- a/spring-boot-starter/src/test/java/io/javaoperatorsdk/operator/springboot/starter/TestController.java +++ b/spring-boot-starter/src/test/java/io/javaoperatorsdk/operator/springboot/starter/TestController.java @@ -12,14 +12,13 @@ @Controller(crdName = "name") public class TestController implements ResourceController { - @Override - public DeleteControl deleteResource(CustomResource resource, Context context) { - return DeleteControl.DEFAULT_DELETE; - } - - @Override - public UpdateControl createOrUpdateResource(CustomResource resource, Context context) { - return UpdateControl.noUpdate(); - } + @Override + public DeleteControl deleteResource(CustomResource resource, Context context) { + return DeleteControl.DEFAULT_DELETE; + } + @Override + public UpdateControl createOrUpdateResource(CustomResource resource, Context context) { + return UpdateControl.noUpdate(); + } } diff --git a/spring-boot-starter/src/test/java/io/javaoperatorsdk/operator/springboot/starter/model/TestResource.java b/spring-boot-starter/src/test/java/io/javaoperatorsdk/operator/springboot/starter/model/TestResource.java index 06e41ad613..91d8b0c2aa 100644 --- a/spring-boot-starter/src/test/java/io/javaoperatorsdk/operator/springboot/starter/model/TestResource.java +++ b/spring-boot-starter/src/test/java/io/javaoperatorsdk/operator/springboot/starter/model/TestResource.java @@ -2,6 +2,4 @@ import io.fabric8.kubernetes.client.CustomResource; -public class TestResource extends CustomResource { - -} +public class TestResource extends CustomResource {} diff --git a/spring-boot-starter/src/test/java/io/javaoperatorsdk/operator/springboot/starter/model/TestResourceDoneable.java b/spring-boot-starter/src/test/java/io/javaoperatorsdk/operator/springboot/starter/model/TestResourceDoneable.java index d3b249a134..fcb5eeaa8d 100644 --- a/spring-boot-starter/src/test/java/io/javaoperatorsdk/operator/springboot/starter/model/TestResourceDoneable.java +++ b/spring-boot-starter/src/test/java/io/javaoperatorsdk/operator/springboot/starter/model/TestResourceDoneable.java @@ -4,7 +4,8 @@ import io.fabric8.kubernetes.client.CustomResourceDoneable; public class TestResourceDoneable extends CustomResourceDoneable { - public TestResourceDoneable(TestResource resource, Function function) { - super(resource, function); - } + public TestResourceDoneable( + TestResource resource, Function function) { + super(resource, function); + } } diff --git a/spring-boot-starter/src/test/java/io/javaoperatorsdk/operator/springboot/starter/model/TestResourceList.java b/spring-boot-starter/src/test/java/io/javaoperatorsdk/operator/springboot/starter/model/TestResourceList.java index 291639b2b8..a4e8a9c3ad 100644 --- a/spring-boot-starter/src/test/java/io/javaoperatorsdk/operator/springboot/starter/model/TestResourceList.java +++ b/spring-boot-starter/src/test/java/io/javaoperatorsdk/operator/springboot/starter/model/TestResourceList.java @@ -2,5 +2,4 @@ import io.fabric8.kubernetes.client.CustomResourceList; -public class TestResourceList extends CustomResourceList { -} +public class TestResourceList extends CustomResourceList {}