micronaut-projects / micronaut-kubernetes

This project includes integration between Micronaut and Kubernetes
https://micronaut-projects.github.io/micronaut-kubernetes/snapshot/guide
Apache License 2.0
44 stars 24 forks source link

Informer library implementation influencing and limiting the naming of Custom Resource java classes #639

Open tired-old-man opened 9 months ago

tired-old-man commented 9 months ago

Expected Behavior

Using Informer to fetch Custom Resources has limitations. When naming classes that will represent Custom Resource, one must prepend them with the version number. This is a nice design choice, but the current implementation in not correct. It will be nice to specify this design choice in the official documentation.

Correct examples: V1MySettings V1alpha1Operation

Incorrect examples: MyCustomResource V2Example

The library should support multiple versions, and not be limited to the predefined versions list (io.micronaut.kubernetes.client.ModelMapper.preBuiltApiVersions).

Now it is not possible to define Custom Resource using any other version other than one specified in the predefined versions list.

Actual Behaviour

Naming java class for custom resource will influence instantiation of shared index informer. If not named correctly (ex: V2CustomResource) the following exception will be thrown:

18:50:34.229 [main] ERROR io.micronaut.runtime.Micronaut - Error starting Micronaut server: Bean definition [org.foo.bar.V2CustomResourceInformer] could not be loaded: version must not be null
io.micronaut.context.exceptions.BeanInstantiationException: Bean definition [org.foo.bar.V2CustomResourceInformer] could not be loaded: version must not be null
        at io.micronaut.context.DefaultBeanContext.initializeContext(DefaultBeanContext.java:1979)
        at io.micronaut.context.DefaultApplicationContext.initializeContext(DefaultApplicationContext.java:290)
        at io.micronaut.context.DefaultBeanContext.readAllBeanDefinitionClasses(DefaultBeanContext.java:3340)
        at io.micronaut.context.DefaultBeanContext.finalizeConfiguration(DefaultBeanContext.java:3693)
        at io.micronaut.context.DefaultBeanContext.start(DefaultBeanContext.java:345)
        at io.micronaut.context.DefaultApplicationContext.start(DefaultApplicationContext.java:198)
        at io.micronaut.runtime.Micronaut.start(Micronaut.java:73)
        at io.micronaut.runtime.Micronaut.run(Micronaut.java:322)
        at io.micronaut.runtime.Micronaut.run(Micronaut.java:308)
        at org.foo.bar.Application.main(Application.java:37)
Caused by: java.lang.IllegalArgumentException: version must not be null
        at io.kubernetes.client.util.Preconditions.precondition(Preconditions.java:41)
        at io.kubernetes.client.apimachinery.GroupVersion.<init>(GroupVersion.java:42)
        at io.kubernetes.client.apimachinery.GroupVersionKind.<init>(GroupVersionKind.java:24)
        at io.micronaut.kubernetes.client.ModelMapper.getGroupVersionKindByClass(ModelMapper.java:114)
        at io.micronaut.kubernetes.client.informer.DefaultSharedIndexInformerFactory.sharedIndexInformerFor(DefaultSharedIndexInformerFactory.java:116)
        at io.micronaut.kubernetes.client.informer.DefaultSharedIndexInformerFactory.sharedIndexInformersFor(DefaultSharedIndexInformerFactory.java:226)
        at io.micronaut.kubernetes.client.informer.ResourceEventHandlerBeanListener.onCreated(ResourceEventHandlerBeanListener.java:97)
        at io.micronaut.kubernetes.client.informer.ResourceEventHandlerBeanListener.onCreated(ResourceEventHandlerBeanListener.java:50)
        at io.micronaut.context.DefaultBeanContext.triggerBeanCreatedEventListener(DefaultBeanContext.java:2360)
        at io.micronaut.context.DefaultBeanContext.postBeanCreated(DefaultBeanContext.java:2337)
        at io.micronaut.context.DefaultBeanContext.doCreateBean(DefaultBeanContext.java:2281)
        at io.micronaut.context.DefaultBeanContext.doCreateBean(DefaultBeanContext.java:2289)
        at io.micronaut.context.DefaultBeanContext.createRegistration(DefaultBeanContext.java:3056)
        at io.micronaut.context.SingletonScope.getOrCreate(SingletonScope.java:81)
        at io.micronaut.context.DefaultBeanContext.findOrCreateSingletonBeanRegistration(DefaultBeanContext.java:2958)
        at io.micronaut.context.DefaultBeanContext.initializeEagerBean(DefaultBeanContext.java:2676)
        at io.micronaut.context.DefaultBeanContext.initializeContext(DefaultBeanContext.java:1973)
        ... 9 common frames omitted

Using either @Informer annotation, or calling sharedIndexInformerFor method on DefaultSharedIndexInformerFactory class will result in exception.

The problem can be traced to line 116 in DefaultSharedIndexInformerFactory class. The internal class ModelMapper will try to parse class name and extract group, kind and version from it. Unfortunately, if class representing custom resource doesn't start with one of the prebuild versions, the null will be used for the version. This will cause validation to fail in io.kubernetes.client.apimachinery.GroupVersion.

Steps To Reproduce

Write a class representing Custom Resource and use the "unsupported" version in the class name.

Examples of problematic class names: V2Setting V1beta3Operation V3Customer

Make a class implements KubernetesObject and try to instantiate new Informer for the class.

@Informer(
        apiType = V2Setting.class,
        apiListType = V2SettingList.class,
        apiGroup = "org.foo.bar",
        resourcePlural = "settings",
        namespace = "default")

or directly using SharedInformerFactory

factory.sharedIndexInformerFor(
    V2Setting.class,
    V2SettingList.class,
    "settings",
    "org.foo.bar",
    "default",
    null,
    null,
    true
);

Environment Information

OS: Windows 11 / WSL 2 (Ubuntu)

$ java -version
openjdk version "17.0.8.1" 2023-08-24
OpenJDK Runtime Environment Temurin-17.0.8.1+1 (build 17.0.8.1+1)
OpenJDK 64-Bit Server VM Temurin-17.0.8.1+1 (build 17.0.8.1+1, mixed mode, sharing)

mvn --version
Apache Maven 3.9.4 (dfbb324ad4a7c8fb0bf182e6d91b0ae20e3d2dd9)
Maven home: /home/user/.sdkman/candidates/maven/current
Java version: 17.0.8.1, vendor: Eclipse Adoptium, runtime: /home/user/.sdkman/candidates/java/17.0.8.1-tem
Default locale: en, platform encoding: UTF-8
OS name: "linux", version: "5.15.90.1-microsoft-standard-wsl2", arch: "amd64", family: "unix"

mn --version
Micronaut Version: 4.1.1

Example Application

No response

Version

4.1.1

cambierr commented 1 month ago

I lots a few tens of minutes on this issue too ...

graemerocher commented 1 month ago

could you send a PR since you diagnosed the issue?

cambierr commented 1 month ago

I'd love to, actually. Will look into this very soon !