fabric8io / kubernetes-client

Java client for Kubernetes & OpenShift

Home Page:http://fabric8.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Serialization Error When Loading Multi-Document YAML in fabric8 Kubernetes Client

fengyinqiao opened this issue · comments

Describe the bug

Encountering a deserialization error when attempting to use the Serialization.unmarshal method to handle a YAML string containing multiple Kubernetes resource definitions separated by ---, and trying to deserialize it as List.class.

Error Details:

java.lang.IllegalArgumentException: Cannot deserialize value of type `java.util.ArrayList<java.lang.Object>` from Object value (token `JsonToken.START_OBJECT`)
 at [Source: UNKNOWN; line: -1, column: -1]

Fabric8 Kubernetes Client version

6.9.2

Steps to reproduce

  1. Prepare a YAML string containing multiple resource definitions separated by ---.
  2. Attempt to load and deserialize the YAML string using the following code:
InputStream is = new ByteArrayInputStream(ymlContent.getBytes(StandardCharsets.UTF_8));
List<HasMetadata> resources = Serialization.unmarshal(is, List.class);

Actual Behavior:
Throws a type mismatch exception when the YAML definitions contain only a single resource object instead of an array.

Expected behavior

The method should correctly parse the multi-document YAML string and deserialize each definition into a corresponding list of Kubernetes resource objects.

Runtime

Kubernetes (vanilla)

Kubernetes API Server version

1.23

Environment

macOS

Fabric8 Kubernetes Client Logs

No response

Additional context

Consider enhancing the Serialization.unmarshal method to more intelligently handle both single objects and arrays of objects, or clarify in the documentation how such cases should be handled.

@fengyinqiao : Hello, we have a test for verifying this multi-line document YAML deserialization. Could you please check if your YAML is similar to this?

---
apiVersion: v1
kind: List
items:

This yaml gets used in KubernetesClientImplTest

@fengyinqiao : Hello, we have a test for verifying this multi-line document YAML deserialization. Could you please check if your YAML is similar to this?

---
apiVersion: v1
kind: List
items:

This yaml gets used in KubernetesClientImplTest

@rohanKanojia Really thank's for your reply! my YAML is as below:

 apiVersion:  elasticsearch.k8s.elastic.co/v1
 kind: Elasticsearch 
---
 apiVersion: kibana.k8s.elastic.co/v1
 kind: Kibana 

@fengyinqiao : When I copy paste your YAML and add this test, it seems to pass.

  @Test
  void multiline() {
    // Given
    // When
    try (KubernetesClient kubernetesClient = new KubernetesClientBuilder().build()) {
      List<HasMetadata> result = kubernetesClient.load(KubernetesClientImplTest.class.getResourceAsStream("/multi-line.yml")).items();
      // Then
      assertThat(result)
        .hasSize(2)
        .hasAtLeastOneElementOfType(GenericKubernetesResource.class)
        .hasAtLeastOneElementOfType(GenericKubernetesResource.class);
    }
  }

@fengyinqiao : When I copy paste your YAML and add this test, it seems to pass.

  @Test
  void multiline() {
    // Given
    // When
    try (KubernetesClient kubernetesClient = new KubernetesClientBuilder().build()) {
      List<HasMetadata> result = kubernetesClient.load(KubernetesClientImplTest.class.getResourceAsStream("/multi-line.yml")).items();
      // Then
      assertThat(result)
        .hasSize(2)
        .hasAtLeastOneElementOfType(GenericKubernetesResource.class)
        .hasAtLeastOneElementOfType(GenericKubernetesResource.class);
    }
  }

@rohanKanojia Thanks for your code. It does work. And I find another way based on my code just now, it also works:

List<HasMetadata> resources = new ArrayList<>();
try (InputStream is = new ByteArrayInputStream(ymlContent.getBytes(StandardCharsets.UTF_8))) {
            resources.addAll(Serialization.unmarshal(is));
            assertThat(resources)
            .hasSize(2)
            .hasAtLeastOneElementOfType(GenericKubernetesResource.class)
            .hasAtLeastOneElementOfType(GenericKubernetesResource.class);
}

so, why my original code is wrong?

so, why my original code is wrong?

Are you using java.util.List in your initial method? Does it work if you use io.fabric8.kubernetes.api.model.KubernetesResourceList ?

Does it work if you use io.fabric8.kubernetes.api.model.KubernetesResourceList ?

@rohanKanojia I get null , use below code:

try (InputStream is = new ByteArrayInputStream(ymlContent.getBytes(StandardCharsets.UTF_8))) {
            KubernetesResourceList kubernetesResourceList = Serialization.unmarshal(is);
            assertNull(kubernetesResourceList)
}