22
33import java .time .Duration ;
44import java .util .ArrayList ;
5+ import java .util .Arrays ;
56import java .util .List ;
67import java .util .Locale ;
78import java .util .UUID ;
9+ import java .util .concurrent .TimeUnit ;
810
11+ import org .awaitility .Awaitility ;
912import org .junit .jupiter .api .extension .*;
13+ import org .slf4j .Logger ;
14+ import org .slf4j .LoggerFactory ;
1015
1116import io .fabric8 .kubernetes .api .model .HasMetadata ;
1217import io .fabric8 .kubernetes .api .model .KubernetesResourceList ;
18+ import io .fabric8 .kubernetes .api .model .NamespaceBuilder ;
1319import io .fabric8 .kubernetes .client .DefaultKubernetesClient ;
1420import io .fabric8 .kubernetes .client .KubernetesClient ;
1521import io .fabric8 .kubernetes .client .dsl .NonNamespaceOperation ;
@@ -26,7 +32,9 @@ public abstract class AbstractOperatorExtension implements HasKubernetesClient,
2632 AfterAllCallback ,
2733 AfterEachCallback {
2834
29- protected final KubernetesClient kubernetesClient ;
35+ private static final Logger LOGGER = LoggerFactory .getLogger (AbstractOperatorExtension .class );
36+
37+ private final KubernetesClient kubernetesClient ;
3038 protected final ConfigurationService configurationService ;
3139 protected final List <HasMetadata > infrastructure ;
3240 protected Duration infrastructureTimeout ;
@@ -55,22 +63,22 @@ protected AbstractOperatorExtension(
5563
5664
5765 @ Override
58- public void beforeAll (ExtensionContext context ) throws Exception {
66+ public void beforeAll (ExtensionContext context ) {
5967 beforeAllImpl (context );
6068 }
6169
6270 @ Override
63- public void beforeEach (ExtensionContext context ) throws Exception {
71+ public void beforeEach (ExtensionContext context ) {
6472 beforeEachImpl (context );
6573 }
6674
6775 @ Override
68- public void afterAll (ExtensionContext context ) throws Exception {
76+ public void afterAll (ExtensionContext context ) {
6977 afterAllImpl (context );
7078 }
7179
7280 @ Override
73- public void afterEach (ExtensionContext context ) throws Exception {
81+ public void afterEach (ExtensionContext context ) {
7482 afterEachImpl (context );
7583 }
7684
@@ -100,6 +108,7 @@ public <T extends HasMetadata> T replace(Class<T> type, T resource) {
100108 return kubernetesClient .resources (type ).inNamespace (namespace ).replace (resource );
101109 }
102110
111+ @ SuppressWarnings ("unchecked" )
103112 public <T extends HasMetadata > boolean delete (Class <T > type , T resource ) {
104113 return kubernetesClient .resources (type ).inNamespace (namespace ).delete (resource );
105114 }
@@ -130,7 +139,20 @@ protected void beforeEachImpl(ExtensionContext context) {
130139 }
131140 }
132141
133- protected abstract void before (ExtensionContext context );
142+ protected void before (ExtensionContext context ) {
143+ LOGGER .info ("Initializing integration test in namespace {}" , namespace );
144+
145+ kubernetesClient
146+ .namespaces ()
147+ .create (new NamespaceBuilder ().withNewMetadata ().withName (namespace ).endMetadata ().build ());
148+
149+ kubernetesClient
150+ .resourceList (infrastructure )
151+ .createOrReplace ();
152+ kubernetesClient
153+ .resourceList (infrastructure )
154+ .waitUntilReady (infrastructureTimeout .toMillis (), TimeUnit .MILLISECONDS );
155+ }
134156
135157 protected void afterAllImpl (ExtensionContext context ) {
136158 if (oneNamespacePerClass ) {
@@ -144,9 +166,32 @@ protected void afterEachImpl(ExtensionContext context) {
144166 }
145167 }
146168
147- protected abstract void after (ExtensionContext context );
169+ protected void after (ExtensionContext context ) {
170+ if (namespace != null ) {
171+ if (preserveNamespaceOnError && context .getExecutionException ().isPresent ()) {
172+ LOGGER .info ("Preserving namespace {}" , namespace );
173+ } else {
174+ kubernetesClient .resourceList (infrastructure ).delete ();
175+ deleteOperator ();
176+ LOGGER .info ("Deleting namespace {} and stopping operator" , namespace );
177+ kubernetesClient .namespaces ().withName (namespace ).delete ();
178+ if (waitForNamespaceDeletion ) {
179+ LOGGER .info ("Waiting for namespace {} to be deleted" , namespace );
180+ Awaitility .await ("namespace deleted" )
181+ .pollInterval (50 , TimeUnit .MILLISECONDS )
182+ .atMost (90 , TimeUnit .SECONDS )
183+ .until (() -> kubernetesClient .namespaces ().withName (namespace ).get () == null );
184+ }
185+ }
186+ }
187+ }
188+
189+ protected void deleteOperator () {
190+ // nothing to do by default: only needed if the operator is deployed to the cluster
191+ }
148192
149- public static abstract class AbstractBuilder {
193+ @ SuppressWarnings ("unchecked" )
194+ public static abstract class AbstractBuilder <T extends AbstractBuilder <T >> {
150195 protected ConfigurationService configurationService ;
151196 protected final List <HasMetadata > infrastructure ;
152197 protected Duration infrastructureTimeout ;
@@ -172,5 +217,41 @@ protected AbstractBuilder() {
172217 "josdk.it.oneNamespacePerClass" ,
173218 false );
174219 }
220+
221+ public T preserveNamespaceOnError (boolean value ) {
222+ this .preserveNamespaceOnError = value ;
223+ return (T ) this ;
224+ }
225+
226+ public T waitForNamespaceDeletion (boolean value ) {
227+ this .waitForNamespaceDeletion = value ;
228+ return (T ) this ;
229+ }
230+
231+ public T oneNamespacePerClass (boolean value ) {
232+ this .oneNamespacePerClass = value ;
233+ return (T ) this ;
234+ }
235+
236+ public T withConfigurationService (ConfigurationService value ) {
237+ configurationService = value ;
238+ return (T ) this ;
239+ }
240+
241+ public T withInfrastructureTimeout (Duration value ) {
242+ infrastructureTimeout = value ;
243+ return (T ) this ;
244+ }
245+
246+ public T withInfrastructure (List <HasMetadata > hm ) {
247+ infrastructure .addAll (hm );
248+ return (T ) this ;
249+ }
250+
251+ public T withInfrastructure (HasMetadata ... hms ) {
252+ infrastructure .addAll (Arrays .asList (hms ));
253+ return (T ) this ;
254+ }
255+
175256 }
176257}
0 commit comments