java-operator-sdk icon indicating copy to clipboard operation
java-operator-sdk copied to clipboard

Adding `HasMetadata` as interface causes resources to be incorrectly deserialized as `GenericKubernetesResource`

Open adriansuarez opened this issue 7 months ago • 9 comments

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.

adriansuarez avatar Aug 04 '24 01:08 adriansuarez