From 250638ee3c2b6e248626da19f4bd525fb68c54fd Mon Sep 17 00:00:00 2001 From: Rohan Kumar Date: Wed, 17 Jul 2024 17:49:56 +0530 Subject: [PATCH] fix (kubernetes-model-generator) : Handle deserialization of `defaultMode` field in custom deserializers for VolumeSource types Add custom deserializers to handle correct octal deserialization for these types: - ConfigMapVolumeSource - SecretVolumeSource - DownwardAPIVolumeSource - ProjectedVolumeSource Signed-off-by: Rohan Kumar --- CHANGELOG.md | 1 + .../client/utils/SerializationTest.java | 35 +++++++++++ .../resources/serialization/cronjob-octal.yml | 44 ++++++++++++++ .../jackson/IntegerOctalHandlerUtil.java | 39 ++++++++++++ .../jackson/IntegerOctalHandlerUtilTest.java | 44 ++++++++++++++ .../cmd/generate/generate.go | 12 ++++ .../api/model/ConfigMapVolumeSource.java | 2 +- .../api/model/DownwardAPIVolumeSource.java | 2 +- .../api/model/ProjectedVolumeSource.java | 2 +- .../api/model/SecretVolumeSource.java | 2 +- .../ConfigMapVolumeSourceDeserializer.java | 60 +++++++++++++++++++ .../DownwardAPIVolumeSourceDeserializer.java | 55 +++++++++++++++++ .../ProjectedVolumeSourceDeserializer.java | 44 ++++++++++++++ .../model/SecretVolumeSourceDeserializer.java | 60 +++++++++++++++++++ .../main/resources/schema/kube-schema.json | 12 ++-- .../resources/schema/validation-schema.json | 12 ++-- .../api/model/ConfigMapVolumeSourceTest.java | 49 +++++++++++++++ .../model/DownwardAPIVolumeSourceTest.java | 48 +++++++++++++++ .../api/model/ProjectedVolumeSourceTest.java | 52 ++++++++++++++++ .../api/model/SecretVolumeSourceTest.java | 49 +++++++++++++++ .../test/resources/configmapvolumesource.json | 11 ++++ .../resources/downwardapivolumesource.json | 12 ++++ .../test/resources/projectedvolumesource.json | 16 +++++ .../test/resources/secretvolumesource.json | 11 ++++ 24 files changed, 662 insertions(+), 12 deletions(-) create mode 100644 kubernetes-client-api/src/test/resources/serialization/cronjob-octal.yml create mode 100644 kubernetes-model-generator/kubernetes-model-common/src/main/java/io/fabric8/kubernetes/model/jackson/IntegerOctalHandlerUtil.java create mode 100644 kubernetes-model-generator/kubernetes-model-common/src/test/java/io/fabric8/kubernetes/model/jackson/IntegerOctalHandlerUtilTest.java create mode 100644 kubernetes-model-generator/kubernetes-model-core/src/main/java/io/fabric8/kubernetes/api/model/ConfigMapVolumeSourceDeserializer.java create mode 100644 kubernetes-model-generator/kubernetes-model-core/src/main/java/io/fabric8/kubernetes/api/model/DownwardAPIVolumeSourceDeserializer.java create mode 100644 kubernetes-model-generator/kubernetes-model-core/src/main/java/io/fabric8/kubernetes/api/model/ProjectedVolumeSourceDeserializer.java create mode 100644 kubernetes-model-generator/kubernetes-model-core/src/main/java/io/fabric8/kubernetes/api/model/SecretVolumeSourceDeserializer.java create mode 100644 kubernetes-model-generator/kubernetes-model-core/src/test/java/io/fabric8/kubernetes/api/model/ConfigMapVolumeSourceTest.java create mode 100644 kubernetes-model-generator/kubernetes-model-core/src/test/java/io/fabric8/kubernetes/api/model/DownwardAPIVolumeSourceTest.java create mode 100644 kubernetes-model-generator/kubernetes-model-core/src/test/java/io/fabric8/kubernetes/api/model/ProjectedVolumeSourceTest.java create mode 100644 kubernetes-model-generator/kubernetes-model-core/src/test/java/io/fabric8/kubernetes/api/model/SecretVolumeSourceTest.java create mode 100644 kubernetes-model-generator/kubernetes-model-core/src/test/resources/configmapvolumesource.json create mode 100644 kubernetes-model-generator/kubernetes-model-core/src/test/resources/downwardapivolumesource.json create mode 100644 kubernetes-model-generator/kubernetes-model-core/src/test/resources/projectedvolumesource.json create mode 100644 kubernetes-model-generator/kubernetes-model-core/src/test/resources/secretvolumesource.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 7dbbda1693e..6c2b2b65577 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ #### Improvements * Fix #6008: removing the optional dependency on bouncy castle * Fix #5264: Remove deprecated `Config.errorMessages` field +* Fix #6110: VolumeSource's Octal `defaultMode` notation in yaml not properly converted to json #### Dependency Upgrade * Fix #6052: Removed dependency on no longer maintained com.github.mifmif:generex 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 206e21bdf38..c9fdf1fc287 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 @@ -27,7 +27,9 @@ import io.fabric8.kubernetes.api.model.AnyType; import io.fabric8.kubernetes.api.model.Config; import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ConfigMapVolumeSource; import io.fabric8.kubernetes.api.model.GenericKubernetesResource; +import io.fabric8.kubernetes.api.model.KeyToPath; import io.fabric8.kubernetes.api.model.KubernetesList; import io.fabric8.kubernetes.api.model.KubernetesResource; import io.fabric8.kubernetes.api.model.Namespace; @@ -35,12 +37,18 @@ import io.fabric8.kubernetes.api.model.Pod; import io.fabric8.kubernetes.api.model.PodBuilder; import io.fabric8.kubernetes.api.model.PodSpec; +import io.fabric8.kubernetes.api.model.PodTemplateSpec; import io.fabric8.kubernetes.api.model.Quantity; import io.fabric8.kubernetes.api.model.Service; import io.fabric8.kubernetes.api.model.Toleration; +import io.fabric8.kubernetes.api.model.Volume; import io.fabric8.kubernetes.api.model.apiextensions.v1beta1.CustomResourceDefinition; import io.fabric8.kubernetes.api.model.apiextensions.v1beta1.JSONSchemaProps; import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.fabric8.kubernetes.api.model.batch.v1.CronJob; +import io.fabric8.kubernetes.api.model.batch.v1.CronJobSpec; +import io.fabric8.kubernetes.api.model.batch.v1.JobSpec; +import io.fabric8.kubernetes.api.model.batch.v1.JobTemplateSpec; import io.fabric8.kubernetes.api.model.coordination.v1.Lease; import io.fabric8.kubernetes.api.model.coordination.v1.LeaseSpec; import io.fabric8.kubernetes.api.model.runtime.RawExtension; @@ -310,6 +318,33 @@ void unmarshalWithValidListShouldReturnKubernetesList() { new Tuple(Pod.class, "v1", "Pod", "a-pod")); } + @Test + @DisplayName("unmarshal, when integer value has octal literal value, then octal literal value correctly parsed") + void unmarshal_whenIntegerValueHasOctalLiteralValue_thenCorrectlyDeserializeInteger() { + // When + final CronJob cronJob = Serialization.unmarshal(getClass().getResourceAsStream("/serialization/cronjob-octal.yml"), + CronJob.class); + // Then + assertThat(cronJob) + .extracting(CronJob::getSpec) + .extracting(CronJobSpec::getJobTemplate) + .extracting(JobTemplateSpec::getSpec) + .extracting(JobSpec::getTemplate) + .extracting(PodTemplateSpec::getSpec) + .extracting(PodSpec::getVolumes) + .asInstanceOf(InstanceOfAssertFactories.list(Volume.class)) + .singleElement() + .extracting(Volume::getConfigMap) + .hasFieldOrPropertyWithValue("defaultMode", Integer.valueOf("0555", 8)) + .hasFieldOrPropertyWithValue("name", "conf") + .extracting(ConfigMapVolumeSource::getItems) + .asInstanceOf(InstanceOfAssertFactories.list(KeyToPath.class)) + .singleElement() + .hasFieldOrPropertyWithValue("key", "key1") + .hasFieldOrPropertyWithValue("path", "target") + .hasFieldOrPropertyWithValue("mode", Integer.valueOf("0555", 8)); + } + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type") @JsonSubTypes(@JsonSubTypes.Type(value = Typed.class, name = "x")) public interface Typeable { diff --git a/kubernetes-client-api/src/test/resources/serialization/cronjob-octal.yml b/kubernetes-client-api/src/test/resources/serialization/cronjob-octal.yml new file mode 100644 index 00000000000..21927d77e15 --- /dev/null +++ b/kubernetes-client-api/src/test/resources/serialization/cronjob-octal.yml @@ -0,0 +1,44 @@ +# +# 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. +# + +apiVersion: batch/v1beta1 +kind: CronJob +metadata: + name: update-db +spec: + schedule: "*/1 * * * *" + jobTemplate: + spec: + template: + spec: + containers: + - name: update-fingerprints + image: python:3.6.2-slim + command: ["/bin/bash"] + args: ["-c", "python /client/test.py"] + volumeMounts: + - name: application-code + mountPath: /where/ever + restartPolicy: OnFailure + volumes: + - name: application-code + configMap: + name: conf + defaultMode: 0555 + items: + - key: key1 + path: target + mode: 0555 diff --git a/kubernetes-model-generator/kubernetes-model-common/src/main/java/io/fabric8/kubernetes/model/jackson/IntegerOctalHandlerUtil.java b/kubernetes-model-generator/kubernetes-model-common/src/main/java/io/fabric8/kubernetes/model/jackson/IntegerOctalHandlerUtil.java new file mode 100644 index 00000000000..29dd6b17fa1 --- /dev/null +++ b/kubernetes-model-generator/kubernetes-model-common/src/main/java/io/fabric8/kubernetes/model/jackson/IntegerOctalHandlerUtil.java @@ -0,0 +1,39 @@ +/* + * 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.model.jackson; + +import com.fasterxml.jackson.databind.JsonNode; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class IntegerOctalHandlerUtil { + private static final Pattern OCTAL_NUMBER = Pattern.compile("(0[oO]?)[0-7]+"); + + private IntegerOctalHandlerUtil() { + } + + public static Integer createIntegerValue(JsonNode node) { + String textValue = node.textValue(); + if (textValue != null) { + Matcher octalNumberMatcher = OCTAL_NUMBER.matcher(textValue); + if (octalNumberMatcher.matches()) { + return Integer.valueOf(textValue.substring(octalNumberMatcher.group(1).length()), 8); + } + } + return node.intValue(); + } +} diff --git a/kubernetes-model-generator/kubernetes-model-common/src/test/java/io/fabric8/kubernetes/model/jackson/IntegerOctalHandlerUtilTest.java b/kubernetes-model-generator/kubernetes-model-common/src/test/java/io/fabric8/kubernetes/model/jackson/IntegerOctalHandlerUtilTest.java new file mode 100644 index 00000000000..6fb1d7373bd --- /dev/null +++ b/kubernetes-model-generator/kubernetes-model-common/src/test/java/io/fabric8/kubernetes/model/jackson/IntegerOctalHandlerUtilTest.java @@ -0,0 +1,44 @@ +/* + * 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.model.jackson; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +class IntegerOctalHandlerUtilTest { + private final ObjectMapper objectMapper = new ObjectMapper(); + + private static Stream shouldParseOctalNumbersCorrectly() { + return Stream.of( + Arguments.of("\"012\"", Integer.valueOf("012", 8)), + Arguments.of("\"0o12\"", Integer.valueOf("012", 8)), + Arguments.of("\"0O12\"", Integer.valueOf("012", 8))); + } + + @ParameterizedTest + @MethodSource + void shouldParseOctalNumbersCorrectly(String input, Integer expectedValue) throws JsonProcessingException { + assertThat(IntegerOctalHandlerUtil.createIntegerValue(objectMapper.readTree(input))) + .isEqualTo(expectedValue); + } +} diff --git a/kubernetes-model-generator/kubernetes-model-core/cmd/generate/generate.go b/kubernetes-model-generator/kubernetes-model-core/cmd/generate/generate.go index 80b6baee55f..69e4d7a6af8 100644 --- a/kubernetes-model-generator/kubernetes-model-core/cmd/generate/generate.go +++ b/kubernetes-model-generator/kubernetes-model-core/cmd/generate/generate.go @@ -152,6 +152,18 @@ func main() { Serializer: "io.fabric8.kubernetes.api.model.MicroTimeSerDes.Serializer.class", Deserializer: "io.fabric8.kubernetes.api.model.MicroTimeSerDes.Deserializer.class", }, + "kubernetes_core_ConfigMapVolumeSource": &schemagen.JavaSerDeDescriptor{ + Deserializer: "io.fabric8.kubernetes.api.model.ConfigMapVolumeSourceDeserializer.class", + }, + "kubernetes_core_SecretVolumeSource": &schemagen.JavaSerDeDescriptor{ + Deserializer: "io.fabric8.kubernetes.api.model.SecretVolumeSourceDeserializer.class", + }, + "kubernetes_core_DownwardAPIVolumeSource": &schemagen.JavaSerDeDescriptor{ + Deserializer: "io.fabric8.kubernetes.api.model.DownwardAPIVolumeSourceDeserializer.class", + }, + "kubernetes_core_ProjectedVolumeSource": &schemagen.JavaSerDeDescriptor{ + Deserializer: "io.fabric8.kubernetes.api.model.ProjectedVolumeSourceDeserializer.class", + }, } for definitionKey, descriptor := range serdes { diff --git a/kubernetes-model-generator/kubernetes-model-core/src/generated/java/io/fabric8/kubernetes/api/model/ConfigMapVolumeSource.java b/kubernetes-model-generator/kubernetes-model-core/src/generated/java/io/fabric8/kubernetes/api/model/ConfigMapVolumeSource.java index d24b9065776..64f3b25230f 100644 --- a/kubernetes-model-generator/kubernetes-model-core/src/generated/java/io/fabric8/kubernetes/api/model/ConfigMapVolumeSource.java +++ b/kubernetes-model-generator/kubernetes-model-core/src/generated/java/io/fabric8/kubernetes/api/model/ConfigMapVolumeSource.java @@ -19,7 +19,7 @@ import lombok.ToString; import lombok.experimental.Accessors; -@JsonDeserialize(using = com.fasterxml.jackson.databind.JsonDeserializer.None.class) +@JsonDeserialize(using = io.fabric8.kubernetes.api.model.ConfigMapVolumeSourceDeserializer.class) @JsonInclude(JsonInclude.Include.NON_NULL) @JsonPropertyOrder({ "defaultMode", diff --git a/kubernetes-model-generator/kubernetes-model-core/src/generated/java/io/fabric8/kubernetes/api/model/DownwardAPIVolumeSource.java b/kubernetes-model-generator/kubernetes-model-core/src/generated/java/io/fabric8/kubernetes/api/model/DownwardAPIVolumeSource.java index 6fadacbdb71..bfb4369674e 100644 --- a/kubernetes-model-generator/kubernetes-model-core/src/generated/java/io/fabric8/kubernetes/api/model/DownwardAPIVolumeSource.java +++ b/kubernetes-model-generator/kubernetes-model-core/src/generated/java/io/fabric8/kubernetes/api/model/DownwardAPIVolumeSource.java @@ -19,7 +19,7 @@ import lombok.ToString; import lombok.experimental.Accessors; -@JsonDeserialize(using = com.fasterxml.jackson.databind.JsonDeserializer.None.class) +@JsonDeserialize(using = io.fabric8.kubernetes.api.model.DownwardAPIVolumeSourceDeserializer.class) @JsonInclude(JsonInclude.Include.NON_NULL) @JsonPropertyOrder({ "defaultMode", diff --git a/kubernetes-model-generator/kubernetes-model-core/src/generated/java/io/fabric8/kubernetes/api/model/ProjectedVolumeSource.java b/kubernetes-model-generator/kubernetes-model-core/src/generated/java/io/fabric8/kubernetes/api/model/ProjectedVolumeSource.java index 1fa31c7e860..f74ea0d947f 100644 --- a/kubernetes-model-generator/kubernetes-model-core/src/generated/java/io/fabric8/kubernetes/api/model/ProjectedVolumeSource.java +++ b/kubernetes-model-generator/kubernetes-model-core/src/generated/java/io/fabric8/kubernetes/api/model/ProjectedVolumeSource.java @@ -19,7 +19,7 @@ import lombok.ToString; import lombok.experimental.Accessors; -@JsonDeserialize(using = com.fasterxml.jackson.databind.JsonDeserializer.None.class) +@JsonDeserialize(using = io.fabric8.kubernetes.api.model.ProjectedVolumeSourceDeserializer.class) @JsonInclude(JsonInclude.Include.NON_NULL) @JsonPropertyOrder({ "defaultMode", diff --git a/kubernetes-model-generator/kubernetes-model-core/src/generated/java/io/fabric8/kubernetes/api/model/SecretVolumeSource.java b/kubernetes-model-generator/kubernetes-model-core/src/generated/java/io/fabric8/kubernetes/api/model/SecretVolumeSource.java index 14c1ddc75f4..62e4d88fbfa 100644 --- a/kubernetes-model-generator/kubernetes-model-core/src/generated/java/io/fabric8/kubernetes/api/model/SecretVolumeSource.java +++ b/kubernetes-model-generator/kubernetes-model-core/src/generated/java/io/fabric8/kubernetes/api/model/SecretVolumeSource.java @@ -19,7 +19,7 @@ import lombok.ToString; import lombok.experimental.Accessors; -@JsonDeserialize(using = com.fasterxml.jackson.databind.JsonDeserializer.None.class) +@JsonDeserialize(using = io.fabric8.kubernetes.api.model.SecretVolumeSourceDeserializer.class) @JsonInclude(JsonInclude.Include.NON_NULL) @JsonPropertyOrder({ "defaultMode", diff --git a/kubernetes-model-generator/kubernetes-model-core/src/main/java/io/fabric8/kubernetes/api/model/ConfigMapVolumeSourceDeserializer.java b/kubernetes-model-generator/kubernetes-model-core/src/main/java/io/fabric8/kubernetes/api/model/ConfigMapVolumeSourceDeserializer.java new file mode 100644 index 00000000000..7ba928e0565 --- /dev/null +++ b/kubernetes-model-generator/kubernetes-model-core/src/main/java/io/fabric8/kubernetes/api/model/ConfigMapVolumeSourceDeserializer.java @@ -0,0 +1,60 @@ +/* + * 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.api.model; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; + +import java.io.IOException; + +import static io.fabric8.kubernetes.model.jackson.IntegerOctalHandlerUtil.createIntegerValue; + +public class ConfigMapVolumeSourceDeserializer extends JsonDeserializer { + + @Override + public ConfigMapVolumeSource deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) + throws IOException { + JsonNode node = jsonParser.getCodec().readTree(jsonParser); + ConfigMapVolumeSourceBuilder builder = new ConfigMapVolumeSourceBuilder(); + if (node.get("items") != null) { + for (final JsonNode keyToPathNode : node.get("items")) { + builder.addToItems(createKeyToPath(keyToPathNode, jsonParser)); + } + } + if (node.get("name") != null) { + builder.withName(node.get("name").textValue()); + } + if (node.get("optional") != null) { + builder.withOptional(node.get("optional").booleanValue()); + } + if (node.get("defaultMode") != null) { + builder.withDefaultMode(createIntegerValue(node.get("defaultMode"))); + } + return builder.build(); + } + + private KeyToPath createKeyToPath(JsonNode node, JsonParser jsonParser) throws JsonProcessingException { + KeyToPath keyToPath = jsonParser.getCodec().treeToValue(node, KeyToPath.class); + KeyToPathBuilder keyToPathBuilder = new KeyToPathBuilder(keyToPath); + if (node.get("mode") != null) { + keyToPathBuilder.withMode(createIntegerValue(node.get("mode"))); + } + return keyToPathBuilder.build(); + } +} diff --git a/kubernetes-model-generator/kubernetes-model-core/src/main/java/io/fabric8/kubernetes/api/model/DownwardAPIVolumeSourceDeserializer.java b/kubernetes-model-generator/kubernetes-model-core/src/main/java/io/fabric8/kubernetes/api/model/DownwardAPIVolumeSourceDeserializer.java new file mode 100644 index 00000000000..f950e120d23 --- /dev/null +++ b/kubernetes-model-generator/kubernetes-model-core/src/main/java/io/fabric8/kubernetes/api/model/DownwardAPIVolumeSourceDeserializer.java @@ -0,0 +1,55 @@ +/* + * 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.api.model; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; + +import java.io.IOException; + +import static io.fabric8.kubernetes.model.jackson.IntegerOctalHandlerUtil.createIntegerValue; + +public class DownwardAPIVolumeSourceDeserializer extends JsonDeserializer { + + @Override + public DownwardAPIVolumeSource deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) + throws IOException { + JsonNode node = jsonParser.getCodec().readTree(jsonParser); + DownwardAPIVolumeSourceBuilder builder = new DownwardAPIVolumeSourceBuilder(); + if (node.get("items") != null) { + for (final JsonNode keyToPathNode : node.get("items")) { + builder.addToItems(createDownwardAPIVolumeFile(keyToPathNode, jsonParser)); + } + } + if (node.get("defaultMode") != null) { + builder.withDefaultMode(createIntegerValue(node.get("defaultMode"))); + } + return builder.build(); + } + + private DownwardAPIVolumeFile createDownwardAPIVolumeFile(JsonNode node, JsonParser jsonParser) + throws JsonProcessingException { + DownwardAPIVolumeFile downwardAPIVolumeFile = jsonParser.getCodec().treeToValue(node, DownwardAPIVolumeFile.class); + DownwardAPIVolumeFileBuilder downwardAPIVolumeFileBuilder = new DownwardAPIVolumeFileBuilder(downwardAPIVolumeFile); + if (node.get("mode") != null) { + downwardAPIVolumeFileBuilder.withMode(createIntegerValue(node.get("mode"))); + } + return downwardAPIVolumeFileBuilder.build(); + } +} diff --git a/kubernetes-model-generator/kubernetes-model-core/src/main/java/io/fabric8/kubernetes/api/model/ProjectedVolumeSourceDeserializer.java b/kubernetes-model-generator/kubernetes-model-core/src/main/java/io/fabric8/kubernetes/api/model/ProjectedVolumeSourceDeserializer.java new file mode 100644 index 00000000000..9af8b0a5fcb --- /dev/null +++ b/kubernetes-model-generator/kubernetes-model-core/src/main/java/io/fabric8/kubernetes/api/model/ProjectedVolumeSourceDeserializer.java @@ -0,0 +1,44 @@ +/* + * 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.api.model; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; + +import java.io.IOException; + +import static io.fabric8.kubernetes.model.jackson.IntegerOctalHandlerUtil.createIntegerValue; + +public class ProjectedVolumeSourceDeserializer extends JsonDeserializer { + + @Override + public ProjectedVolumeSource deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) + throws IOException { + JsonNode node = jsonParser.getCodec().readTree(jsonParser); + ProjectedVolumeSourceBuilder builder = new ProjectedVolumeSourceBuilder(); + if (node.get("sources") != null) { + for (final JsonNode sourceNode : node.get("sources")) { + builder.addToSources(jsonParser.getCodec().treeToValue(sourceNode, VolumeProjection.class)); + } + } + if (node.get("defaultMode") != null) { + builder.withDefaultMode(createIntegerValue(node.get("defaultMode"))); + } + return builder.build(); + } +} diff --git a/kubernetes-model-generator/kubernetes-model-core/src/main/java/io/fabric8/kubernetes/api/model/SecretVolumeSourceDeserializer.java b/kubernetes-model-generator/kubernetes-model-core/src/main/java/io/fabric8/kubernetes/api/model/SecretVolumeSourceDeserializer.java new file mode 100644 index 00000000000..3a130bec5fc --- /dev/null +++ b/kubernetes-model-generator/kubernetes-model-core/src/main/java/io/fabric8/kubernetes/api/model/SecretVolumeSourceDeserializer.java @@ -0,0 +1,60 @@ +/* + * 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.api.model; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; + +import java.io.IOException; + +import static io.fabric8.kubernetes.model.jackson.IntegerOctalHandlerUtil.createIntegerValue; + +public class SecretVolumeSourceDeserializer extends JsonDeserializer { + + @Override + public SecretVolumeSource deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) + throws IOException { + JsonNode node = jsonParser.getCodec().readTree(jsonParser); + SecretVolumeSourceBuilder builder = new SecretVolumeSourceBuilder(); + if (node.get("items") != null) { + for (final JsonNode keyToPathNode : node.get("items")) { + builder.addToItems(createKeyToPath(keyToPathNode, jsonParser)); + } + } + if (node.get("secretName") != null) { + builder.withSecretName(node.get("secretName").textValue()); + } + if (node.get("optional") != null) { + builder.withOptional(node.get("optional").booleanValue()); + } + if (node.get("defaultMode") != null) { + builder.withDefaultMode(createIntegerValue(node.get("defaultMode"))); + } + return builder.build(); + } + + private KeyToPath createKeyToPath(JsonNode node, JsonParser jsonParser) throws JsonProcessingException { + KeyToPath keyToPath = jsonParser.getCodec().treeToValue(node, KeyToPath.class); + KeyToPathBuilder keyToPathBuilder = new KeyToPathBuilder(keyToPath); + if (node.get("mode") != null) { + keyToPathBuilder.withMode(createIntegerValue(node.get("mode"))); + } + return keyToPathBuilder.build(); + } +} diff --git a/kubernetes-model-generator/kubernetes-model-core/src/main/resources/schema/kube-schema.json b/kubernetes-model-generator/kubernetes-model-core/src/main/resources/schema/kube-schema.json index 59da900f808..521c84b552e 100644 --- a/kubernetes-model-generator/kubernetes-model-core/src/main/resources/schema/kube-schema.json +++ b/kubernetes-model-generator/kubernetes-model-core/src/main/resources/schema/kube-schema.json @@ -2237,7 +2237,8 @@ "javaType": "io.fabric8.kubernetes.api.model.ConfigMapVolumeSource", "javaInterfaces": [ "io.fabric8.kubernetes.api.model.KubernetesResource" - ] + ], + "deserializer": "io.fabric8.kubernetes.api.model.ConfigMapVolumeSourceDeserializer.class" }, "kubernetes_core_Container": { "type": "object", @@ -2647,7 +2648,8 @@ "javaType": "io.fabric8.kubernetes.api.model.DownwardAPIVolumeSource", "javaInterfaces": [ "io.fabric8.kubernetes.api.model.KubernetesResource" - ] + ], + "deserializer": "io.fabric8.kubernetes.api.model.DownwardAPIVolumeSourceDeserializer.class" }, "kubernetes_core_EmptyDirVolumeSource": { "type": "object", @@ -6130,7 +6132,8 @@ "javaType": "io.fabric8.kubernetes.api.model.ProjectedVolumeSource", "javaInterfaces": [ "io.fabric8.kubernetes.api.model.KubernetesResource" - ] + ], + "deserializer": "io.fabric8.kubernetes.api.model.ProjectedVolumeSourceDeserializer.class" }, "kubernetes_core_QuobyteVolumeSource": { "type": "object", @@ -6912,7 +6915,8 @@ "javaType": "io.fabric8.kubernetes.api.model.SecretVolumeSource", "javaInterfaces": [ "io.fabric8.kubernetes.api.model.KubernetesResource" - ] + ], + "deserializer": "io.fabric8.kubernetes.api.model.SecretVolumeSourceDeserializer.class" }, "kubernetes_core_SecurityContext": { "type": "object", diff --git a/kubernetes-model-generator/kubernetes-model-core/src/main/resources/schema/validation-schema.json b/kubernetes-model-generator/kubernetes-model-core/src/main/resources/schema/validation-schema.json index 9e0a4d9c6c2..0b23998a896 100644 --- a/kubernetes-model-generator/kubernetes-model-core/src/main/resources/schema/validation-schema.json +++ b/kubernetes-model-generator/kubernetes-model-core/src/main/resources/schema/validation-schema.json @@ -2237,7 +2237,8 @@ "javaType": "io.fabric8.kubernetes.api.model.ConfigMapVolumeSource", "javaInterfaces": [ "io.fabric8.kubernetes.api.model.KubernetesResource" - ] + ], + "deserializer": "io.fabric8.kubernetes.api.model.ConfigMapVolumeSourceDeserializer.class" }, "kubernetes_core_Container": { "type": "object", @@ -2647,7 +2648,8 @@ "javaType": "io.fabric8.kubernetes.api.model.DownwardAPIVolumeSource", "javaInterfaces": [ "io.fabric8.kubernetes.api.model.KubernetesResource" - ] + ], + "deserializer": "io.fabric8.kubernetes.api.model.DownwardAPIVolumeSourceDeserializer.class" }, "kubernetes_core_EmptyDirVolumeSource": { "type": "object", @@ -6130,7 +6132,8 @@ "javaType": "io.fabric8.kubernetes.api.model.ProjectedVolumeSource", "javaInterfaces": [ "io.fabric8.kubernetes.api.model.KubernetesResource" - ] + ], + "deserializer": "io.fabric8.kubernetes.api.model.ProjectedVolumeSourceDeserializer.class" }, "kubernetes_core_QuobyteVolumeSource": { "type": "object", @@ -6912,7 +6915,8 @@ "javaType": "io.fabric8.kubernetes.api.model.SecretVolumeSource", "javaInterfaces": [ "io.fabric8.kubernetes.api.model.KubernetesResource" - ] + ], + "deserializer": "io.fabric8.kubernetes.api.model.SecretVolumeSourceDeserializer.class" }, "kubernetes_core_SecurityContext": { "type": "object", diff --git a/kubernetes-model-generator/kubernetes-model-core/src/test/java/io/fabric8/kubernetes/api/model/ConfigMapVolumeSourceTest.java b/kubernetes-model-generator/kubernetes-model-core/src/test/java/io/fabric8/kubernetes/api/model/ConfigMapVolumeSourceTest.java new file mode 100644 index 00000000000..dc0063b1f4e --- /dev/null +++ b/kubernetes-model-generator/kubernetes-model-core/src/test/java/io/fabric8/kubernetes/api/model/ConfigMapVolumeSourceTest.java @@ -0,0 +1,49 @@ +/* + * 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.api.model; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.assertj.core.api.InstanceOfAssertFactories; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.InputStream; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +class ConfigMapVolumeSourceTest { + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Test + void deserializationWithOctalValueShouldWorkAsExpected() throws IOException { + // Given + InputStream inputStream = getClass().getResourceAsStream("/configmapvolumesource.json"); + + // When + ConfigMapVolumeSource configMapVolumeSource = objectMapper.readValue(inputStream, ConfigMapVolumeSource.class); + + // Then + assertThat(configMapVolumeSource) + .hasFieldOrPropertyWithValue("defaultMode", Integer.valueOf("0555", 8)) + .hasFieldOrPropertyWithValue("name", "conf") + .extracting(ConfigMapVolumeSource::getItems) + .asInstanceOf(InstanceOfAssertFactories.list(KeyToPath.class)) + .singleElement() + .hasFieldOrPropertyWithValue("key", "key1") + .hasFieldOrPropertyWithValue("path", "target") + .hasFieldOrPropertyWithValue("mode", Integer.valueOf("0555", 8)); + } +} diff --git a/kubernetes-model-generator/kubernetes-model-core/src/test/java/io/fabric8/kubernetes/api/model/DownwardAPIVolumeSourceTest.java b/kubernetes-model-generator/kubernetes-model-core/src/test/java/io/fabric8/kubernetes/api/model/DownwardAPIVolumeSourceTest.java new file mode 100644 index 00000000000..085a36e2acc --- /dev/null +++ b/kubernetes-model-generator/kubernetes-model-core/src/test/java/io/fabric8/kubernetes/api/model/DownwardAPIVolumeSourceTest.java @@ -0,0 +1,48 @@ +/* + * 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.api.model; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.assertj.core.api.InstanceOfAssertFactories; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.InputStream; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +class DownwardAPIVolumeSourceTest { + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Test + void deserializationWithOctalValueShouldWorkAsExpected() throws IOException { + // Given + InputStream inputStream = getClass().getResourceAsStream("/downwardapivolumesource.json"); + + // When + DownwardAPIVolumeSource downwardAPIVolumeSource = objectMapper.readValue(inputStream, DownwardAPIVolumeSource.class); + + // Then + assertThat(downwardAPIVolumeSource) + .hasFieldOrPropertyWithValue("defaultMode", Integer.valueOf("0555", 8)) + .extracting(DownwardAPIVolumeSource::getItems) + .asInstanceOf(InstanceOfAssertFactories.list(DownwardAPIVolumeFile.class)) + .singleElement() + .hasFieldOrPropertyWithValue("path", "labels") + .hasFieldOrPropertyWithValue("fieldRef.fieldPath", "metadata.labels") + .hasFieldOrPropertyWithValue("mode", Integer.valueOf("0555", 8)); + } +} diff --git a/kubernetes-model-generator/kubernetes-model-core/src/test/java/io/fabric8/kubernetes/api/model/ProjectedVolumeSourceTest.java b/kubernetes-model-generator/kubernetes-model-core/src/test/java/io/fabric8/kubernetes/api/model/ProjectedVolumeSourceTest.java new file mode 100644 index 00000000000..9cb419793f5 --- /dev/null +++ b/kubernetes-model-generator/kubernetes-model-core/src/test/java/io/fabric8/kubernetes/api/model/ProjectedVolumeSourceTest.java @@ -0,0 +1,52 @@ +/* + * 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.api.model; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.assertj.core.api.InstanceOfAssertFactories; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.InputStream; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +class ProjectedVolumeSourceTest { + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Test + void deserializationWithOctalValueShouldWorkAsExpected() throws IOException { + // Given + InputStream inputStream = getClass().getResourceAsStream("/projectedvolumesource.json"); + + // When + ProjectedVolumeSource projectedVolumeSource = objectMapper.readValue(inputStream, ProjectedVolumeSource.class); + + // Then + assertThat(projectedVolumeSource) + .hasFieldOrPropertyWithValue("defaultMode", Integer.valueOf("0555", 8)) + .extracting(ProjectedVolumeSource::getSources) + .asInstanceOf(InstanceOfAssertFactories.list(VolumeProjection.class)) + .singleElement() + .extracting(VolumeProjection::getSecret) + .hasFieldOrPropertyWithValue("name", "mysecret") + .extracting(SecretProjection::getItems) + .asInstanceOf(InstanceOfAssertFactories.list(KeyToPath.class)) + .singleElement() + .hasFieldOrPropertyWithValue("key", "username") + .hasFieldOrPropertyWithValue("path", "my-group/my-username"); + } +} diff --git a/kubernetes-model-generator/kubernetes-model-core/src/test/java/io/fabric8/kubernetes/api/model/SecretVolumeSourceTest.java b/kubernetes-model-generator/kubernetes-model-core/src/test/java/io/fabric8/kubernetes/api/model/SecretVolumeSourceTest.java new file mode 100644 index 00000000000..b51ab420bfd --- /dev/null +++ b/kubernetes-model-generator/kubernetes-model-core/src/test/java/io/fabric8/kubernetes/api/model/SecretVolumeSourceTest.java @@ -0,0 +1,49 @@ +/* + * 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.api.model; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.assertj.core.api.InstanceOfAssertFactories; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.InputStream; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +class SecretVolumeSourceTest { + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Test + void deserializationWithOctalValueShouldWorkAsExpected() throws IOException { + // Given + InputStream inputStream = getClass().getResourceAsStream("/secretvolumesource.json"); + + // When + SecretVolumeSource secretVolumeSource = objectMapper.readValue(inputStream, SecretVolumeSource.class); + + // Then + assertThat(secretVolumeSource) + .hasFieldOrPropertyWithValue("defaultMode", Integer.valueOf("0555", 8)) + .hasFieldOrPropertyWithValue("secretName", "conf") + .extracting(SecretVolumeSource::getItems) + .asInstanceOf(InstanceOfAssertFactories.list(KeyToPath.class)) + .singleElement() + .hasFieldOrPropertyWithValue("key", "key1") + .hasFieldOrPropertyWithValue("path", "target") + .hasFieldOrPropertyWithValue("mode", Integer.valueOf("0555", 8)); + } +} diff --git a/kubernetes-model-generator/kubernetes-model-core/src/test/resources/configmapvolumesource.json b/kubernetes-model-generator/kubernetes-model-core/src/test/resources/configmapvolumesource.json new file mode 100644 index 00000000000..efbaaafe280 --- /dev/null +++ b/kubernetes-model-generator/kubernetes-model-core/src/test/resources/configmapvolumesource.json @@ -0,0 +1,11 @@ +{ + "name": "conf", + "defaultMode": "0555", + "items": [ + { + "key": "key1", + "path": "target", + "mode": "0555" + } + ] +} diff --git a/kubernetes-model-generator/kubernetes-model-core/src/test/resources/downwardapivolumesource.json b/kubernetes-model-generator/kubernetes-model-core/src/test/resources/downwardapivolumesource.json new file mode 100644 index 00000000000..be5ca3f7ec1 --- /dev/null +++ b/kubernetes-model-generator/kubernetes-model-core/src/test/resources/downwardapivolumesource.json @@ -0,0 +1,12 @@ +{ + "defaultMode": "0555", + "items": [ + { + "path": "labels", + "fieldRef": { + "fieldPath": "metadata.labels" + }, + "mode": "0555" + } + ] +} diff --git a/kubernetes-model-generator/kubernetes-model-core/src/test/resources/projectedvolumesource.json b/kubernetes-model-generator/kubernetes-model-core/src/test/resources/projectedvolumesource.json new file mode 100644 index 00000000000..0cf95a4296c --- /dev/null +++ b/kubernetes-model-generator/kubernetes-model-core/src/test/resources/projectedvolumesource.json @@ -0,0 +1,16 @@ +{ + "defaultMode": "0555", + "sources": [ + { + "secret": { + "name": "mysecret", + "items": [ + { + "key": "username", + "path": "my-group/my-username" + } + ] + } + } + ] +} diff --git a/kubernetes-model-generator/kubernetes-model-core/src/test/resources/secretvolumesource.json b/kubernetes-model-generator/kubernetes-model-core/src/test/resources/secretvolumesource.json new file mode 100644 index 00000000000..d3cccae075e --- /dev/null +++ b/kubernetes-model-generator/kubernetes-model-core/src/test/resources/secretvolumesource.json @@ -0,0 +1,11 @@ +{ + "secretName": "conf", + "defaultMode": "0555", + "items": [ + { + "key": "key1", + "path": "target", + "mode": "0555" + } + ] +}