Marin Sušić
Marin Sušić

Reputation: 44

Simple Kubernetes Spring-Boot operator issue

So I was recently tasked with building a simple k8s operator that would have a CRD that only contains a string. Whenever a new resource of the type is created, it would read the string and log it into the pod logs. Sounds simple right?

Well, considering I have barely any experience with k8s and none in making an operator, not so much, I found some tutorial online that explains how to build an operator using spring-boot (also part of my task is that I have to use spring-boot). I have followed this tutorial then attempted to alter the code for my purpose, but I keep getting an error that ChatGPT describes as library version missmatch, and I have no clue whatsoever how to resolve this issue.

2024-03-27T12:10:33.700Z  INFO 1 --- [operator] [           main] d.example.operator.OperatorApplication   : Starting OperatorApplication v0.0.1-SNAPSHOT using Java 17.0.2 with PID 1 (/app.jar started by root in /)
2024-03-27T12:10:33.702Z  INFO 1 --- [operator] [           main] d.example.operator.OperatorApplication   : No active profile set, falling back to 1 default profile: "default"
2024-03-27T12:10:34.241Z  WARN 1 --- [operator] [           main] s.c.a.AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'kubernetesClient' defined in class path resource [io/javaoperatorsdk/operator/springboot/starter/OperatorAutoConfiguration.class]: Failed to instantiate [io.fabric8.kubernetes.client.KubernetesClient]: Factory method 'kubernetesClient' threw exception with message: class io.fabric8.kubernetes.client.okhttp.OkHttpClientImpl overrides final method io.fabric8.kubernetes.client.http.StandardHttpClient.close()V
2024-03-27T12:10:34.246Z  INFO 1 --- [operator] [           main] .s.b.a.l.ConditionEvaluationReportLogger : 

Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
2024-03-27T12:10:34.254Z ERROR 1 --- [operator] [           main] o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'kubernetesClient' defined in class path resource [io/javaoperatorsdk/operator/springboot/starter/OperatorAutoConfiguration.class]: Failed to instantiate [io.fabric8.kubernetes.client.KubernetesClient]: Factory method 'kubernetesClient' threw exception with message: class io.fabric8.kubernetes.client.okhttp.OkHttpClientImpl overrides final method io.fabric8.kubernetes.client.http.StandardHttpClient.close()V
    at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:648) ~[spring-beans-6.1.5.jar!/:6.1.5]
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:636) ~[spring-beans-6.1.5.jar!/:6.1.5]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1335) ~[spring-beans-6.1.5.jar!/:6.1.5]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1165) ~[spring-beans-6.1.5.jar!/:6.1.5]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562) ~[spring-beans-6.1.5.jar!/:6.1.5]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) ~[spring-beans-6.1.5.jar!/:6.1.5]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[spring-beans-6.1.5.jar!/:6.1.5]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.1.5.jar!/:6.1.5]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[spring-beans-6.1.5.jar!/:6.1.5]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-6.1.5.jar!/:6.1.5]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:975) ~[spring-beans-6.1.5.jar!/:6.1.5]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:962) ~[spring-context-6.1.5.jar!/:6.1.5]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:624) ~[spring-context-6.1.5.jar!/:6.1.5]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754) ~[spring-boot-3.2.4.jar!/:3.2.4]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456) ~[spring-boot-3.2.4.jar!/:3.2.4]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:334) ~[spring-boot-3.2.4.jar!/:3.2.4]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1354) ~[spring-boot-3.2.4.jar!/:3.2.4]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1343) ~[spring-boot-3.2.4.jar!/:3.2.4]
    at de.example.operator.OperatorApplication.main(OperatorApplication.java:10) ~[!/:0.0.1-SNAPSHOT]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
    at org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:91) ~[app.jar:0.0.1-SNAPSHOT]
    at org.springframework.boot.loader.launch.Launcher.launch(Launcher.java:53) ~[app.jar:0.0.1-SNAPSHOT]
    at org.springframework.boot.loader.launch.JarLauncher.main(JarLauncher.java:58) ~[app.jar:0.0.1-SNAPSHOT]
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [io.fabric8.kubernetes.client.KubernetesClient]: Factory method 'kubernetesClient' threw exception with message: class io.fabric8.kubernetes.client.okhttp.OkHttpClientImpl overrides final method io.fabric8.kubernetes.client.http.StandardHttpClient.close()V
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:177) ~[spring-beans-6.1.5.jar!/:6.1.5]
    at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:644) ~[spring-beans-6.1.5.jar!/:6.1.5]
    ... 25 common frames omitted
Caused by: java.lang.IncompatibleClassChangeError: class io.fabric8.kubernetes.client.okhttp.OkHttpClientImpl overrides final method io.fabric8.kubernetes.client.http.StandardHttpClient.close()V
    at java.base/java.lang.ClassLoader.defineClass1(Native Method) ~[na:na]
    at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1012) ~[na:na]
    at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150) ~[na:na]
    at java.base/java.net.URLClassLoader.defineClass(URLClassLoader.java:524) ~[na:na]
    at java.base/java.net.URLClassLoader$1.run(URLClassLoader.java:427) ~[na:na]
    at java.base/java.net.URLClassLoader$1.run(URLClassLoader.java:421) ~[na:na]
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:712) ~[na:na]
    at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:420) ~[na:na]
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:587) ~[na:na]
    at org.springframework.boot.loader.net.protocol.jar.JarUrlClassLoader.loadClass(JarUrlClassLoader.java:104) ~[app.jar:0.0.1-SNAPSHOT]
    at org.springframework.boot.loader.launch.LaunchedClassLoader.loadClass(LaunchedClassLoader.java:91) ~[app.jar:0.0.1-SNAPSHOT]
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520) ~[na:na]
    at io.fabric8.kubernetes.client.okhttp.OkHttpClientBuilderImpl.completeBuild(OkHttpClientBuilderImpl.java:122) ~[kubernetes-httpclient-okhttp-6.9.2.jar!/:na]
    at io.fabric8.kubernetes.client.okhttp.OkHttpClientBuilderImpl.initialBuild(OkHttpClientBuilderImpl.java:94) ~[kubernetes-httpclient-okhttp-6.9.2.jar!/:na]
    at io.fabric8.kubernetes.client.okhttp.OkHttpClientBuilderImpl.build(OkHttpClientBuilderImpl.java:55) ~[kubernetes-httpclient-okhttp-6.9.2.jar!/:na]
    at io.fabric8.kubernetes.client.okhttp.OkHttpClientBuilderImpl.build(OkHttpClientBuilderImpl.java:36) ~[kubernetes-httpclient-okhttp-6.9.2.jar!/:na]
    at io.fabric8.kubernetes.client.KubernetesClientBuilder.getHttpClient(KubernetesClientBuilder.java:93) ~[kubernetes-client-api-6.11.0.jar!/:na]
    at io.fabric8.kubernetes.client.KubernetesClientBuilder.build(KubernetesClientBuilder.java:78) ~[kubernetes-client-api-6.11.0.jar!/:na]
    at io.javaoperatorsdk.operator.springboot.starter.OperatorAutoConfiguration.lambda$kubernetesClient$3(OperatorAutoConfiguration.java:61) ~[operator-framework-spring-boot-starter-5.4.1.jar!/:na]
    at java.base/java.util.Optional.orElseGet(Optional.java:364) ~[na:na]
    at io.javaoperatorsdk.operator.springboot.starter.OperatorAutoConfiguration.kubernetesClient(OperatorAutoConfiguration.java:60) ~[operator-framework-spring-boot-starter-5.4.1.jar!/:na]
    at io.javaoperatorsdk.operator.springboot.starter.OperatorAutoConfiguration$$SpringCGLIB$$0.CGLIB$kubernetesClient$3(<generated>) ~[operator-framework-spring-boot-starter-5.4.1.jar!/:na]
    at io.javaoperatorsdk.operator.springboot.starter.OperatorAutoConfiguration$$SpringCGLIB$$FastClass$$1.invoke(<generated>) ~[operator-framework-spring-boot-starter-5.4.1.jar!/:na]
    at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) ~[spring-core-6.1.5.jar!/:6.1.5]
    at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) ~[spring-context-6.1.5.jar!/:6.1.5]
    at io.javaoperatorsdk.operator.springboot.starter.OperatorAutoConfiguration$$SpringCGLIB$$0.kubernetesClient(<generated>) ~[operator-framework-spring-boot-starter-5.4.1.jar!/:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:140) ~[spring-beans-6.1.5.jar!/:6.1.5]
    ... 26 common frames omitted

I currently have the following code in my spring-boot project.

MyCustomResource.java

package de.example.operator;

import io.fabric8.kubernetes.client.CustomResource;
import io.fabric8.kubernetes.api.model.Namespaced;
import io.javaoperatorsdk.operator.api.ObservedGenerationAwareStatus;
import io.fabric8.kubernetes.model.annotation.Group;
import io.fabric8.kubernetes.model.annotation.Version;

@Group("de.example")
@Version("v1")
public class MyCustomResource extends CustomResource<MyCustomSpec, ObservedGenerationAwareStatus> implements Namespaced {
}

MyCustomResourceReconciler.java

package de.example.operator;

import io.javaoperatorsdk.operator.api.reconciler.Context;
import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration;
import io.javaoperatorsdk.operator.api.reconciler.UpdateControl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import io.javaoperatorsdk.operator.api.reconciler.Reconciler;

@Component
@ControllerConfiguration
public class MyCustomResourceReconciler implements Reconciler<MyCustomResource> {
    
    private static final Logger log = LoggerFactory.getLogger(MyCustomResourceReconciler.class);

    @Override
    public UpdateControl<MyCustomResource> reconcile(MyCustomResource resource, Context<MyCustomResource> context) {
        String message = resource.getSpec().getMessage();
        log.info("Message from CR {}: {}", resource.getMetadata().getName(), message);
        return UpdateControl.noUpdate();
    }
}

MyCustomSpec.java

package de.example.operator;

public class MyCustomSpec {
    private String message;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

OperatorApplication.java

package de.example.operator;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class OperatorApplication {

    public static void main(String[] args) {
        SpringApplication.run(OperatorApplication.class, args);
    }

}

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>de.example</groupId>
    <artifactId>operator</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>operator</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.javaoperatorsdk</groupId>
            <artifactId>operator-framework-spring-boot-starter</artifactId>
            <version>5.4.1</version>
        </dependency>

        <dependency>
            <groupId>io.javaoperatorsdk</groupId>
            <artifactId>operator-framework-spring-boot-starter-test</artifactId>
            <version>5.4.1</version>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.logging.log4j</groupId>
                    <artifactId>log4j-slf4j2-impl</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>io.fabric8</groupId>
            <artifactId>crd-generator-apt</artifactId>
            <version>6.11.0</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk18on</artifactId>
            <version>1.77</version>
        </dependency>

        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcpkix-jdk18on</artifactId>
            <version>1.77</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Maybe someone can see where the issue is?

Upvotes: 0

Views: 411

Answers (1)

Marin Sušić
Marin Sušić

Reputation: 44

Issue was that the newest version of operatorsdk was not using the newest version of fabric8. After changing my dependency version of crd generator to 6.9.2 as is in the dependency tree for operator SDK, the code runs flawlessly.

Upvotes: 0

Related Questions