Skip to content

Commit ad7f1b6

Browse files
author
Alexander Savov
committed
Register ControllerFactory bean definition as opposite to bean instance
With this change KubernetesReconcilerProcessor conforms to BeanFactoryPostProcessor contract to register BeanDefinitions and not to manipulate bean instances. As a result Reconciler beans are: 1) not created at all by postProcessBeanFactory (which was the core issue) 2) participate in Spring container management hooks, and hence autowired
1 parent 70259f3 commit ad7f1b6

File tree

2 files changed

+143
-28
lines changed

2 files changed

+143
-28
lines changed

spring/src/main/java/io/kubernetes/client/spring/extended/controller/KubernetesReconcilerProcessor.java

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,17 @@
1212
*/
1313
package io.kubernetes.client.spring.extended.controller;
1414

15-
import io.kubernetes.client.extended.controller.Controller;
16-
import io.kubernetes.client.extended.controller.ControllerManager;
15+
import static org.springframework.util.Assert.notNull;
16+
1717
import io.kubernetes.client.extended.controller.reconciler.Reconciler;
1818
import io.kubernetes.client.informer.SharedInformerFactory;
19-
import io.kubernetes.client.spring.extended.controller.annotation.KubernetesReconciler;
2019
import io.kubernetes.client.spring.extended.controller.factory.KubernetesControllerFactory;
21-
import java.util.concurrent.ExecutorService;
22-
import java.util.concurrent.Executors;
23-
import org.slf4j.Logger;
24-
import org.slf4j.LoggerFactory;
25-
import org.springframework.beans.BeansException;
26-
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
20+
import java.util.function.Supplier;
21+
import org.springframework.beans.factory.config.BeanDefinition;
2722
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
23+
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
24+
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
25+
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
2826
import org.springframework.core.Ordered;
2927

3028
/**
@@ -34,18 +32,21 @@
3432
* <p>It will create a {@link io.kubernetes.client.extended.controller.Controller} for every
3533
* reconciler instances registered in the spring bean-factory.
3634
*/
37-
public class KubernetesReconcilerProcessor implements BeanFactoryPostProcessor, Ordered {
35+
public class KubernetesReconcilerProcessor implements BeanDefinitionRegistryPostProcessor, Ordered {
3836

39-
private static final Logger log = LoggerFactory.getLogger(KubernetesReconcilerProcessor.class);
37+
public static final String DEFAULT_SHARED_INFORMER_FACTORY_BEAN_NAME = "sharedInformerFactory";
4038

41-
private ControllerManager controllerManager;
39+
private final String sharedInformerFactoryBeanName;
4240

43-
private ExecutorService controllerManagerDaemon = Executors.newSingleThreadExecutor();
41+
private BeanDefinitionRegistry beanDefinitionRegistry;
4442

45-
private SharedInformerFactory sharedInformerFactory;
43+
public KubernetesReconcilerProcessor() {
44+
this(DEFAULT_SHARED_INFORMER_FACTORY_BEAN_NAME);
45+
}
4646

47-
public KubernetesReconcilerProcessor(SharedInformerFactory sharedInformerFactory) {
48-
this.sharedInformerFactory = sharedInformerFactory;
47+
public KubernetesReconcilerProcessor(String sharedInformerFactoryBeanName) {
48+
notNull(sharedInformerFactoryBeanName, "SharedInformerFactory bean name is required");
49+
this.sharedInformerFactoryBeanName = sharedInformerFactoryBeanName;
4950
}
5051

5152
@Override
@@ -54,20 +55,27 @@ public int getOrder() {
5455
}
5556

5657
@Override
57-
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
58-
throws BeansException {
59-
String[] names = beanFactory.getBeanNamesForType(Reconciler.class);
60-
for (String name : names) {
61-
Reconciler reconciler = (Reconciler) beanFactory.getBean(name);
62-
KubernetesReconciler kubernetesReconciler =
63-
reconciler.getClass().getAnnotation(KubernetesReconciler.class);
64-
String reconcilerName = kubernetesReconciler.value();
58+
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
59+
this.beanDefinitionRegistry = registry;
60+
}
61+
62+
@Override
63+
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
64+
for (String reconcilerName : beanFactory.getBeanNamesForType(Reconciler.class)) {
65+
66+
Supplier<KubernetesControllerFactory> kubernetesControllerFactorySupplier =
67+
() ->
68+
new KubernetesControllerFactory(
69+
beanFactory.getBean(sharedInformerFactoryBeanName, SharedInformerFactory.class),
70+
beanFactory.getBean(reconcilerName, Reconciler.class));
6571

66-
KubernetesControllerFactory controllerFactory =
67-
new KubernetesControllerFactory(sharedInformerFactory, reconciler);
72+
BeanDefinition controllerFactoryBeanDefinition =
73+
BeanDefinitionBuilder.genericBeanDefinition(
74+
KubernetesControllerFactory.class, kubernetesControllerFactorySupplier)
75+
.getBeanDefinition();
6876

69-
Controller controller = controllerFactory.getObject();
70-
beanFactory.registerSingleton(reconcilerName, controller);
77+
beanDefinitionRegistry.registerBeanDefinition(
78+
reconcilerName + "Controller", controllerFactoryBeanDefinition);
7179
}
7280
}
7381
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/
13+
package io.kubernetes.client.spring.extended.controller;
14+
15+
import static org.junit.Assert.assertNotNull;
16+
17+
import io.kubernetes.client.extended.controller.Controller;
18+
import io.kubernetes.client.extended.controller.reconciler.Reconciler;
19+
import io.kubernetes.client.extended.controller.reconciler.Request;
20+
import io.kubernetes.client.extended.controller.reconciler.Result;
21+
import io.kubernetes.client.informer.SharedInformer;
22+
import io.kubernetes.client.informer.SharedInformerFactory;
23+
import io.kubernetes.client.openapi.models.V1Pod;
24+
import io.kubernetes.client.openapi.models.V1PodList;
25+
import io.kubernetes.client.spring.extended.controller.annotation.GroupVersionResource;
26+
import io.kubernetes.client.spring.extended.controller.annotation.KubernetesInformer;
27+
import io.kubernetes.client.spring.extended.controller.annotation.KubernetesInformers;
28+
import io.kubernetes.client.spring.extended.controller.annotation.KubernetesReconciler;
29+
import io.kubernetes.client.spring.extended.controller.annotation.KubernetesReconcilerWatches;
30+
import org.junit.Test;
31+
import org.junit.runner.RunWith;
32+
import org.springframework.beans.factory.annotation.Autowired;
33+
import org.springframework.beans.factory.annotation.Qualifier;
34+
import org.springframework.boot.autoconfigure.SpringBootApplication;
35+
import org.springframework.boot.test.context.SpringBootTest;
36+
import org.springframework.context.annotation.Bean;
37+
import org.springframework.test.context.junit4.SpringRunner;
38+
39+
@RunWith(SpringRunner.class)
40+
@SpringBootTest(classes = KubernetesReconcilerProcessorTest.App.class)
41+
public class KubernetesReconcilerProcessorTest {
42+
43+
@SpringBootApplication
44+
static class App {
45+
46+
@Bean
47+
KubernetesReconcilerProcessor reconcilerProcessorUnderTesting() {
48+
return new KubernetesReconcilerProcessor();
49+
}
50+
51+
@Bean
52+
SharedInformerFactory sharedInformerFactory() {
53+
return new TestSharedInformerFactory();
54+
}
55+
56+
@KubernetesInformers({
57+
@KubernetesInformer(
58+
apiTypeClass = V1Pod.class,
59+
apiListTypeClass = V1PodList.class,
60+
groupVersionResource = @GroupVersionResource(resourcePlural = "pods"))
61+
})
62+
static class TestSharedInformerFactory extends SharedInformerFactory {}
63+
64+
@Bean("testReconciler1")
65+
TestReconciler testReconciler1ToBeInjected() {
66+
return new TestReconciler();
67+
}
68+
69+
@Bean("testReconciler2")
70+
TestReconciler testReconciler2ToBeInjected() {
71+
return new TestReconciler();
72+
}
73+
}
74+
75+
@KubernetesReconciler(watches = @KubernetesReconcilerWatches())
76+
static class TestReconciler implements Reconciler {
77+
78+
@Autowired private SharedInformer<V1Pod> informerToBeInjected;
79+
80+
@Override
81+
public Result reconcile(Request request) {
82+
return new Result(false);
83+
}
84+
}
85+
86+
@Autowired
87+
@Qualifier("testReconciler1Controller")
88+
private Controller controller1CreatedByReconcilerProcessor;
89+
90+
@Autowired
91+
@Qualifier("testReconciler2Controller")
92+
private Controller controller2CreatedByReconcilerProcessor;
93+
94+
@Autowired
95+
@Qualifier("testReconciler1")
96+
private TestReconciler testReconciler1ToBeInjected;
97+
98+
@Autowired
99+
@Qualifier("testReconciler2")
100+
private TestReconciler testReconciler2ToBeInjected;
101+
102+
@Test
103+
public void testAutowiredFieldsOfReconcilerBeansAreSet() {
104+
assertNotNull(testReconciler1ToBeInjected.informerToBeInjected);
105+
assertNotNull(testReconciler2ToBeInjected.informerToBeInjected);
106+
}
107+
}

0 commit comments

Comments
 (0)