From db1923b62b44b774077cf5359b0492ab6eda75bb Mon Sep 17 00:00:00 2001 From: "rohaan@redhat.com" Date: Thu, 10 Dec 2020 23:20:09 +0530 Subject: [PATCH] Fix #2510: Yaml containing aliases rejected due to FasterXML bug Modify the way we deserialize Yaml objects. Load them into raw HashMap using SnakeYAML and then use Jackson to convert them to JSON and finally the required object --- CHANGELOG.md | 1 + .../client/utils/Serialization.java | 20 +++++++++-- .../client/utils/SerializationTest.java | 28 +++++++++++++++- .../test-pod-manifest-with-aliases.yml | 33 +++++++++++++++++++ .../client/mock/ResourceQuotaTest.java | 25 ++++++++++++-- 5 files changed, 101 insertions(+), 6 deletions(-) create mode 100644 kubernetes-client/src/test/resources/test-pod-manifest-with-aliases.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b22b9b1ede..bdb19a7e36c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### 5.0-SNAPSHOT #### Bugs +Fix #2510 : Yaml containing aliases rejected due to FasterXML bug #### Improvements diff --git a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/utils/Serialization.java b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/utils/Serialization.java index a965520dbb6..d68101908cc 100644 --- a/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/utils/Serialization.java +++ b/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/utils/Serialization.java @@ -22,6 +22,7 @@ import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import io.fabric8.kubernetes.api.model.KubernetesResource; import io.fabric8.kubernetes.client.KubernetesClientException; +import org.yaml.snakeyaml.Yaml; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; @@ -132,7 +133,7 @@ public static T unmarshal(InputStream is, ObjectMapper mapper, Map T unmarshal(InputStream is, TypeReference type, Map T unmarshal(InputStream is, TypeReference type, Map getKubernetesResourceList(Map parameters, String specFile) { return splitSpecFile(specFile).stream().filter(Serialization::validate) .map(document -> @@ -294,4 +294,18 @@ private static String readSpecFileFromInputStream(InputStream inputStream) { throw new RuntimeException("Unable to read InputStream." + e); } } + + private static T unmarshalYaml(InputStream is, TypeReference type) throws JsonProcessingException { + final Yaml yaml = new Yaml(); + Map obj = yaml.load(is); + String objAsJsonStr = JSON_MAPPER.writeValueAsString(obj); + return unmarshalJsonStr(objAsJsonStr, type); + } + + private static T unmarshalJsonStr(String jsonString, TypeReference type) throws JsonProcessingException { + if (type != null) { + return JSON_MAPPER.readValue(jsonString, type); + } + return JSON_MAPPER.readerFor(KubernetesResource.class).readValue(jsonString); + } } diff --git a/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/utils/SerializationTest.java b/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/utils/SerializationTest.java index 02469e3eda9..861a4c5279c 100644 --- a/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/utils/SerializationTest.java +++ b/kubernetes-client/src/test/java/io/fabric8/kubernetes/client/utils/SerializationTest.java @@ -17,17 +17,19 @@ import io.fabric8.kubernetes.api.model.KubernetesList; import io.fabric8.kubernetes.api.model.KubernetesResource; +import io.fabric8.kubernetes.api.model.Pod; +import io.fabric8.kubernetes.api.model.Quantity; import io.fabric8.kubernetes.api.model.Service; 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 org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.Map; @@ -139,4 +141,28 @@ void containsMultipleDocumentsWithSingleDocumentAndLinuxLineEnds() { // Then assertFalse(result); } + + @Test + void testSerializeYamlWithAlias() { + // Given + InputStream fileInputStream = getClass().getResourceAsStream("/test-pod-manifest-with-aliases.yml"); + + // When + Pod pod = Serialization.unmarshal(fileInputStream); + + // Then + assertNotNull(pod); + assertEquals("test-pod-with-alias", pod.getMetadata().getName()); + assertEquals("build", pod.getSpec().getNodeSelector().get("workload")); + assertEquals(1, pod.getSpec().getTolerations().size()); + assertEquals(1000, pod.getSpec().getSecurityContext().getRunAsGroup().intValue()); + assertEquals(1000, pod.getSpec().getSecurityContext().getRunAsUser().intValue()); + assertEquals(2, pod.getSpec().getContainers().size()); + assertEquals("ubuntu", pod.getSpec().getContainers().get(0).getName()); + assertEquals("ubuntu:bionic", pod.getSpec().getContainers().get(0).getImage()); + assertEquals(new Quantity("100m"), pod.getSpec().getContainers().get(0).getResources().getRequests().get("cpu")); + assertEquals("python3", pod.getSpec().getContainers().get(1).getName()); + assertEquals("python:3.7", pod.getSpec().getContainers().get(1).getImage()); + assertEquals(new Quantity("100m"), pod.getSpec().getContainers().get(1).getResources().getRequests().get("cpu")); + } } diff --git a/kubernetes-client/src/test/resources/test-pod-manifest-with-aliases.yml b/kubernetes-client/src/test/resources/test-pod-manifest-with-aliases.yml new file mode 100644 index 00000000000..3de5553357e --- /dev/null +++ b/kubernetes-client/src/test/resources/test-pod-manifest-with-aliases.yml @@ -0,0 +1,33 @@ +apiVersion: v1 +kind: Pod +metadata: + name: test-pod-with-alias +spec: + nodeSelector: + workload: build + tolerations: + - key: nodeType + operator: Equal + value: build + effect: NoSchedule + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + containers: + - name: ubuntu + image: ubuntu:bionic + imagePullPolicy: Always + command: + - cat + tty: true + resources: + requests: &id001 + cpu: 100m + - name: python3 + image: python:3.7 + imagePullPolicy: Always + command: + - cat + tty: true + resources: + requests: *id001 diff --git a/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/ResourceQuotaTest.java b/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/ResourceQuotaTest.java index 894d296505e..55daf1323af 100644 --- a/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/ResourceQuotaTest.java +++ b/kubernetes-tests/src/test/java/io/fabric8/kubernetes/client/mock/ResourceQuotaTest.java @@ -16,6 +16,8 @@ package io.fabric8.kubernetes.client.mock; +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.api.model.Namespace; import io.fabric8.kubernetes.api.model.Quantity; import io.fabric8.kubernetes.api.model.ResourceQuota; import io.fabric8.kubernetes.api.model.ResourceQuotaBuilder; @@ -28,6 +30,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.migrationsupport.rules.EnableRuleMigrationSupport; +import java.util.List; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; @@ -113,8 +117,25 @@ public void testeDeleteMulti() { @Test public void testLoadFromFile() { KubernetesClient client = server.getClient(); - ResourceQuota resourceQuota = client.resourceQuotas().load(getClass().getResourceAsStream("/test-resourcequota.yml")).get(); - assertEquals("compute-quota", resourceQuota.getMetadata().getName()); + List list = client.load(getClass().getResourceAsStream("/test-resourcequota.yml")).get(); + assertEquals(3, list.size()); + assertTrue(list.get(0) instanceof ResourceQuota); + assertEquals("compute-quota", list.get(0).getMetadata().getName()); + assertEquals("myspace", list.get(0).getMetadata().getNamespace()); + assertEquals(new Quantity("1"), ((ResourceQuota) list.get(0)).getSpec().getHard().get("requests.cpu")); + assertEquals(new Quantity("1Gi"), ((ResourceQuota) list.get(0)).getSpec().getHard().get("requests.memory")); + assertEquals(new Quantity("2"), ((ResourceQuota) list.get(0)).getSpec().getHard().get("limits.cpu")); + assertEquals(new Quantity("2Gi"), ((ResourceQuota) list.get(0)).getSpec().getHard().get("limits.memory")); + assertTrue(list.get(1) instanceof ResourceQuota); + assertEquals("object-quota", list.get(1).getMetadata().getName()); + assertEquals(new Quantity("10"), ((ResourceQuota) list.get(1)).getSpec().getHard().get("configmaps")); + assertEquals(new Quantity("4"), ((ResourceQuota) list.get(1)).getSpec().getHard().get("persistentvolumeclaims")); + assertEquals(new Quantity("20"), ((ResourceQuota) list.get(1)).getSpec().getHard().get("replicationcontrollers")); + assertEquals(new Quantity("10"), ((ResourceQuota) list.get(1)).getSpec().getHard().get("secrets")); + assertEquals(new Quantity("10"), ((ResourceQuota) list.get(1)).getSpec().getHard().get("services")); + assertEquals(new Quantity("2"), ((ResourceQuota) list.get(1)).getSpec().getHard().get("services.loadbalancers")); + assertTrue(list.get(2) instanceof Namespace); + assertEquals("myspace", list.get(2).getMetadata().getName()); } @Test