Skip to content

Commit

Permalink
Merge pull request #10344 from ctron/feature/fix_issue_10295_1
Browse files Browse the repository at this point in the history
[#10295] Allow using the informer interface, and trace down all types
  • Loading branch information
geoand authored Jul 1, 2020
2 parents 1aa3890 + 567d9cf commit 3ffd2fc
Showing 1 changed file with 60 additions and 23 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package io.quarkus.kubernetes.client.deployment;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import javax.inject.Inject;

import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.Type;
import org.jboss.logging.Logger;
Expand All @@ -19,6 +22,7 @@
import io.quarkus.deployment.builditem.ExtensionSslNativeSupportBuildItem;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveHierarchyBuildItem;
import io.quarkus.deployment.util.JandexUtil;
import io.quarkus.jackson.deployment.IgnoreJsonDeserializeClassBuildItem;
import io.quarkus.kubernetes.client.runtime.KubernetesClientProducer;
Expand All @@ -27,8 +31,12 @@
public class KubernetesClientProcessor {

private static final DotName WATCHER = DotName.createSimple("io.fabric8.kubernetes.client.Watcher");
private static final DotName RESOURCE_EVENT_HANDLER = DotName
.createSimple("io.fabric8.kubernetes.client.informers.ResourceEventHandler");
private static final DotName KUBERNETES_RESOURCE = DotName
.createSimple("io.fabric8.kubernetes.api.model.KubernetesResource");
private static final DotName KUBERNETES_RESOURCE_LIST = DotName
.createSimple("io.fabric8.kubernetes.api.model.KubernetesResourceList");

private static final Logger log = Logger.getLogger(KubernetesClientProcessor.class.getName());

Expand All @@ -38,6 +46,9 @@ public class KubernetesClientProcessor {
@Inject
BuildProducer<ReflectiveClassBuildItem> reflectiveClasses;

@Inject
BuildProducer<ReflectiveHierarchyBuildItem> reflectiveHierarchies;

@Inject
BuildProducer<IgnoreJsonDeserializeClassBuildItem> ignoredJsonDeserializationClasses;

Expand All @@ -52,29 +63,19 @@ public void process(ApplicationIndexBuildItem applicationIndex, CombinedIndexBui
featureProducer.produce(new FeatureBuildItem(Feature.KUBERNETES_CLIENT));
roleProducer.produce(new KubernetesRoleBuildItem("view"));

Set<String> watchedClasses = new HashSet<>();
// make sure the watchers fully (and not weakly) register Kubernetes classes for reflection
applicationIndex.getIndex().getAllKnownImplementors(WATCHER)
.forEach(c -> {
try {
final List<Type> watcherGenericTypes = JandexUtil.resolveTypeParameters(c.name(),
WATCHER, combinedIndexBuildItem.getIndex());
if (watcherGenericTypes.size() == 1) {
watchedClasses.add(watcherGenericTypes.get(0).name().toString());
}
} catch (IllegalStateException ignored) {
// when the class has no subclasses and we were not able to determine the generic types, it's likely that
// the watcher will fail due to not being able to deserialize the class
if (applicationIndex.getIndex().getAllKnownSubclasses(c.name()).isEmpty()) {
log.warn("Watcher '" + c.name() + "' will most likely not work correctly in native mode. " +
"Consider specifying the generic type of 'io.fabric8.kubernetes.client.Watcher' that this class handles. "
+
"See https://quarkus.io/guides/kubernetes-client#note-on-implementing-the-watcher-interface for more details");
}
}
});
if (!watchedClasses.isEmpty()) {
reflectiveClasses.produce(new ReflectiveClassBuildItem(true, true, watchedClasses.toArray(new String[0])));
final Set<DotName> watchedClasses = new HashSet<>();
findWatchedClasses(WATCHER, applicationIndex, combinedIndexBuildItem, watchedClasses);
findWatchedClasses(RESOURCE_EVENT_HANDLER, applicationIndex, combinedIndexBuildItem, watchedClasses);

for (DotName className : watchedClasses) {
final ClassInfo watchedClass = combinedIndexBuildItem.getIndex().getClassByName(className);
if (watchedClass == null) {
log.warnv("Unable to lookup class: {0}", className);
} else {
reflectiveHierarchies
.produce(new ReflectiveHierarchyBuildItem(Type.create(watchedClass.name(), Type.Kind.CLASS)));
}
}

final String[] modelClasses = combinedIndexBuildItem.getIndex()
Expand All @@ -85,8 +86,9 @@ public void process(ApplicationIndexBuildItem applicationIndex, CombinedIndexBui
// since we are going to register them weakly
ignoredJsonDeserializationClasses.produce(new IgnoreJsonDeserializeClassBuildItem(c.name()));
})
.map(c -> c.name().toString())
.map(ClassInfo::name)
.filter(c -> !watchedClasses.contains(c))
.map(Object::toString)
.toArray(String[]::new);
reflectiveClasses.produce(ReflectiveClassBuildItem
.builder(modelClasses).weak(true).methods(true).fields(false).build());
Expand Down Expand Up @@ -127,10 +129,45 @@ public void process(ApplicationIndexBuildItem applicationIndex, CombinedIndexBui
reflectiveClasses
.produce(new ReflectiveClassBuildItem(true, false, "io.fabric8.kubernetes.internal.KubernetesDeserializer"));

if (log.isDebugEnabled()) {
final String watchedClassNames = watchedClasses
.stream().map(Object::toString)
.sorted()
.collect(Collectors.joining("\n"));
log.debugv("Watched Classes:\n{0}", watchedClassNames);
Arrays.sort(modelClasses);
log.debugv("Model Classes:\n{0}", String.join("\n", modelClasses));
}

// Enable SSL support by default
sslNativeSupport.produce(new ExtensionSslNativeSupportBuildItem(Feature.KUBERNETES_CLIENT));

// wire up the KubernetesClient bean support
additionalBeanBuildItemBuildItem.produce(AdditionalBeanBuildItem.unremovableOf(KubernetesClientProducer.class));
}

private void findWatchedClasses(final DotName implementor, final ApplicationIndexBuildItem applicationIndex,
final CombinedIndexBuildItem combinedIndexBuildItem, final Set<DotName> watchedClasses) {
applicationIndex.getIndex().getAllKnownImplementors(implementor)
.forEach(c -> {
try {
final List<Type> watcherGenericTypes = JandexUtil.resolveTypeParameters(c.name(),
implementor, combinedIndexBuildItem.getIndex());
if (watcherGenericTypes.size() == 1) {
watchedClasses.add(watcherGenericTypes.get(0).name());
}
} catch (IllegalStateException ignored) {
// when the class has no subclasses and we were not able to determine the generic types, it's likely that
// the watcher will fail due to not being able to deserialize the class
if (applicationIndex.getIndex().getAllKnownSubclasses(c.name()).isEmpty()) {
log.warnv("{0} '{1}' will most likely not work correctly in native mode. " +
"Consider specifying the generic type of '{2}' that this class handles. "
+
"See https://quarkus.io/guides/kubernetes-client#note-on-implementing-the-watcher-interface for more details",
implementor.local(), c.name(), implementor);
}
}
});
}

}

0 comments on commit 3ffd2fc

Please sign in to comment.