Skip to content

Commit

Permalink
fix fabric8io#3972: refining the client to not use static deserializa…
Browse files Browse the repository at this point in the history
…tion logic
  • Loading branch information
shawkins committed Feb 17, 2023
1 parent 90d8acc commit 0334f70
Show file tree
Hide file tree
Showing 55 changed files with 801 additions and 356 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -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)

Expand Down
Expand Up @@ -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.
Expand All @@ -36,14 +33,6 @@ public class DefaultKubernetesClient extends NamespacedKubernetesClientAdapter<N

public static final String KUBERNETES_VERSION_ENDPOINT = "version";

public static DefaultKubernetesClient fromConfig(String config) {
return new DefaultKubernetesClient(Serialization.unmarshal(config, Config.class));
}

public static DefaultKubernetesClient fromConfig(InputStream is) {
return new DefaultKubernetesClient(Serialization.unmarshal(is, Config.class));
}

public DefaultKubernetesClient() {
this(new ConfigBuilder().build());
}
Expand Down
Expand Up @@ -77,7 +77,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;
Expand Down Expand Up @@ -300,15 +299,15 @@ MixedOperation<GenericKubernetesResource, GenericKubernetesResourceList, Resourc
* @param is the input stream containing JSON/YAML content
* @return an operation instance to work on the list of Kubernetes Resource objects
*/
ParameterNamespaceListVisitFromServerGetDeleteRecreateWaitApplicable<HasMetadata> load(InputStream is);
NamespaceListVisitFromServerGetDeleteRecreateWaitApplicable<HasMetadata> load(InputStream is);

/**
* Load a Kubernetes list object
*
* @param s kubernetes list as string
* @return an operation instance to work on the deserialized KubernetesList objects
*/
ParameterNamespaceListVisitFromServerGetDeleteRecreateWaitApplicable<HasMetadata> resourceList(String s);
NamespaceListVisitFromServerGetDeleteRecreateWaitApplicable<HasMetadata> resourceList(String s);

/**
* KubernetesResourceList operations
Expand Down
Expand Up @@ -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;
Expand Down Expand Up @@ -47,6 +48,7 @@ default void onClose(Executor executor) {
private Class<KubernetesClient> clazz;
private ExecutorSupplier executorSupplier;
private Consumer<HttpClient.Builder> builderConsumer;
private KubernetesSerialization kubernetesSerialization = new KubernetesSerialization(new ObjectMapper());

public KubernetesClientBuilder() {
// basically the same logic as in KubernetesResourceUtil for finding list types
Expand Down Expand Up @@ -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;
}

Expand Down
Expand Up @@ -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;
Expand Down Expand Up @@ -255,12 +254,12 @@ public NonNamespaceOperation<ComponentStatus, ComponentStatusList, Resource<Comp
}

@Override
public ParameterNamespaceListVisitFromServerGetDeleteRecreateWaitApplicable<HasMetadata> load(InputStream is) {
public NamespaceListVisitFromServerGetDeleteRecreateWaitApplicable<HasMetadata> load(InputStream is) {
return getClient().load(is);
}

@Override
public ParameterNamespaceListVisitFromServerGetDeleteRecreateWaitApplicable<HasMetadata> resourceList(String s) {
public NamespaceListVisitFromServerGetDeleteRecreateWaitApplicable<HasMetadata> resourceList(String s) {
return getClient().resourceList(s);
}

Expand Down
Expand Up @@ -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;
Expand All @@ -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();
}

/**
Expand All @@ -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<LeaderElectionRecord>() {
});
} catch (JsonProcessingException ex) {
return Serialization.unmarshal(annotation, LeaderElectionRecord.class);
} catch (KubernetesClientException ex) {
LOGGER.error("Error deserializing LeaderElectionRecord from ConfigMap", ex);
return null;
}
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand Down
Expand Up @@ -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;
Expand All @@ -42,6 +43,7 @@ public class ReducedStateItemStore<V extends HasMetadata> implements ItemStore<V
private final List<String[]> fields = new ArrayList<>();
private final Class<V> typeClass;
private final KeyState keyState;
private KubernetesSerialization serialization = Serialization.getKubernetesSerialization();

public static class KeyState {

Expand Down Expand Up @@ -101,7 +103,8 @@ public KeyState(Function<HasMetadata, String> keyFunction, Function<String, Stri
* @param typeClass the expected type
* @param valueFields the additional fields to save
*/
public ReducedStateItemStore(KeyState keyState, Class<V> typeClass, String... valueFields) {
public ReducedStateItemStore(KeyState keyState, Class<V> typeClass,
String... valueFields) {
this.keyState = keyState;
fields.add(new String[] { METADATA, "resourceVersion" });
if (valueFields != null) {
Expand All @@ -110,13 +113,14 @@ public ReducedStateItemStore(KeyState keyState, Class<V> typeClass, String... va
}
}
this.typeClass = typeClass;
this.serialization = serialization;
}

Object[] store(V value) {
if (value == null) {
return null;
}
Map<String, Object> raw = Serialization.jsonMapper().convertValue(value, Map.class);
Map<String, Object> raw = serialization.convertValue(value, Map.class);
return fields.stream().map(f -> GenericKubernetesResource.get(raw, (Object[]) f)).toArray();
}

Expand All @@ -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<String, Object> raw, List<String[]> fields) {
Expand Down
Expand Up @@ -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;
Expand All @@ -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;

Expand All @@ -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);
}

/**
Expand Down Expand Up @@ -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));
}
}
}
Expand Up @@ -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();
Expand All @@ -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);
}

Expand Down
Expand Up @@ -383,7 +383,7 @@ public static List<EnvVar> convertMapToEnvVarList(Map<String, String> 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
*/
Expand All @@ -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
*/
Expand Down Expand Up @@ -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<String, Object> 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);
}
Expand All @@ -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<String, Object> dockerConfigMap = createDockerRegistryConfigMap(dockerServer, username, password);
String dockerConfigAsStr = Serialization.jsonMapper().writeValueAsString(dockerConfigMap);
String dockerConfigAsStr = Serialization.asJson(dockerConfigMap);

return createDockerSecret(secretName, dockerConfigAsStr);
}
Expand Down

0 comments on commit 0334f70

Please sign in to comment.