java-operator-sdk
java-operator-sdk copied to clipboard
Adding `HasMetadata` as interface causes resources to be incorrectly deserialized as `GenericKubernetesResource`
Bug Report
Adding the HasMetadata
as an interface for my class that extends CustomResource<SPEC, STATUS>
cause the operator framework to incorrectly deserialize it as GenericKubernetesResource
instead of its actual class.
What did you do?
I added an interface to my CustomResource
that extends HasMetadata
.
For example,
@Group(...)
@Version(...)
public class MyCRD extends CustomResource<MySpec, MyStatus> implements Namespaced, HasUsefulFunctionality {
}
The HasUsefulFunctionality
interface extends HasMetadata
and is used to factor out common code that would otherwise by duplicated on all CRDs.
It is useful for it to extend HasMetadata
, because it allows default
methods to invoke this.getMetadata()
to do things like inject the observedGeneration
for conditions that get attached to the resource status.
What did you expect to see?
I expected things to work without any problems. The CustomResource
superclass already implements HasMetadata
, so declaring my CRD as an implementer of HasMetadata
again should have no effect.
What did you see instead? Under which circumstances?
The Operator framework seems to get confused by the presence of that interface again, and incorrectly deserializes the resource as KubernetesGenericResource
.
Many different stacktraces similar to the following appear all over the place and the operator is completely unusable:
20:52:42.301 [-1370171034-pool-1-thread-2] ERROR io.fabric8.kubernetes.client.informers.impl.cache.SharedProcessor -- ... failed invoking InformerEventSource{resourceClass: MyCRD} event handler: io.fabric8.kubernetes.api.model.GenericKubernetesResource incompatible with MyCRD
java.lang.ClassCastException: io.fabric8.kubernetes.api.model.GenericKubernetesResource incompatible with MyCRD
at io.javaoperatorsdk.operator.processing.event.source.informer.DefaultPrimaryToSecondaryIndex.onAddOrUpdate(DefaultPrimaryToSecondaryIndex.java:21)
at io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource.onAdd(InformerEventSource.java:120)
at io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource.onAdd(InformerEventSource.java:66)
at io.fabric8.kubernetes.client.informers.impl.cache.ProcessorListener$AddNotification.handle(ProcessorListener.java:103)
at io.fabric8.kubernetes.client.informers.impl.cache.ProcessorListener.add(ProcessorListener.java:50)
at io.fabric8.kubernetes.client.informers.impl.cache.SharedProcessor.lambda$distribute$0(SharedProcessor.java:91)
at io.fabric8.kubernetes.client.informers.impl.cache.SharedProcessor.lambda$distribute$1(SharedProcessor.java:114)
at io.fabric8.kubernetes.client.utils.internal.SerialExecutor.lambda$execute$0(SerialExecutor.java:57)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.lang.Thread.run(Thread.java:857)
Environment
Kubernetes cluster type:
Docker Desktop / Kubernetes 1.29.2
Java Operator SDK 4.9.2
$ java -version
openjdk version "17.0.10" 2024-01-16
IBM Semeru Runtime Open Edition 17.0.10.0 (build 17.0.10+7)
Eclipse OpenJ9 VM 17.0.10.0 (build openj9-0.43.0, JRE 17 Mac OS X amd64-64-Bit Compressed References 20240116_636 (JIT enabled, AOT enabled)
OpenJ9 - 2c3d78b48
OMR - ea8124dbc
JCL - 2aad089841f based on jdk-17.0.10+7)
$ kubectl version
Client Version: v1.29.2
Kustomize Version: v5.0.4-0.20230601165947-6ce0bf390ce3
Server Version: v1.29.2
Possible Solution
Removing HasMetadata
as an interface solves the problem.