diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java index b5e3fffcf0..7d51c74c5f 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java @@ -1,10 +1,7 @@ package io.javaoperatorsdk.operator.api.config; import java.lang.annotation.Annotation; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; import java.time.Duration; -import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; @@ -17,21 +14,17 @@ import io.javaoperatorsdk.operator.OperatorException; import io.javaoperatorsdk.operator.ReconcilerUtils; import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceSpec; -import io.javaoperatorsdk.operator.api.reconciler.Constants; import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; import io.javaoperatorsdk.operator.api.reconciler.Reconciler; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; -import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; -import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource; -import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResourceConfig; +import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.AnnotationDependentResourceConfigurator; import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition; import io.javaoperatorsdk.operator.processing.event.rate.RateLimiter; import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEventFilter; import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEventFilters; import io.javaoperatorsdk.operator.processing.event.source.filter.GenericFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.OnAddFilter; -import io.javaoperatorsdk.operator.processing.event.source.filter.OnDeleteFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.OnUpdateFilter; import io.javaoperatorsdk.operator.processing.retry.Retry; @@ -41,10 +34,6 @@ public class AnnotationControllerConfiguration

implements io.javaoperatorsdk.operator.api.config.ControllerConfiguration

{ - private static final String CONTROLLER_CONFIG_ANNOTATION = - ControllerConfiguration.class.getSimpleName(); - private static final String KUBE_DEPENDENT_NAME = KubernetesDependent.class.getSimpleName(); - protected final Reconciler

reconciler; private final ControllerConfiguration annotation; private List specs; @@ -152,71 +141,70 @@ public Optional maxReconciliationInterval() { @Override public RateLimiter getRateLimiter() { final Class rateLimiterClass = annotation.rateLimiter(); - return instantiateAndConfigureIfNeeded(rateLimiterClass, RateLimiter.class, - CONTROLLER_CONFIG_ANNOTATION); + return Utils.instantiateAndConfigureIfNeeded(rateLimiterClass, RateLimiter.class, + Utils.contextFor(this, null, null), this::configureFromAnnotatedReconciler); } @Override public Retry getRetry() { final Class retryClass = annotation.retry(); - return instantiateAndConfigureIfNeeded(retryClass, Retry.class, CONTROLLER_CONFIG_ANNOTATION); + return Utils.instantiateAndConfigureIfNeeded(retryClass, Retry.class, + Utils.contextFor(this, null, null), this::configureFromAnnotatedReconciler); } + @SuppressWarnings("unchecked") - protected T instantiateAndConfigureIfNeeded(Class targetClass, - Class expectedType, String context) { - try { - final Constructor constructor = targetClass.getDeclaredConstructor(); - constructor.setAccessible(true); - final var instance = constructor.newInstance(); - if (instance instanceof AnnotationConfigurable) { - AnnotationConfigurable configurable = (AnnotationConfigurable) instance; - final Class configurationClass = - (Class) Utils.getFirstTypeArgumentFromSuperClassOrInterface( - targetClass, AnnotationConfigurable.class); - final var configAnnotation = reconciler.getClass().getAnnotation(configurationClass); - if (configAnnotation != null) { - configurable.initFrom(configAnnotation); - } + private void configureFromAnnotatedReconciler(Object instance) { + if (instance instanceof AnnotationConfigurable) { + AnnotationConfigurable configurable = (AnnotationConfigurable) instance; + final Class configurationClass = + (Class) Utils.getFirstTypeArgumentFromSuperClassOrInterface( + instance.getClass(), AnnotationConfigurable.class); + final var configAnnotation = reconciler.getClass().getAnnotation(configurationClass); + if (configAnnotation != null) { + configurable.initFrom(configAnnotation); } - return instance; - } catch (InstantiationException | IllegalAccessException | InvocationTargetException - | NoSuchMethodException e) { - throw new OperatorException("Couldn't instantiate " + expectedType.getSimpleName() + " '" - + targetClass.getName() + "' for '" + getName() - + "' reconciler in " + context - + ". You need to provide an accessible no-arg constructor.", e); } } - @Override @SuppressWarnings("unchecked") - public Optional> onAddFilter() { - return (Optional>) createFilter(annotation.onAddFilter(), OnAddFilter.class, - CONTROLLER_CONFIG_ANNOTATION); + private void configureFromCustomAnnotation(Object instance) { + if (instance instanceof AnnotationDependentResourceConfigurator) { + AnnotationDependentResourceConfigurator configurator = + (AnnotationDependentResourceConfigurator) instance; + final Class configurationClass = + (Class) Utils.getFirstTypeArgumentFromInterface( + instance.getClass(), AnnotationDependentResourceConfigurator.class); + final var configAnnotation = instance.getClass().getAnnotation(configurationClass); + // always called even if the annotation is null so that implementations can provide default + // values + final var config = configurator.configFrom(configAnnotation, this); + configurator.configureWith(config); + } } - protected Optional createFilter(Class filter, Class defaultValue, - String origin) { - if (defaultValue.equals(filter)) { - return Optional.empty(); - } else { - return Optional.of(instantiateAndConfigureIfNeeded(filter, defaultValue, origin)); - } + @Override + @SuppressWarnings("unchecked") + public Optional> onAddFilter() { + return Optional.ofNullable( + Utils.instantiate(annotation.onAddFilter(), OnAddFilter.class, + Utils.contextFor(this, null, null))); } @SuppressWarnings("unchecked") @Override public Optional> onUpdateFilter() { - return (Optional>) createFilter(annotation.onUpdateFilter(), - OnUpdateFilter.class, CONTROLLER_CONFIG_ANNOTATION); + return Optional.ofNullable( + Utils.instantiate(annotation.onUpdateFilter(), OnUpdateFilter.class, + Utils.contextFor(this, null, null))); } @SuppressWarnings("unchecked") @Override public Optional> genericFilter() { - return (Optional>) createFilter(annotation.genericFilter(), - GenericFilter.class, CONTROLLER_CONFIG_ANNOTATION); + return Optional.ofNullable( + Utils.instantiate(annotation.genericFilter(), GenericFilter.class, + Utils.contextFor(this, null, null))); } @SuppressWarnings({"rawtypes", "unchecked"}) @@ -232,11 +220,7 @@ public List getDependentResources() { final var specsMap = new LinkedHashMap(dependents.length); for (Dependent dependent : dependents) { - Object config = null; final Class dependentType = dependent.type(); - if (KubernetesDependentResource.class.isAssignableFrom(dependentType)) { - config = createKubernetesResourceConfig(dependentType); - } final var name = getName(dependent, dependentType); var spec = specsMap.get(name); @@ -244,12 +228,18 @@ public List getDependentResources() { throw new IllegalArgumentException( "A DependentResource named '" + name + "' already exists: " + spec); } - final var context = "DependentResource of type '" + dependentType.getName() + "'"; - spec = new DependentResourceSpec(dependentType, config, name, + + final var dependentResource = Utils.instantiateAndConfigureIfNeeded(dependentType, + DependentResource.class, + Utils.contextFor(this, dependentType, Dependent.class), + this::configureFromCustomAnnotation); + + final var context = Utils.contextFor(this, dependentType, null); + spec = new DependentResourceSpec(dependentResource, name, Set.of(dependent.dependsOn()), - instantiateConditionIfNotDefault(dependent.readyPostcondition(), context), - instantiateConditionIfNotDefault(dependent.reconcilePrecondition(), context), - instantiateConditionIfNotDefault(dependent.deletePostcondition(), context)); + Utils.instantiate(dependent.readyPostcondition(), Condition.class, context), + Utils.instantiate(dependent.reconcilePrecondition(), Condition.class, context), + Utils.instantiate(dependent.deletePostcondition(), Condition.class, context)); specsMap.put(name, spec); } @@ -258,14 +248,6 @@ public List getDependentResources() { return specs; } - protected Condition instantiateConditionIfNotDefault(Class condition, - String context) { - if (condition != Condition.class) { - return instantiateAndConfigureIfNeeded(condition, Condition.class, context); - } - return null; - } - private String getName(Dependent dependent, Class dependentType) { var name = dependent.name(); if (name.isBlank()) { @@ -274,51 +256,6 @@ private String getName(Dependent dependent, Class d return name; } - @SuppressWarnings({"rawtypes", "unchecked"}) - private Object createKubernetesResourceConfig(Class dependentType) { - - Object config; - final var kubeDependent = dependentType.getAnnotation(KubernetesDependent.class); - - var namespaces = getNamespaces(); - var configuredNS = false; - String labelSelector = null; - OnAddFilter onAddFilter = null; - OnUpdateFilter onUpdateFilter = null; - OnDeleteFilter onDeleteFilter = null; - GenericFilter genericFilter = null; - if (kubeDependent != null) { - if (!Arrays.equals(KubernetesDependent.DEFAULT_NAMESPACES, - kubeDependent.namespaces())) { - namespaces = Set.of(kubeDependent.namespaces()); - configuredNS = true; - } - - final var fromAnnotation = kubeDependent.labelSelector(); - labelSelector = Constants.NO_VALUE_SET.equals(fromAnnotation) ? null : fromAnnotation; - - - final var context = - KUBE_DEPENDENT_NAME + " annotation on " + dependentType.getName() + " DependentResource"; - onAddFilter = createFilter(kubeDependent.onAddFilter(), OnAddFilter.class, context) - .orElse(null); - onUpdateFilter = - createFilter(kubeDependent.onUpdateFilter(), OnUpdateFilter.class, context) - .orElse(null); - onDeleteFilter = - createFilter(kubeDependent.onDeleteFilter(), OnDeleteFilter.class, context) - .orElse(null); - genericFilter = - createFilter(kubeDependent.genericFilter(), GenericFilter.class, context) - .orElse(null); - } - - config = - new KubernetesDependentResourceConfig(namespaces, labelSelector, configuredNS, onAddFilter, - onUpdateFilter, onDeleteFilter, genericFilter); - - return config; - } public static T valueOrDefault( ControllerConfiguration controllerConfiguration, diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java index a7f50bcf6c..ae175333a3 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java @@ -140,8 +140,9 @@ default ObjectMapper getObjectMapper() { return Serialization.jsonMapper(); } + @Deprecated(forRemoval = true) default DependentResourceFactory dependentResourceFactory() { - return new DependentResourceFactory() {}; + return null; } default Optional getLeaderElectionConfiguration() { diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverrider.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverrider.java index e9cae2dccf..524888f3d5 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverrider.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverrider.java @@ -11,6 +11,8 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceSpec; +import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; +import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.DependentResourceConfigurator; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResourceConfig; import io.javaoperatorsdk.operator.processing.event.rate.RateLimiter; import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEventFilter; @@ -159,6 +161,7 @@ public ControllerConfigurationOverrider withGenericFilter(GenericFilter ge return this; } + @SuppressWarnings("unchecked") public ControllerConfigurationOverrider replacingNamedDependentResourceConfig(String name, Object dependentResourceConfig) { @@ -166,35 +169,50 @@ public ControllerConfigurationOverrider replacingNamedDependentResourceConfig if (current == null) { throw new IllegalArgumentException("Cannot find a DependentResource named: " + name); } - replaceConfig(name, dependentResourceConfig, current); - return this; - } - private void replaceConfig(String name, Object newConfig, DependentResourceSpec current) { - namedDependentResourceSpecs.put(name, - new DependentResourceSpec<>(current.getDependentResourceClass(), newConfig, name, - current.getDependsOn(), current.getReadyCondition(), current.getReconcileCondition(), - current.getDeletePostCondition())); + var dependentResource = current.getDependentResource(); + if (dependentResource instanceof DependentResourceConfigurator) { + var configurator = (DependentResourceConfigurator) dependentResource; + configurator.configureWith(dependentResourceConfig); + } + + return this; } @SuppressWarnings("unchecked") public ControllerConfiguration build() { + // todo: this should be abstracted by introducing an interface to deal with listening to + // namespace changes as possibly other things than the informers might be interested in reacting + // to such changes // propagate namespaces if needed - final List newDependentSpecs; + final var hasModifiedNamespaces = !original.getNamespaces().equals(namespaces); - newDependentSpecs = namedDependentResourceSpecs.entrySet().stream() - .map(drsEntry -> { - final var spec = drsEntry.getValue(); - - // if the spec has a config and it's a KubernetesDependentResourceConfig, update the - // namespaces if needed, otherwise, just return the existing spec - final Optional maybeConfig = spec.getDependentResourceConfiguration(); - return maybeConfig.filter(KubernetesDependentResourceConfig.class::isInstance) - .map(KubernetesDependentResourceConfig.class::cast) - .filter(Predicate.not(KubernetesDependentResourceConfig::wereNamespacesConfigured)) - .map(c -> updateSpec(drsEntry.getKey(), spec, c)) - .orElse(drsEntry.getValue()); - }).collect(Collectors.toUnmodifiableList()); + final var newDependentSpecs = namedDependentResourceSpecs.values().stream() + .map(spec -> { + // if the dependent resource has a config and it's a KubernetesDependentResourceConfig, + // update + // the namespaces if needed, otherwise, do nothing + DependentResource dependent = spec.getDependentResource(); + Optional updated = Optional.empty(); + if (hasModifiedNamespaces && dependent instanceof DependentResourceConfigurator) { + DependentResourceConfigurator configurator = (DependentResourceConfigurator) dependent; + final Optional config = configurator.configuration(); + updated = config.filter(KubernetesDependentResourceConfig.class::isInstance) + .map(KubernetesDependentResourceConfig.class::cast) + .filter(Predicate.not(KubernetesDependentResourceConfig::wereNamespacesConfigured)) + .map(c -> { + // update the namespaces of the config, configure the dependent with it and update + // the spec + c.setNamespaces(namespaces); + configurator.configureWith(c); + return new DependentResourceSpec(dependent, spec.getName(), spec.getDependsOn(), + spec.getReadyCondition(), spec.getReconcileCondition(), + spec.getDeletePostCondition()); + }); + } + + return updated.orElse(spec); + }).collect(Collectors.toList()); return new DefaultControllerConfiguration<>( original.getAssociatedReconcilerClassName(), @@ -215,14 +233,6 @@ public ControllerConfiguration build() { newDependentSpecs); } - @SuppressWarnings({"rawtypes", "unchecked"}) - private DependentResourceSpec updateSpec(String name, DependentResourceSpec spec, - KubernetesDependentResourceConfig c) { - return new DependentResourceSpec(spec.getDependentResourceClass(), - c.setNamespaces(namespaces), name, spec.getDependsOn(), spec.getReadyCondition(), - spec.getReconcileCondition(), spec.getDeletePostCondition()); - } - public static ControllerConfigurationOverrider override( ControllerConfiguration original) { return new ControllerConfigurationOverrider<>(original); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Utils.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Utils.java index c9f636101a..efcf9d6e3b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Utils.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Utils.java @@ -1,18 +1,23 @@ package io.javaoperatorsdk.operator.api.config; import java.io.IOException; +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.text.SimpleDateFormat; import java.time.Instant; import java.util.Arrays; import java.util.Date; +import java.util.Optional; import java.util.Properties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.javaoperatorsdk.operator.OperatorException; +import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; public class Utils { @@ -106,17 +111,41 @@ public static Class getFirstTypeArgumentFromExtendedClass(Class clazz) { public static Class getFirstTypeArgumentFromInterface(Class clazz, Class expectedImplementedInterface) { - return Arrays.stream(clazz.getGenericInterfaces()) - .filter(type -> type.getTypeName().startsWith(expectedImplementedInterface.getName()) - && type instanceof ParameterizedType) - .map(ParameterizedType.class::cast) - .findFirst() - .map(t -> (Class) t.getActualTypeArguments()[0]) - .orElseThrow(() -> new RuntimeException( - "Couldn't retrieve generic parameter type from " + clazz.getSimpleName() - + " because it doesn't implement " - + expectedImplementedInterface.getSimpleName() - + " directly")); + if (expectedImplementedInterface.isAssignableFrom(clazz)) { + final var genericInterfaces = clazz.getGenericInterfaces(); + Optional> target = Optional.empty(); + if (genericInterfaces.length > 0) { + // try to find the target interface among them + target = Arrays.stream(genericInterfaces) + .filter(type -> type.getTypeName().startsWith(expectedImplementedInterface.getName()) + && type instanceof ParameterizedType) + .map(ParameterizedType.class::cast) + .findFirst() + .map(t -> { + final Type argument = t.getActualTypeArguments()[0]; + if (argument instanceof Class) { + return (Class) argument; + } + throw new IllegalArgumentException(clazz.getSimpleName() + " implements " + + expectedImplementedInterface.getSimpleName() + + " but indirectly. Java type erasure doesn't allow to retrieve the generic type from it. Retrieved type was: " + + argument); + }); + } + + if (target.isPresent()) { + return target.get(); + } + + // try the parent + var parent = clazz.getSuperclass(); + if (!Object.class.equals(parent)) { + return getFirstTypeArgumentFromInterface(parent, expectedImplementedInterface); + } + } + throw new IllegalArgumentException("Couldn't retrieve generic parameter type from " + + clazz.getSimpleName() + " because it or its superclasses don't implement " + + expectedImplementedInterface.getSimpleName()); } public static Class getFirstTypeArgumentFromSuperClassOrInterface(Class clazz, @@ -144,4 +173,58 @@ public static Class getFirstTypeArgumentFromSuperClassOrInterface(Class cl "Couldn't retrieve generic parameter type from " + clazz.getSimpleName(), e); } } + + public static T instantiateAndConfigureIfNeeded(Class targetClass, + Class expectedType, String context, Configurator configurator) { + // if class to instantiate equals the expected interface, we cannot instantiate it so just + // return null as it means we passed on void-type default value + if (expectedType.equals(targetClass)) { + return null; + } + + try { + final Constructor constructor = targetClass.getDeclaredConstructor(); + constructor.setAccessible(true); + final var instance = constructor.newInstance(); + + if (configurator != null) { + configurator.configure(instance); + } + + return instance; + } catch (InstantiationException | IllegalAccessException | InvocationTargetException + | NoSuchMethodException e) { + throw new OperatorException("Couldn't instantiate " + expectedType.getSimpleName() + " '" + + targetClass.getName() + "': you need to provide an accessible no-arg constructor." + + (context != null ? " Context: " + context : ""), e); + } + } + + public static T instantiate(Class toInstantiate, Class expectedType, + String context) { + return instantiateAndConfigureIfNeeded(toInstantiate, expectedType, context, null); + } + + @FunctionalInterface + public interface Configurator { + void configure(T instance); + } + + @SuppressWarnings("rawtypes") + public static String contextFor(ControllerConfiguration controllerConfiguration, + Class dependentType, + Class configurationAnnotation) { + final var annotationName = + configurationAnnotation != null ? configurationAnnotation.getSimpleName() + : io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration.class + .getSimpleName(); + var context = "annotation: " + annotationName + ", "; + if (dependentType != null) { + context += "DependentResource: " + dependentType.getName() + ", "; + } + context += "reconciler: " + controllerConfiguration.getName(); + + + return context; + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/dependent/DependentResourceSpec.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/dependent/DependentResourceSpec.java index f146d127d0..2038418995 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/dependent/DependentResourceSpec.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/dependent/DependentResourceSpec.java @@ -4,14 +4,14 @@ import java.util.Optional; import java.util.Set; +import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; +import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.DependentResourceConfigurator; import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition; -public class DependentResourceSpec, C> { +public class DependentResourceSpec { - private final Class dependentResourceClass; - - private final C dependentResourceConfig; + private final DependentResource dependentResource; private final String name; @@ -23,11 +23,10 @@ public class DependentResourceSpec, C> { private final Condition deletePostCondition; - public DependentResourceSpec(Class dependentResourceClass, C dependentResourceConfig, + public DependentResourceSpec(DependentResource dependentResource, String name, Set dependsOn, Condition readyCondition, Condition reconcileCondition, Condition deletePostCondition) { - this.dependentResourceClass = dependentResourceClass; - this.dependentResourceConfig = dependentResourceConfig; + this.dependentResource = dependentResource; this.name = name; this.dependsOn = dependsOn; this.readyCondition = readyCondition; @@ -35,12 +34,18 @@ public DependentResourceSpec(Class dependentResourceClass, C dependentResourc this.deletePostCondition = deletePostCondition; } - public Class getDependentResourceClass() { - return dependentResourceClass; + @SuppressWarnings("unchecked") + public Class> getDependentResourceClass() { + return (Class>) dependentResource.getClass(); } + @SuppressWarnings({"unchecked", "rawtypes"}) public Optional getDependentResourceConfiguration() { - return Optional.ofNullable(dependentResourceConfig); + if (dependentResource instanceof DependentResourceConfigurator) { + var configurator = (DependentResourceConfigurator) dependentResource; + return configurator.configuration(); + } + return Optional.empty(); } public String getName() { @@ -50,8 +55,7 @@ public String getName() { @Override public String toString() { return "DependentResourceSpec{ name='" + name + - "', type=" + dependentResourceClass.getCanonicalName() + - ", config=" + dependentResourceConfig + '}'; + "', type=" + getDependentResourceClass().getCanonicalName() + '}'; } @Override @@ -62,7 +66,7 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { return false; } - DependentResourceSpec that = (DependentResourceSpec) o; + DependentResourceSpec that = (DependentResourceSpec) o; return name.equals(that.name); } @@ -89,4 +93,8 @@ public Condition getReconcileCondition() { public Condition getDeletePostCondition() { return deletePostCondition; } + + public DependentResource getDependentResource() { + return dependentResource; + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/DependentResourceFactory.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/DependentResourceFactory.java index 11b6e1f059..139d70b002 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/DependentResourceFactory.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/DependentResourceFactory.java @@ -1,23 +1,18 @@ package io.javaoperatorsdk.operator.api.reconciler.dependent; -import java.lang.reflect.InvocationTargetException; - +import io.javaoperatorsdk.operator.api.config.Utils; import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceSpec; +@Deprecated +@SuppressWarnings({"rawtypes", "unchecked"}) public interface DependentResourceFactory { - default > T createFrom(DependentResourceSpec spec) { + default DependentResource createFrom(DependentResourceSpec spec) { return createFrom(spec.getDependentResourceClass()); } - default > T createFrom(Class dependentResourceClass) { - try { - return dependentResourceClass.getConstructor().newInstance(); - } catch (InstantiationException | NoSuchMethodException | IllegalAccessException - | InvocationTargetException e) { - throw new IllegalArgumentException("Cannot instantiate DependentResource " - + dependentResourceClass.getCanonicalName(), e); - } + default T createFrom(Class dependentResourceClass) { + return (T) Utils.instantiate(dependentResourceClass, DependentResource.class, null); } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/AnnotationDependentResourceConfigurator.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/AnnotationDependentResourceConfigurator.java new file mode 100644 index 0000000000..d65249b753 --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/AnnotationDependentResourceConfigurator.java @@ -0,0 +1,11 @@ +package io.javaoperatorsdk.operator.api.reconciler.dependent.managed; + +import java.lang.annotation.Annotation; + +import io.javaoperatorsdk.operator.api.config.ControllerConfiguration; + +public interface AnnotationDependentResourceConfigurator + extends DependentResourceConfigurator { + + C configFrom(A annotation, ControllerConfiguration parentConfiguration); +} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/DependentResourceConfigurator.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/DependentResourceConfigurator.java index bbb4f75da9..2b361626aa 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/DependentResourceConfigurator.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/DependentResourceConfigurator.java @@ -1,5 +1,9 @@ package io.javaoperatorsdk.operator.api.reconciler.dependent.managed; +import java.util.Optional; + public interface DependentResourceConfigurator { void configureWith(C config); + + Optional configuration(); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResource.java index 930e5fd5b4..971b510e80 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResource.java @@ -1,5 +1,6 @@ package io.javaoperatorsdk.operator.processing.dependent.kubernetes; +import java.util.Arrays; import java.util.HashMap; import java.util.Optional; import java.util.Set; @@ -13,19 +14,25 @@ import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation; import io.fabric8.kubernetes.client.dsl.Resource; import io.javaoperatorsdk.operator.OperatorException; +import io.javaoperatorsdk.operator.api.config.ControllerConfiguration; +import io.javaoperatorsdk.operator.api.config.Utils; import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration; import io.javaoperatorsdk.operator.api.reconciler.Constants; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; import io.javaoperatorsdk.operator.api.reconciler.Ignore; import io.javaoperatorsdk.operator.api.reconciler.dependent.GarbageCollected; -import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.DependentResourceConfigurator; +import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.AnnotationDependentResourceConfigurator; import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.KubernetesClientAware; import io.javaoperatorsdk.operator.processing.dependent.AbstractEventSourceHolderDependentResource; import io.javaoperatorsdk.operator.processing.dependent.Matcher; import io.javaoperatorsdk.operator.processing.dependent.Matcher.Result; import io.javaoperatorsdk.operator.processing.event.ResourceID; import io.javaoperatorsdk.operator.processing.event.source.SecondaryToPrimaryMapper; +import io.javaoperatorsdk.operator.processing.event.source.filter.GenericFilter; +import io.javaoperatorsdk.operator.processing.event.source.filter.OnAddFilter; +import io.javaoperatorsdk.operator.processing.event.source.filter.OnDeleteFilter; +import io.javaoperatorsdk.operator.processing.event.source.filter.OnUpdateFilter; import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; import io.javaoperatorsdk.operator.processing.event.source.informer.Mappers; @@ -34,7 +41,7 @@ public abstract class KubernetesDependentResource extends AbstractEventSourceHolderDependentResource> implements KubernetesClientAware, - DependentResourceConfigurator { + AnnotationDependentResourceConfigurator { private static final Logger log = LoggerFactory.getLogger(KubernetesDependentResource.class); @@ -236,4 +243,45 @@ private void cleanupAfterEventFiltering(ResourceID resourceID) { eventSource().cleanupOnCreateOrUpdateEventFiltering(resourceID); } + @Override + @SuppressWarnings("unchecked") + public KubernetesDependentResourceConfig configFrom(KubernetesDependent kubeDependent, + ControllerConfiguration parentConfiguration) { + var namespaces = parentConfiguration.getNamespaces(); + var configuredNS = false; + String labelSelector = null; + OnAddFilter onAddFilter = null; + OnUpdateFilter onUpdateFilter = null; + OnDeleteFilter onDeleteFilter = null; + GenericFilter genericFilter = null; + if (kubeDependent != null) { + if (!Arrays.equals(KubernetesDependent.DEFAULT_NAMESPACES, + kubeDependent.namespaces())) { + namespaces = Set.of(kubeDependent.namespaces()); + configuredNS = true; + } + + final var fromAnnotation = kubeDependent.labelSelector(); + labelSelector = Constants.NO_VALUE_SET.equals(fromAnnotation) ? null : fromAnnotation; + + final var context = + Utils.contextFor(parentConfiguration, getClass(), kubeDependent.annotationType()); + onAddFilter = Utils.instantiate(kubeDependent.onAddFilter(), OnAddFilter.class, context); + onUpdateFilter = + Utils.instantiate(kubeDependent.onUpdateFilter(), OnUpdateFilter.class, context); + onDeleteFilter = + Utils.instantiate(kubeDependent.onDeleteFilter(), OnDeleteFilter.class, context); + genericFilter = + Utils.instantiate(kubeDependent.genericFilter(), GenericFilter.class, context); + } + + return new KubernetesDependentResourceConfig(namespaces, labelSelector, configuredNS, + onAddFilter, + onUpdateFilter, onDeleteFilter, genericFilter); + } + + @Override + public Optional configuration() { + return Optional.ofNullable(kubernetesDependentResourceConfig); + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowSupport.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowSupport.java index aad9475518..fbeadb31b7 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowSupport.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowSupport.java @@ -12,10 +12,8 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.client.KubernetesClient; import io.javaoperatorsdk.operator.OperatorException; -import io.javaoperatorsdk.operator.api.config.ConfigurationServiceProvider; import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceSpec; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; -import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.DependentResourceConfigurator; import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.KubernetesClientAware; import io.javaoperatorsdk.operator.processing.dependent.workflow.builder.WorkflowBuilder; @@ -72,20 +70,15 @@ public

Workflow

createWorkflow( return workflowBuilder.build(); } - @SuppressWarnings({"rawtypes", "unchecked"}) + @SuppressWarnings({"rawtypes"}) public DependentResource createAndConfigureFrom(DependentResourceSpec spec, KubernetesClient client) { - final var dependentResource = - ConfigurationServiceProvider.instance().dependentResourceFactory().createFrom(spec); + final var dependentResource = spec.getDependentResource(); if (dependentResource instanceof KubernetesClientAware) { ((KubernetesClientAware) dependentResource).setKubernetesClient(client); } - if (dependentResource instanceof DependentResourceConfigurator) { - final var configurator = (DependentResourceConfigurator) dependentResource; - spec.getDependentResourceConfiguration().ifPresent(configurator::configureWith); - } return dependentResource; } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ConfigurationServiceOverriderTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ConfigurationServiceOverriderTest.java index 8b973b8159..e46f027d94 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ConfigurationServiceOverriderTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ConfigurationServiceOverriderTest.java @@ -9,10 +9,7 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.client.Config; import io.fabric8.kubernetes.client.ConfigBuilder; -import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceSpec; import io.javaoperatorsdk.operator.api.monitoring.Metrics; -import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; -import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResourceFactory; import com.fasterxml.jackson.databind.ObjectMapper; @@ -24,12 +21,6 @@ class ConfigurationServiceOverriderTest { private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static final LeaderElectionConfiguration LEADER_ELECTION_CONFIGURATION = new LeaderElectionConfiguration("foo", "fooNS"); - private static final DependentResourceFactory FACTORY = new DependentResourceFactory() { - @Override - public > T createFrom(DependentResourceSpec spec) { - return DependentResourceFactory.super.createFrom(spec); - } - }; private static final Cloner CLONER = new Cloner() { @Override @@ -86,11 +77,6 @@ public Cloner getResourceCloner() { return CLONER; } - @Override - public DependentResourceFactory dependentResourceFactory() { - return FACTORY; - } - @Override public Optional getLeaderElectionConfiguration() { return Optional.of(LEADER_ELECTION_CONFIGURATION); @@ -121,7 +107,6 @@ public R clone(R object) { overridden.concurrentReconciliationThreads()); assertNotEquals(config.getTerminationTimeoutSeconds(), overridden.getTerminationTimeoutSeconds()); - assertNotEquals(config.dependentResourceFactory(), overridden.dependentResourceFactory()); assertNotEquals(config.getClientConfiguration(), overridden.getClientConfiguration()); assertNotEquals(config.getExecutorService(), overridden.getExecutorService()); assertNotEquals(config.getMetrics(), overridden.getMetrics()); diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverriderTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverriderTest.java index 127cd535b1..0f8cf1e2e4 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverriderTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverriderTest.java @@ -14,6 +14,7 @@ import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult; +import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.DependentResourceConfigurator; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResourceConfig; @@ -73,7 +74,10 @@ public NamedDependentResource() { } } - private static class ExternalDependentResource implements DependentResource { + private static class ExternalDependentResource implements DependentResource, + DependentResourceConfigurator { + + private String config = "UNSET"; @Override public ReconcileResult reconcile(ConfigMap primary, Context context) { @@ -89,6 +93,16 @@ public Class resourceType() { public Optional getSecondaryResource(ConfigMap primary) { return Optional.empty(); } + + @Override + public void configureWith(String config) { + this.config = config; + } + + @Override + public Optional configuration() { + return Optional.of(config); + } } } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/UtilsTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/UtilsTest.java index 87e60b8aa6..0f6b284807 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/UtilsTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/UtilsTest.java @@ -9,11 +9,14 @@ import io.javaoperatorsdk.operator.api.reconciler.Reconciler; import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; +import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.AnnotationDependentResourceConfigurator; import io.javaoperatorsdk.operator.processing.dependent.EmptyTestDependentResource; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource; import io.javaoperatorsdk.operator.sample.simple.TestCustomResource; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; @@ -89,6 +92,14 @@ void getsFirstTypeArgumentFromInterface() { assertThat(Utils.getFirstTypeArgumentFromInterface(EmptyTestDependentResource.class, DependentResource.class)) .isEqualTo(Deployment.class); + + assertThatIllegalArgumentException().isThrownBy( + () -> Utils.getFirstTypeArgumentFromInterface(TestKubernetesDependentResource.class, + DependentResource.class)); + + assertThat(Utils.getFirstTypeArgumentFromInterface(TestKubernetesDependentResource.class, + AnnotationDependentResourceConfigurator.class)) + .isEqualTo(KubernetesDependent.class); } @Test diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowTestUtils.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowTestUtils.java index 2332778e2e..c7307e631a 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowTestUtils.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowTestUtils.java @@ -10,8 +10,8 @@ public class ManagedWorkflowTestUtils { @SuppressWarnings("unchecked") public static DependentResourceSpec createDRS(String name, String... dependOns) { - return new DependentResourceSpec(EmptyTestDependentResource.class, - null, name, Set.of(dependOns), null, null, null); + return new DependentResourceSpec(new EmptyTestDependentResource(), name, Set.of(dependOns), + null, null, null); } } diff --git a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaConfig.java b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaConfig.java new file mode 100644 index 0000000000..c43a2a060f --- /dev/null +++ b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaConfig.java @@ -0,0 +1,23 @@ +package io.javaoperatorsdk.operator.sample.dependent; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +public @interface SchemaConfig { + int DEFAULT_POLL_PERIOD = 500; + int DEFAULT_PORT = 3306; + + int pollPeriod() default DEFAULT_POLL_PERIOD; + + String host(); + + String user(); + + String password(); + + int port() default DEFAULT_PORT; +} diff --git a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaDependentResource.java b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaDependentResource.java index 48e3f37abe..751b339ad3 100644 --- a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaDependentResource.java +++ b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaDependentResource.java @@ -9,9 +9,11 @@ import org.slf4j.LoggerFactory; import io.fabric8.kubernetes.api.model.Secret; +import io.javaoperatorsdk.operator.api.config.ControllerConfiguration; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter; import io.javaoperatorsdk.operator.api.reconciler.dependent.EventSourceProvider; +import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.AnnotationDependentResourceConfigurator; import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.DependentResourceConfigurator; import io.javaoperatorsdk.operator.processing.dependent.Creator; import io.javaoperatorsdk.operator.processing.dependent.external.PerResourcePollingDependentResource; @@ -24,13 +26,18 @@ import static io.javaoperatorsdk.operator.sample.dependent.SecretDependentResource.MYSQL_SECRET_USERNAME; import static java.lang.String.format; +@SchemaConfig(pollPeriod = 700, host = "127.0.0.1", + port = SchemaDependentResource.LOCAL_PORT, + user = "root", password = "password") // NOSONAR: password is only used locally, example only public class SchemaDependentResource extends PerResourcePollingDependentResource implements EventSourceProvider, DependentResourceConfigurator, + AnnotationDependentResourceConfigurator, Creator, Deleter { public static final String NAME = "schema"; + public static final int LOCAL_PORT = 3307; private static final Logger log = LoggerFactory.getLogger(SchemaDependentResource.class); private MySQLDbConfig dbConfig; @@ -39,12 +46,29 @@ public SchemaDependentResource() { super(Schema.class); } + @Override + public Optional configuration() { + return Optional.of(new ResourcePollerConfig((int) getPollingPeriod(), dbConfig)); + } + @Override public void configureWith(ResourcePollerConfig config) { this.dbConfig = config.getMySQLDbConfig(); setPollingPeriod(config.getPollPeriod()); } + @Override + public ResourcePollerConfig configFrom(SchemaConfig annotation, + ControllerConfiguration parentConfiguration) { + if (annotation != null) { + return new ResourcePollerConfig(annotation.pollPeriod(), + new MySQLDbConfig(annotation.host(), "" + annotation.port(), + annotation.user(), annotation.password())); + } + return new ResourcePollerConfig(SchemaConfig.DEFAULT_POLL_PERIOD, + MySQLDbConfig.loadFromEnvironmentVars()); + } + @Override public Schema desired(MySQLSchema primary, Context context) { return new Schema(primary.getMetadata().getName(), primary.getSpec().getEncoding()); @@ -97,5 +121,4 @@ public Set fetchResources(MySQLSchema primaryResource) { throw new RuntimeException("Error while trying read Schema", e); } } - } diff --git a/sample-operators/mysql-schema/src/test/java/io/javaoperatorsdk/operator/sample/MySQLSchemaOperatorE2E.java b/sample-operators/mysql-schema/src/test/java/io/javaoperatorsdk/operator/sample/MySQLSchemaOperatorE2E.java index 93a0097b6c..24e257956b 100644 --- a/sample-operators/mysql-schema/src/test/java/io/javaoperatorsdk/operator/sample/MySQLSchemaOperatorE2E.java +++ b/sample-operators/mysql-schema/src/test/java/io/javaoperatorsdk/operator/sample/MySQLSchemaOperatorE2E.java @@ -13,12 +13,11 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.api.model.NamespaceBuilder; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; -import io.fabric8.kubernetes.client.DefaultKubernetesClient; import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.KubernetesClientBuilder; import io.javaoperatorsdk.operator.junit.AbstractOperatorExtension; import io.javaoperatorsdk.operator.junit.ClusterDeployedOperatorExtension; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; -import io.javaoperatorsdk.operator.sample.dependent.ResourcePollerConfig; import io.javaoperatorsdk.operator.sample.dependent.SchemaDependentResource; import static java.util.concurrent.TimeUnit.MINUTES; @@ -33,13 +32,12 @@ class MySQLSchemaOperatorE2E { static final Logger log = LoggerFactory.getLogger(MySQLSchemaOperatorE2E.class); - static final KubernetesClient client = new DefaultKubernetesClient(); + static final KubernetesClient client = new KubernetesClientBuilder().build(); static final String MY_SQL_NS = "mysql"; private final static List infrastructure = new ArrayList<>(); public static final String TEST_RESOURCE_NAME = "mydb1"; - public static final Integer LOCAL_PORT = 3307; static { infrastructure.add( @@ -63,15 +61,10 @@ boolean isLocal() { AbstractOperatorExtension operator = isLocal() ? LocallyRunOperatorExtension.builder() - .withReconciler( - new MySQLSchemaReconciler(), - c -> c.replacingNamedDependentResourceConfig( - SchemaDependentResource.NAME, - new ResourcePollerConfig( - 700, new MySQLDbConfig("127.0.0.1", LOCAL_PORT.toString(), "root", - "password")))) + .withReconciler(new MySQLSchemaReconciler()) // configuration for schema comes from + // SchemaDependentResource annotation .withInfrastructure(infrastructure) - .withPortForward(MY_SQL_NS, "app", "mysql", 3306, LOCAL_PORT) + .withPortForward(MY_SQL_NS, "app", "mysql", 3306, SchemaDependentResource.LOCAL_PORT) .build() : ClusterDeployedOperatorExtension.builder() .withOperatorDeployment(client.load(new FileInputStream("k8s/operator.yaml")).get())