diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Cloner.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Cloner.java new file mode 100644 index 0000000000..92f569f36a --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Cloner.java @@ -0,0 +1,9 @@ +package io.javaoperatorsdk.operator.api.config; + +import io.fabric8.kubernetes.client.CustomResource; + +public interface Cloner { + + CustomResource clone(CustomResource object); + +} 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 cb16793f43..ec61108bf2 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 @@ -9,12 +9,24 @@ import io.javaoperatorsdk.operator.Metrics; import io.javaoperatorsdk.operator.api.ResourceController; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; /** An interface from which to retrieve configuration information. */ public interface ConfigurationService { - ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + Cloner DEFAULT_CLONER = new Cloner() { + private final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + @Override + public CustomResource clone(CustomResource object) { + try { + return OBJECT_MAPPER.readValue(OBJECT_MAPPER.writeValueAsString(object), object.getClass()); + } catch (JsonProcessingException e) { + throw new IllegalStateException(e); + } + } + }; /** * Retrieves the configuration associated with the specified controller @@ -79,14 +91,12 @@ default int concurrentReconciliationThreads() { } /** - * The {@link ObjectMapper} that the operator should use to de-/serialize resources. This is - * particularly useful when frameworks can configure a specific mapper that should also be used by - * the SDK. - * + * Used to clone custom resources. + * * @return the ObjectMapper to use */ - default ObjectMapper getObjectMapper() { - return OBJECT_MAPPER; + default Cloner getResourceCloner() { + return DEFAULT_CLONER; } int DEFAULT_TERMINATION_TIMEOUT_SECONDS = 10; 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 ed0bd513c6..f1faef44f8 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 @@ -7,15 +7,13 @@ import io.javaoperatorsdk.operator.Metrics; import io.javaoperatorsdk.operator.api.ResourceController; -import com.fasterxml.jackson.databind.ObjectMapper; - public class ConfigurationServiceOverrider { private final ConfigurationService original; private Metrics metrics; private Config clientConfig; private boolean checkCR; private int threadNumber; - private ObjectMapper mapper; + private Cloner cloner; private int timeoutSeconds; public ConfigurationServiceOverrider( @@ -24,7 +22,7 @@ public ConfigurationServiceOverrider( this.clientConfig = original.getClientConfiguration(); this.checkCR = original.checkCRDAndValidateLocalModel(); this.threadNumber = original.concurrentReconciliationThreads(); - this.mapper = original.getObjectMapper(); + this.cloner = original.getResourceCloner(); this.timeoutSeconds = original.getTerminationTimeoutSeconds(); this.metrics = original.getMetrics(); } @@ -45,8 +43,8 @@ public ConfigurationServiceOverrider withConcurrentReconciliationThreads(int thr return this; } - public ConfigurationServiceOverrider withObjectMapper(ObjectMapper mapper) { - this.mapper = mapper; + public ConfigurationServiceOverrider withResourceCloner(Cloner cloner) { + this.cloner = cloner; return this; } @@ -94,8 +92,8 @@ public int concurrentReconciliationThreads() { } @Override - public ObjectMapper getObjectMapper() { - return mapper; + public Cloner getResourceCloner() { + return cloner; } @Override diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/internal/CustomResourceEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/internal/CustomResourceEventSource.java index ddbbfde692..b9c4c5b6a3 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/internal/CustomResourceEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/internal/CustomResourceEventSource.java @@ -13,15 +13,13 @@ import io.fabric8.kubernetes.client.informers.SharedIndexInformer; import io.fabric8.kubernetes.client.informers.cache.Cache; import io.javaoperatorsdk.operator.MissingCRDException; +import io.javaoperatorsdk.operator.api.config.Cloner; import io.javaoperatorsdk.operator.api.config.ControllerConfiguration; import io.javaoperatorsdk.operator.processing.ConfiguredController; import io.javaoperatorsdk.operator.processing.ResourceCache; import io.javaoperatorsdk.operator.processing.event.AbstractEventSource; import io.javaoperatorsdk.operator.processing.event.CustomResourceID; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; - import static io.javaoperatorsdk.operator.processing.KubernetesResourceUtils.getName; import static io.javaoperatorsdk.operator.processing.KubernetesResourceUtils.getUID; import static io.javaoperatorsdk.operator.processing.KubernetesResourceUtils.getVersion; @@ -40,15 +38,14 @@ public class CustomResourceEventSource> extends A private final ConfiguredController controller; private final Map> sharedIndexInformers = new ConcurrentHashMap<>(); - private final ObjectMapper cloningObjectMapper; + private final CustomResourceEventFilter filter; private final OnceWhitelistEventFilterEventFilter onceWhitelistEventFilterEventFilter; - + private final Cloner cloner; public CustomResourceEventSource(ConfiguredController controller) { this.controller = controller; - this.cloningObjectMapper = - controller.getConfiguration().getConfigurationService().getObjectMapper(); + this.cloner = controller.getConfiguration().getConfigurationService().getResourceCloner(); var filters = new CustomResourceEventFilter[] { CustomResourceEventFilters.finalizerNeededAndApplied(), @@ -160,7 +157,7 @@ public Optional getCustomResource(CustomResourceID resourceID) { if (resource == null) { return Optional.empty(); } else { - return Optional.of(clone(resource)); + return Optional.of((T) (cloner.clone(resource))); } } @@ -176,15 +173,6 @@ public SharedIndexInformer getInformer(String namespace) { return getInformers().get(Objects.requireNonNullElse(namespace, ANY_NAMESPACE_MAP_KEY)); } - private T clone(T customResource) { - try { - return (T) cloningObjectMapper.readValue( - cloningObjectMapper.writeValueAsString(customResource), customResource.getClass()); - } catch (JsonProcessingException e) { - throw new IllegalStateException(e); - } - } - /** * This will ensure that the next event received after this method is called will not be filtered * out. @@ -196,4 +184,5 @@ public void whitelistNextEvent(CustomResourceID customResourceID) { onceWhitelistEventFilterEventFilter.whitelistNextEvent(customResourceID); } } + } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/internal/CustomResourceEventFilterTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/internal/CustomResourceEventFilterTest.java index bd0d2ed72d..3e9e413f1e 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/internal/CustomResourceEventFilterTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/internal/CustomResourceEventFilterTest.java @@ -103,8 +103,8 @@ public void eventNotFilteredByCustomPredicateIfFinalizerIsRequired() { oldResource.getStatus().getConfigMapStatus(), newResource.getStatus().getConfigMapStatus())); - when(config.getConfigurationService().getObjectMapper()) - .thenReturn(ConfigurationService.OBJECT_MAPPER); + when(config.getConfigurationService().getResourceCloner()) + .thenReturn(ConfigurationService.DEFAULT_CLONER); var controller = new TestConfiguredController(config); var eventSource = new CustomResourceEventSource<>(controller); @@ -142,8 +142,8 @@ public TestControllerConfig(String finalizer, boolean generationAware, TestCustomResource.class, mock(ConfigurationService.class)); - when(getConfigurationService().getObjectMapper()) - .thenReturn(ConfigurationService.OBJECT_MAPPER); + when(getConfigurationService().getResourceCloner()) + .thenReturn(ConfigurationService.DEFAULT_CLONER); } } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/internal/CustomResourceEventSourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/internal/CustomResourceEventSourceTest.java index 0bd736b6c2..a24c32a547 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/internal/CustomResourceEventSourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/internal/CustomResourceEventSourceTest.java @@ -164,8 +164,8 @@ public TestConfiguration(boolean generationAware) { null, TestCustomResource.class, mock(ConfigurationService.class)); - when(getConfigurationService().getObjectMapper()) - .thenReturn(ConfigurationService.OBJECT_MAPPER); + when(getConfigurationService().getResourceCloner()) + .thenReturn(ConfigurationService.DEFAULT_CLONER); when(getConfigurationService().getMetrics()) .thenReturn(Metrics.NOOP); }