2
2
3
3
import java .io .ByteArrayInputStream ;
4
4
import java .io .FileInputStream ;
5
+ import java .io .FileNotFoundException ;
5
6
import java .io .IOException ;
6
7
import java .io .InputStream ;
7
8
import java .nio .charset .StandardCharsets ;
21
22
22
23
import io .fabric8 .kubernetes .api .model .HasMetadata ;
23
24
import io .fabric8 .kubernetes .api .model .Namespaced ;
25
+ import io .fabric8 .kubernetes .api .model .apiextensions .v1 .CustomResourceDefinition ;
24
26
import io .fabric8 .kubernetes .client .CustomResource ;
25
27
import io .fabric8 .kubernetes .client .KubernetesClient ;
26
28
import io .fabric8 .kubernetes .client .LocalPortForward ;
@@ -45,7 +47,7 @@ public class LocallyRunOperatorExtension extends AbstractOperatorExtension {
45
47
private final List <LocalPortForward > localPortForwards ;
46
48
private final List <Class <? extends CustomResource >> additionalCustomResourceDefinitions ;
47
49
private final Map <Reconciler , RegisteredController > registeredControllers ;
48
- private final Map <String , String > crdMappings ;
50
+ private final List <String > additionalCrds ;
49
51
50
52
private LocallyRunOperatorExtension (
51
53
List <ReconcilerSpec > reconcilers ,
@@ -60,7 +62,7 @@ private LocallyRunOperatorExtension(
60
62
Consumer <ConfigurationServiceOverrider > configurationServiceOverrider ,
61
63
Function <ExtensionContext , String > namespaceNameSupplier ,
62
64
Function <ExtensionContext , String > perClassNamespaceNameSupplier ,
63
- Map <String , String > crdMappings ) {
65
+ List <String > additionalCrds ) {
64
66
super (
65
67
infrastructure ,
66
68
infrastructureTimeout ,
@@ -80,7 +82,7 @@ private LocallyRunOperatorExtension(
80
82
: overrider -> overrider .withKubernetesClient (kubernetesClient );
81
83
this .operator = new Operator (configurationServiceOverrider );
82
84
this .registeredControllers = new HashMap <>();
83
- this .crdMappings = crdMappings ;
85
+ this .additionalCrds = additionalCrds ;
84
86
}
85
87
86
88
/**
@@ -119,6 +121,10 @@ public static void applyCrd(String resourceTypeName, KubernetesClient client) {
119
121
}
120
122
}
121
123
124
+ public static void applyCrd (CustomResourceDefinition crd , KubernetesClient client ) {
125
+ client .resource (crd ).serverSideApply ();
126
+ }
127
+
122
128
private static void applyCrd (InputStream is , String path , KubernetesClient client ) {
123
129
try {
124
130
if (is == null ) {
@@ -138,6 +144,17 @@ private static void applyCrd(InputStream is, String path, KubernetesClient clien
138
144
}
139
145
}
140
146
147
+ public static List <CustomResourceDefinition > parseCrds (String path , KubernetesClient client ) {
148
+ try (InputStream is = new FileInputStream (path )) {
149
+ return client .load (new ByteArrayInputStream (is .readAllBytes ()))
150
+ .items ().stream ().map (i -> (CustomResourceDefinition ) i ).collect (Collectors .toList ());
151
+ } catch (FileNotFoundException e ) {
152
+ throw new RuntimeException (e );
153
+ } catch (IOException e ) {
154
+ throw new RuntimeException (e );
155
+ }
156
+ }
157
+
141
158
private Stream <Reconciler > reconcilers () {
142
159
return reconcilers .stream ().map (reconcilerSpec -> reconcilerSpec .reconciler );
143
160
}
@@ -190,7 +207,7 @@ protected void before(ExtensionContext context) {
190
207
}
191
208
192
209
additionalCustomResourceDefinitions .forEach (this ::applyCrd );
193
-
210
+ Map < String , CustomResourceDefinition > unappliedCRDs = getAdditionalCRDsFromFiles ();
194
211
for (var ref : reconcilers ) {
195
212
final var config = operator .getConfigurationService ().getConfigurationFor (ref .reconciler );
196
213
final var oconfig = override (config );
@@ -207,29 +224,40 @@ protected void before(ExtensionContext context) {
207
224
ref .controllerConfigurationOverrider .accept (oconfig );
208
225
}
209
226
210
- final var unapplied = new HashMap <>(crdMappings );
211
227
final var resourceTypeName = ReconcilerUtils .getResourceTypeName (resourceClass );
212
228
// only try to apply a CRD for the reconciler if it is associated to a CR
213
229
if (CustomResource .class .isAssignableFrom (resourceClass )) {
214
- applyCrd (resourceTypeName );
215
- unapplied .remove (resourceTypeName );
230
+ if (unappliedCRDs .get (resourceTypeName ) != null ) {
231
+ applyCrd (resourceTypeName );
232
+ unappliedCRDs .remove (resourceTypeName );
233
+ } else {
234
+ applyCrd (resourceClass );
235
+ }
216
236
}
217
237
218
238
// apply yet unapplied CRDs
219
- unapplied .keySet ().forEach (this ::applyCrd );
220
-
221
239
var registeredController = this .operator .register (ref .reconciler , oconfig .build ());
222
240
registeredControllers .put (ref .reconciler , registeredController );
223
241
}
242
+ unappliedCRDs .keySet ().forEach (this ::applyCrd );
224
243
225
244
LOGGER .debug ("Starting the operator locally" );
226
245
this .operator .start ();
227
246
}
228
247
248
+ private Map <String , CustomResourceDefinition > getAdditionalCRDsFromFiles () {
249
+ Map <String , CustomResourceDefinition > crdMappings = new HashMap <>();
250
+ additionalCrds .forEach (p -> {
251
+ var crds = parseCrds (p , getKubernetesClient ());
252
+ crds .forEach (c -> crdMappings .put (c .getMetadata ().getName (), c ));
253
+ });
254
+ return crdMappings ;
255
+ }
256
+
229
257
/**
230
258
* Applies the CRD associated with the specified custom resource, first checking if a CRD has been
231
- * manually specified using {@link Builder#withCRDMapping(Class, String)}, otherwise assuming that
232
- * its CRD should be found in the standard location as explained in
259
+ * manually specified using {@link Builder#withAdditionalCRD( String)}, otherwise assuming that its
260
+ * CRD should be found in the standard location as explained in
233
261
* {@link LocallyRunOperatorExtension#applyCrd(String, KubernetesClient)}
234
262
*
235
263
* @param crClass the custom resource class for which we want to apply the CRD
@@ -239,16 +267,7 @@ public void applyCrd(Class<? extends CustomResource> crClass) {
239
267
}
240
268
241
269
public void applyCrd (String resourceTypeName ) {
242
- final var path = crdMappings .get (resourceTypeName );
243
- if (path != null ) {
244
- try (InputStream inputStream = new FileInputStream (path )) {
245
- applyCrd (inputStream , path , getKubernetesClient ());
246
- } catch (IOException e ) {
247
- throw new IllegalStateException ("Cannot apply CRD yaml: " + path , e );
248
- }
249
- } else {
250
- applyCrd (resourceTypeName , getKubernetesClient ());
251
- }
270
+ applyCrd (resourceTypeName , getKubernetesClient ());
252
271
}
253
272
254
273
@ Override
@@ -277,6 +296,7 @@ public static class Builder extends AbstractBuilder<Builder> {
277
296
private final List <PortForwardSpec > portForwards ;
278
297
private final List <Class <? extends CustomResource >> additionalCustomResourceDefinitions ;
279
298
private final Map <String , String > crdMappings ;
299
+ private final List <String > additionalCRDs = new ArrayList <>();
280
300
private KubernetesClient kubernetesClient ;
281
301
282
302
protected Builder () {
@@ -339,13 +359,8 @@ public Builder withAdditionalCustomResourceDefinition(
339
359
return this ;
340
360
}
341
361
342
- public Builder withCRDMapping (Class <? extends CustomResource > customResourceClass ,
343
- String path ) {
344
- return withCRDMapping (ReconcilerUtils .getResourceTypeName (customResourceClass ), path );
345
- }
346
-
347
- public Builder withCRDMapping (String resourceTypeName , String path ) {
348
- crdMappings .put (resourceTypeName , path );
362
+ public Builder withAdditionalCRD (String path ) {
363
+ additionalCRDs .add (path );
349
364
return this ;
350
365
}
351
366
@@ -360,8 +375,9 @@ public LocallyRunOperatorExtension build() {
360
375
waitForNamespaceDeletion ,
361
376
oneNamespacePerClass ,
362
377
kubernetesClient ,
363
- configurationServiceOverrider , namespaceNameSupplier , perClassNamespaceNameSupplier ,
364
- crdMappings );
378
+ configurationServiceOverrider , namespaceNameSupplier ,
379
+ perClassNamespaceNameSupplier ,
380
+ additionalCRDs );
365
381
}
366
382
}
367
383
0 commit comments