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 12, 2023
1 parent 2f85dad commit a8b005f
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package io.quarkus.kubernetes.client.deployment;

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

import jakarta.inject.Singleton;

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

import io.fabric8.kubernetes.api.model.KubernetesResource;
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.deployment.builditem.nativeimage.ServiceProviderBuildItem;
import io.quarkus.kubernetes.client.KubernetesResources;
import io.quarkus.kubernetes.client.runtime.KubernetesSerializationRecorder;
import io.quarkus.kubernetes.client.spi.KubernetesResourcesBuildItem;

public class KubernetesResourceBuildStep {
@BuildStep
void scanKubernetesResourceClasses(
BuildProducer<ServiceProviderBuildItem> serviceProviderProducer,
BuildProducer<KubernetesResourcesBuildItem> kubernetesResourcesBuildItemBuildProducer) {
serviceProviderProducer.produce(ServiceProviderBuildItem.allProvidersFromClassPath(KubernetesResource.class.getName()));
final Set<Class<? extends KubernetesResource>> resourceClasses = new HashSet<>();
final var serviceLoader = ServiceLoader.load(KubernetesResource.class);
for (var kr : serviceLoader) {
resourceClasses.add(kr.getClass());
}
kubernetesResourcesBuildItemBuildProducer.produce(
new KubernetesResourcesBuildItem(resourceClasses.<Class<? extends KubernetesResource>> toArray(Class[]::new)));
}

@BuildStep
@Record(ExecutionTime.STATIC_INIT)
SyntheticBeanBuildItem kubernetesResourceClasses(
KubernetesSerializationRecorder recorder,
KubernetesResourcesBuildItem kubernetesResourcesBuildItem,
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.initKubernetesResources(kubernetesResourcesBuildItem.getResourceClasses()))
.done();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package io.quarkus.kubernetes.client.runtime;

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

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

@Recorder
public class KubernetesSerializationRecorder {

public RuntimeValue<Class<? extends KubernetesResource>[]> initKubernetesResources(String[] resourceClassNames) {
final Set<Class<?>> resourceClasses = new HashSet<>();
for (var resourceClassName : resourceClassNames) {
try {
resourceClasses.add(
Class.forName(resourceClassName, false, KubernetesSerializationRecorder.class.getClassLoader()));
} catch (ClassNotFoundException e) {
// Filter build-time only available classes from those that are really available at runtime
// e.g. The Kubernetes extension provides KubernetesResource classes only for deployment purposes (not prod code)
}
}
return new RuntimeValue<>(resourceClasses.toArray(Class[]::new));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.quarkus.kubernetes.client;

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
Expand Up @@ -8,16 +8,21 @@
import io.fabric8.kubernetes.client.utils.KubernetesSerialization;
import io.quarkus.arc.DefaultBean;
import io.quarkus.kubernetes.client.KubernetesClientObjectMapper;
import io.quarkus.kubernetes.client.KubernetesResources;

@Singleton
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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.quarkus.kubernetes.client.spi;

import io.fabric8.kubernetes.api.model.KubernetesResource;
import io.quarkus.builder.item.SimpleBuildItem;

public final class KubernetesResourcesBuildItem extends SimpleBuildItem {

private final String[] resourceClasses;

public KubernetesResourcesBuildItem(Class<? extends KubernetesResource>[] resourceClasses) {
this.resourceClasses = new String[resourceClasses.length];
for (int it = 0; it < resourceClasses.length; it++) {
this.resourceClasses[it] = resourceClasses[it].getName();
}
}

public String[] getResourceClasses() {
return resourceClasses;
}
}

0 comments on commit a8b005f

Please sign in to comment.