@@ -67,6 +67,7 @@ public class LocallyRunOperatorExtension extends AbstractOperatorExtension {
6767 private final List <PortForwardSpec > portForwards ;
6868 private final List <LocalPortForward > localPortForwards ;
6969 private final List <Class <? extends CustomResource >> additionalCustomResourceDefinitions ;
70+ private final List <CustomResourceDefinition > additionalCustomResourceDefinitionInstances ;
7071 private final Map <Reconciler , RegisteredController > registeredControllers ;
7172 private final Map <String , String > crdMappings ;
7273 private final Consumer <LocallyRunOperatorExtension > beforeStartHook ;
@@ -76,6 +77,7 @@ private LocallyRunOperatorExtension(
7677 List <HasMetadata > infrastructure ,
7778 List <PortForwardSpec > portForwards ,
7879 List <Class <? extends CustomResource >> additionalCustomResourceDefinitions ,
80+ List <CustomResourceDefinition > additionalCustomResourceDefinitionInstances ,
7981 Duration infrastructureTimeout ,
8082 boolean preserveNamespaceOnError ,
8183 boolean waitForNamespaceDeletion ,
@@ -101,6 +103,7 @@ private LocallyRunOperatorExtension(
101103 this .portForwards = portForwards ;
102104 this .localPortForwards = new ArrayList <>(portForwards .size ());
103105 this .additionalCustomResourceDefinitions = additionalCustomResourceDefinitions ;
106+ this .additionalCustomResourceDefinitionInstances = additionalCustomResourceDefinitionInstances ;
104107 this .beforeStartHook = beforeStartHook ;
105108 configurationServiceOverrider =
106109 configurationServiceOverrider != null
@@ -172,7 +175,7 @@ private static void applyCrd(String crdString, String path, KubernetesClient cli
172175 LOGGER .debug ("Applying CRD: {}" , crdString );
173176 final var crd = client .load (new ByteArrayInputStream (crdString .getBytes ()));
174177 crd .serverSideApply ();
175- appliedCRDs .add (new AppliedCRD (crdString , path ));
178+ appliedCRDs .add (new AppliedCRD . FileCRD (crdString , path ));
176179 Thread .sleep (CRD_READY_WAIT ); // readiness is not applicable for CRD, just wait a little
177180 LOGGER .debug ("Applied CRD with path: {}" , path );
178181 } catch (InterruptedException ex ) {
@@ -195,6 +198,33 @@ public void applyCrd(Class<? extends CustomResource> crClass) {
195198 applyCrd (ReconcilerUtils .getResourceTypeName (crClass ));
196199 }
197200
201+ public void applyCrd (CustomResourceDefinition customResourceDefinition ) {
202+ try {
203+ String resourceTypeName = customResourceDefinition .getMetadata ().getName ();
204+ final var pathAsString = crdMappings .get (resourceTypeName );
205+ if (pathAsString != null ) {
206+ applyCrdFromMappings (pathAsString , resourceTypeName );
207+ } else {
208+ var resource = getKubernetesClient ().resource (customResourceDefinition );
209+ resource .serverSideApply ();
210+ Thread .sleep (CRD_READY_WAIT ); // readiness is not applicable for CRD, just wait a little
211+ appliedCRDs .add (new AppliedCRD .InstanceCRD (customResourceDefinition ));
212+ }
213+ } catch (Exception e ) {
214+ throw new RuntimeException (e );
215+ }
216+ }
217+
218+ private void applyCrdFromMappings (String pathAsString , String resourceTypeName ) {
219+ final var path = Path .of (pathAsString );
220+ try {
221+ applyCrd (Files .readString (path ), pathAsString , getKubernetesClient ());
222+ } catch (IOException e ) {
223+ throw new IllegalStateException ("Cannot open CRD file at " + path .toAbsolutePath (), e );
224+ }
225+ crdMappings .remove (resourceTypeName );
226+ }
227+
198228 /**
199229 * Applies the CRD associated with the specified resource type name, first checking if a CRD has
200230 * been manually specified using {@link Builder#withAdditionalCRD}, otherwise assuming that its
@@ -209,13 +239,7 @@ public void applyCrd(String resourceTypeName) {
209239 // first attempt to use a manually defined CRD
210240 final var pathAsString = crdMappings .get (resourceTypeName );
211241 if (pathAsString != null ) {
212- final var path = Path .of (pathAsString );
213- try {
214- applyCrd (Files .readString (path ), pathAsString , getKubernetesClient ());
215- } catch (IOException e ) {
216- throw new IllegalStateException ("Cannot open CRD file at " + path .toAbsolutePath (), e );
217- }
218- crdMappings .remove (resourceTypeName );
242+ applyCrdFromMappings (pathAsString , resourceTypeName );
219243 } else {
220244 // if no manually defined CRD matches the resource type, apply the generated one
221245 applyCrd (resourceTypeName , getKubernetesClient ());
@@ -280,6 +304,7 @@ protected void before(ExtensionContext context) {
280304 }
281305
282306 additionalCustomResourceDefinitions .forEach (this ::applyCrd );
307+ additionalCustomResourceDefinitionInstances .forEach (this ::applyCrd );
283308 for (var ref : reconcilers ) {
284309 final var config = operator .getConfigurationService ().getConfigurationFor (ref .reconciler );
285310 final var oconfig = override (config );
@@ -361,24 +386,60 @@ private void deleteCrd(AppliedCRD appliedCRD, KubernetesClient client) {
361386 LOGGER .debug ("Skipping deleting CRD because of configuration: {}" , appliedCRD );
362387 return ;
363388 }
364- try {
365- LOGGER .debug ("Deleting CRD: {}" , appliedCRD .crdString );
366- final var crd = client .load (new ByteArrayInputStream (appliedCRD .crdString .getBytes ()));
367- crd .withTimeoutInMillis (CRD_DELETE_TIMEOUT ).delete ();
368- LOGGER .debug ("Deleted CRD with path: {}" , appliedCRD .path );
369- } catch (Exception ex ) {
370- LOGGER .warn (
371- "Cannot delete CRD yaml: {}. You might need to delete it manually." , appliedCRD .path , ex );
372- }
389+ appliedCRD .delete (client );
373390 }
374391
375- private record AppliedCRD (String crdString , String path ) {}
392+ private sealed interface AppliedCRD permits AppliedCRD .FileCRD , AppliedCRD .InstanceCRD {
393+ /**
394+ * Delete this CRD from the cluster
395+ *
396+ * @param client client to use for deletion
397+ */
398+ void delete (KubernetesClient client );
399+
400+ record FileCRD (String crdString , String path ) implements AppliedCRD {
401+
402+ @ Override
403+ public void delete (KubernetesClient client ) {
404+ try {
405+ LOGGER .debug ("Deleting CRD: {}" , crdString );
406+ final var crd = client .load (new ByteArrayInputStream (crdString .getBytes ()));
407+ crd .withTimeoutInMillis (CRD_DELETE_TIMEOUT ).delete ();
408+ LOGGER .debug ("Deleted CRD with path: {}" , path );
409+ } catch (Exception ex ) {
410+ LOGGER .warn (
411+ "Cannot delete CRD yaml: {}. You might need to delete it manually." , path , ex );
412+ }
413+ }
414+ }
415+
416+ record InstanceCRD (CustomResourceDefinition customResourceDefinition ) implements AppliedCRD {
417+
418+ @ Override
419+ public void delete (KubernetesClient client ) {
420+ String type = customResourceDefinition .getMetadata ().getName ();
421+ try {
422+ LOGGER .debug ("Deleting CustomResourceDefinition instance CRD: {}" , type );
423+ final var crd = client .resource (customResourceDefinition );
424+ crd .withTimeoutInMillis (CRD_DELETE_TIMEOUT ).delete ();
425+ LOGGER .debug ("Deleted CustomResourceDefinition instance CRD: {}" , type );
426+ } catch (Exception ex ) {
427+ LOGGER .warn (
428+ "Cannot delete CustomResourceDefinition instance CRD: {}. You might need to delete it"
429+ + " manually." ,
430+ type ,
431+ ex );
432+ }
433+ }
434+ }
435+ }
376436
377437 @ SuppressWarnings ("rawtypes" )
378438 public static class Builder extends AbstractBuilder <Builder > {
379439 private final List <ReconcilerSpec > reconcilers ;
380440 private final List <PortForwardSpec > portForwards ;
381441 private final List <Class <? extends CustomResource >> additionalCustomResourceDefinitions ;
442+ private final List <CustomResourceDefinition > additionalCustomResourceDefinitionInstances ;
382443 private final List <String > additionalCRDs = new ArrayList <>();
383444 private Consumer <LocallyRunOperatorExtension > beforeStartHook ;
384445 private KubernetesClient kubernetesClient ;
@@ -389,6 +450,7 @@ protected Builder() {
389450 this .reconcilers = new ArrayList <>();
390451 this .portForwards = new ArrayList <>();
391452 this .additionalCustomResourceDefinitions = new ArrayList <>();
453+ this .additionalCustomResourceDefinitionInstances = new ArrayList <>();
392454 }
393455
394456 public Builder withReconciler (
@@ -449,6 +511,11 @@ public Builder withAdditionalCustomResourceDefinition(
449511 return this ;
450512 }
451513
514+ public Builder withAdditionalCustomResourceDefinition (CustomResourceDefinition definition ) {
515+ additionalCustomResourceDefinitionInstances .add (definition );
516+ return this ;
517+ }
518+
452519 public Builder withAdditionalCRD (String ... paths ) {
453520 if (paths != null ) {
454521 additionalCRDs .addAll (List .of (paths ));
@@ -471,6 +538,7 @@ public LocallyRunOperatorExtension build() {
471538 infrastructure ,
472539 portForwards ,
473540 additionalCustomResourceDefinitions ,
541+ additionalCustomResourceDefinitionInstances ,
474542 infrastructureTimeout ,
475543 preserveNamespaceOnError ,
476544 waitForNamespaceDeletion ,
0 commit comments