Skip to content

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

@tired-old-man

Description

@tired-old-man

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions