1
1
package io .javaoperatorsdk .operator .junit ;
2
2
3
3
import java .io .InputStream ;
4
+ import java .time .Duration ;
4
5
import java .util .ArrayList ;
5
6
import java .util .List ;
6
7
import java .util .Locale ;
8
+ import java .util .UUID ;
7
9
import java .util .concurrent .TimeUnit ;
8
10
import java .util .stream .Collectors ;
9
11
19
21
import io .fabric8 .kubernetes .api .model .HasMetadata ;
20
22
import io .fabric8 .kubernetes .api .model .KubernetesResourceList ;
21
23
import io .fabric8 .kubernetes .api .model .NamespaceBuilder ;
24
+ import io .fabric8 .kubernetes .api .model .rbac .ClusterRoleBinding ;
22
25
import io .fabric8 .kubernetes .client .DefaultKubernetesClient ;
23
26
import io .fabric8 .kubernetes .client .KubernetesClient ;
24
27
import io .fabric8 .kubernetes .client .dsl .NonNamespaceOperation ;
@@ -47,24 +50,42 @@ public class OperatorExtension
47
50
private final KubernetesClient kubernetesClient ;
48
51
private final ConfigurationService configurationService ;
49
52
private final Operator operator ;
53
+ private final List <HasMetadata > operatorDeployment ;
54
+ private final Duration deploymentTimeout ;
50
55
private final List <ReconcilerSpec > reconcilers ;
56
+ private final List <HasMetadata > infrastructure ;
57
+ private final Duration infrastructureTimeout ;
51
58
private final boolean preserveNamespaceOnError ;
52
59
private final boolean waitForNamespaceDeletion ;
60
+ private final boolean oneNamespacePerClass ;
61
+ private final boolean localOperatorDeployment ;
53
62
54
63
private String namespace ;
55
64
56
65
private OperatorExtension (
57
66
ConfigurationService configurationService ,
58
67
List <ReconcilerSpec > reconcilers ,
68
+ List <HasMetadata > operatorDeployment ,
69
+ Duration deploymentTimeout ,
70
+ List <HasMetadata > infrastructure ,
71
+ Duration infrastructureTimeout ,
59
72
boolean preserveNamespaceOnError ,
60
- boolean waitForNamespaceDeletion ) {
73
+ boolean waitForNamespaceDeletion ,
74
+ boolean oneNamespacePerClass ,
75
+ boolean localOperatorDeployment ) {
61
76
62
77
this .kubernetesClient = new DefaultKubernetesClient ();
63
78
this .configurationService = configurationService ;
64
79
this .reconcilers = reconcilers ;
80
+ this .operatorDeployment = operatorDeployment ;
81
+ this .deploymentTimeout = deploymentTimeout ;
82
+ this .infrastructure = infrastructure ;
83
+ this .infrastructureTimeout = infrastructureTimeout ;
65
84
this .operator = new Operator (this .kubernetesClient , this .configurationService );
66
85
this .preserveNamespaceOnError = preserveNamespaceOnError ;
67
86
this .waitForNamespaceDeletion = waitForNamespaceDeletion ;
87
+ this .oneNamespacePerClass = oneNamespacePerClass ;
88
+ this .localOperatorDeployment = localOperatorDeployment ;
68
89
}
69
90
70
91
/**
@@ -78,22 +99,22 @@ public static Builder builder() {
78
99
79
100
@ Override
80
101
public void beforeAll (ExtensionContext context ) throws Exception {
81
- before (context );
102
+ beforeAllImpl (context );
82
103
}
83
104
84
105
@ Override
85
106
public void beforeEach (ExtensionContext context ) throws Exception {
86
- before (context );
107
+ beforeEachImpl (context );
87
108
}
88
109
89
110
@ Override
90
111
public void afterAll (ExtensionContext context ) throws Exception {
91
- after (context );
112
+ afterAllImpl (context );
92
113
}
93
114
94
115
@ Override
95
116
public void afterEach (ExtensionContext context ) throws Exception {
96
- after (context );
117
+ afterEachImpl (context );
97
118
}
98
119
99
120
@ Override
@@ -118,6 +139,10 @@ public Reconciler getFirstReconciler() {
118
139
.findFirst ().orElseThrow ();
119
140
}
120
141
142
+ public boolean isLocal () {
143
+ return this .localOperatorDeployment ;
144
+ }
145
+
121
146
@ SuppressWarnings ({"rawtypes" })
122
147
public <T extends Reconciler > T getControllerOfType (Class <T > type ) {
123
148
return operator .getControllers ().stream ()
@@ -151,19 +176,47 @@ public <T extends HasMetadata> boolean delete(Class<T> type, T resource) {
151
176
}
152
177
153
178
@ SuppressWarnings ("unchecked" )
154
- protected void before (ExtensionContext context ) {
155
- namespace = context .getRequiredTestClass ().getSimpleName ();
156
- namespace += "-" ;
157
- namespace += context .getRequiredTestMethod ().getName ();
158
- namespace = KubernetesResourceUtil .sanitizeName (namespace ).toLowerCase (Locale .US );
159
- namespace = namespace .substring (0 , Math .min (namespace .length (), 63 ));
179
+ protected void beforeAllImpl (ExtensionContext context ) {
180
+ if (oneNamespacePerClass ) {
181
+ namespace = context .getRequiredTestClass ().getSimpleName ();
182
+ namespace += "-" ;
183
+ namespace += UUID .randomUUID ();
184
+ namespace = KubernetesResourceUtil .sanitizeName (namespace ).toLowerCase (Locale .US );
185
+ namespace = namespace .substring (0 , Math .min (namespace .length (), 63 ));
186
+
187
+ before (context );
188
+ }
189
+ }
160
190
191
+ @ SuppressWarnings ("unchecked" )
192
+ protected void beforeEachImpl (ExtensionContext context ) {
193
+ if (!oneNamespacePerClass ) {
194
+ namespace = context .getRequiredTestClass ().getSimpleName ();
195
+ namespace += "-" ;
196
+ namespace += context .getRequiredTestMethod ().getName ();
197
+ namespace += "-" ;
198
+ namespace += UUID .randomUUID ();
199
+ namespace = KubernetesResourceUtil .sanitizeName (namespace ).toLowerCase (Locale .US );
200
+ namespace = namespace .substring (0 , Math .min (namespace .length (), 63 ));
201
+
202
+ before (context );
203
+ }
204
+ }
205
+
206
+ @ SuppressWarnings ("unchecked" )
207
+ protected void before (ExtensionContext context ) {
161
208
LOGGER .info ("Initializing integration test in namespace {}" , namespace );
162
209
163
210
kubernetesClient
164
211
.namespaces ()
165
212
.create (new NamespaceBuilder ().withNewMetadata ().withName (namespace ).endMetadata ().build ());
166
213
214
+ kubernetesClient
215
+ .resourceList (infrastructure )
216
+ .createOrReplace ();
217
+ kubernetesClient
218
+ .resourceList (infrastructure )
219
+ .waitUntilReady (infrastructureTimeout .toMillis (), TimeUnit .MILLISECONDS );
167
220
168
221
for (var ref : reconcilers ) {
169
222
final var config = configurationService .getConfigurationFor (ref .reconciler );
@@ -191,14 +244,50 @@ protected void before(ExtensionContext context) {
191
244
this .operator .register (ref .reconciler , oconfig .build ());
192
245
}
193
246
194
- this .operator .start ();
247
+ if (isLocal ()) {
248
+ LOGGER .debug ("Starting the operator locally" );
249
+ this .operator .start ();
250
+ } else {
251
+ LOGGER .debug ("Deploying the operator into Kubernetes" );
252
+ operatorDeployment .stream ().forEach (hm -> {
253
+ hm .getMetadata ().setNamespace (namespace );
254
+ if (hm .getKind ().toLowerCase (Locale .ROOT ).equals ("clusterrolebinding" )) {
255
+ var crb = (ClusterRoleBinding ) hm ;
256
+ for (var subject : crb .getSubjects ()) {
257
+ subject .setNamespace (namespace );
258
+ }
259
+ }
260
+ });
261
+
262
+ kubernetesClient
263
+ .resourceList (operatorDeployment )
264
+ .inNamespace (namespace )
265
+ .createOrReplace ();
266
+ kubernetesClient
267
+ .resourceList (operatorDeployment )
268
+ .waitUntilReady (deploymentTimeout .toMillis (), TimeUnit .MILLISECONDS );
269
+ }
270
+ }
271
+
272
+ protected void afterAllImpl (ExtensionContext context ) {
273
+ if (oneNamespacePerClass ) {
274
+ after (context );
275
+ }
276
+ }
277
+
278
+ protected void afterEachImpl (ExtensionContext context ) {
279
+ if (!oneNamespacePerClass ) {
280
+ after (context );
281
+ }
195
282
}
196
283
197
284
protected void after (ExtensionContext context ) {
198
285
if (namespace != null ) {
199
286
if (preserveNamespaceOnError && context .getExecutionException ().isPresent ()) {
200
287
LOGGER .info ("Preserving namespace {}" , namespace );
201
288
} else {
289
+ kubernetesClient .resourceList (infrastructure ).delete ();
290
+ kubernetesClient .resourceList (operatorDeployment ).inNamespace (namespace ).delete ();
202
291
LOGGER .info ("Deleting namespace {} and stopping operator" , namespace );
203
292
kubernetesClient .namespaces ().withName (namespace ).delete ();
204
293
if (waitForNamespaceDeletion ) {
@@ -212,7 +301,9 @@ protected void after(ExtensionContext context) {
212
301
}
213
302
214
303
try {
215
- this .operator .stop ();
304
+ if (isLocal ()) {
305
+ this .operator .stop ();
306
+ }
216
307
} catch (Exception e ) {
217
308
// ignored
218
309
}
@@ -221,13 +312,23 @@ protected void after(ExtensionContext context) {
221
312
@ SuppressWarnings ("rawtypes" )
222
313
public static class Builder {
223
314
private final List <ReconcilerSpec > reconcilers ;
315
+ private final List <HasMetadata > operatorDeployment ;
316
+ private Duration deploymentTimeout ;
317
+ private boolean localOperatorDeployment ;
318
+ private final List <HasMetadata > infrastructure ;
319
+ private Duration infrastructureTimeout ;
224
320
private ConfigurationService configurationService ;
225
321
private boolean preserveNamespaceOnError ;
226
322
private boolean waitForNamespaceDeletion ;
323
+ private boolean oneNamespacePerClass ;
227
324
228
325
protected Builder () {
229
326
this .configurationService = new BaseConfigurationService (Version .UNKNOWN );
230
327
this .reconcilers = new ArrayList <>();
328
+ this .operatorDeployment = new ArrayList <>();
329
+ this .infrastructure = new ArrayList <>();
330
+ this .deploymentTimeout = Duration .ofMinutes (1 );
331
+ this .infrastructureTimeout = Duration .ofMinutes (1 );
231
332
232
333
this .preserveNamespaceOnError = Utils .getSystemPropertyOrEnvVar (
233
334
"josdk.it.preserveNamespaceOnError" ,
@@ -236,6 +337,14 @@ protected Builder() {
236
337
this .waitForNamespaceDeletion = Utils .getSystemPropertyOrEnvVar (
237
338
"josdk.it.waitForNamespaceDeletion" ,
238
339
true );
340
+
341
+ this .oneNamespacePerClass = Utils .getSystemPropertyOrEnvVar (
342
+ "josdk.it.oneNamespacePerClass" ,
343
+ false );
344
+
345
+ this .localOperatorDeployment = Utils .getSystemPropertyOrEnvVar (
346
+ "josdk.it.localOperatorDeployment" ,
347
+ true );
239
348
}
240
349
241
350
public Builder preserveNamespaceOnError (boolean value ) {
@@ -248,11 +357,31 @@ public Builder waitForNamespaceDeletion(boolean value) {
248
357
return this ;
249
358
}
250
359
360
+ public Builder oneNamespacePerClass (boolean value ) {
361
+ this .oneNamespacePerClass = value ;
362
+ return this ;
363
+ }
364
+
365
+ public Builder localOperatorDeployment (boolean value ) {
366
+ this .localOperatorDeployment = value ;
367
+ return this ;
368
+ }
369
+
251
370
public Builder withConfigurationService (ConfigurationService value ) {
252
371
configurationService = value ;
253
372
return this ;
254
373
}
255
374
375
+ public Builder withDeploymentTimeout (Duration value ) {
376
+ deploymentTimeout = value ;
377
+ return this ;
378
+ }
379
+
380
+ public Builder withInfrastructureTimeout (Duration value ) {
381
+ infrastructureTimeout = value ;
382
+ return this ;
383
+ }
384
+
256
385
@ SuppressWarnings ("rawtypes" )
257
386
public Builder withReconciler (Reconciler value ) {
258
387
reconcilers .add (new ReconcilerSpec (value , null ));
@@ -275,12 +404,38 @@ public Builder withReconciler(Class<? extends Reconciler> value) {
275
404
return this ;
276
405
}
277
406
407
+ public Builder withInfrastructure (List <HasMetadata > hm ) {
408
+ infrastructure .addAll (hm );
409
+ return this ;
410
+ }
411
+
412
+ public Builder withInfrastructure (HasMetadata hm ) {
413
+ infrastructure .add (hm );
414
+ return this ;
415
+ }
416
+
417
+ public Builder withDeployment (List <HasMetadata > hm ) {
418
+ operatorDeployment .addAll (hm );
419
+ return this ;
420
+ }
421
+
422
+ public Builder withDeployment (HasMetadata hm ) {
423
+ operatorDeployment .add (hm );
424
+ return this ;
425
+ }
426
+
278
427
public OperatorExtension build () {
279
428
return new OperatorExtension (
280
429
configurationService ,
281
430
reconcilers ,
431
+ operatorDeployment ,
432
+ deploymentTimeout ,
433
+ infrastructure ,
434
+ infrastructureTimeout ,
282
435
preserveNamespaceOnError ,
283
- waitForNamespaceDeletion );
436
+ waitForNamespaceDeletion ,
437
+ oneNamespacePerClass ,
438
+ localOperatorDeployment );
284
439
}
285
440
}
286
441
0 commit comments