Skip to content

Commit

Permalink
Ensure KubernetesInformerFactoryProcessor does not access any beans
Browse files Browse the repository at this point in the history
A `BeanDefinitionRegistryPostProcessor` should not ideally refer to any
bean instances, except lazily. This change makes the bean definitions
that are installed by a `<at>KubernetesInformer` lazier by putting them
behind a `Supplier`

Signed-off-by: Dave Syer <dsyer@vmware.com>
  • Loading branch information
dsyer committed Feb 9, 2021
1 parent 284e6cd commit e6b964e
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 123 deletions.

This file was deleted.

Expand Up @@ -12,6 +12,7 @@
*/
package io.kubernetes.client.spring.extended.controller;

import io.kubernetes.client.common.KubernetesObject;
import io.kubernetes.client.informer.SharedIndexInformer;
import io.kubernetes.client.informer.SharedInformer;
import io.kubernetes.client.informer.SharedInformerFactory;
Expand All @@ -20,18 +21,16 @@
import io.kubernetes.client.spring.extended.controller.annotation.KubernetesInformer;
import io.kubernetes.client.spring.extended.controller.annotation.KubernetesInformers;
import io.kubernetes.client.util.generic.GenericKubernetesApi;
import java.time.Duration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.Ordered;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotatedElementUtils;

/**
* The type Kubernetes informer factory processor which basically does the following things:
Expand All @@ -44,73 +43,14 @@
public class KubernetesInformerFactoryProcessor
implements BeanDefinitionRegistryPostProcessor, Ordered {

private static final Logger log =
LoggerFactory.getLogger(KubernetesInformerFactoryProcessor.class);

public static final int ORDER = 0;

private BeanDefinitionRegistry beanDefinitionRegistry;

private final ApiClient apiClient;
private final SharedInformerFactory sharedInformerFactory;

@Autowired
public KubernetesInformerFactoryProcessor(
ApiClient apiClient, SharedInformerFactory sharedInformerFactory) {
this.apiClient = apiClient;
this.sharedInformerFactory = sharedInformerFactory;
}
private ConfigurableListableBeanFactory beanFactory;

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {

this.apiClient.setHttpClient(
this.apiClient.getHttpClient().newBuilder().readTimeout(Duration.ZERO).build());

KubernetesInformers kubernetesInformers =
sharedInformerFactory.getClass().getAnnotation(KubernetesInformers.class);
if (kubernetesInformers == null || kubernetesInformers.value().length == 0) {
log.info("No informers registered in the sharedInformerFactory..");
return;
}
for (KubernetesInformer kubernetesInformer : kubernetesInformers.value()) {
final GenericKubernetesApi api =
new GenericKubernetesApi(
kubernetesInformer.apiTypeClass(),
kubernetesInformer.apiListTypeClass(),
kubernetesInformer.groupVersionResource().apiGroup(),
kubernetesInformer.groupVersionResource().apiVersion(),
kubernetesInformer.groupVersionResource().resourcePlural(),
apiClient);
SharedIndexInformer sharedIndexInformer =
sharedInformerFactory.sharedIndexInformerFor(
api,
kubernetesInformer.apiTypeClass(),
kubernetesInformer.resyncPeriodMillis(),
kubernetesInformer.namespace());
ResolvableType informerType =
ResolvableType.forClassWithGenerics(
SharedInformer.class, kubernetesInformer.apiTypeClass());
RootBeanDefinition informerBean = new RootBeanDefinition();
informerBean.setTargetType(informerType);
informerBean.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
informerBean.setAutowireCandidate(true);
String informerBeanName = informerType.toString();
this.beanDefinitionRegistry.registerBeanDefinition(informerBeanName, informerBean);
beanFactory.registerSingleton(informerBeanName, sharedIndexInformer);

Lister lister = new Lister(sharedIndexInformer.getIndexer());
ResolvableType listerType =
ResolvableType.forClassWithGenerics(Lister.class, kubernetesInformer.apiTypeClass());
RootBeanDefinition listerBean = new RootBeanDefinition();
listerBean.setTargetType(listerType);
listerBean.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
listerBean.setAutowireCandidate(true);
String listerBeanName = listerType.toString();
this.beanDefinitionRegistry.registerBeanDefinition(listerBeanName, listerBean);
beanFactory.registerSingleton(listerBeanName, lister);
}
this.beanFactory = beanFactory;
}

@Override
Expand All @@ -121,6 +61,79 @@ public int getOrder() {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
throws BeansException {
this.beanDefinitionRegistry = registry;
if (registry instanceof BeanFactory) {
for (String name : registry.getBeanDefinitionNames()) {
Class<?> cls = ((BeanFactory) registry).getType(name);
if (cls != null) {
KubernetesInformers kubernetesInformers =
AnnotatedElementUtils.getMergedAnnotation(cls, KubernetesInformers.class);
if (kubernetesInformers != null && kubernetesInformers.value().length > 0) {
for (KubernetesInformer kubernetesInformer : kubernetesInformers.value()) {
registerInformer(registry, kubernetesInformer);
registerLister(registry, kubernetesInformer);
}
}
}
}
}
}

private void registerInformer(
BeanDefinitionRegistry registry, KubernetesInformer kubernetesInformer) {
RootBeanDefinition informerBean =
(RootBeanDefinition)
BeanDefinitionBuilder.rootBeanDefinition(SharedInformer.class).getBeanDefinition();
informerBean.setInstanceSupplier(
() -> informer(kubernetesInformer.apiTypeClass(), kubernetesInformer));
ResolvableType informerType =
ResolvableType.forClassWithGenerics(
SharedIndexInformer.class, kubernetesInformer.apiTypeClass());
informerBean.setTargetType(informerType);
registry.registerBeanDefinition(
getInformerBeanName(kubernetesInformer.apiTypeClass()), informerBean);
}

private String getInformerBeanName(Class<?> type) {
return type.getName() + "Informer";
}

private void registerLister(
BeanDefinitionRegistry registry, KubernetesInformer kubernetesInformer) {
RootBeanDefinition listerBean =
(RootBeanDefinition)
BeanDefinitionBuilder.rootBeanDefinition(Lister.class).getBeanDefinition();
listerBean.setInstanceSupplier(() -> lister(kubernetesInformer.apiTypeClass()));
ResolvableType listerType =
ResolvableType.forClassWithGenerics(Lister.class, kubernetesInformer.apiTypeClass());
listerBean.setTargetType(listerType);
registry.registerBeanDefinition(listerType.toString(), listerBean);
}

@SuppressWarnings({"unchecked", "rawtypes"})
private <T extends KubernetesObject> Lister<T> lister(Class<T> type) {
SharedIndexInformer sharedInformer =
this.beanFactory.getBean(getInformerBeanName(type), SharedIndexInformer.class);
Lister<T> lister = new Lister<>(sharedInformer.getIndexer());
return lister;
}

@SuppressWarnings({"unchecked", "rawtypes"})
private <T extends KubernetesObject> SharedInformer<T> informer(
Class<T> type, KubernetesInformer kubernetesInformer) {
ApiClient apiClient = this.beanFactory.getBean(ApiClient.class);
SharedInformerFactory sharedInformerFactory =
this.beanFactory.getBean(SharedInformerFactory.class);
final GenericKubernetesApi api =
new GenericKubernetesApi(
kubernetesInformer.apiTypeClass(),
kubernetesInformer.apiListTypeClass(),
kubernetesInformer.groupVersionResource().apiGroup(),
kubernetesInformer.groupVersionResource().apiVersion(),
kubernetesInformer.groupVersionResource().resourcePlural(),
apiClient);
SharedIndexInformer<T> sharedIndexInformer =
sharedInformerFactory.sharedIndexInformerFor(
api, type, kubernetesInformer.resyncPeriodMillis(), kubernetesInformer.namespace());
return sharedIndexInformer;
}
}
Expand Up @@ -14,7 +14,7 @@

import io.kubernetes.client.informer.SharedInformerFactory;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.spring.extended.controller.KubernetesInformerConfigurer;
import io.kubernetes.client.spring.extended.controller.KubernetesInformerFactoryProcessor;
import io.kubernetes.client.util.ClientBuilder;
import java.io.IOException;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
Expand All @@ -39,8 +39,7 @@ public SharedInformerFactory sharedInformerFactory() {

@Bean
@ConditionalOnMissingBean
public KubernetesInformerConfigurer kubernetesInformerConfigurer(
ApiClient apiClient, SharedInformerFactory sharedInformerFactory) {
return new KubernetesInformerConfigurer(apiClient, sharedInformerFactory);
public static KubernetesInformerFactoryProcessor kubernetesInformerConfigurer() {
return new KubernetesInformerFactoryProcessor();
}
}
Expand Up @@ -66,7 +66,7 @@ public ApiClient testingApiClient() {
}

@Bean
public SharedInformerFactory sharedInformerFactory() {
public TestSharedInformerFactory testSharedInformerFactory() {
return new TestSharedInformerFactory();
}

Expand All @@ -86,7 +86,7 @@ public SharedInformerFactory sharedInformerFactory() {
apiVersion = "v1",
resourcePlural = "configmaps")),
})
static class TestSharedInformerFactory extends SharedInformerFactory {}
static class TestSharedInformerFactory {}
}

@Autowired private SharedInformerFactory informerFactory;
Expand Down
Expand Up @@ -75,8 +75,8 @@ public ApiClient testingApiClient() {
}

@Bean
public SharedInformerFactory sharedInformerFactory() {
return new KubernetesInformerCreatorTest.App.TestSharedInformerFactory();
public TestSharedInformerFactory testSharedInformerFactory() {
return new TestSharedInformerFactory();
}

@KubernetesInformers({
Expand All @@ -94,7 +94,7 @@ public SharedInformerFactory sharedInformerFactory() {
apiVersion = "v1",
resourcePlural = "configmaps")),
})
static class TestSharedInformerFactory extends SharedInformerFactory {}
static class TestSharedInformerFactory {}

@Bean
public TestReconciler testReconciler() {
Expand Down
Expand Up @@ -19,7 +19,6 @@
import io.kubernetes.client.extended.controller.reconciler.Request;
import io.kubernetes.client.extended.controller.reconciler.Result;
import io.kubernetes.client.informer.SharedInformer;
import io.kubernetes.client.informer.SharedInformerFactory;
import io.kubernetes.client.openapi.models.V1Pod;
import io.kubernetes.client.openapi.models.V1PodList;
import io.kubernetes.client.spring.extended.controller.annotation.GroupVersionResource;
Expand Down Expand Up @@ -49,7 +48,7 @@ KubernetesReconcilerProcessor reconcilerProcessorUnderTesting() {
}

@Bean
SharedInformerFactory sharedInformerFactory() {
TestSharedInformerFactory testSharedInformerFactory() {
return new TestSharedInformerFactory();
}

Expand All @@ -59,7 +58,7 @@ SharedInformerFactory sharedInformerFactory() {
apiListTypeClass = V1PodList.class,
groupVersionResource = @GroupVersionResource(resourcePlural = "pods"))
})
static class TestSharedInformerFactory extends SharedInformerFactory {}
static class TestSharedInformerFactory {}

@Bean("testReconciler1")
TestReconciler testReconciler1ToBeInjected() {
Expand Down

0 comments on commit e6b964e

Please sign in to comment.