Skip to content

Commit

Permalink
fix (kubernetes-model-generator) : Handle deserialization of `default…
Browse files Browse the repository at this point in the history
…Mode` 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 <[email protected]>
  • Loading branch information
rohanKanojia committed Jul 17, 2024
1 parent 202b625 commit 250638e
Show file tree
Hide file tree
Showing 24 changed files with 662 additions and 12 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,28 @@
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;
import io.fabric8.kubernetes.api.model.ObjectMeta;
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;
Expand Down Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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<Arguments> 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
@@ -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<ConfigMapVolumeSource> {

@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();
}
}
Original file line number Diff line number Diff line change
@@ -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<DownwardAPIVolumeSource> {

@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();
}
}
Loading

0 comments on commit 250638e

Please sign in to comment.