Search code examples
javaspring-bootkubernetesfabric8

ClassCastException Using Fabric8 to Retrieve Custom Resource Spec


I am using Fabric8 to perform CRUD operations on various K8S custom resources. When reading a resource and attempting to retrieve its spec, I am experiencing a ClassCastException between RawExtension and my POJO which defines the resource spec MyCRDSpec

java.lang.ClassCastException: class io.fabric8.kubernetes.api.model.runtime.RawExtension cannot be cast to class my.package.MyCRDSpec

The custom resource class

@Group("my.group")
@Version("v1alpha1")
public class MyCRD extends CustomResource<MyCRDSpec, MyCRDStatus> implements
    Namespaced {
}

Spec POJO

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MyCRDSpec implements KubernetesResource {

  private String id;
  private String name;
  ...
}

Status POJO

@Data
@AllArgsConstructor
@NoArgsConstructor
public class MyCRDStatus implements KubernetesResource {
  private String msg;
}

And this is how I'm reading the resource and attempting to retrieve the spec

try (final KubernetesClient client = new KubernetesClientBuilder().build()) {
    final MixedOperation<MyCRD, KubernetesResourceList<MyCRD>, Resource<MyCRD>> crdClient = 
        client.resources(MyCRD.class);
    final MyCRD crd= crdClient.inNamespace("ns").withName("my-existing-custom-resource-name").get();
    return crd.getSpec().getName();
}

The reason for retrieving the spec is I want to be able to map to a domain object using the properties contained in the custom resource. However, I encounter this ClassCastException each time. Logging the full result of getSpec() shows that it appears to be bound to RawExtension rather than MyCRDSpec as expected

spec=RawExtension(super=AnyType(value={...}))

I've been following this and this as guides so far. I have also tried changing my client configuration but nothing has yielded any results.

I haven't included the CRD yaml definition or how I'm creating the custom resources because that functionality is working as expected, but it's very similar to my code above and in those guides. I am able to view both the CRD and my created resources in cluster without issue.

I am using spring-cloud-kubernetes-fabric8-all version 3.1.0 and JDK 21.

EDIT

The ClassCastException also contains an error about the class loader

io.fabric8.kubernetes.api.model.runtime.RawExtension and my.package.MyCRDSpec are in unnamed module of loader org.springframework.boot.loader.launch.LaunchedClassLoader

Having checked some of the answers in this question, I tried replacing Lombok annotations with manual getters/setters but this made no difference.

EDIT 2

Progress. Annotating MyCRDSpec with @JsonDeserialize() solves the problem. The question is, why do I need to do this? Is there something in the Fabric8 client setting a custom deserializer unexpectedly or incorrectly?


Solution

  • You don't need extend MyCRDSpec and MyCRDStatus with KubernetesResource. Extending them with this class seems to register it with KubernetesDeserializer that tries to map it as an actual Kubernetes resource (like Deployment, Pod etc). The issue was getting fixed by adding @JsonDeserialize(using = com.fasterxml.jackson.databind.JsonDeserializer.None.class) because this configured Jackson to not use KubernetesDeserializer.