diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/LeaderElectionManager.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/LeaderElectionManager.java index 75e018b27b..1509d87f2a 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/LeaderElectionManager.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/LeaderElectionManager.java @@ -9,7 +9,6 @@ import io.fabric8.kubernetes.api.model.authorization.v1.SelfSubjectRulesReview; import io.fabric8.kubernetes.api.model.authorization.v1.SelfSubjectRulesReviewSpecBuilder; -import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.extended.leaderelection.LeaderCallbacks; import io.fabric8.kubernetes.client.extended.leaderelection.LeaderElectionConfig; import io.fabric8.kubernetes.client.extended.leaderelection.LeaderElector; @@ -32,14 +31,11 @@ public class LeaderElectionManager { private final ControllerManager controllerManager; private String identity; private CompletableFuture leaderElectionFuture; - private KubernetesClient kubernetesClient; private final ConfigurationService configurationService; private String leaseNamespace; - public LeaderElectionManager(KubernetesClient kubernetesClient, - ControllerManager controllerManager, + LeaderElectionManager(ControllerManager controllerManager, ConfigurationService configurationService) { - this.kubernetesClient = kubernetesClient; this.controllerManager = controllerManager; this.configurationService = configurationService; } @@ -52,7 +48,7 @@ private void init(LeaderElectionConfiguration config) { this.identity = identity(config); leaseNamespace = config.getLeaseNamespace().orElseGet( - () -> configurationService.getClientConfiguration().getNamespace()); + () -> configurationService.getKubernetesClient().getConfiguration().getNamespace()); if (leaseNamespace == null) { final var message = "Lease namespace is not set and cannot be inferred. Leader election cannot continue."; @@ -62,7 +58,8 @@ private void init(LeaderElectionConfiguration config) { final var lock = new LeaseLock(leaseNamespace, config.getLeaseName(), identity); // releaseOnCancel is not used in the underlying implementation leaderElector = new LeaderElectorBuilder( - kubernetesClient, configurationService.getExecutorServiceManager().cachingExecutorService()) + configurationService.getKubernetesClient(), + configurationService.getExecutorServiceManager().cachingExecutorService()) .withConfig( new LeaderElectionConfig( lock, @@ -122,7 +119,7 @@ private void checkLeaseAccess() { var verbs = Arrays.asList("create", "update", "get"); SelfSubjectRulesReview review = new SelfSubjectRulesReview(); review.setSpec(new SelfSubjectRulesReviewSpecBuilder().withNamespace(leaseNamespace).build()); - var reviewResult = kubernetesClient.resource(review).create(); + var reviewResult = configurationService.getKubernetesClient().resource(review).create(); log.debug("SelfSubjectRulesReview result: {}", reviewResult); var foundRule = reviewResult.getStatus().getResourceRules().stream() .filter(rule -> rule.getApiGroups().contains(COORDINATION_GROUP) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/Operator.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/Operator.java index c3d031b130..d684124fb2 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/Operator.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/Operator.java @@ -10,11 +10,8 @@ import org.slf4j.LoggerFactory; import io.fabric8.kubernetes.api.model.HasMetadata; -import io.fabric8.kubernetes.client.ConfigBuilder; import io.fabric8.kubernetes.client.KubernetesClient; -import io.fabric8.kubernetes.client.KubernetesClientBuilder; import io.fabric8.kubernetes.client.Version; -import io.javaoperatorsdk.operator.api.config.BaseConfigurationService; import io.javaoperatorsdk.operator.api.config.ConfigurationService; import io.javaoperatorsdk.operator.api.config.ConfigurationServiceOverrider; import io.javaoperatorsdk.operator.api.config.ControllerConfiguration; @@ -26,7 +23,7 @@ @SuppressWarnings("rawtypes") public class Operator implements LifecycleAware { private static final Logger log = LoggerFactory.getLogger(Operator.class); - private static final int DEFAULT_MAX_CONCURRENT_REQUEST = 512; + private final KubernetesClient kubernetesClient; private final ControllerManager controllerManager; private final LeaderElectionManager leaderElectionManager; @@ -38,8 +35,8 @@ public Operator() { this((KubernetesClient) null); } - public Operator(KubernetesClient kubernetesClient) { - this(kubernetesClient, new BaseConfigurationService()); + Operator(KubernetesClient kubernetesClient) { + this(kubernetesClient, null); } /** @@ -47,40 +44,47 @@ public Operator(KubernetesClient kubernetesClient) { * @deprecated Use {@link #Operator(Consumer)} instead */ @Deprecated(forRemoval = true) + @SuppressWarnings("unused") public Operator(ConfigurationService configurationService) { - this(null, configurationService); + this(null, null); } public Operator(Consumer overrider) { this(null, overrider); } - public Operator(KubernetesClient client, Consumer overrider) { - this(client, ConfigurationService - .newOverriddenConfigurationService(new BaseConfigurationService(), overrider)); - } - /** * Note that Operator by default closes the client on stop, this can be changed using * {@link ConfigurationService} * - * @param kubernetesClient client to use to all Kubernetes related operations - * @param configurationService provides configuration + * @param client client to use to all Kubernetes related operations + * @param overrider a {@link ConfigurationServiceOverrider} consumer used to override the default + * {@link ConfigurationService} values + * @deprecated Use {@link Operator#Operator(Consumer)} instead, passing your custom client with + * {@link ConfigurationServiceOverrider#withKubernetesClient(KubernetesClient)} */ - public Operator(KubernetesClient kubernetesClient, ConfigurationService configurationService) { - this.configurationService = configurationService; + @Deprecated + public Operator(KubernetesClient client, Consumer overrider) { + // initialize the client if the user didn't provide one + if (client == null) { + var configurationService = ConfigurationService.newOverriddenConfigurationService(overrider); + client = configurationService.getKubernetesClient(); + } + + this.kubernetesClient = client; + + // override the configuration service to use the same client + if (overrider != null) { + overrider = overrider.andThen(o -> o.withKubernetesClient(this.kubernetesClient)); + } else { + overrider = o -> o.withKubernetesClient(this.kubernetesClient); + } + this.configurationService = ConfigurationService.newOverriddenConfigurationService(overrider); + final var executorServiceManager = configurationService.getExecutorServiceManager(); controllerManager = new ControllerManager(executorServiceManager); - this.kubernetesClient = - kubernetesClient != null ? kubernetesClient - : new KubernetesClientBuilder() - .withConfig(new ConfigBuilder() - .withMaxConcurrentRequests(DEFAULT_MAX_CONCURRENT_REQUEST).build()) - .build(); - - leaderElectionManager = - new LeaderElectionManager(kubernetesClient, controllerManager, configurationService); + leaderElectionManager = new LeaderElectionManager(controllerManager, configurationService); } /** diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AbstractConfigurationService.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AbstractConfigurationService.java index bf90a230a6..21039b2215 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AbstractConfigurationService.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AbstractConfigurationService.java @@ -9,44 +9,38 @@ import io.javaoperatorsdk.operator.ReconcilerUtils; import io.javaoperatorsdk.operator.api.reconciler.Reconciler; -import com.fasterxml.jackson.databind.ObjectMapper; - @SuppressWarnings("rawtypes") public class AbstractConfigurationService implements ConfigurationService { private final Map configurations = new ConcurrentHashMap<>(); private final Version version; private Cloner cloner; - private ObjectMapper mapper; private ExecutorServiceManager executorServiceManager; public AbstractConfigurationService(Version version) { - this(version, null, null, null); + this(version, null, null); } public AbstractConfigurationService(Version version, Cloner cloner) { - this(version, cloner, null, null); + this(version, cloner, null); } - public AbstractConfigurationService(Version version, Cloner cloner, ObjectMapper mapper, + public AbstractConfigurationService(Version version, Cloner cloner, ExecutorServiceManager executorServiceManager) { this.version = version; - init(cloner, mapper, executorServiceManager); + init(cloner, executorServiceManager); } /** - * Subclasses can call this method to more easily initialize the {@link Cloner} - * {@link ObjectMapper} and {@link ExecutorServiceManager} associated with this - * ConfigurationService implementation. This is useful in situations where the cloner depends on a - * mapper that might require additional configuration steps before it's ready to be used. + * Subclasses can call this method to more easily initialize the {@link Cloner} and + * {@link ExecutorServiceManager} associated with this ConfigurationService implementation. This + * is useful in situations where the cloner depends on a mapper that might require additional + * configuration steps before it's ready to be used. * * @param cloner the {@link Cloner} instance to be used - * @param mapper the {@link ObjectMapper} instance to be used * @param executorServiceManager the {@link ExecutorServiceManager} instance to be used */ - protected void init(Cloner cloner, ObjectMapper mapper, - ExecutorServiceManager executorServiceManager) { + protected void init(Cloner cloner, ExecutorServiceManager executorServiceManager) { this.cloner = cloner != null ? cloner : ConfigurationService.super.getResourceCloner(); - this.mapper = mapper != null ? mapper : ConfigurationService.super.getObjectMapper(); this.executorServiceManager = executorServiceManager; } @@ -133,11 +127,6 @@ public Cloner getResourceCloner() { return cloner; } - @Override - public ObjectMapper getObjectMapper() { - return mapper; - } - @Override public ExecutorServiceManager getExecutorServiceManager() { // lazy init to avoid initializing thread pools for nothing in an overriding scenario diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/BaseConfigurationService.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/BaseConfigurationService.java index d92294d048..b7d4c2bda2 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/BaseConfigurationService.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/BaseConfigurationService.java @@ -31,8 +31,6 @@ import io.javaoperatorsdk.operator.processing.event.source.filter.OnUpdateFilter; import io.javaoperatorsdk.operator.processing.retry.Retry; -import com.fasterxml.jackson.databind.ObjectMapper; - import static io.javaoperatorsdk.operator.api.config.ControllerConfiguration.CONTROLLER_NAME_AS_FIELD_MANAGER; import static io.javaoperatorsdk.operator.api.reconciler.Constants.DEFAULT_NAMESPACES_SET; @@ -45,10 +43,6 @@ public BaseConfigurationService(Version version) { super(version); } - public BaseConfigurationService(Version version, Cloner cloner, ObjectMapper mapper) { - super(version, cloner, mapper, null); - } - public BaseConfigurationService(Version version, Cloner cloner) { super(version, cloner); } 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 1ab358155e..325b547e8f 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 @@ -11,16 +11,16 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.ConfigBuilder; import io.fabric8.kubernetes.client.CustomResource; -import io.fabric8.kubernetes.client.utils.Serialization; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.KubernetesClientBuilder; +import io.fabric8.kubernetes.client.utils.KubernetesSerialization; import io.javaoperatorsdk.operator.api.monitoring.Metrics; import io.javaoperatorsdk.operator.api.reconciler.Reconciler; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResourceFactory; import io.javaoperatorsdk.operator.processing.dependent.workflow.ManagedWorkflowFactory; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; - import static io.javaoperatorsdk.operator.api.config.ExecutorServiceManager.newThreadPoolExecutor; /** An interface from which to retrieve configuration information. */ @@ -28,6 +28,8 @@ public interface ConfigurationService { Logger log = LoggerFactory.getLogger(ConfigurationService.class); + int DEFAULT_MAX_CONCURRENT_REQUEST = 512; + /** * Retrieves the configuration associated with the specified reconciler * @@ -38,14 +40,30 @@ public interface ConfigurationService { */ ControllerConfiguration getConfigurationFor(Reconciler reconciler); + /** - * Retrieves the Kubernetes client configuration + * Used to clone custom resources. It is strongly suggested that implementors override this method + * since the default implementation creates a new {@link Cloner} instance each time this method is + * called. * - * @return the configuration of the Kubernetes client, defaulting to the provided - * auto-configuration + * @return the configured {@link Cloner} */ - default Config getClientConfiguration() { - return Config.autoConfigure(null); + default Cloner getResourceCloner() { + return new Cloner() { + @Override + public R clone(R object) { + return getKubernetesClient().getKubernetesSerialization().clone(object); + } + }; + } + + default KubernetesClient getKubernetesClient() { + return new KubernetesClientBuilder() + .withConfig(new ConfigBuilder(Config.autoConfigure(null)) + .withMaxConcurrentRequests(DEFAULT_MAX_CONCURRENT_REQUEST) + .build()) + .withKubernetesSerialization(new KubernetesSerialization()) + .build(); } /** @@ -120,28 +138,6 @@ default int minConcurrentWorkflowExecutorThreads() { return MIN_DEFAULT_WORKFLOW_EXECUTOR_THREAD_NUMBER; } - /** - * Used to clone custom resources. It is strongly suggested that implementors override this method - * since the default implementation creates a new {@link Cloner} instance each time this method is - * called. - * - * @return the configured {@link Cloner} - */ - default Cloner getResourceCloner() { - return new Cloner() { - @SuppressWarnings("unchecked") - @Override - public HasMetadata clone(HasMetadata object) { - try { - final var mapper = getObjectMapper(); - return mapper.readValue(mapper.writeValueAsString(object), object.getClass()); - } catch (JsonProcessingException e) { - throw new IllegalStateException(e); - } - } - }; - } - int DEFAULT_TERMINATION_TIMEOUT_SECONDS = 10; /** @@ -176,10 +172,6 @@ default boolean closeClientOnStop() { return true; } - default ObjectMapper getObjectMapper() { - return Serialization.jsonMapper(); - } - @SuppressWarnings("rawtypes") default DependentResourceFactory dependentResourceFactory() { return DependentResourceFactory.DEFAULT; @@ -261,6 +253,11 @@ static ConfigurationService newOverriddenConfigurationService( return baseConfiguration; } + static ConfigurationService newOverriddenConfigurationService( + Consumer overrider) { + return newOverriddenConfigurationService(new BaseConfigurationService(), overrider); + } + default ExecutorServiceManager getExecutorServiceManager() { return new ExecutorServiceManager(this); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationServiceOverrider.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationServiceOverrider.java index b2b4b93b12..e8ad632c95 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationServiceOverrider.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationServiceOverrider.java @@ -6,16 +6,18 @@ import java.util.concurrent.ExecutorService; import java.util.function.Consumer; -import io.fabric8.kubernetes.client.Config; -import io.javaoperatorsdk.operator.api.monitoring.Metrics; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import com.fasterxml.jackson.databind.ObjectMapper; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.javaoperatorsdk.operator.api.monitoring.Metrics; @SuppressWarnings("unused") public class ConfigurationServiceOverrider { + + private static final Logger log = LoggerFactory.getLogger(ConfigurationServiceOverrider.class); private final ConfigurationService original; private Metrics metrics; - private Config clientConfig; private Boolean checkCR; private Integer concurrentReconciliationThreads; private Integer minConcurrentReconciliationThreads; @@ -24,7 +26,7 @@ public class ConfigurationServiceOverrider { private Cloner cloner; private Integer timeoutSeconds; private Boolean closeClientOnStop; - private ObjectMapper objectMapper; + private KubernetesClient client; private ExecutorService executorService; private ExecutorService workflowExecutorService; private LeaderElectionConfiguration leaderElectionConfiguration; @@ -39,11 +41,6 @@ public class ConfigurationServiceOverrider { this.original = original; } - public ConfigurationServiceOverrider withClientConfiguration(Config configuration) { - this.clientConfig = configuration; - return this; - } - public ConfigurationServiceOverrider checkingCRDAndValidateLocalModel(boolean check) { this.checkCR = check; return this; @@ -108,8 +105,8 @@ public ConfigurationServiceOverrider withWorkflowExecutorService( return this; } - public ConfigurationServiceOverrider withObjectMapper(ObjectMapper objectMapper) { - this.objectMapper = objectMapper; + public ConfigurationServiceOverrider withKubernetesClient(KubernetesClient client) { + this.client = client; return this; } @@ -154,17 +151,12 @@ public ConfigurationServiceOverrider withSSABasedDefaultMatchingForDependentReso } public ConfigurationService build() { - return new BaseConfigurationService(original.getVersion(), cloner, objectMapper) { + return new BaseConfigurationService(original.getVersion(), cloner) { @Override public Set getKnownReconcilerNames() { return original.getKnownReconcilerNames(); } - @Override - public Config getClientConfiguration() { - return clientConfig != null ? clientConfig : original.getClientConfiguration(); - } - @Override public boolean checkCRDAndValidateLocalModel() { return checkCR != null ? checkCR : original.checkCRDAndValidateLocalModel(); @@ -230,8 +222,8 @@ public ExecutorService getWorkflowExecutorService() { } @Override - public ObjectMapper getObjectMapper() { - return objectMapper != null ? objectMapper : original.getObjectMapper(); + public KubernetesClient getKubernetesClient() { + return client != null ? client : original.getKubernetesClient(); } @Override diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResourceConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResourceConfiguration.java index 02fcb0d957..34b05a4573 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResourceConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResourceConfiguration.java @@ -115,7 +115,7 @@ default Set getEffectiveNamespaces(ConfigurationService configurationSer var targetNamespaces = getNamespaces(); if (watchCurrentNamespace()) { final String namespace = - configurationService.getClientConfiguration().getNamespace(); + configurationService.getKubernetesClient().getConfiguration().getNamespace(); if (namespace == null) { throw new OperatorException( "Couldn't retrieve the currently connected namespace. Make sure it's correctly set in your ~/.kube/config file, using, e.g. 'kubectl config set-context --namespace='"); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcher.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcher.java index f0ca32bd98..1d189bd5c9 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcher.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcher.java @@ -1,6 +1,11 @@ package io.javaoperatorsdk.operator.processing.dependent.kubernetes; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.HasMetadata; @@ -11,7 +16,6 @@ import io.javaoperatorsdk.operator.processing.dependent.kubernetes.processors.GenericResourceUpdatePreProcessor; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; public class GenericKubernetesResourceMatcher implements Matcher { @@ -56,8 +60,7 @@ static Matcher matcherFor( @Override public Result match(R actualResource, P primary, Context

context) { var desired = dependentResource.desired(primary, context); - return match(desired, actualResource, false, false, false, - context.getControllerConfiguration().getConfigurationService().getObjectMapper()); + return match(desired, actualResource, false, false, false, context); } /** @@ -86,11 +89,12 @@ public Result match(R actualResource, P primary, Context

context) { * @param resource * @return results of matching */ - public static Result match(R desired, R actualResource, + public static Result match(R desired, + R actualResource, boolean considerLabelsAndAnnotations, boolean labelsAndAnnotationsEquality, - boolean specEquality, ObjectMapper objectMapper) { + boolean specEquality, Context

context) { return match(desired, actualResource, considerLabelsAndAnnotations, - labelsAndAnnotationsEquality, specEquality, objectMapper, EMPTY_ARRAY); + labelsAndAnnotationsEquality, specEquality, context, EMPTY_ARRAY); } /** @@ -112,11 +116,12 @@ public static Result match(R desired, R actualResourc * @param resource * @return results of matching */ - public static Result match(R desired, R actualResource, + public static Result match(R desired, + R actualResource, boolean considerLabelsAndAnnotations, boolean labelsAndAnnotationsEquality, - ObjectMapper objectMapper, String... ignorePaths) { + Context

context, String... ignorePaths) { return match(desired, actualResource, considerLabelsAndAnnotations, - labelsAndAnnotationsEquality, false, objectMapper, ignorePaths); + labelsAndAnnotationsEquality, false, context, ignorePaths); } /** @@ -152,8 +157,7 @@ public static Result match( String... ignorePaths) { final var desired = dependentResource.desired(primary, context); return match(desired, actualResource, considerLabelsAndAnnotations, - labelsAndAnnotationsEquality, context.getControllerConfiguration() - .getConfigurationService().getObjectMapper(), + labelsAndAnnotationsEquality, context, ignorePaths); } @@ -164,14 +168,14 @@ public static Result match( boolean specEquality) { final var desired = dependentResource.desired(primary, context); return match(desired, actualResource, considerLabelsAndAnnotations, - labelsAndAnnotationsEquality, specEquality, context.getControllerConfiguration() - .getConfigurationService().getObjectMapper()); + labelsAndAnnotationsEquality, specEquality, context); } @SuppressWarnings("unchecked") - public static Result match(R desired, R actualResource, + public static Result match(R desired, + R actualResource, boolean considerMetadata, boolean labelsAndAnnotationsEquality, boolean specEquality, - ObjectMapper objectMapper, + Context

context, String... ignoredPaths) { final List ignoreList = ignoredPaths != null && ignoredPaths.length > 0 ? Arrays.asList(ignoredPaths) @@ -184,7 +188,7 @@ public static Result match(R desired, R actualResourc if (considerMetadata) { Optional> res = - matchMetadata(desired, actualResource, labelsAndAnnotationsEquality, objectMapper); + matchMetadata(desired, actualResource, labelsAndAnnotationsEquality, context); if (res.isPresent()) { return res.orElseThrow(); } @@ -193,15 +197,15 @@ public static Result match(R desired, R actualResourc final ResourceUpdatePreProcessor processor = GenericResourceUpdatePreProcessor.processorFor((Class) desired.getClass()); final var matched = - processor.matches(actualResource, desired, specEquality, objectMapper, ignoredPaths); + processor.matches(actualResource, desired, specEquality, context, ignoredPaths); return Result.computed(matched, desired); } - private static Optional> matchMetadata(R desired, + private static Optional> matchMetadata( + R desired, R actualResource, - boolean labelsAndAnnotationsEquality, - ObjectMapper objectMapper) { + boolean labelsAndAnnotationsEquality, Context

context) { if (labelsAndAnnotationsEquality) { final var desiredMetadata = desired.getMetadata(); @@ -214,8 +218,9 @@ private static Optional> matchMetadata(R desir return Optional.of(Result.computed(false, desired)); } } else { - var desiredNode = objectMapper.valueToTree(desired); - var actualNode = objectMapper.valueToTree(actualResource); + final var objectMapper = context.getClient().getKubernetesSerialization(); + var desiredNode = objectMapper.convertValue(desired, JsonNode.class); + var actualNode = objectMapper.convertValue(actualResource, JsonNode.class); var wholeDiffJsonPatch = JsonDiff.asJson(desiredNode, actualNode); var metadataJSonDiffs = getDiffsImpactingPathsWithPrefixes(wholeDiffJsonPatch, METADATA_LABELS, @@ -264,8 +269,7 @@ public static Result match( KubernetesDependentResource dependentResource, R actualResource, P primary, Context

context, boolean considerLabelsAndAnnotations, boolean specEquality) { final var desired = dependentResource.desired(primary, context); - return match(desired, actualResource, considerLabelsAndAnnotations, specEquality, - context.getControllerConfiguration().getConfigurationService().getObjectMapper()); + return match(desired, actualResource, considerLabelsAndAnnotations, specEquality, context); } @Deprecated(forRemoval = true) @@ -273,9 +277,7 @@ public static Result match( KubernetesDependentResource dependentResource, R actualResource, P primary, Context

context, boolean considerLabelsAndAnnotations, String... ignorePaths) { final var desired = dependentResource.desired(primary, context); - return match(desired, actualResource, considerLabelsAndAnnotations, true, - context.getControllerConfiguration().getConfigurationService().getObjectMapper(), - ignorePaths); + return match(desired, actualResource, considerLabelsAndAnnotations, true, context, ignorePaths); } } 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 c42ec06841..b564f41ebc 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 @@ -169,8 +169,7 @@ public Result match(R actualResource, P primary, Context

context) { @SuppressWarnings("unused") public Result match(R actualResource, R desired, P primary, Context

context) { return GenericKubernetesResourceMatcher.match(desired, actualResource, false, - false, false, - context.getControllerConfiguration().getConfigurationService().getObjectMapper()); + false, false, context); } protected void handleDelete(P primary, R secondary, Context

context) { diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/ResourceUpdatePreProcessor.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/ResourceUpdatePreProcessor.java index 02518e9e64..e2ebc85d48 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/ResourceUpdatePreProcessor.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/ResourceUpdatePreProcessor.java @@ -9,9 +9,10 @@ import io.javaoperatorsdk.operator.api.reconciler.Context; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import static io.javaoperatorsdk.operator.processing.dependent.kubernetes.GenericKubernetesResourceMatcher.*; +import static io.javaoperatorsdk.operator.processing.dependent.kubernetes.GenericKubernetesResourceMatcher.allDiffsAreAddOps; +import static io.javaoperatorsdk.operator.processing.dependent.kubernetes.GenericKubernetesResourceMatcher.getDiffsImpactingPathsWithPrefixes; +import static io.javaoperatorsdk.operator.processing.dependent.kubernetes.GenericKubernetesResourceMatcher.nodeIsChildOf; public interface ResourceUpdatePreProcessor { @@ -19,11 +20,12 @@ public interface ResourceUpdatePreProcessor { R replaceSpecOnActual(R actual, R desired, Context context); - default boolean matches(R actual, R desired, boolean equality, ObjectMapper objectMapper, + default boolean matches(R actual, R desired, boolean equality, Context context, String[] ignoredPaths) { - var desiredNode = objectMapper.valueToTree(desired); - var actualNode = objectMapper.valueToTree(actual); + final var objectMapper = context.getClient().getKubernetesSerialization(); + var desiredNode = objectMapper.convertValue(desired, JsonNode.class); + var actualNode = objectMapper.convertValue(actual, JsonNode.class); var wholeDiffJsonPatch = JsonDiff.asJson(desiredNode, actualNode); final List ignoreList = diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/SSABasedGenericKubernetesResourceMatcher.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/SSABasedGenericKubernetesResourceMatcher.java index 50158cad18..36b2eecbda 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/SSABasedGenericKubernetesResourceMatcher.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/SSABasedGenericKubernetesResourceMatcher.java @@ -4,6 +4,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Optional; import java.util.Set; import java.util.SortedMap; @@ -15,12 +16,11 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.api.model.ManagedFieldsEntry; +import io.fabric8.kubernetes.client.utils.KubernetesSerialization; import io.javaoperatorsdk.operator.OperatorException; import io.javaoperatorsdk.operator.api.reconciler.Context; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; /** * Matches the actual state on the server vs the desired state. Based on the managedFields of SSA. @@ -52,8 +52,6 @@ public static SSABasedGenericKubernetesResourceMatcher> typeRef = new TypeReference<>() {}; - private static final String F_PREFIX = "f:"; private static final String K_PREFIX = "k:"; private static final String V_PREFIX = "v:"; @@ -81,11 +79,10 @@ public boolean matches(R actual, R desired, Context context) { var managedFieldsEntry = optionalManagedFieldsEntry.orElseThrow(); - var objectMapper = - context.getControllerConfiguration().getConfigurationService().getObjectMapper(); + var objectMapper = context.getClient().getKubernetesSerialization(); - var actualMap = objectMapper.convertValue(actual, typeRef); - var desiredMap = objectMapper.convertValue(desired, typeRef); + var actualMap = objectMapper.convertValue(actual, Map.class); + var desiredMap = objectMapper.convertValue(desired, Map.class); log.trace("Original actual: \n {} \n original desired: \n {} ", actual, desiredMap); @@ -118,7 +115,8 @@ private static void removeIrrelevantValues(Map desiredMap) { @SuppressWarnings("unchecked") private static void keepOnlyManagedFields(Map result, Map actualMap, - Map managedFields, ObjectMapper objectMapper) throws JsonProcessingException { + Map managedFields, KubernetesSerialization objectMapper) + throws JsonProcessingException { if (managedFields.isEmpty()) { result.putAll(actualMap); @@ -158,7 +156,8 @@ private static void keepOnlyManagedFields(Map result, @SuppressWarnings("unchecked") private static void fillResultsAndTraverseFurther(Map result, - Map actualMap, Map managedFields, ObjectMapper objectMapper, + Map actualMap, Map managedFields, + KubernetesSerialization objectMapper, String key, String keyInActual, Object managedFieldValue) throws JsonProcessingException { var emptyMapValue = new HashMap(); result.put(keyInActual, emptyMapValue); @@ -187,8 +186,8 @@ private static boolean isNestedValue(Map managedFieldValue) { @SuppressWarnings("unchecked") private static void handleListKeyEntrySet(Map result, Map actualMap, - ObjectMapper objectMapper, String keyInActual, - Set> managedEntrySet) { + KubernetesSerialization objectMapper, String keyInActual, + Set> managedEntrySet) { var valueList = new ArrayList<>(); result.put(keyInActual, valueList); var actualValueList = (List>) actualMap.get(keyInActual); @@ -226,8 +225,8 @@ private static void handleListKeyEntrySet(Map result, */ @SuppressWarnings("rawtypes") private static void handleSetValues(Map result, Map actualMap, - ObjectMapper objectMapper, String keyInActual, - Set> managedEntrySet) { + KubernetesSerialization objectMapper, String keyInActual, + Set> managedEntrySet) { var valueList = new ArrayList<>(); result.put(keyInActual, valueList); for (Map.Entry valueEntry : managedEntrySet) { @@ -241,23 +240,18 @@ private static void handleSetValues(Map result, Map targetClass, - ObjectMapper objectMapper) { - try { - stringValue = stringValue.trim(); - if (targetClass != null) { - return objectMapper.readValue(stringValue, targetClass); - } else { - return objectMapper.readValue(stringValue, typeRef); - } - } catch (JsonProcessingException e) { - throw new IllegalStateException(e); + KubernetesSerialization objectMapper) { + stringValue = stringValue.trim(); + if (targetClass != null) { + return objectMapper.unmarshal(stringValue, targetClass); + } else { + return objectMapper.unmarshal(stringValue, Map.class); } } @@ -287,46 +281,42 @@ private static boolean isKeyPrefixedSkippingDotKey(Set private static java.util.Map.Entry> selectListEntryBasedOnKey( String key, List> values, - ObjectMapper objectMapper) { - try { - Map ids = objectMapper.readValue(key, typeRef); - List> possibleTargets = new ArrayList<>(1); - int index = -1; - for (int i = 0; i < values.size(); i++) { - var v = values.get(i); - if (v.entrySet().containsAll(ids.entrySet())) { - possibleTargets.add(v); - index = i; - } + KubernetesSerialization objectMapper) { + Map ids = objectMapper.unmarshal(key, Map.class); + List> possibleTargets = new ArrayList<>(1); + int index = -1; + for (int i = 0; i < values.size(); i++) { + var v = values.get(i); + if (v.entrySet().containsAll(ids.entrySet())) { + possibleTargets.add(v); + index = i; } - if (possibleTargets.isEmpty()) { - throw new IllegalStateException( - "Cannot find list element for key:" + key + ", in map: " + values); - } - if (possibleTargets.size() > 1) { - throw new IllegalStateException( - "More targets found in list element for key:" + key + ", in map: " + values); + } + if (possibleTargets.isEmpty()) { + throw new IllegalStateException( + "Cannot find list element for key:" + key + ", in map: " + values); + } + if (possibleTargets.size() > 1) { + throw new IllegalStateException( + "More targets found in list element for key:" + key + ", in map: " + values); + } + final var finalIndex = index; + return new Map.Entry<>() { + @Override + public Integer getKey() { + return finalIndex; } - final var finalIndex = index; - return new Map.Entry<>() { - @Override - public Integer getKey() { - return finalIndex; - } - @Override - public Map getValue() { - return possibleTargets.get(0); - } + @Override + public Map getValue() { + return possibleTargets.get(0); + } - @Override - public Map setValue(Map stringObjectMap) { - throw new IllegalStateException("should not be called"); - } - }; - } catch (JsonProcessingException e) { - throw new IllegalStateException(e); - } + @Override + public Map setValue(Map stringObjectMap) { + throw new IllegalStateException("should not be called"); + } + }; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/processors/ClusterRoleBindingResourceUpdatePreProcessor.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/processors/ClusterRoleBindingResourceUpdatePreProcessor.java index 62e2e98076..9fc5718a45 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/processors/ClusterRoleBindingResourceUpdatePreProcessor.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/processors/ClusterRoleBindingResourceUpdatePreProcessor.java @@ -3,8 +3,7 @@ import java.util.Objects; import io.fabric8.kubernetes.api.model.rbac.ClusterRoleBinding; - -import com.fasterxml.jackson.databind.ObjectMapper; +import io.javaoperatorsdk.operator.api.reconciler.Context; public class ClusterRoleBindingResourceUpdatePreProcessor extends GenericResourceUpdatePreProcessor { @@ -17,8 +16,7 @@ protected void updateClonedActual(ClusterRoleBinding actual, ClusterRoleBinding @Override public boolean matches(ClusterRoleBinding actual, ClusterRoleBinding desired, boolean equality, - ObjectMapper objectMapper, - String[] ignoredPaths) { + Context context, String[] ignoredPaths) { return Objects.equals(actual.getRoleRef(), desired.getRoleRef()) && Objects.equals(actual.getSubjects(), desired.getSubjects()); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/processors/ClusterRoleResourceUpdatePreProcessor.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/processors/ClusterRoleResourceUpdatePreProcessor.java index 34fd4cb8a3..26e2431e21 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/processors/ClusterRoleResourceUpdatePreProcessor.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/processors/ClusterRoleResourceUpdatePreProcessor.java @@ -3,8 +3,7 @@ import java.util.Objects; import io.fabric8.kubernetes.api.model.rbac.ClusterRole; - -import com.fasterxml.jackson.databind.ObjectMapper; +import io.javaoperatorsdk.operator.api.reconciler.Context; public class ClusterRoleResourceUpdatePreProcessor extends GenericResourceUpdatePreProcessor { @@ -17,8 +16,7 @@ protected void updateClonedActual(ClusterRole actual, ClusterRole desired) { @Override public boolean matches(ClusterRole actual, ClusterRole desired, boolean equality, - ObjectMapper objectMapper, - String[] ignoredPaths) { + Context context, String[] ignoredPaths) { return Objects.equals(actual.getRules(), desired.getRules()) && Objects.equals(actual.getAggregationRule(), desired.getAggregationRule()); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/processors/ConfigMapResourceUpdatePreProcessor.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/processors/ConfigMapResourceUpdatePreProcessor.java index b3b8335620..f306d733f7 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/processors/ConfigMapResourceUpdatePreProcessor.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/processors/ConfigMapResourceUpdatePreProcessor.java @@ -3,8 +3,7 @@ import java.util.Objects; import io.fabric8.kubernetes.api.model.ConfigMap; - -import com.fasterxml.jackson.databind.ObjectMapper; +import io.javaoperatorsdk.operator.api.reconciler.Context; public class ConfigMapResourceUpdatePreProcessor extends GenericResourceUpdatePreProcessor { @@ -18,8 +17,7 @@ protected void updateClonedActual(ConfigMap actual, ConfigMap desired) { @Override public boolean matches(ConfigMap actual, ConfigMap desired, boolean equality, - ObjectMapper objectMapper, - String[] ignoredPaths) { + Context context, String[] ignoredPaths) { return Objects.equals(actual.getImmutable(), desired.getImmutable()) && Objects.equals(actual.getData(), desired.getData()) && Objects.equals(actual.getBinaryData(), desired.getBinaryData()); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/processors/RoleBindingResourceUpdatePreProcessor.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/processors/RoleBindingResourceUpdatePreProcessor.java index 8b82ffe9ec..45c8cb50ba 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/processors/RoleBindingResourceUpdatePreProcessor.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/processors/RoleBindingResourceUpdatePreProcessor.java @@ -3,8 +3,7 @@ import java.util.Objects; import io.fabric8.kubernetes.api.model.rbac.RoleBinding; - -import com.fasterxml.jackson.databind.ObjectMapper; +import io.javaoperatorsdk.operator.api.reconciler.Context; public class RoleBindingResourceUpdatePreProcessor extends GenericResourceUpdatePreProcessor { @@ -17,7 +16,7 @@ protected void updateClonedActual(RoleBinding actual, RoleBinding desired) { @Override public boolean matches(RoleBinding actual, RoleBinding desired, boolean equality, - ObjectMapper objectMapper, + Context context, String[] ignoredPaths) { return Objects.equals(actual.getRoleRef(), desired.getRoleRef()) && Objects.equals(actual.getSubjects(), desired.getSubjects()); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/processors/RoleResourceUpdatePreProcessor.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/processors/RoleResourceUpdatePreProcessor.java index 044c88c24e..86241e3957 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/processors/RoleResourceUpdatePreProcessor.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/processors/RoleResourceUpdatePreProcessor.java @@ -3,8 +3,7 @@ import java.util.Objects; import io.fabric8.kubernetes.api.model.rbac.Role; - -import com.fasterxml.jackson.databind.ObjectMapper; +import io.javaoperatorsdk.operator.api.reconciler.Context; public class RoleResourceUpdatePreProcessor extends GenericResourceUpdatePreProcessor { @@ -14,7 +13,7 @@ protected void updateClonedActual(Role actual, Role desired) { } @Override - public boolean matches(Role actual, Role desired, boolean equality, ObjectMapper objectMapper, + public boolean matches(Role actual, Role desired, boolean equality, Context context, String[] ignoredPaths) { return Objects.equals(actual.getRules(), desired.getRules()); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/processors/SecretResourceUpdatePreProcessor.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/processors/SecretResourceUpdatePreProcessor.java index a8bfe33d83..163c93aba6 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/processors/SecretResourceUpdatePreProcessor.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/processors/SecretResourceUpdatePreProcessor.java @@ -3,8 +3,7 @@ import java.util.Objects; import io.fabric8.kubernetes.api.model.Secret; - -import com.fasterxml.jackson.databind.ObjectMapper; +import io.javaoperatorsdk.operator.api.reconciler.Context; public class SecretResourceUpdatePreProcessor extends GenericResourceUpdatePreProcessor { @@ -17,7 +16,7 @@ protected void updateClonedActual(Secret actual, Secret desired) { } @Override - public boolean matches(Secret actual, Secret desired, boolean equality, ObjectMapper objectMapper, + public boolean matches(Secret actual, Secret desired, boolean equality, Context context, String[] ignoredPaths) { return Objects.equals(actual.getImmutable(), desired.getImmutable()) && Objects.equals(actual.getType(), desired.getType()) && diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/processors/ServiceAccountResourceUpdateProcessor.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/processors/ServiceAccountResourceUpdateProcessor.java index faca30484b..60f3e559bc 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/processors/ServiceAccountResourceUpdateProcessor.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/processors/ServiceAccountResourceUpdateProcessor.java @@ -3,8 +3,7 @@ import java.util.Objects; import io.fabric8.kubernetes.api.model.ServiceAccount; - -import com.fasterxml.jackson.databind.ObjectMapper; +import io.javaoperatorsdk.operator.api.reconciler.Context; public class ServiceAccountResourceUpdateProcessor extends GenericResourceUpdatePreProcessor { @@ -18,8 +17,7 @@ protected void updateClonedActual(ServiceAccount actual, ServiceAccount desired) @Override public boolean matches(ServiceAccount actual, ServiceAccount desired, boolean equality, - ObjectMapper objectMapper, - String[] ignoredPaths) { + Context context, String[] ignoredPaths) { return Objects.equals(actual.getAutomountServiceAccountToken(), desired.getAutomountServiceAccountToken()) && Objects.equals(actual.getImagePullSecrets(), desired.getImagePullSecrets()) && diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/LeaderElectionManagerTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/LeaderElectionManagerTest.java index 57229ddd34..98a5917230 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/LeaderElectionManagerTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/LeaderElectionManagerTest.java @@ -5,12 +5,11 @@ import java.nio.file.Path; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import io.fabric8.kubernetes.api.model.coordination.v1.Lease; -import io.javaoperatorsdk.operator.api.config.BaseConfigurationService; +import io.fabric8.kubernetes.client.Config; import io.javaoperatorsdk.operator.api.config.ConfigurationService; import io.javaoperatorsdk.operator.api.config.LeaderElectionConfiguration; @@ -19,20 +18,19 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; class LeaderElectionManagerTest { - private LeaderElectionManager leaderElectionManager; - - @BeforeEach - void setUp() { + private LeaderElectionManager leaderElectionManager() { ControllerManager controllerManager = mock(ControllerManager.class); final var kubernetesClient = MockKubernetesClient.client(Lease.class); + when(kubernetesClient.getConfiguration()).thenReturn(Config.autoConfigure(null)); var configurationService = - ConfigurationService.newOverriddenConfigurationService(new BaseConfigurationService(), - o -> o.withLeaderElectionConfiguration(new LeaderElectionConfiguration("test"))); - leaderElectionManager = - new LeaderElectionManager(kubernetesClient, controllerManager, configurationService); + ConfigurationService.newOverriddenConfigurationService( + o -> o.withLeaderElectionConfiguration(new LeaderElectionConfiguration("test")) + .withKubernetesClient(kubernetesClient)); + return new LeaderElectionManager(controllerManager, configurationService); } @AfterEach @@ -50,6 +48,7 @@ void testInitInferLeaseNamespace(@TempDir Path tempDir) throws IOException { System.setProperty(KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false"); System.setProperty(KUBERNETES_NAMESPACE_FILE, namespacePath.toString()); + final var leaderElectionManager = leaderElectionManager(); leaderElectionManager.start(); assertTrue(leaderElectionManager.isLeaderElectionEnabled()); } @@ -57,6 +56,6 @@ void testInitInferLeaseNamespace(@TempDir Path tempDir) throws IOException { @Test void testFailedToInitInferLeaseNamespace() { System.setProperty(KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false"); - assertThrows(IllegalArgumentException.class, () -> leaderElectionManager.start()); + assertThrows(IllegalArgumentException.class, () -> leaderElectionManager().start()); } } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/MockKubernetesClient.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/MockKubernetesClient.java index 47b47a2c4d..78e1b0c789 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/MockKubernetesClient.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/MockKubernetesClient.java @@ -12,13 +12,28 @@ import io.fabric8.kubernetes.api.model.authorization.v1.SubjectRulesReviewStatus; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.V1ApiextensionAPIGroupDSL; -import io.fabric8.kubernetes.client.dsl.*; +import io.fabric8.kubernetes.client.dsl.AnyNamespaceOperation; +import io.fabric8.kubernetes.client.dsl.ApiextensionsAPIGroupDSL; +import io.fabric8.kubernetes.client.dsl.FilterWatchListDeletable; +import io.fabric8.kubernetes.client.dsl.MixedOperation; +import io.fabric8.kubernetes.client.dsl.NamespaceableResource; +import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation; +import io.fabric8.kubernetes.client.dsl.Resource; import io.fabric8.kubernetes.client.extended.leaderelection.LeaderElectorBuilder; import io.fabric8.kubernetes.client.informers.SharedIndexInformer; import io.fabric8.kubernetes.client.informers.cache.Indexer; +import io.fabric8.kubernetes.client.utils.KubernetesSerialization; -import static io.javaoperatorsdk.operator.LeaderElectionManager.*; -import static org.mockito.Mockito.*; +import static io.javaoperatorsdk.operator.LeaderElectionManager.COORDINATION_GROUP; +import static io.javaoperatorsdk.operator.LeaderElectionManager.LEASES_RESOURCE; +import static io.javaoperatorsdk.operator.LeaderElectionManager.UNIVERSAL_VERB; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyLong; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.nullable; +import static org.mockito.Mockito.when; public class MockKubernetesClient { @@ -79,6 +94,9 @@ public static KubernetesClient client(Class clazz, when(v1.customResourceDefinitions()).thenReturn(operation); when(operation.withName(any())).thenReturn(mock(Resource.class)); + final var serialization = new KubernetesSerialization(); + when(client.getKubernetesSerialization()).thenReturn(serialization); + return client; } 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 e9d7eda37f..7a4a5793ba 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 @@ -6,19 +6,15 @@ import org.junit.jupiter.api.Test; import io.fabric8.kubernetes.api.model.HasMetadata; -import io.fabric8.kubernetes.client.Config; -import io.fabric8.kubernetes.client.ConfigBuilder; import io.javaoperatorsdk.operator.api.monitoring.Metrics; -import com.fasterxml.jackson.databind.ObjectMapper; - import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; class ConfigurationServiceOverriderTest { private static final Metrics METRICS = new Metrics() {}; - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + private static final LeaderElectionConfiguration LEADER_ELECTION_CONFIGURATION = new LeaderElectionConfiguration("foo", "fooNS"); @@ -37,21 +33,11 @@ public boolean checkCRDAndValidateLocalModel() { return false; } - @Override - public Config getClientConfiguration() { - return new ConfigBuilder().withNamespace("namespace").build(); - } - @Override public Metrics getMetrics() { return METRICS; } - @Override - public ObjectMapper getObjectMapper() { - return OBJECT_MAPPER; - } - @Override public Cloner getResourceCloner() { return CLONER; @@ -63,12 +49,10 @@ public Optional getLeaderElectionConfiguration() { } }; final var overridden = new ConfigurationServiceOverrider(config) - .withClientConfiguration(new ConfigBuilder().withNamespace("newNS").build()) .checkingCRDAndValidateLocalModel(true) .withExecutorService(Executors.newSingleThreadExecutor()) .withWorkflowExecutorService(Executors.newFixedThreadPool(4)) .withCloseClientOnStop(false) - .withObjectMapper(new ObjectMapper()) .withResourceCloner(new Cloner() { @Override public R clone(R object) { @@ -90,11 +74,9 @@ public R clone(R object) { overridden.concurrentReconciliationThreads()); assertNotEquals(config.getTerminationTimeoutSeconds(), overridden.getTerminationTimeoutSeconds()); - assertNotEquals(config.getClientConfiguration(), overridden.getClientConfiguration()); assertNotEquals(config.getExecutorService(), overridden.getExecutorService()); assertNotEquals(config.getWorkflowExecutorService(), overridden.getWorkflowExecutorService()); assertNotEquals(config.getMetrics(), overridden.getMetrics()); - assertNotEquals(config.getObjectMapper(), overridden.getObjectMapper()); assertNotEquals(config.getLeaderElectionConfiguration(), overridden.getLeaderElectionConfiguration()); assertNotEquals(config.getInformerStoppedHandler(), diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcherTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcherTest.java index 259142369b..2544e3977b 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcherTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcherTest.java @@ -10,9 +10,8 @@ import io.fabric8.kubernetes.api.model.ServiceAccountBuilder; import io.fabric8.kubernetes.api.model.apps.Deployment; import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder; +import io.javaoperatorsdk.operator.MockKubernetesClient; import io.javaoperatorsdk.operator.ReconcilerUtils; -import io.javaoperatorsdk.operator.api.config.BaseConfigurationService; -import io.javaoperatorsdk.operator.api.config.ControllerConfiguration; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.processing.dependent.Matcher; @@ -28,15 +27,12 @@ class GenericKubernetesResourceMatcherTest { Deployment actual = createDeployment(); Deployment desired = createDeployment(); TestDependentResource dependentResource = new TestDependentResource(desired); - Matcher matcher = - GenericKubernetesResourceMatcher.matcherFor(dependentResource); + Matcher matcher = GenericKubernetesResourceMatcher.matcherFor(dependentResource); @BeforeAll static void setUp() { - final var controllerConfiguration = mock(ControllerConfiguration.class); - when(controllerConfiguration.getConfigurationService()) - .thenReturn(new BaseConfigurationService()); - when(context.getControllerConfiguration()).thenReturn(controllerConfiguration); + final var client = MockKubernetesClient.client(HasMetadata.class); + when(context.getClient()).thenReturn(client); } @Test diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericResourceUpdatePreProcessorTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericResourceUpdatePreProcessorTest.java index ff9edf090b..bcd0ca6c6e 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericResourceUpdatePreProcessorTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericResourceUpdatePreProcessorTest.java @@ -6,14 +6,16 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.api.model.Namespace; import io.fabric8.kubernetes.api.model.NamespaceBuilder; import io.fabric8.kubernetes.api.model.NamespaceSpec; import io.fabric8.kubernetes.api.model.Secret; import io.fabric8.kubernetes.api.model.SecretBuilder; import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.javaoperatorsdk.operator.MockKubernetesClient; import io.javaoperatorsdk.operator.ReconcilerUtils; -import io.javaoperatorsdk.operator.api.config.BaseConfigurationService; +import io.javaoperatorsdk.operator.api.config.ConfigurationService; import io.javaoperatorsdk.operator.api.config.ControllerConfiguration; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.processors.GenericResourceUpdatePreProcessor; @@ -30,8 +32,13 @@ class GenericResourceUpdatePreProcessorTest { @BeforeAll static void setUp() { final var controllerConfiguration = mock(ControllerConfiguration.class); - when(controllerConfiguration.getConfigurationService()) - .thenReturn(new BaseConfigurationService()); + final var configService = mock(ConfigurationService.class); + when(controllerConfiguration.getConfigurationService()).thenReturn(configService); + + final var client = MockKubernetesClient.client(HasMetadata.class); + when(configService.getKubernetesClient()).thenReturn(client); + when(configService.getResourceCloner()).thenCallRealMethod(); + when(context.getControllerConfiguration()).thenReturn(controllerConfiguration); } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/SSABasedGenericKubernetesResourceMatcherTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/SSABasedGenericKubernetesResourceMatcherTest.java index 3864ad1c43..cf0f67887b 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/SSABasedGenericKubernetesResourceMatcherTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/SSABasedGenericKubernetesResourceMatcherTest.java @@ -8,6 +8,7 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.javaoperatorsdk.operator.MockKubernetesClient; import io.javaoperatorsdk.operator.ReconcilerUtils; import io.javaoperatorsdk.operator.api.config.ConfigurationService; import io.javaoperatorsdk.operator.api.config.ControllerConfiguration; @@ -30,7 +31,10 @@ void setup() { var controllerConfiguration = mock(ControllerConfiguration.class); when(controllerConfiguration.fieldManager()).thenReturn("controller"); var configurationService = mock(ConfigurationService.class); - when(configurationService.getObjectMapper()).thenCallRealMethod(); + + final var client = MockKubernetesClient.client(HasMetadata.class); + when(mockedContext.getClient()).thenReturn(client); + when(controllerConfiguration.getConfigurationService()).thenReturn(configurationService); when(mockedContext.getControllerConfiguration()).thenReturn(controllerConfiguration); } diff --git a/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/AbstractOperatorExtension.java b/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/AbstractOperatorExtension.java index 6a9e363210..ff5a67686e 100644 --- a/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/AbstractOperatorExtension.java +++ b/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/AbstractOperatorExtension.java @@ -27,8 +27,6 @@ import io.fabric8.kubernetes.client.dsl.Resource; import io.fabric8.kubernetes.client.utils.KubernetesResourceUtil; import io.fabric8.kubernetes.client.utils.Utils; -import io.javaoperatorsdk.operator.api.config.BaseConfigurationService; -import io.javaoperatorsdk.operator.api.config.ConfigurationService; import io.javaoperatorsdk.operator.api.config.ConfigurationServiceOverrider; public abstract class AbstractOperatorExtension implements HasKubernetesClient, @@ -221,7 +219,7 @@ public static abstract class AbstractBuilder> { protected boolean waitForNamespaceDeletion; protected boolean oneNamespacePerClass; protected int namespaceDeleteTimeout; - protected ConfigurationService configurationService = new BaseConfigurationService(); + protected Consumer configurationServiceOverrider; protected AbstractBuilder() { this.infrastructure = new ArrayList<>(); @@ -260,8 +258,7 @@ public T oneNamespacePerClass(boolean value) { } public T withConfigurationService(Consumer overrider) { - configurationService = - ConfigurationService.newOverriddenConfigurationService(configurationService, overrider); + configurationServiceOverrider = overrider; return (T) this; } diff --git a/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/LocallyRunOperatorExtension.java b/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/LocallyRunOperatorExtension.java index b5fcf1aa41..6b276b43d9 100644 --- a/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/LocallyRunOperatorExtension.java +++ b/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/LocallyRunOperatorExtension.java @@ -24,7 +24,7 @@ import io.javaoperatorsdk.operator.Operator; import io.javaoperatorsdk.operator.ReconcilerUtils; import io.javaoperatorsdk.operator.RegisteredController; -import io.javaoperatorsdk.operator.api.config.ConfigurationService; +import io.javaoperatorsdk.operator.api.config.ConfigurationServiceOverrider; import io.javaoperatorsdk.operator.api.config.ControllerConfigurationOverrider; import io.javaoperatorsdk.operator.api.reconciler.Reconciler; import io.javaoperatorsdk.operator.processing.retry.Retry; @@ -53,7 +53,7 @@ private LocallyRunOperatorExtension( boolean waitForNamespaceDeletion, boolean oneNamespacePerClass, KubernetesClient kubernetesClient, - ConfigurationService configurationService) { + Consumer configurationServiceOverrider) { super( infrastructure, infrastructureTimeout, @@ -65,7 +65,7 @@ private LocallyRunOperatorExtension( this.portForwards = portForwards; this.localPortForwards = new ArrayList<>(portForwards.size()); this.additionalCustomResourceDefinitions = additionalCustomResourceDefinitions; - this.operator = new Operator(getKubernetesClient(), configurationService); + this.operator = new Operator(getKubernetesClient(), configurationServiceOverrider); this.registeredControllers = new HashMap<>(); } @@ -289,7 +289,7 @@ public LocallyRunOperatorExtension build() { waitForNamespaceDeletion, oneNamespacePerClass, kubernetesClient, - configurationService); + configurationServiceOverrider); } } diff --git a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchemaOperator.java b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchemaOperator.java index f312716f44..e9e1ac1d49 100644 --- a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchemaOperator.java +++ b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchemaOperator.java @@ -9,8 +9,6 @@ import org.takes.http.Exit; import org.takes.http.FtBasic; -import io.fabric8.kubernetes.client.KubernetesClient; -import io.fabric8.kubernetes.client.KubernetesClientBuilder; import io.javaoperatorsdk.operator.Operator; import io.javaoperatorsdk.operator.monitoring.micrometer.MicrometerMetrics; import io.javaoperatorsdk.operator.sample.dependent.ResourcePollerConfig; @@ -24,10 +22,8 @@ public class MySQLSchemaOperator { public static void main(String[] args) throws IOException { log.info("MySQL Schema Operator starting"); - KubernetesClient client = new KubernetesClientBuilder().build(); - Operator operator = new Operator(client, - overrider -> overrider - .withMetrics(MicrometerMetrics.withoutPerResourceMetrics(new LoggingMeterRegistry()))); + Operator operator = new Operator(overrider -> overrider + .withMetrics(MicrometerMetrics.withoutPerResourceMetrics(new LoggingMeterRegistry()))); MySQLSchemaReconciler schemaReconciler = new MySQLSchemaReconciler(); diff --git a/sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/TomcatOperator.java b/sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/TomcatOperator.java index 054626eb8b..370a488bc9 100644 --- a/sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/TomcatOperator.java +++ b/sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/TomcatOperator.java @@ -9,7 +9,6 @@ import org.takes.http.Exit; import org.takes.http.FtBasic; -import io.fabric8.kubernetes.client.*; import io.javaoperatorsdk.operator.Operator; public class TomcatOperator { @@ -18,10 +17,9 @@ public class TomcatOperator { public static void main(String[] args) throws IOException { - KubernetesClient client = new KubernetesClientBuilder().build(); - Operator operator = new Operator(client); + Operator operator = new Operator(); operator.register(new TomcatReconciler()); - operator.register(new WebappReconciler(client)); + operator.register(new WebappReconciler(operator.getKubernetesClient())); operator.start(); new FtBasic(new TkFork(new FkRegex("/health", "ALL GOOD.")), 8080).start(Exit.NEVER);