Skip to content

Commit 1735651

Browse files
lburgazzolimetacosm
authored andcommitted
feat: delay the registration of controller till the operator is started
1 parent 2071918 commit 1735651

File tree

5 files changed

+97
-34
lines changed

5 files changed

+97
-34
lines changed

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/Operator.java

+92-28
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,21 @@
1818

1919
@SuppressWarnings("rawtypes")
2020
public class Operator implements AutoCloseable {
21-
2221
private static final Logger log = LoggerFactory.getLogger(Operator.class);
2322
private final KubernetesClient k8sClient;
2423
private final ConfigurationService configurationService;
2524
private final List<Closeable> closeables;
25+
private final Object lock;
26+
private final List<ControllerRef> controllers;
27+
private volatile boolean started;
2628

2729
public Operator(KubernetesClient k8sClient, ConfigurationService configurationService) {
2830
this.k8sClient = k8sClient;
2931
this.configurationService = configurationService;
3032
this.closeables = new ArrayList<>();
33+
this.lock = new Object();
34+
this.controllers = new ArrayList<>();
35+
this.started = false;
3136

3237
Runtime.getRuntime().addShutdownHook(new Thread(this::close));
3338
}
@@ -45,43 +50,65 @@ public ConfigurationService getConfigurationService() {
4550
* where there is no obvious entrypoint to the application which can trigger the injection process
4651
* and start the cluster monitoring processes.
4752
*/
53+
@SuppressWarnings("unchecked")
4854
public void start() {
49-
final var version = configurationService.getVersion();
50-
log.info(
51-
"Operator SDK {} (commit: {}) built on {} starting...",
52-
version.getSdkVersion(),
53-
version.getCommit(),
54-
version.getBuiltTime());
55-
log.info("Client version: {}", Version.clientVersion());
56-
try {
57-
final var k8sVersion = k8sClient.getVersion();
58-
if (k8sVersion != null) {
59-
log.info("Server version: {}.{}", k8sVersion.getMajor(), k8sVersion.getMinor());
55+
synchronized (lock) {
56+
if (started) {
57+
return;
58+
}
59+
60+
final var version = configurationService.getVersion();
61+
log.info(
62+
"Operator SDK {} (commit: {}) built on {} starting...",
63+
version.getSdkVersion(),
64+
version.getCommit(),
65+
version.getBuiltTime());
66+
log.info("Client version: {}", Version.clientVersion());
67+
try {
68+
final var k8sVersion = k8sClient.getVersion();
69+
if (k8sVersion != null) {
70+
log.info("Server version: {}.{}", k8sVersion.getMajor(), k8sVersion.getMinor());
71+
}
72+
} catch (Exception e) {
73+
log.error("Error retrieving the server version. Exiting!", e);
74+
throw new OperatorException("Error retrieving the server version", e);
75+
}
76+
77+
for (ControllerRef ref : controllers) {
78+
startController(ref.controller, ref.configuration);
6079
}
61-
} catch (Exception e) {
62-
log.error("Error retrieving the server version. Exiting!", e);
63-
throw new OperatorException("Error retrieving the server version", e);
80+
81+
started = true;
6482
}
6583
}
6684

6785
/** Stop the operator. */
6886
@Override
6987
public void close() {
70-
log.info(
71-
"Operator SDK {} is shutting down...", configurationService.getVersion().getSdkVersion());
88+
synchronized (lock) {
89+
if (!started) {
90+
return;
91+
}
7292

73-
for (Closeable closeable : this.closeables) {
74-
try {
75-
log.debug("closing {}", closeable);
76-
closeable.close();
77-
} catch (IOException e) {
78-
log.warn("Error closing {}", closeable, e);
93+
log.info(
94+
"Operator SDK {} is shutting down...", configurationService.getVersion().getSdkVersion());
95+
96+
for (Closeable closeable : this.closeables) {
97+
try {
98+
log.debug("closing {}", closeable);
99+
closeable.close();
100+
} catch (IOException e) {
101+
log.warn("Error closing {}", closeable, e);
102+
}
79103
}
104+
105+
started = false;
80106
}
81107
}
82108

83109
/**
84-
* Registers the specified controller with this operator.
110+
* Add a registration requests for the specified controller with this operator. The effective
111+
* registration of the controller is delayed till the operator is started.
85112
*
86113
* @param controller the controller to register
87114
* @param <R> the {@code CustomResource} type associated with the controller
@@ -92,6 +119,32 @@ public <R extends CustomResource> void register(ResourceController<R> controller
92119
register(controller, null);
93120
}
94121

122+
/**
123+
* Add a registration requests for the specified controller with this operator, overriding its
124+
* default configuration by the specified one (usually created via {@link
125+
* io.javaoperatorsdk.operator.api.config.ControllerConfigurationOverrider#override(ControllerConfiguration)},
126+
* passing it the controller's original configuration. The effective registration of the
127+
* controller is delayed till the operator is started.
128+
*
129+
* @param controller the controller to register
130+
* @param configuration the configuration with which we want to register the controller, if {@code
131+
* null}, the controller's original configuration is used
132+
* @param <R> the {@code CustomResource} type associated with the controller
133+
* @throws OperatorException if a problem occurred during the registration process
134+
*/
135+
public <R extends CustomResource> void register(
136+
ResourceController<R> controller, ControllerConfiguration<R> configuration)
137+
throws OperatorException {
138+
synchronized (lock) {
139+
if (!started) {
140+
this.controllers.add(new ControllerRef(controller, configuration));
141+
} else {
142+
this.controllers.add(new ControllerRef(controller, configuration));
143+
startController(controller, configuration);
144+
}
145+
}
146+
}
147+
95148
/**
96149
* Registers the specified controller with this operator, overriding its default configuration by
97150
* the specified one (usually created via {@link
@@ -104,9 +157,10 @@ public <R extends CustomResource> void register(ResourceController<R> controller
104157
* @param <R> the {@code CustomResource} type associated with the controller
105158
* @throws OperatorException if a problem occurred during the registration process
106159
*/
107-
public <R extends CustomResource> void register(
160+
private <R extends CustomResource> void startController(
108161
ResourceController<R> controller, ControllerConfiguration<R> configuration)
109162
throws OperatorException {
163+
110164
final var existing = configurationService.getConfigurationFor(controller);
111165
if (existing == null) {
112166
log.warn(
@@ -120,7 +174,7 @@ public <R extends CustomResource> void register(
120174
configuration = existing;
121175
}
122176

123-
Class<R> resClass = configuration.getCustomResourceClass();
177+
final Class<R> resClass = configuration.getCustomResourceClass();
124178
final String controllerName = configuration.getName();
125179
final var crdName = configuration.getCRDName();
126180
final var specVersion = "v1";
@@ -137,10 +191,10 @@ public <R extends CustomResource> void register(
137191
CustomResourceUtils.assertCustomResource(resClass, crd);
138192
}
139193

140-
final var client = k8sClient.customResources(resClass);
141194
try {
142195
DefaultEventSourceManager eventSourceManager =
143-
new DefaultEventSourceManager(controller, configuration, client);
196+
new DefaultEventSourceManager(
197+
controller, configuration, k8sClient.customResources(resClass));
144198
controller.init(eventSourceManager);
145199
closeables.add(eventSourceManager);
146200
} catch (MissingCRDException e) {
@@ -195,4 +249,14 @@ private static <R extends CustomResource> boolean failOnMissingCurrentNS(
195249
}
196250
return false;
197251
}
252+
253+
private static class ControllerRef {
254+
public final ResourceController controller;
255+
public final ControllerConfiguration configuration;
256+
257+
public ControllerRef(ResourceController controller, ControllerConfiguration configuration) {
258+
this.controller = controller;
259+
this.configuration = configuration;
260+
}
261+
}
198262
}

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/DefaultEventSourceManager.java

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
package io.javaoperatorsdk.operator.processing.event;
22

3-
import io.fabric8.kubernetes.api.model.KubernetesResourceList;
43
import io.fabric8.kubernetes.client.CustomResource;
54
import io.fabric8.kubernetes.client.KubernetesClientException;
65
import io.fabric8.kubernetes.client.dsl.MixedOperation;
7-
import io.fabric8.kubernetes.client.dsl.Resource;
86
import io.javaoperatorsdk.operator.MissingCRDException;
97
import io.javaoperatorsdk.operator.OperatorException;
108
import io.javaoperatorsdk.operator.api.ResourceController;
@@ -45,10 +43,9 @@ public class DefaultEventSourceManager implements EventSourceManager {
4543
}
4644
}
4745

46+
@SuppressWarnings({"rawtypes", "unchecked"})
4847
public <R extends CustomResource<?, ?>> DefaultEventSourceManager(
49-
ResourceController<R> controller,
50-
ControllerConfiguration<R> configuration,
51-
MixedOperation<R, KubernetesResourceList<R>, Resource<R>> client) {
48+
ResourceController controller, ControllerConfiguration configuration, MixedOperation client) {
5249
this(new DefaultEventHandler(controller, configuration, client), true);
5350
registerEventSource(
5451
CUSTOM_RESOURCE_EVENT_SOURCE_NAME, new CustomResourceEventSource<>(client, configuration));

operator-framework/src/test/java/io/javaoperatorsdk/operator/IntegrationTestSupport.java

+1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ public void initialize(KubernetesClient k8sClient, ResourceController controller
6868
overriddenConfig.withRetry(retry);
6969
}
7070
operator.register(controller, overriddenConfig.build());
71+
operator.start();
7172
log.info("Operator is running with {}", controller.getClass().getCanonicalName());
7273
}
7374

samples/pure-java/src/main/java/io/javaoperatorsdk/operator/sample/PureJavaApplicationRunner.java

+1
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ public static void main(String[] args) {
1111
KubernetesClient client = new DefaultKubernetesClient();
1212
Operator operator = new Operator(client, DefaultConfigurationService.instance());
1313
operator.register(new CustomServiceController(client));
14+
operator.start();
1415
}
1516
}

samples/spring-boot-plain/src/main/java/io/javaoperatorsdk/operator/sample/Config.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public CustomServiceController customServiceController(KubernetesClient client)
2323
}
2424

2525
// Register all controller beans
26-
@Bean
26+
@Bean(initMethod = "start", destroyMethod = "stop")
2727
public Operator operator(KubernetesClient client, List<ResourceController> controllers) {
2828
Operator operator = new Operator(client, DefaultConfigurationService.instance());
2929
controllers.forEach(operator::register);

0 commit comments

Comments
 (0)