Skip to content

Commit ac9fd36

Browse files
committed
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 <kamalcis@gmail.com>
1 parent 3312d5e commit ac9fd36

5 files changed

Lines changed: 57 additions & 11 deletions

File tree

spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/ConfigMapConfigProperties.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.Map;
2121

2222
import org.springframework.boot.context.properties.ConfigurationProperties;
23+
import org.springframework.boot.context.properties.bind.ConstructorBinding;
2324
import org.springframework.boot.context.properties.bind.DefaultValue;
2425

2526
/**
@@ -36,6 +37,7 @@ public final class ConfigMapConfigProperties extends SourceConfigProperties {
3637
*/
3738
public static final String PREFIX = "spring.cloud.kubernetes.config";
3839

40+
@ConstructorBinding
3941
public ConfigMapConfigProperties(@DefaultValue("true") boolean enabled, @DefaultValue List<Source> sources,
4042
@DefaultValue Map<String, String> labels, String name, String namespace, boolean useNameAsPrefix,
4143
@DefaultValue("true") boolean includeProfileSpecificSources, boolean failFast,

spring-cloud-kubernetes-commons/src/main/java/org/springframework/cloud/kubernetes/commons/config/SecretsConfigProperties.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.Map;
2121

2222
import org.springframework.boot.context.properties.ConfigurationProperties;
23+
import org.springframework.boot.context.properties.bind.ConstructorBinding;
2324
import org.springframework.boot.context.properties.bind.DefaultValue;
2425

2526
/**
@@ -37,6 +38,7 @@ public final class SecretsConfigProperties extends SourceConfigProperties {
3738
*/
3839
public static final String PREFIX = "spring.cloud.kubernetes.secrets";
3940

41+
@ConstructorBinding
4042
public SecretsConfigProperties(@DefaultValue("false") boolean enabled, @DefaultValue List<Source> sources,
4143
@DefaultValue Map<String, String> labels, String name, String namespace, boolean useNameAsPrefix,
4244
@DefaultValue("true") boolean includeProfileSpecificSources, boolean failFast,

spring-cloud-kubernetes-fabric8-autoconfig/src/main/java/org/springframework/cloud/kubernetes/fabric8/Fabric8AutoConfiguration.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import org.springframework.cloud.kubernetes.commons.KubernetesNamespaceProvider;
3333
import org.springframework.context.annotation.Bean;
3434
import org.springframework.context.annotation.Configuration;
35-
import org.springframework.context.annotation.ImportRuntimeHints;
3635
import org.springframework.core.env.Environment;
3736

3837
/**
@@ -42,7 +41,6 @@
4241
* @author Eddú Meléndez
4342
* @author Tim Ysewyn
4443
*/
45-
@ImportRuntimeHints(Fabric8RuntimeHints.class)
4644
@Configuration(proxyBeanMethods = false)
4745
@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)
4846
@AutoConfigureAfter(KubernetesCommonsAutoConfiguration.class)

spring-cloud-kubernetes-fabric8-autoconfig/src/main/java/org/springframework/cloud/kubernetes/fabric8/Fabric8RuntimeHints.java

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,38 @@
1616

1717
package org.springframework.cloud.kubernetes.fabric8;
1818

19+
import io.fabric8.kubernetes.api.model.ConfigMap;
20+
import io.fabric8.kubernetes.api.model.ConfigMapList;
21+
import io.fabric8.kubernetes.api.model.ObjectMeta;
22+
import io.fabric8.kubernetes.api.model.Secret;
23+
import io.fabric8.kubernetes.api.model.SecretList;
1924
import io.fabric8.kubernetes.client.Config;
25+
import io.fabric8.kubernetes.client.ConfigBuilder;
2026
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
2127
import io.fabric8.kubernetes.client.impl.KubernetesClientImpl;
2228

2329
import org.springframework.aot.hint.MemberCategory;
2430
import org.springframework.aot.hint.RuntimeHints;
2531
import org.springframework.aot.hint.RuntimeHintsRegistrar;
2632
import org.springframework.aot.hint.TypeReference;
33+
import org.springframework.cloud.kubernetes.commons.config.ConfigMapConfigProperties;
34+
import org.springframework.cloud.kubernetes.commons.config.SecretsConfigProperties;
2735

2836
/**
2937
* {@link RuntimeHintsRegistrar} for Spring Cloud Kubernetes Fabric8 native image support.
3038
*
31-
* Registers reflection, resource, and proxy hints required for GraalVM native image
32-
* compilation when using the Fabric8 Kubernetes client with Spring Cloud Kubernetes.
39+
* <p>
40+
* Registers reflection hints required for GraalVM native image compilation when using the
41+
* Fabric8 Kubernetes client with Spring Cloud Kubernetes.
3342
*
3443
* @author Abu Hena Mostafa Kamal
44+
* @since 5.0.3
3545
*/
3646
public class Fabric8RuntimeHints implements RuntimeHintsRegistrar {
3747

3848
@Override
3949
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
50+
// ===== FABRIC8 CLIENT CLASSES =====
4051
// Fabric8 client impl - loaded reflectively via KubernetesClientBuilder
4152
hints.reflection()
4253
.registerType(TypeReference.of(KubernetesClientImpl.class), MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
@@ -52,17 +63,48 @@ public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
5263
.registerType(TypeReference.of(Config.class), MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
5364
MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.ACCESS_DECLARED_FIELDS);
5465

55-
// EnvironmentPostProcessor loaded via spring.factories
66+
// ConfigBuilder - used in Fabric8AutoConfiguration.kubernetesClientConfig()
5667
hints.reflection()
57-
.registerType(TypeReference.of(Fabric8ProfileEnvironmentPostProcessor.class),
68+
.registerType(TypeReference.of(ConfigBuilder.class), MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
69+
MemberCategory.INVOKE_DECLARED_METHODS);
70+
71+
// ===== KUBERNETES RESOURCES (CRITICAL FOR CONFIGMAP READING) =====
72+
// ConfigMap - the actual resource being read
73+
hints.reflection()
74+
.registerType(TypeReference.of(ConfigMap.class), MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
75+
MemberCategory.ACCESS_DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS);
76+
77+
// ConfigMapList - for listing ConfigMaps
78+
hints.reflection()
79+
.registerType(TypeReference.of(ConfigMapList.class), MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
80+
MemberCategory.ACCESS_DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS);
81+
82+
// ObjectMeta - metadata for all Kubernetes resources
83+
hints.reflection()
84+
.registerType(TypeReference.of(ObjectMeta.class), MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
85+
MemberCategory.ACCESS_DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS);
86+
87+
// Secret - if using secrets
88+
hints.reflection()
89+
.registerType(TypeReference.of(Secret.class), MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
90+
MemberCategory.ACCESS_DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS);
91+
92+
hints.reflection()
93+
.registerType(TypeReference.of(SecretList.class), MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
94+
MemberCategory.ACCESS_DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS);
95+
96+
hints.reflection()
97+
.registerType(TypeReference.of(ConfigMapConfigProperties.class),
5898
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
5999

60-
// Kubernetes service account and kubeconfig resources
61-
hints.resources().registerPattern(".kube/config");
62-
hints.resources().registerPattern("var/run/secrets/kubernetes.io/serviceaccount/token");
63-
hints.resources().registerPattern("var/run/secrets/kubernetes.io/serviceaccount/ca.crt");
64-
hints.resources().registerPattern("var/run/secrets/kubernetes.io/serviceaccount/namespace");
100+
hints.reflection()
101+
.registerType(TypeReference.of(SecretsConfigProperties.class), MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
102+
103+
// ===== RESOURCE HINTS =====
104+
// Kubernetes service account and Fabric8 service files
105+
hints.resources().registerPattern("classpath*:META-INF/services/io.fabric8.kubernetes.client.*");
65106

107+
hints.resources().registerPattern("classpath*:META-INF/fabric8/*");
66108
}
67109

68110
}

spring-cloud-kubernetes-fabric8-config/src/main/resources/META-INF/spring.factories

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@ org.springframework.cloud.kubernetes.fabric8.config.Fabric8RetryBootstrapConfigu
44
# ConfigData Location Resolvers
55
org.springframework.boot.context.config.ConfigDataLocationResolver=\
66
org.springframework.cloud.kubernetes.fabric8.config.Fabric8ConfigDataLocationResolver
7+
org.springframework.boot.context.config.ConfigDataLoader=\
8+
org.springframework.cloud.kubernetes.commons.configdata.KubernetesConfigDataLoader

0 commit comments

Comments
 (0)