diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 8ceee82e5a..9588665131 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -29,7 +29,11 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v3
-
+ - name: Setup Java
+ uses: actions/setup-java@v3
+ with:
+ distribution: 'temurin'
+ java-version: 17.0.x
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index 43b79823ac..04cfd86995 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -43,16 +43,23 @@ jobs:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ matrix.java }}-${{ hashFiles('pom.xml', '**/pom.xml') }}
- name: Build with Maven
+ shell: bash
run: |
- mvn clean test -q -B --define=org.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn
+ if [ $(grep -E '^(8|11)\.' <<< '${{ matrix.java }}') ]; then
+ # some module doesn't compile on java platform lower than 17, need to skip them by specifying a profile
+ MODS_OVERRIDES='-pl !spring-aot'
+ fi
+ mvn -q -B --define=org.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn $MODS_OVERRIDES clean test
build-graalvm:
runs-on: ubuntu-latest
name: GraalVM Maven Test
steps:
- uses: actions/checkout@v3
- - uses: DeLaGuardo/setup-graalvm@48f2bf339ab7d35e31029b1822a213681fdfc42e
+ - uses: graalvm/setup-graalvm@v1
with:
- graalvm-version: '19.3.0.java8'
+ version: '22.3.0'
+ java-version: '17'
+ components: 'native-image'
- name: Build with Maven
run: mvn -q test -B -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn
e2e:
@@ -62,6 +69,11 @@ jobs:
- uses: actions/checkout@v3
- name: Create k8s Kind Cluster
uses: helm/kind-action@v1.4.0
+ - name: Setup Java
+ uses: actions/setup-java@v3
+ with:
+ distribution: 'temurin'
+ java-version: 17.0.x
- name: Run E2E with Maven
run: |
mvn clean install \
@@ -81,7 +93,7 @@ jobs:
uses: actions/setup-java@v3
with:
distribution: 'temurin'
- java-version: 11.0.x
+ java-version: 17.0.x
- name: Cache local Maven repository
uses: actions/cache@v3
with:
diff --git a/pom.xml b/pom.xml
index c63fc964c2..cc27c642ae 100644
--- a/pom.xml
+++ b/pom.xml
@@ -15,6 +15,7 @@
extended
fluent
spring
+ spring-aot
e2e
examples
client-java-contrib/cert-manager
@@ -63,6 +64,7 @@
2.7.5
5.3.23
0.15.0
+ 0.10.2
true
diff --git a/spring-aot/pom.xml b/spring-aot/pom.xml
new file mode 100644
index 0000000000..adbd697fee
--- /dev/null
+++ b/spring-aot/pom.xml
@@ -0,0 +1,87 @@
+
+
+ 4.0.0
+ io.kubernetes
+ client-java-spring-aot-integration
+ bundle
+ client-java-spring-aot-integration
+ https://github.com/kubernetes-client/java
+
+ client-java-parent
+ io.kubernetes
+ 16.0.0-SNAPSHOT
+ ../pom.xml
+
+
+ 3.0.0
+
+
+
+ org.reflections
+ reflections
+ ${reflections.version}
+
+
+ io.kubernetes
+ client-java-spring-integration
+ ${project.version}
+
+
+ org.springframework.boot
+ spring-boot
+
+
+ junit
+ junit
+ test
+
+
+ org.springframework.boot
+ spring-boot-test
+ test
+
+
+ org.springframework
+ spring-test
+ test
+
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ 0.8.8
+
+
+ jacoco-initialize
+
+ prepare-agent
+
+
+
+ jacoco-report
+ test
+
+ report
+
+
+
+
+
+ org.apache.felix
+ maven-bundle-plugin
+ true
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 17
+ 17
+
+
+
+
+
diff --git a/spring-aot/src/main/java/io/kubernetes/client/spring/aot/KubernetesBeanFactoryInitializationAotProcessor.java b/spring-aot/src/main/java/io/kubernetes/client/spring/aot/KubernetesBeanFactoryInitializationAotProcessor.java
new file mode 100644
index 0000000000..e7cbcd64cb
--- /dev/null
+++ b/spring-aot/src/main/java/io/kubernetes/client/spring/aot/KubernetesBeanFactoryInitializationAotProcessor.java
@@ -0,0 +1,105 @@
+/*
+Copyright 2022 The Kubernetes Authors.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package io.kubernetes.client.spring.aot;
+
+import com.google.gson.annotations.JsonAdapter;
+import io.kubernetes.client.extended.controller.Controller;
+import io.swagger.annotations.ApiModel;
+import java.lang.annotation.Annotation;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import org.jetbrains.annotations.NotNull;
+import org.reflections.Reflections;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.aot.hint.MemberCategory;
+import org.springframework.aot.hint.RuntimeHints;
+import org.springframework.aot.hint.TypeReference;
+import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
+import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
+
+/**
+ * GraalVM native images need to know about when you might reflectively work with types at runtime.
+ * The Kubernetes Java client works with several types reflectively at runtime. This code uses the
+ * third-party Reflections library to reflect upon all the code in your Spring Boot application or
+ * in the official Kubernetes Java client that has {@link ApiModel}, {@link JsonAdapter}, and
+ * registers them. It also registers a few other specific handful of classes that should be
+ * accounted for, in specific cases.
+ *
+ * @author Josh Long
+ */
+@SuppressWarnings("unused")
+public class KubernetesBeanFactoryInitializationAotProcessor
+ implements BeanFactoryInitializationAotProcessor {
+
+ private static final Logger LOGGER =
+ LoggerFactory.getLogger(KubernetesBeanFactoryInitializationAotProcessor.class);
+
+ private final MemberCategory[] allMemberCategories = MemberCategory.values();
+
+ @Override
+ public BeanFactoryInitializationAotContribution processAheadOfTime(
+ @NotNull ConfigurableListableBeanFactory beanFactory) {
+ return (generationContext, beanFactoryInitializationCode) -> {
+ RuntimeHints hints = generationContext.getRuntimeHints();
+ String[] classNames =
+ new String[] {
+ "com.google.gson.JsonElement", //
+ "io.kubernetes.client.informer.cache.ProcessorListener", //
+ "io.kubernetes.client.extended.controller.Controller", //
+ "io.kubernetes.client.util.generic.GenericKubernetesApi$StatusPatch", //
+ "io.kubernetes.client.util.Watch$Response" //
+ };
+ for (String className : classNames) {
+ LOGGER.info("registering " + className + " for reflection");
+ hints.reflection().registerType(TypeReference.of(className), allMemberCategories);
+ }
+ registerForPackage("io.kubernetes", hints);
+ Collection packages = AutoConfigurationPackages.get(beanFactory);
+ for (String packageName : packages) {
+ registerForPackage(packageName, hints);
+ }
+ };
+ }
+
+ private void registerForPackage(String packageName, RuntimeHints hints) {
+ Reflections reflections = new Reflections(packageName);
+ Set> apiModels = reflections.getTypesAnnotatedWith(ApiModel.class);
+ Set> controllers = reflections.getSubTypesOf(Controller.class);
+ Set> jsonAdapters = findJsonAdapters(reflections);
+ Set> all = new HashSet<>();
+ all.addAll(jsonAdapters);
+ all.addAll(controllers);
+ all.addAll(apiModels);
+ for (Class> clazz : all) {
+ LOGGER.info("registering " + clazz.getName() + " for reflection");
+ hints.reflection().registerType(clazz, allMemberCategories);
+ }
+ }
+
+ private Set> findJsonAdapters(Reflections reflections) {
+ Class jsonAdapterClass = JsonAdapter.class;
+ Set> classes = new HashSet<>();
+ for (Class> clazz : reflections.getTypesAnnotatedWith(jsonAdapterClass)) {
+ JsonAdapter annotation = clazz.getAnnotation(jsonAdapterClass);
+ if (null != annotation) {
+ classes.add(annotation.value());
+ }
+ classes.add(clazz);
+ }
+ return classes;
+ }
+}
diff --git a/spring-aot/src/main/resources/META-INF/spring/aot.factories b/spring-aot/src/main/resources/META-INF/spring/aot.factories
new file mode 100644
index 0000000000..d8b3f2434e
--- /dev/null
+++ b/spring-aot/src/main/resources/META-INF/spring/aot.factories
@@ -0,0 +1 @@
+org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor=io.kubernetes.client.spring.aot.KubernetesBeanFactoryInitializationAotProcessor
\ No newline at end of file
diff --git a/spring/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 0000000000..7e78a1ccfa
--- /dev/null
+++ b/spring/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1,3 @@
+io.kubernetes.client.spring.extended.controller.config.KubernetesInformerAutoConfiguration
+io.kubernetes.client.spring.extended.controller.config.KubernetesReconcilerAutoConfiguration
+io.kubernetes.client.spring.extended.manifests.config.KubernetesManifestsAutoConfiguration
\ No newline at end of file