Skip to content

Commit

Permalink
feat: static KubernetesResource class registration in synthetic bean
Browse files Browse the repository at this point in the history
Signed-off-by: Marc Nuri <marc@marcnuri.com>
  • Loading branch information
manusa committed Jun 9, 2023
1 parent 4f1bf57 commit f6ebdc8
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 39 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package io.quarkus.kubernetes.client.deployment;

import jakarta.inject.Singleton;

import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.DotName;
import org.jboss.jandex.Type;

import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.arc.deployment.SyntheticBeanBuildItem;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.ExecutionTime;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.kubernetes.client.runtime.KubernetesResources;
import io.quarkus.kubernetes.client.runtime.KubernetesSerializationRecorder;

public class KubernetesResourceBuildStep {
@BuildStep
@Record(ExecutionTime.STATIC_INIT)
SyntheticBeanBuildItem kubernetesResourceClasses(
KubernetesSerializationRecorder recorder, BuildProducer<AdditionalBeanBuildItem> additionalBeans) {
additionalBeans.produce(AdditionalBeanBuildItem.unremovableOf(KubernetesResources.class));
final var classArray = Type.create(DotName.createSimple(Class[].class.getName()), Type.Kind.ARRAY);
return SyntheticBeanBuildItem
.configure(Object.class).providerType(classArray).addType(classArray)
.scope(Singleton.class)
.qualifiers(AnnotationInstance.builder(KubernetesResources.class).build())
.runtimeValue(recorder.scanKubernetesResources())
.done();
}
}
Original file line number Diff line number Diff line change
@@ -1,42 +1,22 @@
package io.quarkus.kubernetes.client.runtime;

import java.time.Duration;
import java.util.HashSet;
import java.util.ServiceLoader;
import java.util.Set;

import org.eclipse.microprofile.config.ConfigProvider;

import com.fasterxml.jackson.databind.ObjectMapper;

import io.fabric8.kubernetes.api.model.KubernetesResource;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.ConfigBuilder;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
import io.fabric8.kubernetes.client.utils.KubernetesSerialization;
import io.fabric8.kubernetes.internal.KubernetesDeserializer;
import io.quarkus.runtime.TlsConfig;

public class KubernetesClientUtils {

private static final String PREFIX = "quarkus.kubernetes-client.";
// Unless we grab a static reference to the KubernetesResource classes, native mode will fail when deserializing
// since those classes won't be available and won't be registered in the KubernetesSerialization instance
private static final KubernetesSerialization SERIALIZATION = new KubernetesSerialization(new ObjectMapper(), false);
static {
for (var kubernetesResource : scanKubernetesResources()) {
SERIALIZATION.registerKubernetesResource(kubernetesResource);
}
}

private KubernetesClientUtils() {
}

private static KubernetesClientBuilder kubernetesClientBuilder() {
return new KubernetesClientBuilder().withKubernetesSerialization(SERIALIZATION);
}

public static Config createConfig(KubernetesClientBuildConfig buildConfig, TlsConfig tlsConfig) {
Config base = Config.autoConfigure(null);
boolean trustAll = buildConfig.trustCerts.isPresent() ? buildConfig.trustCerts.get() : tlsConfig.trustAll;
Expand Down Expand Up @@ -71,13 +51,13 @@ public static Config createConfig(KubernetesClientBuildConfig buildConfig, TlsCo
}

public static KubernetesClient createClient(KubernetesClientBuildConfig buildConfig, TlsConfig tlsConfig) {
return kubernetesClientBuilder().withConfig(createConfig(buildConfig, tlsConfig)).build();
return new KubernetesClientBuilder().withConfig(createConfig(buildConfig, tlsConfig)).build();
}

public static KubernetesClient createClient() {
org.eclipse.microprofile.config.Config config = ConfigProvider.getConfig();
Config base = Config.autoConfigure(null);
return kubernetesClientBuilder().withConfig(new ConfigBuilder()
return new KubernetesClientBuilder().withConfig(new ConfigBuilder()
.withTrustCerts(config.getOptionalValue(PREFIX + "trust-certs", Boolean.class).orElse(base.isTrustCerts()))
.withWatchReconnectLimit(config.getOptionalValue(PREFIX + "watch-reconnect-limit", Integer.class)
.orElse(base.getWatchReconnectLimit()))
Expand Down Expand Up @@ -116,19 +96,4 @@ public static KubernetesClient createClient() {
.build())
.build();
}

public static Set<Class<? extends KubernetesResource>> scanKubernetesResources() {
final Set<Class<? extends KubernetesResource>> ret = new HashSet<>();
for (var cl : new ClassLoader[] { Thread.currentThread().getContextClassLoader(),
KubernetesDeserializer.class.getClassLoader() }) {
if (cl == null) {
continue;
}
final var serviceLoader = ServiceLoader.load(KubernetesResource.class, cl);
for (var kr : serviceLoader) {
ret.add(kr.getClass());
}
}
return ret;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.quarkus.kubernetes.client.runtime;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import jakarta.inject.Qualifier;

@Qualifier
@Retention(RUNTIME)
@Target({ METHOD, FIELD, PARAMETER, TYPE })
public @interface KubernetesResources {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.quarkus.kubernetes.client.runtime;

import java.util.HashSet;
import java.util.ServiceLoader;
import java.util.Set;

import io.fabric8.kubernetes.api.model.KubernetesResource;
import io.fabric8.kubernetes.internal.KubernetesDeserializer;
import io.quarkus.runtime.RuntimeValue;
import io.quarkus.runtime.annotations.Recorder;

@Recorder
public class KubernetesSerializationRecorder {

public RuntimeValue<Class<? extends KubernetesResource>[]> scanKubernetesResources() {
final var classLoaders = new ClassLoader[] {
Thread.currentThread().getContextClassLoader(),
KubernetesDeserializer.class.getClassLoader() };
final Set<Class<? extends KubernetesResource>> resourceClasses = new HashSet<>();
for (var classLoader : classLoaders) {
if (classLoader == null) {
continue;
}
final var serviceLoader = ServiceLoader.load(KubernetesResource.class, classLoader);
for (var kr : serviceLoader) {
resourceClasses.add(kr.getClass());
}
}
return new RuntimeValue<>(resourceClasses.<Class<? extends KubernetesResource>> toArray(Class[]::new));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@ public class KubernetesSerializationProducer {
@DefaultBean
@Singleton
@Produces
public KubernetesSerialization kubernetesSerialization(@KubernetesClientObjectMapper ObjectMapper objectMapper) {
public KubernetesSerialization kubernetesSerialization(
@KubernetesClientObjectMapper ObjectMapper objectMapper,
@KubernetesResources Class[] kubernetesResources) {
final var kubernetesSerialization = new KubernetesSerialization(objectMapper, false);
KubernetesClientUtils.scanKubernetesResources().forEach(kubernetesSerialization::registerKubernetesResource);
for (var kubernetesResource : kubernetesResources) {
kubernetesSerialization.registerKubernetesResource(kubernetesResource);
}
return kubernetesSerialization;
}
}

0 comments on commit f6ebdc8

Please sign in to comment.