diff --git a/CHANGELOG.md b/CHANGELOG.md index 6237be1130e..82a6ca225e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ #### Bugs * Fix #6066: Added support for missing `v1.APIVersions` in KubernetesClient +* Fix #6110: VolumeSource (and other file mode fields) in Octal are correctly interpreted ### 6.13.1 (2024-07-02) 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 index db04df0247c..7f7bb505178 100644 --- 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 @@ -39,6 +39,7 @@ 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.GoCompatibilityModule; import io.fabric8.kubernetes.model.jackson.UnmatchedFieldTypeModule; import org.snakeyaml.engine.v2.api.Dump; import org.snakeyaml.engine.v2.api.DumpSettings; @@ -89,7 +90,7 @@ public KubernetesSerialization(ObjectMapper mapper, boolean searchClassloaders) } protected void configureMapper(ObjectMapper mapper) { - mapper.registerModules(new JavaTimeModule(), unmatchedFieldTypeModule); + mapper.registerModules(new JavaTimeModule(), new GoCompatibilityModule(), unmatchedFieldTypeModule); mapper.disable(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE); mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); mapper.disable(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS); 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 5dde896af5a..0fc3970f90c 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 @@ -20,6 +20,7 @@ import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator; import io.fabric8.kubernetes.api.model.KubernetesResource; +import io.fabric8.kubernetes.model.jackson.GoCompatibilityModule; import io.fabric8.kubernetes.model.jackson.UnmatchedFieldTypeModule; import java.io.InputStream; @@ -88,7 +89,7 @@ public static ObjectMapper yamlMapper() { if (YAML_MAPPER == null) { YAML_MAPPER = new ObjectMapper( new YAMLFactory().disable(YAMLGenerator.Feature.USE_NATIVE_TYPE_ID)); - YAML_MAPPER.registerModules(UNMATCHED_FIELD_TYPE_MODULE); + YAML_MAPPER.registerModules(new GoCompatibilityModule(), UNMATCHED_FIELD_TYPE_MODULE); } } } 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..5123000578e --- /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: 0o555 + items: + - key: key1 + path: target + mode: 0555 diff --git a/kubernetes-model-generator/kubernetes-model-common/src/main/java/io/fabric8/kubernetes/model/jackson/GoCompatibilityModule.java b/kubernetes-model-generator/kubernetes-model-common/src/main/java/io/fabric8/kubernetes/model/jackson/GoCompatibilityModule.java new file mode 100644 index 00000000000..3da432b9019 --- /dev/null +++ b/kubernetes-model-generator/kubernetes-model-common/src/main/java/io/fabric8/kubernetes/model/jackson/GoCompatibilityModule.java @@ -0,0 +1,25 @@ +/* + * 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.module.SimpleModule; + +public class GoCompatibilityModule extends SimpleModule { + + public GoCompatibilityModule() { + addDeserializer(Integer.class, new GoIntegerDeserializer()); + } +} diff --git a/kubernetes-model-generator/kubernetes-model-common/src/main/java/io/fabric8/kubernetes/model/jackson/GoIntegerDeserializer.java b/kubernetes-model-generator/kubernetes-model-common/src/main/java/io/fabric8/kubernetes/model/jackson/GoIntegerDeserializer.java new file mode 100644 index 00000000000..7bc012103f1 --- /dev/null +++ b/kubernetes-model-generator/kubernetes-model-common/src/main/java/io/fabric8/kubernetes/model/jackson/GoIntegerDeserializer.java @@ -0,0 +1,71 @@ +/* + * 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.JsonParser; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.deser.ContextualDeserializer; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class GoIntegerDeserializer extends StdDeserializer implements ContextualDeserializer { + + private static final Pattern OCTAL = Pattern.compile("(0[oO]?)([0-7]+)"); + private static final GoIntegerDeserializer APPLICABLE_INSTANCE = new GoIntegerDeserializer(true); + private static final Set APPLICABLE_FIELDS = new HashSet<>(Arrays.asList("mode", "defaultMode")); + + private final boolean applicable; + + protected GoIntegerDeserializer() { + this(false); + } + + private GoIntegerDeserializer(boolean applicable) { + super(Integer.class); + this.applicable = applicable; + } + + @Override + public Integer deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + final String value = p.getText(); + if (value == null) { + return null; + } + if (applicable) { + final Matcher matcher = OCTAL.matcher(value); + if (matcher.find()) { + return Integer.valueOf(matcher.group(2), 8); + } + } + return _parseInteger(ctxt, value); + } + + @Override + public JsonDeserializer createContextual(DeserializationContext ctxt, BeanProperty property) { + if (property != null && APPLICABLE_FIELDS.contains(property.getName())) { + return APPLICABLE_INSTANCE; + } + return this; + } +} diff --git a/kubernetes-model-generator/kubernetes-model-common/src/test/java/io/fabric8/kubernetes/model/jackson/GoIntegerDeserializerTest.java b/kubernetes-model-generator/kubernetes-model-common/src/test/java/io/fabric8/kubernetes/model/jackson/GoIntegerDeserializerTest.java new file mode 100644 index 00000000000..d7a3a564f68 --- /dev/null +++ b/kubernetes-model-generator/kubernetes-model-common/src/test/java/io/fabric8/kubernetes/model/jackson/GoIntegerDeserializerTest.java @@ -0,0 +1,101 @@ +/* + * 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.ObjectMapper; +import com.fasterxml.jackson.databind.exc.InvalidFormatException; +import lombok.Data; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +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.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS; + +class GoIntegerDeserializerTest { + + private ObjectMapper context; + + @BeforeEach + void setUp() { + context = new ObjectMapper(); + context.registerModule(new GoCompatibilityModule()); + } + + @Nested + @TestInstance(PER_CLASS) + class Applicable { + + @ParameterizedTest(name = "{index}: with '{'\"{0}\": {1}'}' parses as {2}") + @MethodSource + void parsesOctals(String fieldName, String content, Integer expected) throws Exception { + final IntegerFieldsContainer result = context + .readValue(String.format("{\"%s\": %s}", fieldName, content), IntegerFieldsContainer.class); + assertThat(result).hasFieldOrPropertyWithValue(fieldName, expected); + } + + private Stream parsesOctals() { + return Stream.of("mode", "defaultMode") + .flatMap(field -> Stream.of( + Arguments.of(field, "null", null), + Arguments.of(field, "\"0555\"", 365), + Arguments.of(field, "\"0o555\"", 365), + Arguments.of(field, "\"0O555\"", 365), + Arguments.of(field, "\"555\"", 555), + Arguments.of(field, "\"0888\"", 888), + Arguments.of(field, "\"0o12\"", 10), + Arguments.of(field, "\"0O12\"", 10))); + } + } + + // + @Nested + class NotApplicable { + + @Test + void parsesOctalsAsDecimal() throws Exception { + final IntegerFieldsContainer result = context + .readValue("{\"notApplicable\": \"0555\"}", IntegerFieldsContainer.class); + assertThat(result).hasFieldOrPropertyWithValue("notApplicable", 555); + } + + @Test + void throwsExceptionForInvalidOctal() { + assertThatThrownBy(() -> context.readValue("{\"mode\": \"0o955\"}", IntegerFieldsContainer.class)) + .isInstanceOf(InvalidFormatException.class); + } + + @Test + void throwsExceptionForOctalWithSeparator() { + assertThatThrownBy(() -> context.readValue("{\"notApplicable\": \"0o555\"}", IntegerFieldsContainer.class)) + .isInstanceOf(InvalidFormatException.class); + } + } + + @Data + private static final class IntegerFieldsContainer { + private Integer mode; + private Integer defaultMode; + private Integer notApplicable; + } +} 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..e977f932f87 --- /dev/null +++ b/kubernetes-model-generator/kubernetes-model-core/src/test/java/io/fabric8/kubernetes/api/model/ConfigMapVolumeSourceTest.java @@ -0,0 +1,58 @@ +/* + * 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 io.fabric8.kubernetes.model.jackson.GoCompatibilityModule; +import org.assertj.core.api.InstanceOfAssertFactories; +import org.junit.jupiter.api.BeforeEach; +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 ObjectMapper objectMapper; + + @BeforeEach + void setUp() { + objectMapper = new ObjectMapper(); + objectMapper.registerModule(new GoCompatibilityModule()); + } + + @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..9da8b8bb2df --- /dev/null +++ b/kubernetes-model-generator/kubernetes-model-core/src/test/java/io/fabric8/kubernetes/api/model/DownwardAPIVolumeSourceTest.java @@ -0,0 +1,57 @@ +/* + * 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 io.fabric8.kubernetes.model.jackson.GoCompatibilityModule; +import org.assertj.core.api.InstanceOfAssertFactories; +import org.junit.jupiter.api.BeforeEach; +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 ObjectMapper objectMapper; + + @BeforeEach + void setUp() { + objectMapper = new ObjectMapper(); + objectMapper.registerModule(new GoCompatibilityModule()); + } + + @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..00388fa2fdd --- /dev/null +++ b/kubernetes-model-generator/kubernetes-model-core/src/test/java/io/fabric8/kubernetes/api/model/ProjectedVolumeSourceTest.java @@ -0,0 +1,61 @@ +/* + * 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 io.fabric8.kubernetes.model.jackson.GoCompatibilityModule; +import org.assertj.core.api.InstanceOfAssertFactories; +import org.junit.jupiter.api.BeforeEach; +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 ObjectMapper objectMapper; + + @BeforeEach + void setUp() { + objectMapper = new ObjectMapper(); + objectMapper.registerModule(new GoCompatibilityModule()); + } + + @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..c998975793d --- /dev/null +++ b/kubernetes-model-generator/kubernetes-model-core/src/test/java/io/fabric8/kubernetes/api/model/SecretVolumeSourceTest.java @@ -0,0 +1,58 @@ +/* + * 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 io.fabric8.kubernetes.model.jackson.GoCompatibilityModule; +import org.assertj.core.api.InstanceOfAssertFactories; +import org.junit.jupiter.api.BeforeEach; +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 ObjectMapper objectMapper; + + @BeforeEach + void setUp() { + objectMapper = new ObjectMapper(); + objectMapper.registerModule(new GoCompatibilityModule()); + } + + @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..5e84791039d --- /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": "0O555" + } + ] +} 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" + } + ] +}