1818
1919@ SuppressWarnings ("rawtypes" )
2020public 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}
0 commit comments