From 0334f709bfed84050592f4c236ea04b67b0bc539 Mon Sep 17 00:00:00 2001 From: Steve Hawkins Date: Fri, 17 Feb 2023 09:09:08 -0500 Subject: [PATCH] fix #3972: refining the client to not use static deserialization logic --- CHANGELOG.md | 1 + .../client/DefaultKubernetesClient.java | 11 - .../kubernetes/client/KubernetesClient.java | 5 +- .../client/KubernetesClientBuilder.java | 13 +- .../NamespacedKubernetesClientAdapter.java | 5 +- .../resourcelock/ConfigMapLock.java | 23 +- .../cache/ReducedStateItemStore.java | 10 +- .../client/internal/KubeConfigUtils.java | 13 +- .../kubernetes/client/utils/IOHelpers.java | 17 +- .../client/utils/KubernetesResourceUtil.java | 8 +- .../client/utils/KubernetesSerialization.java | 391 ++++++++++++++++++ .../client/utils/OpenIDConnectionUtils.java | 2 +- .../client/utils/Serialization.java | 124 +++--- .../resourcelock/ConfigMapLockTest.java | 10 +- .../cache/ReducedStateItemStoreTest.java | 4 +- .../client/utils/SerializationTest.java | 13 +- .../serialization/test-crd-schema.yml | 2 +- .../dsl/internal/AbstractWatchManager.java | 15 +- .../client/dsl/internal/BaseOperation.java | 9 +- .../dsl/internal/ExecWebSocketListener.java | 14 +- .../dsl/internal/HasMetadataOperation.java | 3 +- ...hDeleteRecreateWaitApplicableListImpl.java | 13 +- .../client/dsl/internal/OperationSupport.java | 60 +-- .../client/dsl/internal/PatchUtils.java | 19 +- .../dsl/internal/WatchConnectionManager.java | 2 +- .../v1/RollableScalableResourceOperation.java | 5 +- .../dsl/internal/apps/v1/RollingUpdater.java | 18 +- .../apps/v1/StatefulSetOperationsImpl.java | 4 +- .../internal/core/v1/PodOperationsImpl.java | 5 +- .../kubernetes/client/impl/BaseClient.java | 7 + .../kubernetes/client/impl/Handlers.java | 26 +- .../client/impl/KubernetesClientImpl.java | 14 +- .../utils/internal/CreateOrReplaceHelper.java | 8 +- .../internal/ExecWebSocketListenerTest.java | 15 +- .../dsl/internal/OperationSupportTest.java | 10 +- .../internal/uploadable/PodUploadTest.java | 2 + .../client/impl/KubernetesClientImplTest.java | 4 +- .../internal/CreateOrReplaceHelperTest.java | 9 +- .../internal/KubernetesDeserializer.java | 14 +- .../client/mock/ClusterRoleTest.java | 4 +- .../client/mock/CustomResourceCrudTest.java | 5 +- .../CustomResourceCrudWithCRDContextTest.java | 8 +- .../mock/DefaultSharedIndexInformerTest.java | 5 +- .../client/mock/LeaderElectionTest.java | 1 + .../src/test/resources/parameters.yml | 1 + .../resources/template-with-json-params.yml | 3 + .../client/DefaultOpenShiftClient.java | 11 - .../NamespacedOpenShiftClientAdapter.java | 3 +- .../openshift/client/OpenShiftClient.java | 121 +++++- .../client/dsl/TemplateResource.java | 36 +- .../build/BuildConfigOperationsImpl.java | 5 +- .../internal/core/TemplateOperationsImpl.java | 15 +- .../client/impl/OpenShiftClientImpl.java | 3 +- .../internal/OpenShiftOAuthInterceptor.java | 6 +- .../build/BuildConfigOperationsImplTest.java | 2 + 55 files changed, 801 insertions(+), 356 deletions(-) create mode 100644 kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/KubernetesSerialization.java diff --git a/CHANGELOG.md b/CHANGELOG.md index bbc2d3720da..32e1b4b24db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -126,6 +126,7 @@ * Fix #4579: the implicit registration of resource and list types that happens when using the resource(class) methods has been removed. This makes the behavior of the client more predictable as that was an undocumented side-effect. If you expect to see instances of a custom type from an untyped api call - typically KubernetesClient.load, KubernetesClient.resourceList, KubernetesClient.resource(InputStream|String), then you must either create a META-INF/services/io.fabric8.kubernetes.api.model.KubernetesResource file (see above #3923), or make calls to KubernetesDeserializer.registerCustomKind - however since KubernetesDeserializer is an internal class that mechanism is not preferred. * Fix #4597: remove the deprecated support for `javax.validation.constraints.NotNull` in the `crd-generator`, to mark a property as `required` it needs to be annotated with `io.fabric8.generator.annotation.Required` * Fix #4363: deprecated existing ResourceCompare methods such as compareKubernetesResource as they are not for general use +* Fix #3973: deprecated Serialization methods that take parameters and methods for the json and yaml mappers. ### 6.2.0 (2022-10-20) diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/DefaultKubernetesClient.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/DefaultKubernetesClient.java index 292aaf456d7..47ef633c919 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/DefaultKubernetesClient.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/DefaultKubernetesClient.java @@ -21,9 +21,6 @@ import io.fabric8.kubernetes.client.http.HttpClient.Factory; import io.fabric8.kubernetes.client.http.StandardHttpClientBuilder; import io.fabric8.kubernetes.client.utils.HttpClientUtils; -import io.fabric8.kubernetes.client.utils.Serialization; - -import java.io.InputStream; /** * Class for Default Kubernetes Client implementing KubernetesClient interface. @@ -36,14 +33,6 @@ public class DefaultKubernetesClient extends NamespacedKubernetesClientAdapter load(InputStream is); + NamespaceListVisitFromServerGetDeleteRecreateWaitApplicable load(InputStream is); /** * Load a Kubernetes list object @@ -308,7 +307,7 @@ MixedOperation resourceList(String s); + NamespaceListVisitFromServerGetDeleteRecreateWaitApplicable resourceList(String s); /** * KubernetesResourceList operations diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/KubernetesClientBuilder.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/KubernetesClientBuilder.java index e4957f51735..6f8f47d49e6 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/KubernetesClientBuilder.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/KubernetesClientBuilder.java @@ -16,10 +16,11 @@ package io.fabric8.kubernetes.client; +import com.fasterxml.jackson.databind.ObjectMapper; import io.fabric8.kubernetes.client.http.HttpClient; import io.fabric8.kubernetes.client.informers.ResourceEventHandler; import io.fabric8.kubernetes.client.utils.HttpClientUtils; -import io.fabric8.kubernetes.client.utils.Serialization; +import io.fabric8.kubernetes.client.utils.KubernetesSerialization; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; @@ -47,6 +48,7 @@ default void onClose(Executor executor) { private Class clazz; private ExecutorSupplier executorSupplier; private Consumer builderConsumer; + private KubernetesSerialization kubernetesSerialization = new KubernetesSerialization(new ObjectMapper()); public KubernetesClientBuilder() { // basically the same logic as in KubernetesResourceUtil for finding list types @@ -96,13 +98,18 @@ public KubernetesClientBuilder withConfig(Config config) { return this; } + public KubernetesClientBuilder withKubernetesSerialization(KubernetesSerialization kubernetesSerialization) { + this.kubernetesSerialization = kubernetesSerialization; + return this; + } + public KubernetesClientBuilder withConfig(String config) { - this.config = Serialization.unmarshal(config, Config.class); + this.config = kubernetesSerialization.unmarshal(config, Config.class); return this; } public KubernetesClientBuilder withConfig(InputStream config) { - this.config = Serialization.unmarshal(config, Config.class); + this.config = kubernetesSerialization.unmarshal(config, Config.class); return this; } diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/NamespacedKubernetesClientAdapter.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/NamespacedKubernetesClientAdapter.java index b402a39153b..7d327382c04 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/NamespacedKubernetesClientAdapter.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/NamespacedKubernetesClientAdapter.java @@ -78,7 +78,6 @@ import io.fabric8.kubernetes.client.dsl.NamespaceableResource; import io.fabric8.kubernetes.client.dsl.NetworkAPIGroupDSL; import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation; -import io.fabric8.kubernetes.client.dsl.ParameterNamespaceListVisitFromServerGetDeleteRecreateWaitApplicable; import io.fabric8.kubernetes.client.dsl.PodResource; import io.fabric8.kubernetes.client.dsl.PolicyAPIGroupDSL; import io.fabric8.kubernetes.client.dsl.RbacAPIGroupDSL; @@ -255,12 +254,12 @@ public NonNamespaceOperation load(InputStream is) { + public NamespaceListVisitFromServerGetDeleteRecreateWaitApplicable load(InputStream is) { return getClient().load(is); } @Override - public ParameterNamespaceListVisitFromServerGetDeleteRecreateWaitApplicable resourceList(String s) { + public NamespaceListVisitFromServerGetDeleteRecreateWaitApplicable resourceList(String s) { return getClient().resourceList(s); } diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extended/leaderelection/resourcelock/ConfigMapLock.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extended/leaderelection/resourcelock/ConfigMapLock.java index 0395ea26d69..576b262b339 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extended/leaderelection/resourcelock/ConfigMapLock.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/extended/leaderelection/resourcelock/ConfigMapLock.java @@ -15,13 +15,11 @@ */ package io.fabric8.kubernetes.client.extended.leaderelection.resourcelock; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ConfigMapBuilder; import io.fabric8.kubernetes.api.model.ObjectMeta; import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.KubernetesClientException; import io.fabric8.kubernetes.client.utils.Serialization; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,13 +34,11 @@ public class ConfigMapLock implements Lock { private final String configMapNamespace; private final String configMapName; private final String identity; - private final ObjectMapper objectMapper; public ConfigMapLock(String configMapNamespace, String configMapName, String identity) { this.configMapNamespace = Objects.requireNonNull(configMapNamespace, "configMapNamespace is required"); this.configMapName = Objects.requireNonNull(configMapName, "configMapName is required"); this.identity = Objects.requireNonNull(identity, "identity is required"); - objectMapper = Serialization.jsonMapper(); } /** @@ -58,9 +54,8 @@ public LeaderElectionRecord get(KubernetesClient client) { .map(annotations -> annotations.get(LEADER_ELECTION_RECORD_ANNOTATION_KEY)) .map(annotation -> { try { - return objectMapper.readValue(annotation, new TypeReference() { - }); - } catch (JsonProcessingException ex) { + return Serialization.unmarshal(annotation, LeaderElectionRecord.class); + } catch (KubernetesClientException ex) { LOGGER.error("Error deserializing LeaderElectionRecord from ConfigMap", ex); return null; } @@ -80,11 +75,11 @@ public void create( KubernetesClient client, LeaderElectionRecord leaderElectionRecord) throws LockException { try { - client.configMaps().inNamespace(configMapNamespace).withName(configMapName).create(new ConfigMapBuilder() + client.configMaps().inNamespace(configMapNamespace).resource(new ConfigMapBuilder() .editOrNewMetadata().withNamespace(configMapNamespace).withName(configMapName) - .addToAnnotations(LEADER_ELECTION_RECORD_ANNOTATION_KEY, objectMapper.writeValueAsString(leaderElectionRecord)) + .addToAnnotations(LEADER_ELECTION_RECORD_ANNOTATION_KEY, Serialization.asJson(leaderElectionRecord)) .endMetadata() - .build()); + .build()).create(); } catch (Exception e) { throw new LockException("Unable to create ConfigMapLock", e); } @@ -100,11 +95,11 @@ public void update( try { final ConfigMap toReplace = client.configMaps().inNamespace(configMapNamespace).withName(configMapName).get(); toReplace.getMetadata().getAnnotations() - .put(LEADER_ELECTION_RECORD_ANNOTATION_KEY, objectMapper.writeValueAsString(leaderElectionRecord)); + .put(LEADER_ELECTION_RECORD_ANNOTATION_KEY, Serialization.asJson(leaderElectionRecord)); // Use replace instead of edit to avoid concurrent modifications, resourceVersion is locked to original record version - client.configMaps().inNamespace(configMapNamespace).withName(configMapName) + client.configMaps().inNamespace(configMapNamespace).resource(toReplace) .lockResourceVersion((String) Objects.requireNonNull(leaderElectionRecord.getVersion())) - .replace(toReplace); + .replace(); } catch (Exception e) { throw new LockException("Unable to update ConfigMapLock", e); } diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/informers/cache/ReducedStateItemStore.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/informers/cache/ReducedStateItemStore.java index 951c1438bee..31053b06c34 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/informers/cache/ReducedStateItemStore.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/informers/cache/ReducedStateItemStore.java @@ -18,6 +18,7 @@ import io.fabric8.kubernetes.api.model.GenericKubernetesResource; import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.client.utils.KubernetesSerialization; import io.fabric8.kubernetes.client.utils.Serialization; import java.util.ArrayList; @@ -42,6 +43,7 @@ public class ReducedStateItemStore implements ItemStore fields = new ArrayList<>(); private final Class typeClass; private final KeyState keyState; + private KubernetesSerialization serialization = Serialization.getKubernetesSerialization(); public static class KeyState { @@ -101,7 +103,8 @@ public KeyState(Function keyFunction, Function typeClass, String... valueFields) { + public ReducedStateItemStore(KeyState keyState, Class typeClass, + String... valueFields) { this.keyState = keyState; fields.add(new String[] { METADATA, "resourceVersion" }); if (valueFields != null) { @@ -110,13 +113,14 @@ public ReducedStateItemStore(KeyState keyState, Class typeClass, String... va } } this.typeClass = typeClass; + this.serialization = serialization; } Object[] store(V value) { if (value == null) { return null; } - Map raw = Serialization.jsonMapper().convertValue(value, Map.class); + Map raw = serialization.convertValue(value, Map.class); return fields.stream().map(f -> GenericKubernetesResource.get(raw, (Object[]) f)).toArray(); } @@ -129,7 +133,7 @@ V restore(String key, Object[] values) { String[] keyParts = this.keyState.keyFieldFunction.apply(key); applyFields(keyParts, raw, this.keyState.keyFields); - return Serialization.jsonMapper().convertValue(raw, typeClass); + return serialization.convertValue(raw, typeClass); } private static void applyFields(Object[] values, Map raw, List fields) { diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/internal/KubeConfigUtils.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/internal/KubeConfigUtils.java index 7d5a53383f9..a788beadc41 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/internal/KubeConfigUtils.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/internal/KubeConfigUtils.java @@ -15,7 +15,6 @@ */ package io.fabric8.kubernetes.client.internal; -import com.fasterxml.jackson.databind.ObjectMapper; import io.fabric8.kubernetes.api.model.AuthInfo; import io.fabric8.kubernetes.api.model.Cluster; import io.fabric8.kubernetes.api.model.Config; @@ -26,6 +25,8 @@ import io.fabric8.kubernetes.client.utils.Serialization; import java.io.File; +import java.io.FileInputStream; +import java.io.FileWriter; import java.io.IOException; import java.util.List; @@ -39,13 +40,11 @@ private KubeConfigUtils() { } public static Config parseConfig(File file) throws IOException { - ObjectMapper mapper = Serialization.yamlMapper(); - return mapper.readValue(file, Config.class); + return Serialization.unmarshal(new FileInputStream(file), Config.class); } public static Config parseConfigFromString(String contents) throws IOException { - ObjectMapper mapper = Serialization.yamlMapper(); - return mapper.readValue(contents, Config.class); + return Serialization.unmarshal(contents, Config.class); } /** @@ -158,6 +157,8 @@ public static int getNamedUserIndexFromConfig(Config config, String userName) { * @throws IOException in case of failure while writing to file */ public static void persistKubeConfigIntoFile(Config kubeConfig, String kubeConfigPath) throws IOException { - Serialization.yamlMapper().writeValue(new File(kubeConfigPath), kubeConfig); + try (FileWriter writer = new FileWriter(kubeConfigPath)) { + writer.write(Serialization.asYaml(kubeConfig)); + } } } diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/IOHelpers.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/IOHelpers.java index e51978245ad..cbecace0dce 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/IOHelpers.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/IOHelpers.java @@ -64,6 +64,10 @@ private static void copy(Reader reader, Writer writer) throws IOException { } } + /** + * @deprecated to be removed in future versions + */ + @Deprecated public static boolean isJSONValid(String json) { try { ObjectMapper objectMapper = Serialization.jsonMapper(); @@ -74,18 +78,15 @@ public static boolean isJSONValid(String json) { return true; } + /** + * @deprecated to be removed in future versions + */ + @Deprecated public static String convertYamlToJson(String yaml) throws IOException { - ObjectMapper yamlReader = Serialization.yamlMapper(); - Object obj = yamlReader.readValue(yaml, Object.class); - - ObjectMapper jsonWriter = Serialization.jsonMapper(); - return jsonWriter.writeValueAsString(obj); + return Serialization.asJson(Serialization.unmarshal(yaml, Object.class)); } public static String convertToJson(String jsonOrYaml) throws IOException { - if (isJSONValid(jsonOrYaml)) { - return jsonOrYaml; - } return convertYamlToJson(jsonOrYaml); } diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/KubernetesResourceUtil.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/KubernetesResourceUtil.java index 554af3c3d6a..d7e0691c3f7 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/KubernetesResourceUtil.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/KubernetesResourceUtil.java @@ -383,7 +383,7 @@ public static List convertMapToEnvVarList(Map envVarMap) * Check whether a Kubernetes resource is Ready or not. Applicable only to * Deployment, ReplicaSet, Pod, ReplicationController, Endpoints, Node and * StatefulSet - * + * * @param item item which needs to be checked * @return boolean value indicating it's status */ @@ -393,7 +393,7 @@ public static boolean isResourceReady(HasMetadata item) { /** * Calculates age of a kubernetes resource - * + * * @param kubernetesResource * @return a positive duration indicating age of the kubernetes resource */ @@ -435,7 +435,7 @@ private static Class loadRelated(Class type, String suffix, Class defau public static Secret createDockerRegistrySecret(String dockerServer, String username, String password) throws JsonProcessingException { Map dockerConfigMap = createDockerRegistryConfigMap(dockerServer, username, password); - String dockerConfigAsStr = Serialization.jsonMapper().writeValueAsString(dockerConfigMap); + String dockerConfigAsStr = Serialization.asJson(dockerConfigMap); return createDockerSecret(DEFAULT_CONTAINER_IMAGE_REGISTRY_SECRET_NAME, dockerConfigAsStr); } @@ -452,7 +452,7 @@ public static Secret createDockerRegistrySecret(String dockerServer, String user public static Secret createDockerRegistrySecret(String dockerServer, String username, String password, String secretName) throws JsonProcessingException { Map dockerConfigMap = createDockerRegistryConfigMap(dockerServer, username, password); - String dockerConfigAsStr = Serialization.jsonMapper().writeValueAsString(dockerConfigMap); + String dockerConfigAsStr = Serialization.asJson(dockerConfigMap); return createDockerSecret(secretName, dockerConfigAsStr); } diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/KubernetesSerialization.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/KubernetesSerialization.java new file mode 100644 index 00000000000..5393d3592b3 --- /dev/null +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/KubernetesSerialization.java @@ -0,0 +1,391 @@ +/** + * Copyright (C) 2015 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fabric8.kubernetes.client.utils; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationConfig; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.KeyDeserializer; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationConfig; +import com.fasterxml.jackson.databind.cfg.HandlerInstantiator; +import com.fasterxml.jackson.databind.cfg.MapperConfig; +import com.fasterxml.jackson.databind.introspect.Annotated; +import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; +import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import io.fabric8.kubernetes.api.model.KubernetesResource; +import io.fabric8.kubernetes.api.model.runtime.RawExtension; +import io.fabric8.kubernetes.client.KubernetesClientException; +import io.fabric8.kubernetes.internal.KubernetesDeserializer; +import io.fabric8.kubernetes.model.jackson.UnmatchedFieldTypeModule; +import org.snakeyaml.engine.v2.api.Dump; +import org.snakeyaml.engine.v2.api.DumpSettings; +import org.snakeyaml.engine.v2.api.Load; +import org.snakeyaml.engine.v2.api.LoadSettings; +import org.snakeyaml.engine.v2.common.FlowStyle; +import org.snakeyaml.engine.v2.common.ScalarStyle; +import org.snakeyaml.engine.v2.nodes.Node; +import org.snakeyaml.engine.v2.nodes.NodeTuple; +import org.snakeyaml.engine.v2.nodes.ScalarNode; +import org.snakeyaml.engine.v2.nodes.Tag; +import org.snakeyaml.engine.v2.representer.StandardRepresenter; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +public class KubernetesSerialization { + + private final ObjectMapper mapper; + private final UnmatchedFieldTypeModule unmatchedFieldTypeModule = new UnmatchedFieldTypeModule(); + private final KubernetesDeserializer kubernetesDeserializer = new KubernetesDeserializer(); + + public KubernetesSerialization(ObjectMapper mapper) { + this.mapper = mapper; + mapper.registerModules(new JavaTimeModule(), unmatchedFieldTypeModule); + mapper.disable(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE); + HandlerInstantiator instanciator = mapper.getDeserializationConfig().getHandlerInstantiator(); + mapper.setConfig(mapper.getDeserializationConfig().with(new HandlerInstantiator() { + + @Override + public JsonDeserializer deserializerInstance(DeserializationConfig config, Annotated annotated, Class deserClass) { + if (deserClass == KubernetesDeserializer.class) { + return kubernetesDeserializer; + } + if (instanciator == null) { + return null; + } + return instanciator.deserializerInstance(config, annotated, deserClass); + } + + @Override + public KeyDeserializer keyDeserializerInstance(DeserializationConfig config, Annotated annotated, + Class keyDeserClass) { + if (instanciator == null) { + return null; + } + return instanciator.keyDeserializerInstance(config, annotated, keyDeserClass); + } + + @Override + public JsonSerializer serializerInstance(SerializationConfig config, Annotated annotated, Class serClass) { + if (instanciator == null) { + return null; + } + return instanciator.serializerInstance(config, annotated, serClass); + } + + @Override + public TypeResolverBuilder typeResolverBuilderInstance(MapperConfig config, Annotated annotated, + Class builderClass) { + if (instanciator == null) { + return null; + } + return instanciator.typeResolverBuilderInstance(config, annotated, builderClass); + } + + @Override + public TypeIdResolver typeIdResolverInstance(MapperConfig config, Annotated annotated, Class resolverClass) { + if (instanciator == null) { + return null; + } + return instanciator.typeIdResolverInstance(config, annotated, resolverClass); + } + + })); + } + + private static final String DOCUMENT_DELIMITER = "---"; + + /** + * Returns a JSON representation of the given object. + * + *

+ * If the provided object contains a JsonAnyGetter annotated method with a Map that contains an entry that + * overrides a field of the provided object, the Map entry will take precedence upon serialization. Properties won't + * be duplicated. + * + * @param object the object to serialize. + * @param the type of the object being serialized. + * @return a String containing a JSON representation of the provided object. + */ + public String asJson(T object) { + try { + return mapper.writeValueAsString(object); + } catch (JsonProcessingException e) { + throw KubernetesClientException.launderThrowable(e); + } + } + + /** + * Returns a YAML representation of the given object. + * + *

+ * If the provided object contains a JsonAnyGetter annotated method with a Map that contains an entry that + * overrides a field of the provided object, the Map entry will take precedence upon serialization. Properties won't + * be duplicated. + * + * @param object the object to serialize. + * @param the type of the object being serialized. + * @return a String containing a JSON representation of the provided object. + */ + public String asYaml(T object) { + DumpSettings settings = DumpSettings.builder() + .setExplicitStart(true).setDefaultFlowStyle(FlowStyle.BLOCK).build(); + final Dump yaml = new Dump(settings, new StandardRepresenter(settings) { + private boolean quote = true; + + @Override + protected NodeTuple representMappingEntry(Entry entry) { + Object key = entry.getKey(); + if (key instanceof String) { + quote = false; + } + Node nodeKey = representData(key); + quote = true; + return new NodeTuple(nodeKey, representData(entry.getValue())); + } + + @Override + protected Node representScalar(Tag tag, String value, ScalarStyle style) { + if (style == ScalarStyle.PLAIN) { + style = quote && tag == Tag.STR ? ScalarStyle.DOUBLE_QUOTED : this.defaultScalarStyle; + } + return new ScalarNode(tag, value, style); + } + }); + try { + return yaml.dumpToString(mapper.convertValue(object, Map.class)); + } catch (IllegalArgumentException e) { + // a scalar or an array - requires a double conversion + return yaml.dumpToString(mapper.convertValue(mapper.convertValue(object, JsonNode.class), Object.class)); + } + } + + /** + * Unmarshals a stream. + *

+ * The type is assumed to be {@link KubernetesResource}, but will also look for multiple + * yaml documents in the same file. That result will be returned as List of {@link KubernetesResource}. + * + * @param is The {@link InputStream}. + * @param The target type. + * + * @return returns de-serialized object + */ + public T unmarshal(InputStream is) { + String specFile; + try { + specFile = IOHelpers.readFully(is); + } catch (IOException e1) { + throw new RuntimeException("Could not read stream"); + } + if (containsMultipleDocuments(specFile)) { + return (T) getKubernetesResourceList(Collections.emptyMap(), specFile); + } else if (specFile.contains(DOCUMENT_DELIMITER)) { + specFile = specFile.replaceAll("^---([ \\t].*?)?\\r?\\n", ""); + specFile = specFile.replaceAll("\\n---([ \\t].*?)?\\r?\\n?$", "\n"); + } + + return unmarshal(new ByteArrayInputStream(specFile.getBytes(StandardCharsets.UTF_8)), new TypeReference() { + @Override + public Type getType() { + return KubernetesResource.class; + } + }); + } + + public T unmarshal(InputStream is, TypeReference type) { + try (BufferedInputStream bis = new BufferedInputStream(is)) { + bis.mark(-1); + int intch; + do { + intch = bis.read(); + } while (intch > -1 && Character.isWhitespace(intch)); + bis.reset(); + + final T result; + if (intch != '{' && intch != '[') { + try { + final Load yaml = new Load(LoadSettings.builder().build()); + final Object obj = yaml.loadFromInputStream(bis); + if (obj instanceof Map) { + result = mapper.convertValue(obj, type); + } else { + result = mapper.convertValue(new RawExtension(obj), type); + } + } catch (IllegalArgumentException e) { + throw new KubernetesClientException("Could not convert from yaml to requested type", e); + } + } else { + result = mapper.readerFor(type).readValue(bis); + } + return result; + } catch (IOException e) { + throw KubernetesClientException.launderThrowable(e); + } + } + + /** + * Unmarshals a {@link String} + *

+ * The type is assumed to be {@link KubernetesResource} + * + * @param str The {@link String}. + * @param template argument denoting type + * @return returns de-serialized object + */ + public T unmarshal(String str) { + return (T) unmarshal(str, KubernetesResource.class); + } + + /** + * Unmarshals a {@link String} + * + * @param str The {@link String}. + * @param type The target type. + * @param template argument denoting type + * @return returns de-serialized object + */ + public T unmarshal(String str, final Class type) { + try (InputStream is = new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8))) { + return unmarshal(is, type); + } catch (IOException e) { + throw KubernetesClientException.launderThrowable(e); + } + } + + /** + * Unmarshals an {@link InputStream}. + * + * @param is The {@link InputStream}. + * @param type The type. + * @param Template argument denoting type + * @return returns de-serialized object + */ + public T unmarshal(InputStream is, final Class type) { + return unmarshal(is, new TypeReference() { + @Override + public Type getType() { + return type; + } + }); + } + + List getKubernetesResourceList(Map parameters, String specFile) { + return splitSpecFile(specFile).stream().filter(KubernetesSerialization::validate) + .map( + document -> (KubernetesResource) unmarshal(document)) + .collect(Collectors.toList()); + } + + static boolean containsMultipleDocuments(String specFile) { + final long validDocumentCount = splitSpecFile(specFile).stream().filter(KubernetesSerialization::validate) + .count(); + return validDocumentCount > 1; + } + + static List splitSpecFile(String aSpecFile) { + final List documents = new ArrayList<>(); + final StringBuilder documentBuilder = new StringBuilder(); + for (String line : aSpecFile.split("\r?\n")) { + if (line.startsWith(DOCUMENT_DELIMITER)) { + documents.add(documentBuilder.toString()); + documentBuilder.setLength(0); + } else { + documentBuilder.append(line).append(System.lineSeparator()); + } + } + if (documentBuilder.length() > 0) { + documents.add(documentBuilder.toString()); + } + return documents; + } + + private static boolean validate(String document) { + Matcher keyValueMatcher = Pattern.compile("(\\S+):\\s(\\S*)(?:\\b(?!:)|$)").matcher(document); + return !document.isEmpty() && keyValueMatcher.find(); + } + + /** + * Create a copy of the resource via serialization. + * + * @return a deep clone of the resource + * @throws IllegalArgumentException if the cloning cannot be performed + */ + public T clone(T resource) { + // if full serialization seems too expensive, there is also + //return (T) JSON_MAPPER.convertValue(resource, resource.getClass()); + try { + return (T) mapper.readValue( + mapper.writeValueAsString(resource), resource.getClass()); + } catch (JsonProcessingException e) { + throw new IllegalStateException(e); + } + } + + public T convertValue(Object value, Class type) { + return mapper.convertValue(value, type); + } + + public Type constructParametricType(Class parameterizedClass, Class parameterType) { + return mapper.getTypeFactory().constructParametricType(parameterizedClass, parameterType); + } + + public Class getRegisteredKind(String apiVersion, String kind) { + return this.kubernetesDeserializer.getRegisteredKind(apiVersion, kind); + } + + public UnmatchedFieldTypeModule getUnmatchedFieldTypeModule() { + return unmatchedFieldTypeModule; + } + + ObjectMapper getMapper() { + return mapper; + } + + /** + * Registers a Custom Resource Definition Kind + */ + public void registerCustomKind(String kind, Class clazz) { + registerCustomKind(null, kind, clazz); + } + + /** + * Registers a Custom Resource Definition Kind + */ + public void registerCustomKind(String apiVersion, String kind, Class clazz) { + kubernetesDeserializer.registerCustomKind(apiVersion, kind, clazz); + } + +} diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/OpenIDConnectionUtils.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/OpenIDConnectionUtils.java index b086bd4c607..2c4adc9ed45 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/OpenIDConnectionUtils.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/OpenIDConnectionUtils.java @@ -263,7 +263,7 @@ public static boolean persistKubeConfigWithUpdatedAuthInfo(Config currentConfig, } private static Map convertJsonStringToMap(String jsonString) throws JsonProcessingException { - return Serialization.jsonMapper().readValue(jsonString, Map.class); + return Serialization.unmarshal(jsonString, Map.class); } private static HttpClient getDefaultHttpClientWithPemCert(String idpCert, HttpClient.Builder clientBuilder) { diff --git a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/Serialization.java b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/Serialization.java index 92499eb4eb7..5c432d4dd88 100644 --- a/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/Serialization.java +++ b/kubernetes-client-api/src/main/java/io/fabric8/kubernetes/client/utils/Serialization.java @@ -15,13 +15,10 @@ */ package io.fabric8.kubernetes.client.utils; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import io.fabric8.kubernetes.api.model.KubernetesResource; import io.fabric8.kubernetes.api.model.runtime.RawExtension; import io.fabric8.kubernetes.client.KubernetesClientException; @@ -35,25 +32,18 @@ import java.io.InputStream; import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; import java.util.Collections; -import java.util.List; import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; public class Serialization { private Serialization() { } - public static final UnmatchedFieldTypeModule UNMATCHED_FIELD_TYPE_MODULE = new UnmatchedFieldTypeModule(); + private static KubernetesSerialization kubernetesSerialization = new KubernetesSerialization(new ObjectMapper()); + public static final UnmatchedFieldTypeModule UNMATCHED_FIELD_TYPE_MODULE = kubernetesSerialization + .getUnmatchedFieldTypeModule(); - private static final ObjectMapper JSON_MAPPER = new ObjectMapper(); - static { - JSON_MAPPER.registerModules(new JavaTimeModule(), UNMATCHED_FIELD_TYPE_MODULE); - JSON_MAPPER.disable(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE); - } + private static final ObjectMapper JSON_MAPPER = kubernetesSerialization.getMapper(); private static volatile ObjectMapper YAML_MAPPER; @@ -71,7 +61,10 @@ private Serialization() { * n.b. the use of this module gives precedence to properties present in the additionalProperties Map present * in most KubernetesResource instances. If a property is both defined in the Map and in the original field, the * one from the additionalProperties Map will be serialized. + * + * @deprecated */ + @Deprecated public static ObjectMapper jsonMapper() { return JSON_MAPPER; } @@ -88,7 +81,10 @@ public static ObjectMapper jsonMapper() { * n.b. the use of this module gives precedence to properties present in the additionalProperties Map present * in most KubernetesResource instances. If a property is both defined in the Map and in the original field, the * one from the additionalProperties Map will be serialized. + * + * @deprecated will be removed in future versions */ + @Deprecated public static ObjectMapper yamlMapper() { if (YAML_MAPPER == null) { synchronized (Serialization.class) { @@ -107,7 +103,10 @@ public static ObjectMapper yamlMapper() { * This is useful because in a lot of cases the YAML mapper is only need at application startup * when the client is created, so there is no reason to keep the very heavy (in terms of memory) mapper * around indefinitely. + * + * @deprecated */ + @Deprecated public static void clearYamlMapper() { YAML_MAPPER = null; } @@ -125,11 +124,7 @@ public static void clearYamlMapper() { * @return a String containing a JSON representation of the provided object. */ public static String asJson(T object) { - try { - return JSON_MAPPER.writeValueAsString(object); - } catch (JsonProcessingException e) { - throw KubernetesClientException.launderThrowable(e); - } + return kubernetesSerialization.asJson(object); } /** @@ -145,11 +140,7 @@ public static String asJson(T object) { * @return a String containing a JSON representation of the provided object. */ public static String asYaml(T object) { - try { - return yamlMapper().writeValueAsString(object); - } catch (JsonProcessingException e) { - throw KubernetesClientException.launderThrowable(e); - } + return kubernetesSerialization.asYaml(object); } /** @@ -161,7 +152,10 @@ public static String asYaml(T object) { * @param The target type. * * @return returns de-serialized object + * + * @deprecated */ + @Deprecated public static T unmarshal(InputStream is) { return unmarshal(is, JSON_MAPPER); } @@ -193,7 +187,10 @@ public static T unmarshal(InputStream is, Map parameters) { * @param mapper The {@link ObjectMapper} to use. * @param The target type. * @return returns de-serialized object + * + * @deprecated to be removed in future versions */ + @Deprecated public static T unmarshal(InputStream is, ObjectMapper mapper) { return unmarshal(is, mapper, Collections.emptyMap()); } @@ -213,6 +210,7 @@ public static T unmarshal(InputStream is, ObjectMapper mapper) { */ @Deprecated public static T unmarshal(InputStream is, ObjectMapper mapper, Map parameters) { + // TODO: this defeats the purpose of accepting a stream... // it's not well documented which Serialization methods are aware of input that can contain // multiple docs String specFile; @@ -221,8 +219,8 @@ public static T unmarshal(InputStream is, ObjectMapper mapper, Map T unmarshal(InputStream is, ObjectMapper mapper, TypeReferenc final T result; if (intch != '{' && intch != '[') { - final Load yaml = new Load(LoadSettings.builder().build()); - final Object obj = yaml.loadFromInputStream(bis); - if (obj instanceof Map) { - result = mapper.convertValue(obj, type); - } else { - result = mapper.convertValue(new RawExtension(obj), type); + try { + final Load yaml = new Load(LoadSettings.builder().build()); + final Object obj = yaml.loadFromInputStream(bis); + if (obj instanceof Map) { + result = mapper.convertValue(obj, type); + } else { + result = mapper.convertValue(new RawExtension(obj), type); + } + } catch (IllegalArgumentException e) { + throw new KubernetesClientException("Could not convert from yaml to requested type", e); } } else { result = mapper.readerFor(type).readValue(bis); @@ -273,7 +275,10 @@ private static T unmarshal(InputStream is, ObjectMapper mapper, TypeReferenc * @param str The {@link String}. * @param template argument denoting type * @return returns de-serialized object + * + * @deprecated */ + @Deprecated public static T unmarshal(String str) { try (InputStream is = new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8))) { return unmarshal(is); @@ -382,42 +387,6 @@ public static T unmarshal(InputStream is, TypeReference type, Map getKubernetesResourceList(Map parameters, String specFile) { - return splitSpecFile(specFile).stream().filter(Serialization::validate) - .map( - document -> (KubernetesResource) Serialization.unmarshal(new ByteArrayInputStream(document.getBytes()), parameters)) - .filter(o -> o != null) - .collect(Collectors.toList()); - } - - static boolean containsMultipleDocuments(String specFile) { - final long validDocumentCount = splitSpecFile(specFile).stream().filter(Serialization::validate) - .count(); - return validDocumentCount > 1; - } - - private static List splitSpecFile(String aSpecFile) { - final List documents = new ArrayList<>(); - final StringBuilder documentBuilder = new StringBuilder(); - for (String line : aSpecFile.split("\r?\n")) { - if (line.startsWith(DOCUMENT_DELIMITER)) { - documents.add(documentBuilder.toString()); - documentBuilder.setLength(0); - } else { - documentBuilder.append(line).append(System.lineSeparator()); - } - } - if (documentBuilder.length() > 0) { - documents.add(documentBuilder.toString()); - } - return documents; - } - - private static boolean validate(String document) { - Matcher keyValueMatcher = Pattern.compile("(\\S+):\\s(\\S*)(?:\\b(?!:)|$)").matcher(document); - return !document.isEmpty() && keyValueMatcher.find(); - } - /** * Create a copy of the resource via serialization. * @@ -425,13 +394,16 @@ private static boolean validate(String document) { * @throws IllegalArgumentException if the cloning cannot be performed */ public static T clone(T resource) { - // if full serialization seems too expensive, there is also - //return (T) JSON_MAPPER.convertValue(resource, resource.getClass()); - try { - return (T) JSON_MAPPER.readValue( - JSON_MAPPER.writeValueAsString(resource), resource.getClass()); - } catch (JsonProcessingException e) { - throw new IllegalStateException(e); - } + return kubernetesSerialization.clone(resource); + } + + /** + * + * @deprecated + */ + @Deprecated + public static KubernetesSerialization getKubernetesSerialization() { + return kubernetesSerialization; } + } diff --git a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/extended/leaderelection/resourcelock/ConfigMapLockTest.java b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/extended/leaderelection/resourcelock/ConfigMapLockTest.java index a6f2bb8bc47..31848cbcde6 100644 --- a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/extended/leaderelection/resourcelock/ConfigMapLockTest.java +++ b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/extended/leaderelection/resourcelock/ConfigMapLockTest.java @@ -21,13 +21,11 @@ import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.dsl.MixedOperation; -import io.fabric8.kubernetes.client.dsl.ReplaceDeletable; import io.fabric8.kubernetes.client.dsl.Resource; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.function.Executable; -import org.mockito.Answers; import org.mockito.ArgumentMatchers; import org.mockito.Mockito; @@ -42,7 +40,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -128,15 +125,13 @@ void createWithValidLeaderElectionRecordShouldSendPostRequest() throws Exception // When lock.create(kc, record); // Then - verify(configMaps.withName("name"), times(1)).create(any(ConfigMap.class)); + verify(configMaps.resource(any(ConfigMap.class)), times(1)).create(); } @Test void updateWithValidLeaderElectionRecordShouldSendPutRequest() throws Exception { // Given final Resource configMapResource = configMaps.withName("name"); - final ReplaceDeletable replaceable = mock(ReplaceDeletable.class, Answers.RETURNS_DEEP_STUBS); - when(configMapResource.lockResourceVersion(any())).thenReturn(replaceable); final ConfigMap configMapInTheCluster = new ConfigMap(); configMapInTheCluster.setMetadata(new ObjectMetaBuilder().withAnnotations(new HashMap<>()).build()); when(configMapResource.get()).thenReturn(configMapInTheCluster); @@ -144,10 +139,11 @@ void updateWithValidLeaderElectionRecordShouldSendPutRequest() throws Exception "1337", Duration.ofSeconds(1), ZonedDateTime.now(), ZonedDateTime.now(), 0); record.setVersion("313373"); final ConfigMapLock lock = new ConfigMapLock("namespace", "name", "1337"); + // When lock.update(kc, record); // Then - verify(replaceable, times(1)).replace(eq(configMapInTheCluster)); + verify(configMaps.resource(any()).lockResourceVersion("313373")).replace(); } @Test diff --git a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/informers/cache/ReducedStateItemStoreTest.java b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/informers/cache/ReducedStateItemStoreTest.java index 2e73bca1977..7aef8a1f360 100644 --- a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/informers/cache/ReducedStateItemStoreTest.java +++ b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/informers/cache/ReducedStateItemStoreTest.java @@ -27,8 +27,8 @@ class ReducedStateItemStoreTest { @Test void testStoreRestore() { - ReducedStateItemStore store = new ReducedStateItemStore<>(ReducedStateItemStore.UID_KEY_STATE, Pod.class, - "metadata.labels", "foo.bar"); + ReducedStateItemStore store = new ReducedStateItemStore<>(ReducedStateItemStore.UID_KEY_STATE, + Pod.class, "metadata.labels", "foo.bar"); Pod pod = new PodBuilder().withNewSpec().endSpec().withNewMetadata().withUid("x").withName("y").addToLabels("one", "1") .addToLabels("two", "2").withResourceVersion("2").endMetadata().withNewStatus().endStatus().build(); diff --git a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/utils/SerializationTest.java b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/utils/SerializationTest.java index e604755000e..a2f18a69317 100644 --- a/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/utils/SerializationTest.java +++ b/kubernetes-client-api/src/test/java/io/fabric8/kubernetes/client/utils/SerializationTest.java @@ -72,7 +72,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.junit.jupiter.api.Assertions.assertEquals; class SerializationTest { @@ -138,7 +137,7 @@ void containsMultipleDocumentsWithMultipleDocumentsAndWindowsLineEnds() { // Given final String multiDocument = "---\r\napiVersion: v1\r\nKind: Something\r\n\r\n---\r\napiVersion: v2\r\nKind: Other"; // When - final boolean result = Serialization.containsMultipleDocuments(multiDocument); + final boolean result = KubernetesSerialization.containsMultipleDocuments(multiDocument); // Then assertThat(result).isTrue(); } @@ -149,7 +148,7 @@ void containsMultipleDocumentsWithSingleDocumentAndWindowsLineEnds() { // Given final String multiDocument = "---\r\napiVersion: v1\r\nKind: Something\r\n\r\n"; // When - final boolean result = Serialization.containsMultipleDocuments(multiDocument); + final boolean result = KubernetesSerialization.containsMultipleDocuments(multiDocument); // Then assertThat(result).isFalse(); } @@ -160,7 +159,7 @@ void containsMultipleDocumentsWithMultipleDocumentsAndLinuxLineEnds() { // Given final String multiDocument = "---\napiVersion: v1\nKind: Something\n\n---\napiVersion: v2\nKind: Other"; // When - final boolean result = Serialization.containsMultipleDocuments(multiDocument); + final boolean result = KubernetesSerialization.containsMultipleDocuments(multiDocument); // Then assertThat(result).isTrue(); } @@ -171,7 +170,7 @@ void containsMultipleDocumentsWithSingleDocumentAndLinuxLineEnds() { // Given final String multiDocument = "---\napiVersion: v1\nKind: Something\n\n"; // When - final boolean result = Serialization.containsMultipleDocuments(multiDocument); + final boolean result = KubernetesSerialization.containsMultipleDocuments(multiDocument); // Then assertThat(result).isFalse(); } @@ -253,8 +252,10 @@ void unmarshalWithInvalidYamlShouldReturnRawExtension() { @Test void unmarshalYamlArrayShouldThrowException() { - assertThatIllegalArgumentException() + assertThatExceptionOfType(KubernetesClientException.class) .isThrownBy(() -> Serialization.unmarshal("- 1\n- 2")) + .withMessage("Could not convert from yaml to requested type") + .havingCause() .withMessageStartingWith("Cannot parse a nested array containing non-object resource"); } diff --git a/kubernetes-client-api/src/test/resources/serialization/test-crd-schema.yml b/kubernetes-client-api/src/test/resources/serialization/test-crd-schema.yml index 93d641104bc..a9c3f72b41a 100644 --- a/kubernetes-client-api/src/test/resources/serialization/test-crd-schema.yml +++ b/kubernetes-client-api/src/test/resources/serialization/test-crd-schema.yml @@ -49,7 +49,7 @@ spec: maximum: 100.0 minimum: 0.0 type: "integer" - "y": + y: default: 10 maximum: 100.0 minimum: 0.0 diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/AbstractWatchManager.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/AbstractWatchManager.java index e806a06f5f3..63537bb2d49 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/AbstractWatchManager.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/AbstractWatchManager.java @@ -30,7 +30,7 @@ import io.fabric8.kubernetes.client.WatcherException; import io.fabric8.kubernetes.client.http.HttpClient; import io.fabric8.kubernetes.client.utils.ExponentialBackoffIntervalCalculator; -import io.fabric8.kubernetes.client.utils.Serialization; +import io.fabric8.kubernetes.client.utils.KubernetesSerialization; import io.fabric8.kubernetes.client.utils.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -183,7 +183,7 @@ void eventReceived(Watcher.Action action, HasMetadata resource) { // the WatchEvent deserialization is not specifically typed // modify the type here if needed if (resource != null && !baseOperation.getType().isAssignableFrom(resource.getClass())) { - resource = Serialization.jsonMapper().convertValue(resource, baseOperation.getType()); + resource = this.baseOperation.getKubernetesSerialization().convertValue(resource, baseOperation.getType()); } @SuppressWarnings("unchecked") final T t = (T) resource; @@ -231,17 +231,20 @@ public void close() { private WatchEvent contextAwareWatchEventDeserializer(String messageSource) throws JsonProcessingException { + KubernetesSerialization kubernetesSerialization = this.baseOperation.getKubernetesSerialization(); try { - return Serialization.unmarshal(messageSource, WatchEvent.class); + return kubernetesSerialization.unmarshal(messageSource, WatchEvent.class); } catch (Exception ex1) { - JsonNode json = Serialization.jsonMapper().readTree(messageSource); + // TODO: this is not necessarily correct - it will force the object to be the expected type + // even though it is not (for example Status could be converted to the typed result) + JsonNode json = kubernetesSerialization.unmarshal(messageSource, JsonNode.class); JsonNode objectJson = null; if (json instanceof ObjectNode && json.has("object")) { objectJson = ((ObjectNode) json).remove("object"); } - WatchEvent watchEvent = Serialization.jsonMapper().treeToValue(json, WatchEvent.class); - KubernetesResource object = Serialization.jsonMapper().treeToValue(objectJson, baseOperation.getType()); + WatchEvent watchEvent = kubernetesSerialization.convertValue(json, WatchEvent.class); + KubernetesResource object = kubernetesSerialization.convertValue(objectJson, baseOperation.getType()); watchEvent.setObject(object); return watchEvent; diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/BaseOperation.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/BaseOperation.java index 6b20fc745f8..68d6e3598cd 100755 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/BaseOperation.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/BaseOperation.java @@ -57,7 +57,6 @@ import io.fabric8.kubernetes.client.readiness.Readiness; import io.fabric8.kubernetes.client.utils.ApiVersionUtil; import io.fabric8.kubernetes.client.utils.KubernetesResourceUtil; -import io.fabric8.kubernetes.client.utils.Serialization; import io.fabric8.kubernetes.client.utils.URLUtils; import io.fabric8.kubernetes.client.utils.URLUtils.URLBuilder; import io.fabric8.kubernetes.client.utils.Utils; @@ -164,7 +163,7 @@ public T require() { */ public T getItemOrRequireFromServer() { if (item != null) { - return Serialization.clone(item); + return getKubernetesSerialization().clone(item); } return requireFromServer(); } @@ -258,7 +257,7 @@ public BaseOperation inAnyNamespace() { @Override public R load(InputStream is) { - T unmarshal = unmarshal(is, type); + T unmarshal = this.getKubernetesSerialization().unmarshal(is, type); // if you do something like client.foo().v2().load(v1 resource) // it will parse as v2, but have a v1 apiVersion, so we need to // force the apiVersion @@ -305,7 +304,7 @@ public final T createOrReplace() { resource::create, resource::replace, m -> resource.waitUntilCondition(Objects::nonNull, 1, TimeUnit.SECONDS), - m -> resource.fromServer().get()); + m -> resource.fromServer().get(), this.getKubernetesSerialization()); return createOrReplaceHelper.createOrReplace(item); } @@ -403,7 +402,7 @@ public CompletableFuture submitList(ListOptions listOptions) { URL fetchListUrl = fetchListUrl(getNamespacedUrl(), defaultListOptions(listOptions, null)); HttpRequest.Builder requestBuilder = httpClient.newHttpRequestBuilder().url(fetchListUrl); Type refinedType = listType.equals(DefaultKubernetesResourceList.class) - ? Serialization.jsonMapper().getTypeFactory().constructParametricType(listType, type) + ? this.getKubernetesSerialization().constructParametricType(listType, type) : listType; TypeReference listTypeReference = new TypeReference() { @Override diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/ExecWebSocketListener.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/ExecWebSocketListener.java index 60bb79298ce..fb26adcecd0 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/ExecWebSocketListener.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/ExecWebSocketListener.java @@ -28,7 +28,7 @@ import io.fabric8.kubernetes.client.http.WebSocket; import io.fabric8.kubernetes.client.http.WebSocketHandshakeException; import io.fabric8.kubernetes.client.utils.InputStreamPumper; -import io.fabric8.kubernetes.client.utils.Serialization; +import io.fabric8.kubernetes.client.utils.KubernetesSerialization; import io.fabric8.kubernetes.client.utils.internal.SerialExecutor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -142,16 +142,14 @@ private void handle(ByteBuffer byteString, WebSocket webSocket) throws IOExcepti private final AtomicBoolean closed = new AtomicBoolean(false); private final CompletableFuture exitCode = new CompletableFuture<>(); private ObjectMapper objectMapper = new ObjectMapper(); + private KubernetesSerialization serialization; public static String toString(ByteBuffer buffer) { return StandardCharsets.UTF_8.decode(buffer).toString(); } - public ExecWebSocketListener(PodOperationContext context) { - this(context, Runnable::run); - } - - public ExecWebSocketListener(PodOperationContext context, Executor executor) { + public ExecWebSocketListener(PodOperationContext context, Executor executor, KubernetesSerialization serialization) { + this.serialization = serialization; this.listener = context.getExecListener(); Integer bufferSize = context.getBufferSize(); @@ -266,7 +264,7 @@ public void onError(WebSocket webSocket, Throwable t) { if (t instanceof WebSocketHandshakeException) { response = ((WebSocketHandshakeException) t).getResponse(); } - Status status = OperationSupport.createStatus(response); + Status status = OperationSupport.createStatus(response, serialization); status.setMessage(t.getMessage()); t = new KubernetesClientException(status); cleanUpOnce(); @@ -340,7 +338,7 @@ private void handleExitStatus(ByteBuffer bytes) { int code = -1; try { String stringValue = toString(bytes); - status = Serialization.unmarshal(stringValue, Status.class); + status = serialization.unmarshal(stringValue, Status.class); if (status != null) { code = parseExitCode(status); } diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/HasMetadataOperation.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/HasMetadataOperation.java index c53091ce74f..bc7301ac778 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/HasMetadataOperation.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/HasMetadataOperation.java @@ -26,7 +26,6 @@ import io.fabric8.kubernetes.client.dsl.base.PatchContext; import io.fabric8.kubernetes.client.dsl.base.PatchType; import io.fabric8.kubernetes.client.utils.KubernetesResourceUtil; -import io.fabric8.kubernetes.client.utils.Serialization; import java.io.IOException; import java.util.concurrent.TimeUnit; @@ -57,7 +56,7 @@ public T edit(UnaryOperator function) { } private T clone(T item) { - return Serialization.clone(item); + return this.getKubernetesSerialization().clone(item); } @Override diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/NamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableListImpl.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/NamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableListImpl.java index 329c10cf3b2..57b4b6861e5 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/NamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableListImpl.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/NamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableListImpl.java @@ -32,21 +32,17 @@ import io.fabric8.kubernetes.client.dsl.ListVisitFromServerWritable; import io.fabric8.kubernetes.client.dsl.NamespaceListVisitFromServerGetDeleteRecreateWaitApplicable; import io.fabric8.kubernetes.client.dsl.NamespaceableResource; -import io.fabric8.kubernetes.client.dsl.ParameterNamespaceListVisitFromServerGetDeleteRecreateWaitApplicable; import io.fabric8.kubernetes.client.dsl.Resource; import io.fabric8.kubernetes.client.dsl.Waitable; import io.fabric8.kubernetes.client.readiness.Readiness; -import io.fabric8.kubernetes.client.utils.Serialization; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -58,7 +54,7 @@ import java.util.stream.Stream; public class NamespaceVisitFromServerGetWatchDeleteRecreateWaitApplicableListImpl - implements ParameterNamespaceListVisitFromServerGetDeleteRecreateWaitApplicable, + implements NamespaceListVisitFromServerGetDeleteRecreateWaitApplicable, Waitable, HasMetadata> { private static final Logger LOGGER = LoggerFactory @@ -173,13 +169,6 @@ private static void logAsNotReady(Throwable t, HasMetadata meta) { LOGGER.debug("The error stack trace:", t); } - @Override - public NamespaceListVisitFromServerGetDeleteRecreateWaitApplicable withParameters( - Map parameters) { - Object item = Serialization.unmarshal((InputStream) context.getItem(), parameters); - return newInstance(context.withItem(item)); - } - @Override public ListVisitFromServerWritable dryRun(boolean isDryRun) { return newInstance(this.context.withDryRun(isDryRun)); diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/OperationSupport.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/OperationSupport.java index 921cd1bcb8e..28524c3da45 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/OperationSupport.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/OperationSupport.java @@ -16,7 +16,6 @@ package io.fabric8.kubernetes.client.dsl.internal; import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; import io.fabric8.kubernetes.api.model.DeleteOptions; import io.fabric8.kubernetes.api.model.DeletionPropagation; import io.fabric8.kubernetes.api.model.HasMetadata; @@ -36,8 +35,9 @@ import io.fabric8.kubernetes.client.http.HttpClient; import io.fabric8.kubernetes.client.http.HttpRequest; import io.fabric8.kubernetes.client.http.HttpResponse; +import io.fabric8.kubernetes.client.impl.BaseClient; import io.fabric8.kubernetes.client.utils.KubernetesResourceUtil; -import io.fabric8.kubernetes.client.utils.Serialization; +import io.fabric8.kubernetes.client.utils.KubernetesSerialization; import io.fabric8.kubernetes.client.utils.URLUtils; import io.fabric8.kubernetes.client.utils.Utils; import org.slf4j.Logger; @@ -45,7 +45,6 @@ import java.io.ByteArrayInputStream; import java.io.IOException; -import java.io.InputStream; import java.io.InterruptedIOException; import java.lang.reflect.Type; import java.net.HttpURLConnection; @@ -54,7 +53,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -70,7 +68,6 @@ public class OperationSupport { public static final String STRATEGIC_MERGE_JSON_PATCH = "application/strategic-merge-patch+json"; public static final String JSON_MERGE_PATCH = "application/merge-patch+json"; - protected static final ObjectMapper JSON_MAPPER = Serialization.jsonMapper(); private static final Logger LOG = LoggerFactory.getLogger(OperationSupport.class); private static final String CLIENT_STATUS_FLAG = "CLIENT_STATUS_FLAG"; @@ -258,7 +255,7 @@ protected T correctNamespace(T item) { String itemNs = KubernetesResourceUtil.getNamespace((HasMetadata) item); if (Utils.isNotNullOrEmpty(namespace) && Utils.isNotNullOrEmpty(itemNs) && !namespace.equals(itemNs)) { - item = Serialization.clone(item); + item = getKubernetesSerialization().clone(item); KubernetesResourceUtil.setNamespace((HasMetadata) item, namespace); } return item; @@ -323,7 +320,7 @@ protected KubernetesResource handleDelete(URL requestUrl, long gracePeriodSecond } HttpRequest.Builder requestBuilder = httpClient.newHttpRequestBuilder() - .delete(JSON, JSON_MAPPER.writeValueAsString(deleteOptions)).url(requestUrl); + .delete(JSON, getKubernetesSerialization().asJson(deleteOptions)).url(requestUrl); return handleResponse(requestBuilder, KubernetesResource.class); } @@ -343,7 +340,7 @@ protected KubernetesResource handleDelete(URL requestUrl, long gracePeriodSecond protected T handleCreate(I resource, Class outputType) throws InterruptedException, IOException { resource = correctNamespace(resource); HttpRequest.Builder requestBuilder = httpClient.newHttpRequestBuilder() - .post(JSON, JSON_MAPPER.writeValueAsString(resource)) + .post(JSON, getKubernetesSerialization().asJson(resource)) .url(getResourceURLForWriteOperation(getResourceUrl(checkNamespace(resource), null))); return handleResponse(requestBuilder, outputType); } @@ -362,7 +359,7 @@ protected T handleCreate(I resource, Class outputType) throws Interrup protected T handleUpdate(T updated, Class type, boolean status) throws IOException { updated = correctNamespace(updated); HttpRequest.Builder requestBuilder = httpClient.newHttpRequestBuilder() - .put(JSON, JSON_MAPPER.writeValueAsString(updated)) + .put(JSON, getKubernetesSerialization().asJson(updated)) .url(getResourceURLForWriteOperation(getResourceUrl(checkNamespace(updated), checkName(updated), status))); return handleResponse(requestBuilder, type); } @@ -405,7 +402,7 @@ protected T handlePatch(PatchContext patchContext, T current, T updated, Cla } } } - patchForUpdate = Serialization.asJson(updated); + patchForUpdate = getKubernetesSerialization().asJson(updated); current = updated; // use the updated to determine the path } return handlePatch(patchContext, current, patchForUpdate, type, status); @@ -446,7 +443,7 @@ protected T handlePatch(PatchContext patchContext, T current, String patchFo protected Scale handleScale(String resourceUrl, Scale scale) throws InterruptedException, IOException { HttpRequest.Builder requestBuilder = httpClient.newHttpRequestBuilder().uri(resourceUrl + "/scale"); if (scale != null) { - requestBuilder.put(JSON, JSON_MAPPER.writeValueAsString(scale)); + requestBuilder.put(JSON, getKubernetesSerialization().asJson(scale)); } return handleResponse(requestBuilder, Scale.class); } @@ -463,7 +460,7 @@ protected Scale handleScale(String resourceUrl, Scale scale) throws InterruptedE protected Status handleDeploymentRollback(String resourceUrl, DeploymentRollback deploymentRollback) throws InterruptedException, IOException { HttpRequest.Builder requestBuilder = httpClient.newHttpRequestBuilder().uri(resourceUrl + "/rollback").post(JSON, - JSON_MAPPER.writeValueAsString(deploymentRollback)); + getKubernetesSerialization().asJson(deploymentRollback)); return handleResponse(requestBuilder, Status.class); } @@ -475,10 +472,9 @@ protected Status handleDeploymentRollback(String resourceUrl, DeploymentRollback * @param template argument provided * * @return returns a deserialized object as api server response of provided type. - * @throws InterruptedException Interrupted Exception * @throws IOException IOException */ - protected T handleGet(URL resourceUrl, Class type) throws InterruptedException, IOException { + protected T handleGet(URL resourceUrl, Class type) throws IOException { HttpRequest.Builder requestBuilder = httpClient.newHttpRequestBuilder().url(resourceUrl); return handleResponse(requestBuilder, type); } @@ -486,7 +482,7 @@ protected T handleGet(URL resourceUrl, Class type) throws InterruptedExce protected T handleApproveOrDeny(T csr, Class type) throws IOException, InterruptedException { String uri = URLUtils.join(getResourceUrl(null, csr.getMetadata().getName(), false).toString(), "approval"); HttpRequest.Builder requestBuilder = httpClient.newHttpRequestBuilder() - .put(JSON, JSON_MAPPER.writeValueAsString(csr)).uri(uri); + .put(JSON, getKubernetesSerialization().asJson(csr)).uri(uri); return handleResponse(requestBuilder, type); } @@ -581,7 +577,7 @@ protected CompletableFuture handleResponse(HttpClient client, HttpRequest try { assertResponseCode(request, response); if (type != null && type.getType() != null) { - return Serialization.unmarshal(new ByteArrayInputStream(response.body()), type); + return getKubernetesSerialization().unmarshal(new ByteArrayInputStream(response.body()), type); } else { return null; } @@ -616,9 +612,10 @@ protected void assertResponseCode(HttpRequest request, HttpResponse response) String customMessage = config.getErrorMessages().get(statusCode); if (customMessage != null) { - throw requestFailure(request, createStatus(statusCode, combineMessages(customMessage, createStatus(response)))); + throw requestFailure(request, + createStatus(statusCode, combineMessages(customMessage, createStatus(response, getKubernetesSerialization())))); } else { - throw requestFailure(request, createStatus(response)); + throw requestFailure(request, createStatus(response, getKubernetesSerialization())); } } @@ -632,7 +629,7 @@ private String combineMessages(String customMessage, Status defaultStatus) { return customMessage; } - public static Status createStatus(HttpResponse response) { + public static Status createStatus(HttpResponse response, KubernetesSerialization kubernetesSerialization) { String statusMessage = ""; int statusCode = response != null ? response.code() : 0; if (response == null) { @@ -641,13 +638,13 @@ public static Status createStatus(HttpResponse response) { try { String bodyString = response.bodyString(); if (Utils.isNotNullOrEmpty(bodyString)) { - Status status = JSON_MAPPER.readValue(bodyString, Status.class); + Status status = kubernetesSerialization.unmarshal(bodyString, Status.class); if (status.getCode() == null) { status = new StatusBuilder(status).withCode(statusCode).build(); } return status; } - } catch (IOException e) { + } catch (IOException | KubernetesClientException e) { // ignored } if (response.message() != null) { @@ -707,23 +704,6 @@ public static KubernetesClientException requestException(HttpRequest request, Ex return requestException(request, e, null); } - protected static T unmarshal(InputStream is) { - return Serialization.unmarshal(is); - } - - protected static T unmarshal(InputStream is, final Class type) { - return Serialization.unmarshal(is, type); - } - - protected static T unmarshal(InputStream is, TypeReference type) { - return Serialization.unmarshal(is, type); - } - - protected static Map getObjectValueAsMap(T object) { - return JSON_MAPPER.convertValue(object, new TypeReference>() { - }); - } - public Config getConfig() { return config; } @@ -754,4 +734,8 @@ public R1 restCall(Class result, String... path) { } } + public KubernetesSerialization getKubernetesSerialization() { + return context.getClient().adapt(BaseClient.class).getKubernetesSerialization(); + } + } diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/PatchUtils.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/PatchUtils.java index 18474f5255f..b06053a5263 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/PatchUtils.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/PatchUtils.java @@ -15,17 +15,16 @@ */ package io.fabric8.kubernetes.client.dsl.internal; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.node.ObjectNode; -import io.fabric8.kubernetes.client.KubernetesClientException; import io.fabric8.kubernetes.client.utils.ResourceCompare; import io.fabric8.kubernetes.client.utils.Serialization; import io.fabric8.zjsonpatch.JsonDiff; import java.util.Optional; +import java.util.function.Function; public class PatchUtils { @@ -48,12 +47,8 @@ private static class SingletonHolder { } public static String withoutRuntimeState(Object object, Format format, boolean omitStatus) { - ObjectMapper mapper = format == Format.JSON ? Serialization.jsonMapper() : Serialization.yamlMapper(); - try { - return mapper.writeValueAsString(withoutRuntimeState(object, omitStatus)); - } catch (JsonProcessingException e) { - throw KubernetesClientException.launderThrowable(e); - } + Function mapper = format == Format.JSON ? Serialization::asJson : Serialization::asYaml; + return mapper.apply(withoutRuntimeState(object, omitStatus)); } /** @@ -76,12 +71,8 @@ static JsonNode withoutRuntimeState(Object object, boolean omitStatus) { } public static String jsonDiff(Object current, Object updated, boolean omitStatus) { - try { - return Serialization.jsonMapper().writeValueAsString( - JsonDiff.asJson(withoutRuntimeState(current, omitStatus), withoutRuntimeState(updated, omitStatus))); - } catch (JsonProcessingException e) { - throw KubernetesClientException.launderThrowable(e); - } + return Serialization + .asJson(JsonDiff.asJson(withoutRuntimeState(current, omitStatus), withoutRuntimeState(updated, omitStatus))); } } diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/WatchConnectionManager.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/WatchConnectionManager.java index 7fc97203402..54044d877d7 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/WatchConnectionManager.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/WatchConnectionManager.java @@ -111,7 +111,7 @@ protected void start(URL url, Map headers) { // We do not expect a 200 in response to the websocket connection. If it occurs, we throw // an exception and try the watch via a persistent HTTP Get. // Newer Kubernetes might also return 503 Service Unavailable in case WebSockets are not supported - Status status = OperationSupport.createStatus(response); + Status status = OperationSupport.createStatus(response, this.baseOperation.getKubernetesSerialization()); if (HTTP_OK == code || HTTP_UNAVAILABLE == code) { throw OperationSupport.requestFailure(client.newHttpRequestBuilder().url(url).build(), status, "Received " + code + " on websocket"); diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/apps/v1/RollableScalableResourceOperation.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/apps/v1/RollableScalableResourceOperation.java index 8b633c1c04a..573776dbedb 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/apps/v1/RollableScalableResourceOperation.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/apps/v1/RollableScalableResourceOperation.java @@ -31,7 +31,6 @@ import io.fabric8.kubernetes.client.dsl.internal.HasMetadataOperation; import io.fabric8.kubernetes.client.dsl.internal.OperationContext; import io.fabric8.kubernetes.client.dsl.internal.PodOperationContext; -import io.fabric8.kubernetes.client.utils.Serialization; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -141,7 +140,7 @@ public T edit(UnaryOperator function) { } try { T oldObj = getItemOrRequireFromServer(); - T newObj = function.apply(Serialization.clone(oldObj)); + T newObj = function.apply(getKubernetesSerialization().clone(oldObj)); return getRollingUpdater(context.getTimeout(), context.getTimeoutUnit()).rollUpdate(oldObj, newObj); } catch (Exception e) { throw KubernetesClientException.launderThrowable(e); @@ -227,7 +226,7 @@ public T updateImage(Map containerToImageMap) { throw new KubernetesClientException("Resource doesn't exist"); } - T base = Serialization.clone(value); + T base = getKubernetesSerialization().clone(value); List containers = getContainers(value); diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/apps/v1/RollingUpdater.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/apps/v1/RollingUpdater.java index 208b991bfa4..a026b1ca189 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/apps/v1/RollingUpdater.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/apps/v1/RollingUpdater.java @@ -35,7 +35,7 @@ import io.fabric8.kubernetes.client.dsl.internal.PatchUtils; import io.fabric8.kubernetes.client.dsl.internal.PatchUtils.Format; import io.fabric8.kubernetes.client.dsl.internal.core.v1.PodOperationsImpl; -import io.fabric8.kubernetes.client.utils.Serialization; +import io.fabric8.kubernetes.client.utils.KubernetesSerialization; import io.fabric8.kubernetes.client.utils.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -175,20 +175,20 @@ public T rollUpdate(T oldObj, T newObj) { } } - private static T applyPatch(Resource resource, Map map) { - return resource.patch(PatchContext.of(PatchType.STRATEGIC_MERGE), Serialization.asJson(map)); + private static T applyPatch(Resource resource, Map map, KubernetesSerialization serialization) { + return resource.patch(PatchContext.of(PatchType.STRATEGIC_MERGE), serialization.asJson(map)); } - public static T resume(Resource resource) { - return applyPatch(resource, RollingUpdater.requestPayLoadForRolloutResume()); + public static T resume(RollableScalableResourceOperation resource) { + return applyPatch(resource, RollingUpdater.requestPayLoadForRolloutResume(), resource.getKubernetesSerialization()); } - public static T pause(Resource resource) { - return applyPatch(resource, RollingUpdater.requestPayLoadForRolloutPause()); + public static T pause(RollableScalableResourceOperation resource) { + return applyPatch(resource, RollingUpdater.requestPayLoadForRolloutPause(), resource.getKubernetesSerialization()); } - public static T restart(Resource resource) { - return applyPatch(resource, RollingUpdater.requestPayLoadForRolloutRestart()); + public static T restart(RollableScalableResourceOperation resource) { + return applyPatch(resource, RollingUpdater.requestPayLoadForRolloutRestart(), resource.getKubernetesSerialization()); } public static Map requestPayLoadForRolloutPause() { diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/apps/v1/StatefulSetOperationsImpl.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/apps/v1/StatefulSetOperationsImpl.java index c2dc2207d92..0b99a1de72e 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/apps/v1/StatefulSetOperationsImpl.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/apps/v1/StatefulSetOperationsImpl.java @@ -38,7 +38,6 @@ import io.fabric8.kubernetes.client.dsl.internal.HasMetadataOperationsImpl; import io.fabric8.kubernetes.client.dsl.internal.OperationContext; import io.fabric8.kubernetes.client.dsl.internal.PodOperationContext; -import io.fabric8.kubernetes.client.utils.Serialization; import io.fabric8.kubernetes.client.utils.internal.PodOperationUtil; import java.io.InputStream; @@ -182,7 +181,8 @@ public StatefulSet undo() { }); ControllerRevision previousControllerRevision = controllerRevisions.get(1); - return patch(PatchContext.of(PatchType.STRATEGIC_MERGE), Serialization.asJson(previousControllerRevision.getData())); + return patch(PatchContext.of(PatchType.STRATEGIC_MERGE), + getKubernetesSerialization().asJson(previousControllerRevision.getData())); } private ControllerRevisionList getControllerRevisionListForStatefulSet(StatefulSet statefulSet) { diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/core/v1/PodOperationsImpl.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/core/v1/PodOperationsImpl.java index 29e731d9cf0..a9095ea204f 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/core/v1/PodOperationsImpl.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/dsl/internal/core/v1/PodOperationsImpl.java @@ -265,7 +265,7 @@ private boolean handleEvict(HasMetadata eviction) { URL requestUrl = new URL(URLUtils.join(getResourceUrl().toString(), "eviction")); HttpRequest.Builder requestBuilder = httpClient.newHttpRequestBuilder() - .post(JSON, JSON_MAPPER.writeValueAsString(eviction)).url(requestUrl); + .post(JSON, getKubernetesSerialization().asJson(eviction)).url(requestUrl); handleResponse(requestBuilder, null); return true; } catch (KubernetesClientException e) { @@ -378,7 +378,8 @@ private boolean hasEphemeralContainer(List containers, Strin private ExecWebSocketListener setupConnectionToPod(URI uri) { HttpClient clone = httpClient.newBuilder().readTimeout(0, TimeUnit.MILLISECONDS).build(); - ExecWebSocketListener execWebSocketListener = new ExecWebSocketListener(getContext(), this.context.getExecutor()); + ExecWebSocketListener execWebSocketListener = new ExecWebSocketListener(getContext(), this.context.getExecutor(), + this.getKubernetesSerialization()); CompletableFuture startedFuture = clone.newWebSocketBuilder() .subprotocol("v4.channel.k8s.io") .uri(uri) diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/BaseClient.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/BaseClient.java index 8ff5dd2033b..0102defcf32 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/BaseClient.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/BaseClient.java @@ -16,6 +16,7 @@ package io.fabric8.kubernetes.client.impl; +import com.fasterxml.jackson.databind.ObjectMapper; import io.fabric8.kubernetes.api.model.APIGroup; import io.fabric8.kubernetes.api.model.APIGroupList; import io.fabric8.kubernetes.api.model.APIResourceList; @@ -39,6 +40,7 @@ import io.fabric8.kubernetes.client.extension.SupportTestingClient; import io.fabric8.kubernetes.client.http.HttpClient; import io.fabric8.kubernetes.client.utils.ApiVersionUtil; +import io.fabric8.kubernetes.client.utils.KubernetesSerialization; import io.fabric8.kubernetes.client.utils.Utils; import java.net.MalformedURLException; @@ -81,6 +83,7 @@ public void onClose(Executor executor) { private OperationSupport operationSupport; private ExecutorSupplier executorSupplier; private Executor executor; + protected KubernetesSerialization kubernetesSerialization = new KubernetesSerialization(new ObjectMapper()); private OperationContext operationContext; @@ -338,4 +341,8 @@ public Executor getExecutor() { return executor; } + public KubernetesSerialization getKubernetesSerialization() { + return kubernetesSerialization; + } + } diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/Handlers.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/Handlers.java index 66c55868800..5c9cc53ce8d 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/Handlers.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/Handlers.java @@ -21,6 +21,7 @@ import io.fabric8.kubernetes.api.model.GenericKubernetesResource; import io.fabric8.kubernetes.api.model.GenericKubernetesResourceList; import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.api.model.KubernetesResource; import io.fabric8.kubernetes.api.model.KubernetesResourceList; import io.fabric8.kubernetes.client.Client; import io.fabric8.kubernetes.client.KubernetesClientException; @@ -30,7 +31,6 @@ import io.fabric8.kubernetes.client.dsl.internal.HasMetadataOperation; import io.fabric8.kubernetes.client.utils.ApiVersionUtil; import io.fabric8.kubernetes.client.utils.KubernetesResourceUtil; -import io.fabric8.kubernetes.client.utils.Serialization; import java.util.Arrays; import java.util.List; @@ -57,7 +57,7 @@ public void unregister(Class type) { /** * Returns a {@link ResourceHandler} for the given item. The client is optional, if it is supplied, then the server can be * consulted for resource metadata if a generic item is passed in. - * + * * @return the handler * @throws KubernetesClientException if a handler cannot be found */ @@ -65,7 +65,8 @@ public > ResourceHandler Class type = (Class) meta.getClass(); if (type.equals(GenericKubernetesResource.class)) { - ResourceDefinitionContext rdc = getResourceDefinitionContext((GenericKubernetesResource) meta, client); + GenericKubernetesResource gkr = (GenericKubernetesResource) meta; + ResourceDefinitionContext rdc = getResourceDefinitionContext(gkr.getApiVersion(), gkr.getKind(), client); if (rdc != null) { return new ResourceHandlerImpl(GenericKubernetesResource.class, GenericKubernetesResourceList.class, rdc); @@ -79,26 +80,17 @@ public > ResourceHandler return result; } - public ResourceDefinitionContext getResourceDefinitionContext(String apiVersion, String kind, BaseClient client) { - GenericKubernetesResource resource = new GenericKubernetesResource(); - resource.setKind(kind); - resource.setApiVersion(apiVersion); - return getResourceDefinitionContext(resource, client); - } - - public ResourceDefinitionContext getResourceDefinitionContext(GenericKubernetesResource meta, + public ResourceDefinitionContext getResourceDefinitionContext(String apiVersion, String kind, Client client) { // check if it's built-in - Object value = Serialization.unmarshal(Serialization.asJson(meta)); - Class parsedType = (Class) value.getClass(); + Class clazz = client.adapt(BaseClient.class).getKubernetesSerialization() + .getRegisteredKind(apiVersion, kind); ResourceDefinitionContext rdc = null; - if (!parsedType.equals(GenericKubernetesResource.class)) { - rdc = ResourceDefinitionContext.fromResourceType(parsedType); + if (clazz != null) { + rdc = ResourceDefinitionContext.fromResourceType(clazz); } else if (client != null) { // if a client has been supplied, we can try to look this up from the server - String kind = meta.getKind(); - String apiVersion = meta.getApiVersion(); if (kind == null || apiVersion == null) { return null; } diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/KubernetesClientImpl.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/KubernetesClientImpl.java index e64707f4d8d..e26e742fe5c 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/KubernetesClientImpl.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/impl/KubernetesClientImpl.java @@ -114,7 +114,6 @@ import io.fabric8.kubernetes.client.dsl.NamespaceableResource; import io.fabric8.kubernetes.client.dsl.NetworkAPIGroupDSL; import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation; -import io.fabric8.kubernetes.client.dsl.ParameterNamespaceListVisitFromServerGetDeleteRecreateWaitApplicable; import io.fabric8.kubernetes.client.dsl.PodResource; import io.fabric8.kubernetes.client.dsl.PolicyAPIGroupDSL; import io.fabric8.kubernetes.client.dsl.RbacAPIGroupDSL; @@ -159,7 +158,6 @@ import io.fabric8.kubernetes.client.informers.impl.SharedInformerFactoryImpl; import io.fabric8.kubernetes.client.utils.ApiVersionUtil; import io.fabric8.kubernetes.client.utils.HttpClientUtils; -import io.fabric8.kubernetes.client.utils.Serialization; import io.fabric8.kubernetes.client.utils.Utils; import java.io.InputStream; @@ -315,8 +313,8 @@ public NonNamespaceOperation load(InputStream is) { - return resourceListFor(Serialization.unmarshal(is)); + public NamespaceListVisitFromServerGetDeleteRecreateWaitApplicable load(InputStream is) { + return resourceListFor(kubernetesSerialization.unmarshal(is)); } /** @@ -352,8 +350,8 @@ public NamespaceListVisitFromServerGetDeleteRecreateWaitApplicable * {@inheritDoc} */ @Override - public ParameterNamespaceListVisitFromServerGetDeleteRecreateWaitApplicable resourceList(String s) { - return resourceListFor(Serialization.unmarshal(s)); + public NamespaceListVisitFromServerGetDeleteRecreateWaitApplicable resourceList(String s) { + return resourceListFor(kubernetesSerialization.unmarshal(s)); } @Override @@ -377,7 +375,7 @@ private NamespaceableResource resource(Object resource) { */ @Override public NamespaceableResource resource(String s) { - return resource(Serialization. unmarshal(s)); + return resource(kubernetesSerialization. unmarshal(s)); } /** @@ -385,7 +383,7 @@ public NamespaceableResource resource(String s) { */ @Override public NamespaceableResource resource(InputStream is) { - return resource(Serialization. unmarshal(is)); + return resource(kubernetesSerialization. unmarshal(is)); } /** diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/utils/internal/CreateOrReplaceHelper.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/utils/internal/CreateOrReplaceHelper.java index 6671516336d..7abf75db7f2 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/utils/internal/CreateOrReplaceHelper.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/utils/internal/CreateOrReplaceHelper.java @@ -18,7 +18,7 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.client.KubernetesClientException; import io.fabric8.kubernetes.client.utils.KubernetesResourceUtil; -import io.fabric8.kubernetes.client.utils.Serialization; +import io.fabric8.kubernetes.client.utils.KubernetesSerialization; import java.net.HttpURLConnection; import java.util.concurrent.CompletableFuture; @@ -30,20 +30,22 @@ public class CreateOrReplaceHelper { private final UnaryOperator replaceTask; private final UnaryOperator waitTask; private final UnaryOperator reloadTask; + private final KubernetesSerialization serialization; public CreateOrReplaceHelper(UnaryOperator createTask, UnaryOperator replaceTask, UnaryOperator waitTask, - UnaryOperator reloadTask) { + UnaryOperator reloadTask, KubernetesSerialization serialization) { this.createTask = createTask; this.replaceTask = replaceTask; this.waitTask = waitTask; this.reloadTask = reloadTask; + this.serialization = serialization; } public T createOrReplace(T item) { String resourceVersion = KubernetesResourceUtil.getResourceVersion(item); final CompletableFuture future = new CompletableFuture<>(); int nTries = 0; - item = Serialization.clone(item); + item = serialization.clone(item); while (!future.isDone() && nTries < CREATE_OR_REPLACE_RETRIES) { try { // Create diff --git a/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/dsl/internal/ExecWebSocketListenerTest.java b/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/dsl/internal/ExecWebSocketListenerTest.java index 5e1e2f10fe0..0425d431a94 100644 --- a/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/dsl/internal/ExecWebSocketListenerTest.java +++ b/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/dsl/internal/ExecWebSocketListenerTest.java @@ -20,6 +20,7 @@ import io.fabric8.kubernetes.api.model.StatusCause; import io.fabric8.kubernetes.client.KubernetesClientException; import io.fabric8.kubernetes.client.http.WebSocket; +import io.fabric8.kubernetes.client.utils.Serialization; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -70,7 +71,7 @@ void testSendShouldTruncateAndSendFlaggedWebSocketData() { final WebSocket mockedWebSocket = Mockito.mock(WebSocket.class); Mockito.when(mockedWebSocket.send(Mockito.any())).thenReturn(true); - ExecWebSocketListener listner = new ExecWebSocketListener(new PodOperationContext()); + ExecWebSocketListener listner = newExecWebSocketListener(new PodOperationContext()); listner.onOpen(mockedWebSocket); final byte[] toSend = new byte[] { 1, 3, 3, 7, 0 }; @@ -83,7 +84,7 @@ void testSendShouldTruncateAndSendFlaggedWebSocketData() { @Test void testCheckErrorHasErrorFromMessageShouldThrowException() { - ExecWebSocketListener listener = new ExecWebSocketListener( + ExecWebSocketListener listener = newExecWebSocketListener( new PodOperationContext().toBuilder().terminateOnError(true).build()); listener.onMessage(null, ByteBuffer.wrap(new byte[] { (byte) 2, (byte) 1, (byte) 1 })); @@ -93,16 +94,20 @@ void testCheckErrorHasErrorFromMessageShouldThrowException() { @Test void testCheckErrorHasErrorFromFailureShouldThrowException() { - ExecWebSocketListener listener = new ExecWebSocketListener(new PodOperationContext()); + ExecWebSocketListener listener = newExecWebSocketListener(new PodOperationContext()); listener.onError(null, new IOException("here")); assertThrows(KubernetesClientException.class, () -> listener.checkError()); } + private ExecWebSocketListener newExecWebSocketListener(PodOperationContext context) { + return new ExecWebSocketListener(context, Runnable::run, Serialization.getKubernetesSerialization()); + } + @Test void testGracefulClose() { - ExecWebSocketListener listener = new ExecWebSocketListener(new PodOperationContext()); + ExecWebSocketListener listener = newExecWebSocketListener(new PodOperationContext()); WebSocket mock = Mockito.mock(WebSocket.class); listener.onOpen(mock); listener.close(); @@ -113,7 +118,7 @@ void testGracefulClose() { @Test void testOnClose() { - ExecWebSocketListener listener = new ExecWebSocketListener(new PodOperationContext()); + ExecWebSocketListener listener = newExecWebSocketListener(new PodOperationContext()); WebSocket mock = Mockito.mock(WebSocket.class); listener.onClose(mock, 1000, "testing"); diff --git a/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/dsl/internal/OperationSupportTest.java b/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/dsl/internal/OperationSupportTest.java index 899648f923b..03dd9f75c1b 100644 --- a/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/dsl/internal/OperationSupportTest.java +++ b/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/dsl/internal/OperationSupportTest.java @@ -17,19 +17,21 @@ import io.fabric8.kubernetes.api.model.Pod; import io.fabric8.kubernetes.api.model.PodBuilder; -import io.fabric8.kubernetes.client.Client; import io.fabric8.kubernetes.client.Config; import io.fabric8.kubernetes.client.KubernetesClientException; import io.fabric8.kubernetes.client.http.HttpRequest; import io.fabric8.kubernetes.client.http.HttpResponse; import io.fabric8.kubernetes.client.http.StandardHttpRequest; import io.fabric8.kubernetes.client.http.TestHttpResponse; +import io.fabric8.kubernetes.client.impl.BaseClient; +import io.fabric8.kubernetes.client.utils.Serialization; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.Mockito; import java.net.MalformedURLException; import java.net.URI; @@ -39,7 +41,6 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.params.provider.Arguments.arguments; -import static org.mockito.Mockito.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -49,7 +50,10 @@ class OperationSupportTest { @BeforeEach void setUp() { - final OperationContext context = new OperationContext().withClient(mock(Client.class, RETURNS_DEEP_STUBS)); + BaseClient mock = mock(BaseClient.class, Mockito.RETURNS_SELF); + Mockito.when(mock.adapt(BaseClient.class).getKubernetesSerialization()) + .thenReturn(Serialization.getKubernetesSerialization()); + final OperationContext context = new OperationContext().withClient(mock); when(context.getClient().getConfiguration()).thenReturn(Config.empty()); operationSupport = new OperationSupport(context); } diff --git a/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/dsl/internal/uploadable/PodUploadTest.java b/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/dsl/internal/uploadable/PodUploadTest.java index 9bd515a93a6..dbf57a27e2f 100644 --- a/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/dsl/internal/uploadable/PodUploadTest.java +++ b/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/dsl/internal/uploadable/PodUploadTest.java @@ -82,6 +82,8 @@ void setUp() { BaseClient client = mock(BaseClient.class, Mockito.RETURNS_SELF); Mockito.when(client.adapt(BaseClient.class).getExecutor()).thenReturn(CommonThreadPool.get()); + Mockito.when(client.adapt(BaseClient.class).getKubernetesSerialization()) + .thenReturn(Serialization.getKubernetesSerialization()); Config config = mock(Config.class, Mockito.RETURNS_DEEP_STUBS); when(config.getMasterUrl()).thenReturn("https://openshift.com:8443"); when(config.getNamespace()).thenReturn("default"); diff --git a/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/impl/KubernetesClientImplTest.java b/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/impl/KubernetesClientImplTest.java index 9a8cdb24b5c..31241e4d61a 100644 --- a/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/impl/KubernetesClientImplTest.java +++ b/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/impl/KubernetesClientImplTest.java @@ -24,7 +24,7 @@ import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.KubernetesClientBuilder; import io.fabric8.kubernetes.client.KubernetesClientException; -import io.fabric8.kubernetes.client.dsl.ParameterNamespaceListVisitFromServerGetDeleteRecreateWaitApplicable; +import io.fabric8.kubernetes.client.dsl.NamespaceListVisitFromServerGetDeleteRecreateWaitApplicable; import io.fabric8.kubernetes.client.http.BasicBuilder; import io.fabric8.kubernetes.client.http.HttpHeaders; import io.fabric8.kubernetes.client.utils.HttpClientUtils; @@ -211,7 +211,7 @@ void loadFromInputStreamWithHasMetadata() throws IOException { void loadFromInputStreamWithInvalid() throws IOException { final String podYaml = "NotAPod"; try (InputStream is = new ByteArrayInputStream(podYaml.getBytes(StandardCharsets.UTF_8))) { - final ParameterNamespaceListVisitFromServerGetDeleteRecreateWaitApplicable load = new KubernetesClientImpl() + final NamespaceListVisitFromServerGetDeleteRecreateWaitApplicable load = new KubernetesClientImpl() .load(is); assertThatIllegalArgumentException() .isThrownBy(load::get) diff --git a/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/utils/internal/CreateOrReplaceHelperTest.java b/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/utils/internal/CreateOrReplaceHelperTest.java index 7940721f077..aef8f57b7cc 100644 --- a/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/utils/internal/CreateOrReplaceHelperTest.java +++ b/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/utils/internal/CreateOrReplaceHelperTest.java @@ -19,6 +19,7 @@ import io.fabric8.kubernetes.api.model.PodBuilder; import io.fabric8.kubernetes.api.model.StatusBuilder; import io.fabric8.kubernetes.client.KubernetesClientException; +import io.fabric8.kubernetes.client.utils.Serialization; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -47,7 +48,7 @@ void testCreateOrReplaceShouldCreate() { createPodTask, p -> getPod(), p -> getPod(), - p -> getPod()); + p -> getPod(), Serialization.getKubernetesSerialization()); Pod p = getPod(); p.getMetadata().setResourceVersion("1"); @@ -77,7 +78,7 @@ void testCreateOrReplaceShouldReplace() { createPodTask, replacePodTask, p -> getPod(), - p -> getPod()); + p -> getPod(), Serialization.getKubernetesSerialization()); // When Pod podCreated = podCreateOrReplaceHelper.createOrReplace(getPod()); @@ -106,7 +107,7 @@ HttpURLConnection.HTTP_INTERNAL_ERROR, new StatusBuilder().withCode(HttpURLConne createPodTask, p -> getPod(), waitTask, - reloadTask); + reloadTask, Serialization.getKubernetesSerialization()); // When Pod podCreated = podCreateOrReplaceHelper.createOrReplace(getPod()); @@ -125,7 +126,7 @@ void testCreateOrReplaceThrowExceptionOnErrorCodeLessThan500() { HttpURLConnection.HTTP_BAD_REQUEST, new StatusBuilder().withCode(HttpURLConnection.HTTP_BAD_REQUEST).build()); }; CreateOrReplaceHelper podCreateOrReplaceHelper = new CreateOrReplaceHelper<>(createPodTask, - p -> getPod(), p -> getPod(), p -> getPod()); + p -> getPod(), p -> getPod(), p -> getPod(), Serialization.getKubernetesSerialization()); Pod podToCreate = getPod(); // When diff --git a/kubernetes-model-generator/kubernetes-model-core/src/main/java/io/fabric8/kubernetes/internal/KubernetesDeserializer.java b/kubernetes-model-generator/kubernetes-model-core/src/main/java/io/fabric8/kubernetes/internal/KubernetesDeserializer.java index 7aa89cbfdda..a78c388c067 100644 --- a/kubernetes-model-generator/kubernetes-model-core/src/main/java/io/fabric8/kubernetes/internal/KubernetesDeserializer.java +++ b/kubernetes-model-generator/kubernetes-model-core/src/main/java/io/fabric8/kubernetes/internal/KubernetesDeserializer.java @@ -75,7 +75,7 @@ public boolean equals(Object obj) { private static final String KIND = "kind"; private static final String API_VERSION = "apiVersion"; - private static final Mapping mapping = new Mapping(); + private final Mapping mapping = new Mapping(); @Override public KubernetesResource deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { @@ -110,7 +110,7 @@ private KubernetesResource fromArrayNode(JsonParser jp, JsonNode node) throws IO return new KubernetesListBuilder().withItems(list).build(); } - private static KubernetesResource fromObjectNode(JsonParser jp, JsonNode node) throws IOException { + private KubernetesResource fromObjectNode(JsonParser jp, JsonNode node) throws IOException { TypeKey key = getKey(node); Class resourceType = mapping.getForKey(key); if (resourceType == null) { @@ -132,7 +132,7 @@ private static KubernetesResource fromObjectNode(JsonParser jp, JsonNode node) t /** * Return a string representation of the key of the type: #. */ - private static TypeKey getKey(JsonNode node) { + private TypeKey getKey(JsonNode node) { JsonNode apiVersion = node.get(API_VERSION); JsonNode kind = node.get(KIND); @@ -144,14 +144,14 @@ private static TypeKey getKey(JsonNode node) { /** * Registers a Custom Resource Definition Kind */ - public static void registerCustomKind(String kind, Class clazz) { + public void registerCustomKind(String kind, Class clazz) { registerCustomKind(null, kind, clazz); } /** * Registers a Custom Resource Definition Kind */ - public static void registerCustomKind(String apiVersion, String kind, Class clazz) { + public void registerCustomKind(String apiVersion, String kind, Class clazz) { mapping.registerKind(apiVersion, kind, clazz); } @@ -223,4 +223,8 @@ private void addMapping(Class clazz) { } } } + + public Class getRegisteredKind(String apiVersion, String kind) { + return mapping.getForKey(mapping.createKey(apiVersion, kind)); + } } diff --git a/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/ClusterRoleTest.java b/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/ClusterRoleTest.java index 75127d90b11..dfdd68d5b43 100644 --- a/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/ClusterRoleTest.java +++ b/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/ClusterRoleTest.java @@ -19,7 +19,7 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.api.model.rbac.ClusterRole; import io.fabric8.kubernetes.client.KubernetesClient; -import io.fabric8.kubernetes.client.dsl.ParameterNamespaceListVisitFromServerGetDeleteRecreateWaitApplicable; +import io.fabric8.kubernetes.client.dsl.NamespaceListVisitFromServerGetDeleteRecreateWaitApplicable; import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient; import org.junit.jupiter.api.Test; @@ -44,7 +44,7 @@ void testLoadFromFile() { @Test void testHandlersLoadFromFile() { - ParameterNamespaceListVisitFromServerGetDeleteRecreateWaitApplicable load = client + NamespaceListVisitFromServerGetDeleteRecreateWaitApplicable load = client .load(getClass().getResourceAsStream("/test-clusterrole.yml")); assertNotNull(load); diff --git a/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/CustomResourceCrudTest.java b/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/CustomResourceCrudTest.java index 391bb91468e..0e97546e111 100644 --- a/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/CustomResourceCrudTest.java +++ b/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/CustomResourceCrudTest.java @@ -22,11 +22,11 @@ import io.fabric8.kubernetes.client.dsl.MixedOperation; import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation; import io.fabric8.kubernetes.client.dsl.Resource; +import io.fabric8.kubernetes.client.impl.BaseClient; import io.fabric8.kubernetes.client.mock.crd.CronTab; import io.fabric8.kubernetes.client.mock.crd.CronTabSpec; import io.fabric8.kubernetes.client.mock.crd.CronTabStatus; import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient; -import io.fabric8.kubernetes.internal.KubernetesDeserializer; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -48,7 +48,8 @@ class CustomResourceCrudTest { @BeforeEach void setUp() { - KubernetesDeserializer.registerCustomKind("stable.example.com/v1", "CronTab", CronTab.class); + client.adapt(BaseClient.class).getKubernetesSerialization().registerCustomKind("stable.example.com/v1", "CronTab", + CronTab.class); cronTabCrd = client .apiextensions().v1() .customResourceDefinitions() diff --git a/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/CustomResourceCrudWithCRDContextTest.java b/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/CustomResourceCrudWithCRDContextTest.java index cdfe80b955f..cf03b8af945 100644 --- a/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/CustomResourceCrudWithCRDContextTest.java +++ b/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/CustomResourceCrudWithCRDContextTest.java @@ -20,11 +20,11 @@ import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.dsl.MixedOperation; import io.fabric8.kubernetes.client.dsl.Resource; +import io.fabric8.kubernetes.client.impl.BaseClient; import io.fabric8.kubernetes.client.mock.crd.EntandoBundleRelease; import io.fabric8.kubernetes.client.mock.crd.EntandoBundleReleaseList; import io.fabric8.kubernetes.client.mock.crd.EntandoBundleReleaseSpec; import io.fabric8.kubernetes.client.server.mock.EnableKubernetesMockClient; -import io.fabric8.kubernetes.internal.KubernetesDeserializer; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -38,7 +38,8 @@ class CustomResourceCrudWithCRDContextTest { @Test void testCreateAndGet() { // Given - KubernetesDeserializer.registerCustomKind("demo.fabric8.io/v1alpha1", "EntandoBundleRelease", EntandoBundleRelease.class); + client.adapt(BaseClient.class).getKubernetesSerialization().registerCustomKind("demo.fabric8.io/v1alpha1", + "EntandoBundleRelease", EntandoBundleRelease.class); MixedOperation> ebrClient = client .resources(EntandoBundleRelease.class, EntandoBundleReleaseList.class); @@ -54,7 +55,8 @@ void testCreateAndGet() { @Test void testCreateAndGetWithInferredContext() { // Given - KubernetesDeserializer.registerCustomKind("demo.fabric8.io/v1alpha1", "EntandoBundleRelease", EntandoBundleRelease.class); + client.adapt(BaseClient.class).getKubernetesSerialization().registerCustomKind("demo.fabric8.io/v1alpha1", + "EntandoBundleRelease", EntandoBundleRelease.class); MixedOperation, Resource> ebrClient = client .resources(EntandoBundleRelease.class); diff --git a/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/DefaultSharedIndexInformerTest.java b/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/DefaultSharedIndexInformerTest.java index bebb87163da..a0cd165f4b8 100644 --- a/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/DefaultSharedIndexInformerTest.java +++ b/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/DefaultSharedIndexInformerTest.java @@ -42,6 +42,7 @@ import io.fabric8.kubernetes.client.WatcherException; import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext; import io.fabric8.kubernetes.client.dsl.base.ResourceDefinitionContext; +import io.fabric8.kubernetes.client.impl.BaseClient; import io.fabric8.kubernetes.client.informers.ResourceEventHandler; import io.fabric8.kubernetes.client.informers.SharedIndexInformer; import io.fabric8.kubernetes.client.informers.SharedInformerFactory; @@ -58,7 +59,6 @@ import io.fabric8.kubernetes.client.utils.URLUtils; import io.fabric8.kubernetes.client.utils.URLUtils.URLBuilder; import io.fabric8.kubernetes.client.utils.Utils; -import io.fabric8.kubernetes.internal.KubernetesDeserializer; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -1158,7 +1158,8 @@ void testGenericKubernetesResourceSharedIndexInformerWithAdditionalDeserializers r -> new WatchEvent(getAnimal("red-panda", "Carnivora", r), "ADDED"), null, null); // When - KubernetesDeserializer.registerCustomKind("jungle.example.com/v1", "Animal", CronTab.class); + client.adapt(BaseClient.class).getKubernetesSerialization().registerCustomKind("jungle.example.com/v1", "Animal", + CronTab.class); SharedIndexInformer animalSharedIndexInformer = client .genericKubernetesResources(animalContext) .inNamespace("ns1") diff --git a/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/LeaderElectionTest.java b/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/LeaderElectionTest.java index a3b2e7c4afc..63df1237701 100644 --- a/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/LeaderElectionTest.java +++ b/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/LeaderElectionTest.java @@ -64,6 +64,7 @@ public void singleLeaderConfigMapLockUpdateTest() throws Exception { server.expect().get().withPath("/api/v1/namespaces/namespace/configmaps/name") .andReturn(200, new ConfigMapBuilder() .withNewMetadata() + .withName("name") .withResourceVersion("1") .withAnnotations(Collections.singletonMap( "control-plane.alpha.kubernetes.io/leader", diff --git a/kubernetes-tests/src/test/resources/parameters.yml b/kubernetes-tests/src/test/resources/parameters.yml index c796e7c8d1d..bc77995943a 100644 --- a/kubernetes-tests/src/test/resources/parameters.yml +++ b/kubernetes-tests/src/test/resources/parameters.yml @@ -17,3 +17,4 @@ USERNAME: root REQUIRED: requiredValue REQUIRED_BOOLEAN: false +CONTAINER_PORT: 8080 diff --git a/kubernetes-tests/src/test/resources/template-with-json-params.yml b/kubernetes-tests/src/test/resources/template-with-json-params.yml index 37556c7887f..7568eacb055 100644 --- a/kubernetes-tests/src/test/resources/template-with-json-params.yml +++ b/kubernetes-tests/src/test/resources/template-with-json-params.yml @@ -57,3 +57,6 @@ parameters: - description: Required boolean parameter name: REQUIRED_BOOLEAN required: true +- description: container port parameter + name: CONTAINER_PORT + required: true diff --git a/openshift-client-api/src/main/java/io/fabric8/openshift/client/DefaultOpenShiftClient.java b/openshift-client-api/src/main/java/io/fabric8/openshift/client/DefaultOpenShiftClient.java index 21169f5aaa3..2e9e98ca0b1 100644 --- a/openshift-client-api/src/main/java/io/fabric8/openshift/client/DefaultOpenShiftClient.java +++ b/openshift-client-api/src/main/java/io/fabric8/openshift/client/DefaultOpenShiftClient.java @@ -22,9 +22,6 @@ import io.fabric8.kubernetes.client.http.HttpClient.Factory; import io.fabric8.kubernetes.client.http.StandardHttpClientBuilder; import io.fabric8.kubernetes.client.utils.HttpClientUtils; -import io.fabric8.kubernetes.client.utils.Serialization; - -import java.io.InputStream; /** * Class for Default Openshift Client implementing KubernetesClient interface. @@ -37,14 +34,6 @@ public class DefaultOpenShiftClient extends NamespacedOpenShiftClientAdapter { public static final String OPENSHIFT_VERSION_ENDPOINT = "version/openshift"; - public static DefaultOpenShiftClient fromConfig(String config) { - return new DefaultOpenShiftClient(Serialization.unmarshal(config, OpenShiftConfig.class)); - } - - public static DefaultOpenShiftClient fromConfig(InputStream is) { - return new DefaultOpenShiftClient(Serialization.unmarshal(is, OpenShiftConfig.class)); - } - public DefaultOpenShiftClient() { this(new OpenShiftConfigBuilder().build()); } diff --git a/openshift-client-api/src/main/java/io/fabric8/openshift/client/NamespacedOpenShiftClientAdapter.java b/openshift-client-api/src/main/java/io/fabric8/openshift/client/NamespacedOpenShiftClientAdapter.java index a4b26fdde04..686c16e066b 100644 --- a/openshift-client-api/src/main/java/io/fabric8/openshift/client/NamespacedOpenShiftClientAdapter.java +++ b/openshift-client-api/src/main/java/io/fabric8/openshift/client/NamespacedOpenShiftClientAdapter.java @@ -18,7 +18,6 @@ import io.fabric8.kubernetes.api.model.ComponentStatus; import io.fabric8.kubernetes.api.model.ComponentStatusList; -import io.fabric8.kubernetes.api.model.KubernetesList; import io.fabric8.kubernetes.client.NamespacedKubernetesClientAdapter; import io.fabric8.kubernetes.client.RequestConfig; import io.fabric8.kubernetes.client.VersionInfo; @@ -348,7 +347,7 @@ public MixedOperation> routes() { } @Override - public ParameterMixedOperation> templates() { + public ParameterMixedOperation templates() { return getClient().templates(); } diff --git a/openshift-client-api/src/main/java/io/fabric8/openshift/client/OpenShiftClient.java b/openshift-client-api/src/main/java/io/fabric8/openshift/client/OpenShiftClient.java index d492bcbb329..78ddb467ed7 100644 --- a/openshift-client-api/src/main/java/io/fabric8/openshift/client/OpenShiftClient.java +++ b/openshift-client-api/src/main/java/io/fabric8/openshift/client/OpenShiftClient.java @@ -16,13 +16,105 @@ package io.fabric8.openshift.client; -import io.fabric8.kubernetes.api.model.KubernetesList; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.RequestConfig; import io.fabric8.kubernetes.client.VersionInfo; -import io.fabric8.kubernetes.client.dsl.*; +import io.fabric8.kubernetes.client.dsl.AppsAPIGroupDSL; +import io.fabric8.kubernetes.client.dsl.AutoscalingAPIGroupDSL; +import io.fabric8.kubernetes.client.dsl.BatchAPIGroupDSL; +import io.fabric8.kubernetes.client.dsl.ExtensionsAPIGroupDSL; +import io.fabric8.kubernetes.client.dsl.FunctionCallable; +import io.fabric8.kubernetes.client.dsl.Gettable; +import io.fabric8.kubernetes.client.dsl.InOutCreateable; +import io.fabric8.kubernetes.client.dsl.MixedOperation; +import io.fabric8.kubernetes.client.dsl.Nameable; +import io.fabric8.kubernetes.client.dsl.Namespaceable; +import io.fabric8.kubernetes.client.dsl.NamespacedInOutCreateable; +import io.fabric8.kubernetes.client.dsl.NetworkAPIGroupDSL; +import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation; +import io.fabric8.kubernetes.client.dsl.ParameterMixedOperation; +import io.fabric8.kubernetes.client.dsl.RbacAPIGroupDSL; +import io.fabric8.kubernetes.client.dsl.Resource; +import io.fabric8.kubernetes.client.dsl.SchedulingAPIGroupDSL; +import io.fabric8.kubernetes.client.dsl.StorageAPIGroupDSL; import io.fabric8.kubernetes.client.extension.SupportTestingClient; -import io.fabric8.openshift.api.model.*; +import io.fabric8.openshift.api.model.BrokerTemplateInstance; +import io.fabric8.openshift.api.model.BrokerTemplateInstanceList; +import io.fabric8.openshift.api.model.Build; +import io.fabric8.openshift.api.model.BuildConfig; +import io.fabric8.openshift.api.model.BuildConfigList; +import io.fabric8.openshift.api.model.BuildList; +import io.fabric8.openshift.api.model.ClusterNetwork; +import io.fabric8.openshift.api.model.ClusterNetworkList; +import io.fabric8.openshift.api.model.ClusterRole; +import io.fabric8.openshift.api.model.ClusterRoleBinding; +import io.fabric8.openshift.api.model.ClusterRoleBindingList; +import io.fabric8.openshift.api.model.ClusterRoleList; +import io.fabric8.openshift.api.model.DeploymentConfig; +import io.fabric8.openshift.api.model.DeploymentConfigList; +import io.fabric8.openshift.api.model.EgressNetworkPolicy; +import io.fabric8.openshift.api.model.EgressNetworkPolicyList; +import io.fabric8.openshift.api.model.Group; +import io.fabric8.openshift.api.model.GroupList; +import io.fabric8.openshift.api.model.HelmChartRepository; +import io.fabric8.openshift.api.model.HelmChartRepositoryList; +import io.fabric8.openshift.api.model.HostSubnet; +import io.fabric8.openshift.api.model.HostSubnetList; +import io.fabric8.openshift.api.model.Identity; +import io.fabric8.openshift.api.model.IdentityList; +import io.fabric8.openshift.api.model.Image; +import io.fabric8.openshift.api.model.ImageList; +import io.fabric8.openshift.api.model.ImageStream; +import io.fabric8.openshift.api.model.ImageStreamImage; +import io.fabric8.openshift.api.model.ImageStreamImport; +import io.fabric8.openshift.api.model.ImageStreamList; +import io.fabric8.openshift.api.model.ImageStreamMapping; +import io.fabric8.openshift.api.model.ImageStreamTag; +import io.fabric8.openshift.api.model.ImageStreamTagList; +import io.fabric8.openshift.api.model.ImageTag; +import io.fabric8.openshift.api.model.ImageTagList; +import io.fabric8.openshift.api.model.LocalResourceAccessReview; +import io.fabric8.openshift.api.model.LocalSubjectAccessReview; +import io.fabric8.openshift.api.model.NetNamespace; +import io.fabric8.openshift.api.model.NetNamespaceList; +import io.fabric8.openshift.api.model.OAuthAccessToken; +import io.fabric8.openshift.api.model.OAuthAccessTokenList; +import io.fabric8.openshift.api.model.OAuthAuthorizeToken; +import io.fabric8.openshift.api.model.OAuthAuthorizeTokenList; +import io.fabric8.openshift.api.model.OAuthClient; +import io.fabric8.openshift.api.model.OAuthClientAuthorization; +import io.fabric8.openshift.api.model.OAuthClientAuthorizationList; +import io.fabric8.openshift.api.model.OAuthClientList; +import io.fabric8.openshift.api.model.PodSecurityPolicyReview; +import io.fabric8.openshift.api.model.PodSecurityPolicySelfSubjectReview; +import io.fabric8.openshift.api.model.PodSecurityPolicySubjectReview; +import io.fabric8.openshift.api.model.RangeAllocation; +import io.fabric8.openshift.api.model.RangeAllocationList; +import io.fabric8.openshift.api.model.ResourceAccessReview; +import io.fabric8.openshift.api.model.ResourceAccessReviewResponse; +import io.fabric8.openshift.api.model.Role; +import io.fabric8.openshift.api.model.RoleBinding; +import io.fabric8.openshift.api.model.RoleBindingList; +import io.fabric8.openshift.api.model.RoleBindingRestriction; +import io.fabric8.openshift.api.model.RoleBindingRestrictionList; +import io.fabric8.openshift.api.model.RoleList; +import io.fabric8.openshift.api.model.Route; +import io.fabric8.openshift.api.model.RouteList; +import io.fabric8.openshift.api.model.SecurityContextConstraints; +import io.fabric8.openshift.api.model.SecurityContextConstraintsList; +import io.fabric8.openshift.api.model.SelfSubjectRulesReview; +import io.fabric8.openshift.api.model.SubjectAccessReview; +import io.fabric8.openshift.api.model.SubjectAccessReviewResponse; +import io.fabric8.openshift.api.model.SubjectRulesReview; +import io.fabric8.openshift.api.model.Template; +import io.fabric8.openshift.api.model.TemplateInstance; +import io.fabric8.openshift.api.model.TemplateInstanceList; +import io.fabric8.openshift.api.model.TemplateList; +import io.fabric8.openshift.api.model.User; +import io.fabric8.openshift.api.model.UserIdentityMapping; +import io.fabric8.openshift.api.model.UserList; +import io.fabric8.openshift.api.model.UserOAuthAccessToken; +import io.fabric8.openshift.api.model.UserOAuthAccessTokenList; import io.fabric8.openshift.api.model.miscellaneous.apiserver.v1.APIRequestCount; import io.fabric8.openshift.api.model.miscellaneous.apiserver.v1.APIRequestCountList; import io.fabric8.openshift.api.model.miscellaneous.cloudcredential.v1.CredentialsRequest; @@ -35,7 +127,26 @@ import io.fabric8.openshift.api.model.miscellaneous.network.operator.v1.EgressRouterList; import io.fabric8.openshift.api.model.miscellaneous.network.operator.v1.OperatorPKI; import io.fabric8.openshift.api.model.miscellaneous.network.operator.v1.OperatorPKIList; -import io.fabric8.openshift.client.dsl.*; +import io.fabric8.openshift.client.dsl.BuildConfigResource; +import io.fabric8.openshift.client.dsl.BuildResource; +import io.fabric8.openshift.client.dsl.DeployableScalableResource; +import io.fabric8.openshift.client.dsl.MachineConfigurationAPIGroupDSL; +import io.fabric8.openshift.client.dsl.NameableCreateOrDeleteable; +import io.fabric8.openshift.client.dsl.OpenShiftClusterAutoscalingAPIGroupDSL; +import io.fabric8.openshift.client.dsl.OpenShiftConfigAPIGroupDSL; +import io.fabric8.openshift.client.dsl.OpenShiftConsoleAPIGroupDSL; +import io.fabric8.openshift.client.dsl.OpenShiftHiveAPIGroupDSL; +import io.fabric8.openshift.client.dsl.OpenShiftMachineAPIGroupDSL; +import io.fabric8.openshift.client.dsl.OpenShiftMonitoringAPIGroupDSL; +import io.fabric8.openshift.client.dsl.OpenShiftOperatorAPIGroupDSL; +import io.fabric8.openshift.client.dsl.OpenShiftOperatorHubAPIGroupDSL; +import io.fabric8.openshift.client.dsl.OpenShiftQuotaAPIGroupDSL; +import io.fabric8.openshift.client.dsl.OpenShiftStorageVersionMigratorApiGroupDSL; +import io.fabric8.openshift.client.dsl.OpenShiftTunedAPIGroupDSL; +import io.fabric8.openshift.client.dsl.OpenShiftWhereaboutsAPIGroupDSL; +import io.fabric8.openshift.client.dsl.ProjectOperation; +import io.fabric8.openshift.client.dsl.ProjectRequestOperation; +import io.fabric8.openshift.client.dsl.TemplateResource; import java.net.URL; @@ -448,7 +559,7 @@ public interface OpenShiftClient extends KubernetesClient, SupportTestingClient * * @return {@link ParameterMixedOperation} object for Template operations */ - ParameterMixedOperation> templates(); + ParameterMixedOperation templates(); /** * API entrypoint for TemplateInstance(template.openshift.io/v1) diff --git a/openshift-client-api/src/main/java/io/fabric8/openshift/client/dsl/TemplateResource.java b/openshift-client-api/src/main/java/io/fabric8/openshift/client/dsl/TemplateResource.java index 7620a103cc4..2595562b8df 100644 --- a/openshift-client-api/src/main/java/io/fabric8/openshift/client/dsl/TemplateResource.java +++ b/openshift-client-api/src/main/java/io/fabric8/openshift/client/dsl/TemplateResource.java @@ -15,85 +15,87 @@ */ package io.fabric8.openshift.client.dsl; +import io.fabric8.kubernetes.api.model.KubernetesList; import io.fabric8.kubernetes.client.dsl.Resource; +import io.fabric8.openshift.api.model.Template; import io.fabric8.openshift.client.ParameterValue; import java.io.File; import java.io.InputStream; import java.util.Map; -public interface TemplateResource extends Resource { +public interface TemplateResource extends Resource