From 3312d5e7744c7f3317c3a41a185cada4120eaee8 Mon Sep 17 00:00:00 2001 From: Abu Hena Mostafa Kamal Date: Mon, 8 Jun 2026 02:46:57 +0600 Subject: [PATCH 1/4] Add GraalVM native image support for Fabric8 autoconfig module Signed-off-by: Abu Hena Mostafa Kamal --- .../fabric8/Fabric8AutoConfiguration.java | 2 + .../fabric8/Fabric8RuntimeHints.java | 68 +++++++++++++++++++ .../resources/META-INF/spring/aot.factories | 2 + 3 files changed, 72 insertions(+) create mode 100644 spring-cloud-kubernetes-fabric8-autoconfig/src/main/java/org/springframework/cloud/kubernetes/fabric8/Fabric8RuntimeHints.java create mode 100644 spring-cloud-kubernetes-fabric8-autoconfig/src/main/resources/META-INF/spring/aot.factories diff --git a/spring-cloud-kubernetes-fabric8-autoconfig/src/main/java/org/springframework/cloud/kubernetes/fabric8/Fabric8AutoConfiguration.java b/spring-cloud-kubernetes-fabric8-autoconfig/src/main/java/org/springframework/cloud/kubernetes/fabric8/Fabric8AutoConfiguration.java index c85d8db13b..cccd170de9 100644 --- a/spring-cloud-kubernetes-fabric8-autoconfig/src/main/java/org/springframework/cloud/kubernetes/fabric8/Fabric8AutoConfiguration.java +++ b/spring-cloud-kubernetes-fabric8-autoconfig/src/main/java/org/springframework/cloud/kubernetes/fabric8/Fabric8AutoConfiguration.java @@ -32,6 +32,7 @@ import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.ImportRuntimeHints; import org.springframework.core.env.Environment; /** @@ -41,6 +42,7 @@ * @author Eddú Meléndez * @author Tim Ysewyn */ +@ImportRuntimeHints(Fabric8RuntimeHints.class) @Configuration(proxyBeanMethods = false) @ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES) @AutoConfigureAfter(KubernetesCommonsAutoConfiguration.class) diff --git a/spring-cloud-kubernetes-fabric8-autoconfig/src/main/java/org/springframework/cloud/kubernetes/fabric8/Fabric8RuntimeHints.java b/spring-cloud-kubernetes-fabric8-autoconfig/src/main/java/org/springframework/cloud/kubernetes/fabric8/Fabric8RuntimeHints.java new file mode 100644 index 0000000000..053c140ccd --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-autoconfig/src/main/java/org/springframework/cloud/kubernetes/fabric8/Fabric8RuntimeHints.java @@ -0,0 +1,68 @@ +/* + * Copyright 2013-present the original author or 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 + * + * https://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 org.springframework.cloud.kubernetes.fabric8; + +import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.KubernetesClientBuilder; +import io.fabric8.kubernetes.client.impl.KubernetesClientImpl; + +import org.springframework.aot.hint.MemberCategory; +import org.springframework.aot.hint.RuntimeHints; +import org.springframework.aot.hint.RuntimeHintsRegistrar; +import org.springframework.aot.hint.TypeReference; + +/** + * {@link RuntimeHintsRegistrar} for Spring Cloud Kubernetes Fabric8 native image support. + * + * Registers reflection, resource, and proxy hints required for GraalVM native image + * compilation when using the Fabric8 Kubernetes client with Spring Cloud Kubernetes. + * + * @author Abu Hena Mostafa Kamal + */ +public class Fabric8RuntimeHints implements RuntimeHintsRegistrar { + + @Override + public void registerHints(RuntimeHints hints, ClassLoader classLoader) { + // Fabric8 client impl - loaded reflectively via KubernetesClientBuilder + hints.reflection() + .registerType(TypeReference.of(KubernetesClientImpl.class), MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, + MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.ACCESS_DECLARED_FIELDS); + + // KubernetesClientBuilder - used in Fabric8AutoConfiguration + hints.reflection() + .registerType(TypeReference.of(KubernetesClientBuilder.class), MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, + MemberCategory.INVOKE_DECLARED_METHODS); + + // Fabric8 Config - used in Fabric8AutoConfiguration + hints.reflection() + .registerType(TypeReference.of(Config.class), MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, + MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.ACCESS_DECLARED_FIELDS); + + // EnvironmentPostProcessor loaded via spring.factories + hints.reflection() + .registerType(TypeReference.of(Fabric8ProfileEnvironmentPostProcessor.class), + MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); + + // Kubernetes service account and kubeconfig resources + hints.resources().registerPattern(".kube/config"); + hints.resources().registerPattern("var/run/secrets/kubernetes.io/serviceaccount/token"); + hints.resources().registerPattern("var/run/secrets/kubernetes.io/serviceaccount/ca.crt"); + hints.resources().registerPattern("var/run/secrets/kubernetes.io/serviceaccount/namespace"); + + } + +} diff --git a/spring-cloud-kubernetes-fabric8-autoconfig/src/main/resources/META-INF/spring/aot.factories b/spring-cloud-kubernetes-fabric8-autoconfig/src/main/resources/META-INF/spring/aot.factories new file mode 100644 index 0000000000..3d348bed44 --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-autoconfig/src/main/resources/META-INF/spring/aot.factories @@ -0,0 +1,2 @@ +org.springframework.aot.hint.RuntimeHintsRegistrar=\ +org.springframework.cloud.kubernetes.fabric8.Fabric8RuntimeHints From ac9fd366e1b311da87d6f8b94bb0c1d70e06675e Mon Sep 17 00:00:00 2001 From: Abu Hena Mostafa Kamal Date: Sun, 21 Jun 2026 20:48:37 +0600 Subject: [PATCH 2/4] fix: add GraalVM native image support for Fabric8 Kubernetes client Add RuntimeHintsRegistrar and @ConstructorBinding fixes to enable Spring Cloud Kubernetes to run as a GraalVM native image. Changes: - Add Fabric8RuntimeHints implementing RuntimeHintsRegistrar with reflection hints for Fabric8 client classes (KubernetesClientImpl, KubernetesClientBuilder, Config, ConfigBuilder), Kubernetes resources (ConfigMap, ConfigMapList, Secret, SecretList, ObjectMeta), and config properties (ConfigMapConfigProperties, SecretsConfigProperties) - Remove @ImportRuntimeHints(Fabric8RuntimeHints.class) from Fabric8AutoConfiguration and register Fabric8RuntimeHints via META-INF/spring/aot.factories instead for proper AOT processing - Add @ConstructorBinding to ConfigMapConfigProperties constructor to ensure correct binding behavior in native AOT mode - Add @ConstructorBinding to SecretsConfigProperties constructor for the same reason - Register KubernetesConfigDataLoader in spring.factories to fix silent config data loading failure in native AOT mode Validated on GraalVM CE 25.0.2 with Spring Boot 4.1.0-SNAPSHOT and Kubernetes 1.34.1 on a live kubeadm cluster (93ms startup time). Fixes: native image startup failure with NoSuchMethodException for ConfigMapConfigProperties and SecretsConfigProperties, and silent config data load skip in AOT mode. Signed-off-by: Abu Hena Mostafa Kamal --- .../config/ConfigMapConfigProperties.java | 2 + .../config/SecretsConfigProperties.java | 2 + .../fabric8/Fabric8AutoConfiguration.java | 2 - .../fabric8/Fabric8RuntimeHints.java | 60 ++++++++++++++++--- .../main/resources/META-INF/spring.factories | 2 + 5 files changed, 57 insertions(+), 11 deletions(-) diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapConfigProperties.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapConfigProperties.java index 9b3c5d4a33..ee50007cfc 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapConfigProperties.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapConfigProperties.java @@ -20,6 +20,7 @@ import java.util.Map; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.bind.ConstructorBinding; import org.springframework.boot.context.properties.bind.DefaultValue; /** @@ -36,6 +37,7 @@ public final class ConfigMapConfigProperties extends SourceConfigProperties { */ public static final String PREFIX = "spring.cloud.kubernetes.config"; + @ConstructorBinding public ConfigMapConfigProperties(@DefaultValue("true") boolean enabled, @DefaultValue List sources, @DefaultValue Map labels, String name, String namespace, boolean useNameAsPrefix, @DefaultValue("true") boolean includeProfileSpecificSources, boolean failFast, diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigProperties.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigProperties.java index 0cc77dfae5..76ed3052cb 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigProperties.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigProperties.java @@ -20,6 +20,7 @@ import java.util.Map; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.bind.ConstructorBinding; import org.springframework.boot.context.properties.bind.DefaultValue; /** @@ -37,6 +38,7 @@ public final class SecretsConfigProperties extends SourceConfigProperties { */ public static final String PREFIX = "spring.cloud.kubernetes.secrets"; + @ConstructorBinding public SecretsConfigProperties(@DefaultValue("false") boolean enabled, @DefaultValue List sources, @DefaultValue Map labels, String name, String namespace, boolean useNameAsPrefix, @DefaultValue("true") boolean includeProfileSpecificSources, boolean failFast, diff --git a/spring-cloud-kubernetes-fabric8-autoconfig/src/main/java/org/springframework/cloud/kubernetes/fabric8/Fabric8AutoConfiguration.java b/spring-cloud-kubernetes-fabric8-autoconfig/src/main/java/org/springframework/cloud/kubernetes/fabric8/Fabric8AutoConfiguration.java index cccd170de9..c85d8db13b 100644 --- a/spring-cloud-kubernetes-fabric8-autoconfig/src/main/java/org/springframework/cloud/kubernetes/fabric8/Fabric8AutoConfiguration.java +++ b/spring-cloud-kubernetes-fabric8-autoconfig/src/main/java/org/springframework/cloud/kubernetes/fabric8/Fabric8AutoConfiguration.java @@ -32,7 +32,6 @@ import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.ImportRuntimeHints; import org.springframework.core.env.Environment; /** @@ -42,7 +41,6 @@ * @author Eddú Meléndez * @author Tim Ysewyn */ -@ImportRuntimeHints(Fabric8RuntimeHints.class) @Configuration(proxyBeanMethods = false) @ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES) @AutoConfigureAfter(KubernetesCommonsAutoConfiguration.class) diff --git a/spring-cloud-kubernetes-fabric8-autoconfig/src/main/java/org/springframework/cloud/kubernetes/fabric8/Fabric8RuntimeHints.java b/spring-cloud-kubernetes-fabric8-autoconfig/src/main/java/org/springframework/cloud/kubernetes/fabric8/Fabric8RuntimeHints.java index 053c140ccd..8071f5b4d4 100644 --- a/spring-cloud-kubernetes-fabric8-autoconfig/src/main/java/org/springframework/cloud/kubernetes/fabric8/Fabric8RuntimeHints.java +++ b/spring-cloud-kubernetes-fabric8-autoconfig/src/main/java/org/springframework/cloud/kubernetes/fabric8/Fabric8RuntimeHints.java @@ -16,7 +16,13 @@ package org.springframework.cloud.kubernetes.fabric8; +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ConfigMapList; +import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.fabric8.kubernetes.api.model.Secret; +import io.fabric8.kubernetes.api.model.SecretList; import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.ConfigBuilder; import io.fabric8.kubernetes.client.KubernetesClientBuilder; import io.fabric8.kubernetes.client.impl.KubernetesClientImpl; @@ -24,19 +30,24 @@ import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHintsRegistrar; import org.springframework.aot.hint.TypeReference; +import org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties; +import org.springframework.cloud.kubernetes.commons.config.SecretsConfigProperties; /** * {@link RuntimeHintsRegistrar} for Spring Cloud Kubernetes Fabric8 native image support. * - * Registers reflection, resource, and proxy hints required for GraalVM native image - * compilation when using the Fabric8 Kubernetes client with Spring Cloud Kubernetes. + *

+ * Registers reflection hints required for GraalVM native image compilation when using the + * Fabric8 Kubernetes client with Spring Cloud Kubernetes. * * @author Abu Hena Mostafa Kamal + * @since 5.0.3 */ public class Fabric8RuntimeHints implements RuntimeHintsRegistrar { @Override public void registerHints(RuntimeHints hints, ClassLoader classLoader) { + // ===== FABRIC8 CLIENT CLASSES ===== // Fabric8 client impl - loaded reflectively via KubernetesClientBuilder hints.reflection() .registerType(TypeReference.of(KubernetesClientImpl.class), MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, @@ -52,17 +63,48 @@ public void registerHints(RuntimeHints hints, ClassLoader classLoader) { .registerType(TypeReference.of(Config.class), MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.ACCESS_DECLARED_FIELDS); - // EnvironmentPostProcessor loaded via spring.factories + // ConfigBuilder - used in Fabric8AutoConfiguration.kubernetesClientConfig() hints.reflection() - .registerType(TypeReference.of(Fabric8ProfileEnvironmentPostProcessor.class), + .registerType(TypeReference.of(ConfigBuilder.class), MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, + MemberCategory.INVOKE_DECLARED_METHODS); + + // ===== KUBERNETES RESOURCES (CRITICAL FOR CONFIGMAP READING) ===== + // ConfigMap - the actual resource being read + hints.reflection() + .registerType(TypeReference.of(ConfigMap.class), MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, + MemberCategory.ACCESS_DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS); + + // ConfigMapList - for listing ConfigMaps + hints.reflection() + .registerType(TypeReference.of(ConfigMapList.class), MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, + MemberCategory.ACCESS_DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS); + + // ObjectMeta - metadata for all Kubernetes resources + hints.reflection() + .registerType(TypeReference.of(ObjectMeta.class), MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, + MemberCategory.ACCESS_DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS); + + // Secret - if using secrets + hints.reflection() + .registerType(TypeReference.of(Secret.class), MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, + MemberCategory.ACCESS_DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS); + + hints.reflection() + .registerType(TypeReference.of(SecretList.class), MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, + MemberCategory.ACCESS_DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS); + + hints.reflection() + .registerType(TypeReference.of(ConfigMapConfigProperties.class), MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); - // Kubernetes service account and kubeconfig resources - hints.resources().registerPattern(".kube/config"); - hints.resources().registerPattern("var/run/secrets/kubernetes.io/serviceaccount/token"); - hints.resources().registerPattern("var/run/secrets/kubernetes.io/serviceaccount/ca.crt"); - hints.resources().registerPattern("var/run/secrets/kubernetes.io/serviceaccount/namespace"); + hints.reflection() + .registerType(TypeReference.of(SecretsConfigProperties.class), MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); + + // ===== RESOURCE HINTS ===== + // Kubernetes service account and Fabric8 service files + hints.resources().registerPattern("classpath*:META-INF/services/io.fabric8.kubernetes.client.*"); + hints.resources().registerPattern("classpath*:META-INF/fabric8/*"); } } diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/resources/META-INF/spring.factories b/spring-cloud-kubernetes-fabric8-config/src/main/resources/META-INF/spring.factories index 10f5725278..35a7e4056d 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-kubernetes-fabric8-config/src/main/resources/META-INF/spring.factories @@ -4,3 +4,5 @@ org.springframework.cloud.kubernetes.fabric8.config.Fabric8RetryBootstrapConfigu # ConfigData Location Resolvers org.springframework.boot.context.config.ConfigDataLocationResolver=\ org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigDataLocationResolver +org.springframework.boot.context.config.ConfigDataLoader=\ +org.springframework.cloud.kubernetes.commons.configdata.KubernetesConfigDataLoader From a4990c84c79c797fecc87f9e9af0bcfdce2ab1c7 Mon Sep 17 00:00:00 2001 From: Abu Hena Mostafa Kamal Date: Mon, 22 Jun 2026 00:24:43 +0600 Subject: [PATCH 3/4] fix: support GraalVM native image for Fabric8 Spring Cloud Kubernetes Remove @ConstructorBinding from ConfigMapConfigProperties and SecretsConfigProperties as it is not needed in this case. Register both classes in Fabric8RuntimeHints with INVOKE_DECLARED_CONSTRUCTORS to ensure GraalVM can construct them at runtime. Add unit tests for Fabric8RuntimeHints covering reflection hint registrations for Fabric8 client classes, Kubernetes API resources, and config properties. Signed-off-by: Abu Hena Mostafa Kamal --- .../config/ConfigMapConfigProperties.java | 2 - .../config/SecretsConfigProperties.java | 2 - .../fabric8/Fabric8RuntimeHintsTests.java | 88 +++++++++++++++++++ 3 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 spring-cloud-kubernetes-fabric8-autoconfig/src/test/java/org/springframework/cloud/kubernetes/fabric8/Fabric8RuntimeHintsTests.java diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapConfigProperties.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapConfigProperties.java index ee50007cfc..9b3c5d4a33 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapConfigProperties.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapConfigProperties.java @@ -20,7 +20,6 @@ import java.util.Map; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.context.properties.bind.ConstructorBinding; import org.springframework.boot.context.properties.bind.DefaultValue; /** @@ -37,7 +36,6 @@ public final class ConfigMapConfigProperties extends SourceConfigProperties { */ public static final String PREFIX = "spring.cloud.kubernetes.config"; - @ConstructorBinding public ConfigMapConfigProperties(@DefaultValue("true") boolean enabled, @DefaultValue List sources, @DefaultValue Map labels, String name, String namespace, boolean useNameAsPrefix, @DefaultValue("true") boolean includeProfileSpecificSources, boolean failFast, diff --git a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigProperties.java b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigProperties.java index 76ed3052cb..0cc77dfae5 100644 --- a/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigProperties.java +++ b/spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigProperties.java @@ -20,7 +20,6 @@ import java.util.Map; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.context.properties.bind.ConstructorBinding; import org.springframework.boot.context.properties.bind.DefaultValue; /** @@ -38,7 +37,6 @@ public final class SecretsConfigProperties extends SourceConfigProperties { */ public static final String PREFIX = "spring.cloud.kubernetes.secrets"; - @ConstructorBinding public SecretsConfigProperties(@DefaultValue("false") boolean enabled, @DefaultValue List sources, @DefaultValue Map labels, String name, String namespace, boolean useNameAsPrefix, @DefaultValue("true") boolean includeProfileSpecificSources, boolean failFast, diff --git a/spring-cloud-kubernetes-fabric8-autoconfig/src/test/java/org/springframework/cloud/kubernetes/fabric8/Fabric8RuntimeHintsTests.java b/spring-cloud-kubernetes-fabric8-autoconfig/src/test/java/org/springframework/cloud/kubernetes/fabric8/Fabric8RuntimeHintsTests.java new file mode 100644 index 0000000000..5c0c55938d --- /dev/null +++ b/spring-cloud-kubernetes-fabric8-autoconfig/src/test/java/org/springframework/cloud/kubernetes/fabric8/Fabric8RuntimeHintsTests.java @@ -0,0 +1,88 @@ +/* + * Copyright 2013-present the original author or 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 + * + * https://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 org.springframework.cloud.kubernetes.fabric8; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ConfigMapList; +import io.fabric8.kubernetes.api.model.ObjectMeta; +import io.fabric8.kubernetes.api.model.Secret; +import io.fabric8.kubernetes.api.model.SecretList; +import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.ConfigBuilder; +import io.fabric8.kubernetes.client.KubernetesClientBuilder; +import io.fabric8.kubernetes.client.impl.KubernetesClientImpl; +import org.junit.jupiter.api.Test; + +import org.springframework.aot.hint.RuntimeHints; +import org.springframework.aot.hint.predicate.RuntimeHintsPredicates; +import org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties; +import org.springframework.cloud.kubernetes.commons.config.SecretsConfigProperties; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link Fabric8RuntimeHints}. + * + * @author Abu Hena Mostafa Kamal + * @since 5.0.3 + */ +class Fabric8RuntimeHintsTests { + + private final Fabric8RuntimeHints hintsRegistrar = new Fabric8RuntimeHints(); + + @Test + void fabric8ClientClassesShouldBeRegistered() { + RuntimeHints hints = new RuntimeHints(); + hintsRegistrar.registerHints(hints, null); + + assertThat(RuntimeHintsPredicates.reflection().onType(KubernetesClientImpl.class)).accepts(hints); + assertThat(RuntimeHintsPredicates.reflection().onType(KubernetesClientBuilder.class)).accepts(hints); + assertThat(RuntimeHintsPredicates.reflection().onType(Config.class)).accepts(hints); + assertThat(RuntimeHintsPredicates.reflection().onType(ConfigBuilder.class)).accepts(hints); + } + + @Test + void kubernetesResourcesShouldBeRegistered() { + RuntimeHints hints = new RuntimeHints(); + hintsRegistrar.registerHints(hints, null); + + assertThat(RuntimeHintsPredicates.reflection().onType(ConfigMap.class)).accepts(hints); + assertThat(RuntimeHintsPredicates.reflection().onType(ConfigMapList.class)).accepts(hints); + assertThat(RuntimeHintsPredicates.reflection().onType(ObjectMeta.class)).accepts(hints); + assertThat(RuntimeHintsPredicates.reflection().onType(Secret.class)).accepts(hints); + assertThat(RuntimeHintsPredicates.reflection().onType(SecretList.class)).accepts(hints); + } + + @Test + void configPropertiesShouldBeRegistered() { + RuntimeHints hints = new RuntimeHints(); + hintsRegistrar.registerHints(hints, null); + + assertThat(RuntimeHintsPredicates.reflection().onType(ConfigMapConfigProperties.class)).accepts(hints); + assertThat(RuntimeHintsPredicates.reflection().onType(SecretsConfigProperties.class)).accepts(hints); + } + + @Test + void unrelatedClassesShouldNotBeRegistered() { + RuntimeHints hints = new RuntimeHints(); + hintsRegistrar.registerHints(hints, null); + + assertThat(RuntimeHintsPredicates.reflection().onType(String.class)).rejects(hints); + assertThat(RuntimeHintsPredicates.reflection().onType(Integer.class)).rejects(hints); + } + +} From 4dd0f5a61f87f700db3510207f7c37d6d717501c Mon Sep 17 00:00:00 2001 From: Abu Hena Mostafa kamal Date: Mon, 22 Jun 2026 14:02:22 +0600 Subject: [PATCH 4/4] fix: remove redundant KubernetesConfigDataLoader spring.factories entry Spring Boot AOT engine automatically processes ConfigDataLoader registrations from META-INF/spring/ imports file, making the spring.factories entry unnecessary and redundant. Signed-off-by: Abu Hena Mostafa Kamal --- .../src/main/resources/META-INF/spring.factories | 2 -- 1 file changed, 2 deletions(-) diff --git a/spring-cloud-kubernetes-fabric8-config/src/main/resources/META-INF/spring.factories b/spring-cloud-kubernetes-fabric8-config/src/main/resources/META-INF/spring.factories index 35a7e4056d..10f5725278 100644 --- a/spring-cloud-kubernetes-fabric8-config/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-kubernetes-fabric8-config/src/main/resources/META-INF/spring.factories @@ -4,5 +4,3 @@ org.springframework.cloud.kubernetes.fabric8.config.Fabric8RetryBootstrapConfigu # ConfigData Location Resolvers org.springframework.boot.context.config.ConfigDataLocationResolver=\ org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigDataLocationResolver -org.springframework.boot.context.config.ConfigDataLoader=\ -org.springframework.cloud.kubernetes.commons.configdata.KubernetesConfigDataLoader