Repository: EnMasseProject/enmasse
Pull request #3528 (2019-12-02 11:14:05)

add OLM tests that create address space in different namespace
### Type of change

<!--

_Select the type of your PR_

-->
- Enhancement / new feature

### Description

This PR adds some refactor to OLM tests and adds new tests to create address space in different namespace than the one the operator is installed

### Checklist

<!--

_Please go through this checklist and make sure all applicable tasks have been done_

-->

- [ ] Update/write design documentation in `./documentation/design`
- [x] Write tests and make sure they pass
- [ ] Update documentation
- [ ] Check RBAC rights for Kubernetes / OpenShift roles
- [x] Try your changes from Pod inside your Kubernetes and OpenShift cluster, not just locally
- [ ] Reference relevant issue(s) and close them after merging
- [ ] Update CHANGELOG.md


famartinrh: @enmasse-ci run tests testcase=OLMSpecificTest,OLMDefaultTest test_only ocp4 OLM

Patch:
diff --git a/pom.xml b/pom.xml
index c101a9bb5..8fc98a5c4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -66,6 +66,7 @@
         <junit.jupiter.version>5.5.0</junit.jupiter.version>
         <junit.platform.runner.version>1.5.0</junit.platform.runner.version>
         <junit.platform.launcher.version>1.5.0</junit.platform.launcher.version>
+        <rerunner-jupiter.version>2.1.3</rerunner-jupiter.version>
         <artemis.version>2.10.1</artemis.version>
         <ngwebdriver.version>1.1.4</ngwebdriver.version>
         <paho.version>1.2.1</paho.version>
@@ -615,6 +616,12 @@
                 <version>${junit.platform.runner.version}</version>
             </dependency>
 
+            <dependency>
+                <groupId>io.github.artsok</groupId>
+                <artifactId>rerunner-jupiter</artifactId>
+                <version>${rerunner-jupiter.version}</version>
+            </dependency>
+
             <dependency>
                 <groupId>org.mockito</groupId>
                 <artifactId>mockito-core</artifactId>


Test patch:
diff --git a/systemtests/pom.xml b/systemtests/pom.xml
index 63eb8ce9a..5d427e964 100644
--- a/systemtests/pom.xml
+++ b/systemtests/pom.xml
@@ -108,6 +108,11 @@
             <artifactId>junit-jupiter-params</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>io.github.artsok</groupId>
+            <artifactId>rerunner-jupiter</artifactId>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>org.seleniumhq.selenium</groupId>
             <artifactId>selenium-java</artifactId>
diff --git a/systemtests/src/main/java/io/enmasse/systemtest/Environment.java b/systemtests/src/main/java/io/enmasse/systemtest/Environment.java
index 802261a79..634bf1dc4 100644
--- a/systemtests/src/main/java/io/enmasse/systemtest/Environment.java
+++ b/systemtests/src/main/java/io/enmasse/systemtest/Environment.java
@@ -62,7 +62,8 @@ public class Environment {
     private final Duration kubernetesApiReadTimeout = Optional.ofNullable(System.getenv().get(K8S_API_READ_TIMEOUT)).map(i -> Duration.ofSeconds(Long.parseLong(i))).orElse(Duration.ofSeconds(60));
     private final Duration kubernetesApiWriteTimeout = Optional.ofNullable(System.getenv().get(K8S_API_WRITE_TIMEOUT)).map(i -> Duration.ofSeconds(Long.parseLong(i))).orElse(Duration.ofSeconds(60));
     private final EnmasseInstallType installType = Optional.ofNullable(System.getenv().get(INSTALL_TYPE)).map(EnmasseInstallType::valueOf).orElse(EnmasseInstallType.BUNDLE);
-    private final OLMInstallationType olmInstallType = Optional.ofNullable(System.getenv().get(OLM_INSTALL_TYPE)).map(OLMInstallationType::valueOf).orElse(OLMInstallationType.SPECIFIC);
+    private final OLMInstallationType olmInstallType = Optional.ofNullable(System.getenv().get(OLM_INSTALL_TYPE)).map(s->s.isEmpty() ? OLMInstallationType.SPECIFIC.name() : s)
+            .map(OLMInstallationType::valueOf).orElse(OLMInstallationType.SPECIFIC);
     protected String templatesPath = System.getenv().getOrDefault(TEMPLATES_PATH,
             Paths.get(System.getProperty("user.dir"), "..", "templates", "build", "enmasse-latest").toString());
     protected UserCredentials managementCredentials = new UserCredentials(null, null);
diff --git a/systemtests/src/main/java/io/enmasse/systemtest/info/TestInfo.java b/systemtests/src/main/java/io/enmasse/systemtest/info/TestInfo.java
index 4620cc593..830539dfa 100644
--- a/systemtests/src/main/java/io/enmasse/systemtest/info/TestInfo.java
+++ b/systemtests/src/main/java/io/enmasse/systemtest/info/TestInfo.java
@@ -57,41 +57,41 @@ public class TestInfo {
                 .toArray(new TestIdentifier[0])[0]).toArray(new TestIdentifier[0]));
         testClasses.forEach(testIdentifier -> {
             testPlan.getChildren(testIdentifier)
-            .forEach(test -> {
-                if (test.getSource().isPresent() && test.getSource().get() instanceof MethodSource) {
-                    MethodSource testSource = (MethodSource) test.getSource().get();
-                    try {
-                        Optional<Method> testMethod = ReflectionUtils.findMethod(Class.forName(testSource.getClassName()), testSource.getMethodName(), testSource.getMethodParameterTypes());
-                        if (testMethod.isPresent()) {
-                            MethodBasedExtensionContext extensionContext = new MethodBasedExtensionContext(testMethod);
-                            ExecutionCondition[] conditions = new ExecutionCondition[] {this::disabledCondition, new SupportedInstallTypeCondition(), new AssumeKubernetesCondition(), new AssumeOpenshiftCondition()};
-                            if (evaluateTestDisabled(extensionContext, conditions)) {
-                                LOGGER.debug("Test {}.{} is disabled", testSource.getClassName(), testSource.getMethodName());
-                            } else {
-                                tests.add(test);
+                    .forEach(test -> {
+                        if (test.getSource().isPresent() && test.getSource().get() instanceof MethodSource) {
+                            MethodSource testSource = (MethodSource) test.getSource().get();
+                            try {
+                                Optional<Method> testMethod = ReflectionUtils.findMethod(Class.forName(testSource.getClassName()), testSource.getMethodName(), testSource.getMethodParameterTypes());
+                                if (testMethod.isPresent()) {
+                                    MethodBasedExtensionContext extensionContext = new MethodBasedExtensionContext(testMethod);
+                                    ExecutionCondition[] conditions = new ExecutionCondition[]{this::disabledCondition, new SupportedInstallTypeCondition(), new AssumeKubernetesCondition(), new AssumeOpenshiftCondition()};
+                                    if (evaluateTestDisabled(extensionContext, conditions)) {
+                                        LOGGER.debug("Test {}.{} is disabled", testSource.getClassName(), testSource.getMethodName());
+                                    } else {
+                                        tests.add(test);
+                                    }
+                                }
+                            } catch (ClassNotFoundException e) {
+                                throw new IllegalArgumentException(e);
                             }
+                        } else {
+                            LOGGER.warn("Test {} doesn't have MethodSource", test.getUniqueId());
                         }
-                    } catch ( ClassNotFoundException e ) {
-                        throw new IllegalArgumentException(e);
-                    }
-                } else {
-                    LOGGER.warn("Test {} doesn't have MethodSource", test.getUniqueId());
-                }
-            });
+                    });
         });
         LOGGER.debug("Final tests are {}", tests);
     }
 
     private ConditionEvaluationResult disabledCondition(ExtensionContext ctx) {
         return AnnotationSupport.findAnnotation(ctx.getElement().get(), Disabled.class)
-            .map(a -> ConditionEvaluationResult.disabled("Disabled annotation"))
-            .orElseGet(() -> ConditionEvaluationResult.enabled("No disabled annotation"));
+                .map(a -> ConditionEvaluationResult.disabled("Disabled annotation"))
+                .orElseGet(() -> ConditionEvaluationResult.enabled("No disabled annotation"));
     }
 
     private boolean evaluateTestDisabled(ExtensionContext context, ExecutionCondition... conditions) {
-        for(ExecutionCondition condition : conditions) {
+        for (ExecutionCondition condition : conditions) {
             if (condition.evaluateExecutionCondition(context).isDisabled()) {
-               return true;
+                return true;
             }
         }
         return false;
@@ -153,13 +153,13 @@ public class TestInfo {
     }
 
     public boolean isClassIoT() {
-        return currentTestClass.getTags().stream().anyMatch(tag -> TestTag.IOT_TAGS.contains(tag));
+        return currentTestClass.getTags().stream().anyMatch(TestTag.IOT_TAGS::contains);
     }
 
     public boolean isEndOfIotTests() {
         int currentTestIndex = getCurrentTestIndex();
         if (currentTestIndex + 1 < tests.size()) {
-            return getTags(tests.get(currentTestIndex + 1)).stream().noneMatch(tag -> TestTag.IOT_TAGS.contains(tag));
+            return getTags(tests.get(currentTestIndex + 1)).stream().noneMatch(TestTag.IOT_TAGS::contains);
         }
         return true;
     }
@@ -170,8 +170,8 @@ public class TestInfo {
 
     public boolean isOLMTest() {
         return AnnotationSupport.findAnnotation(currentTestClass.getElement(), SupportedInstallType.class)
-            .map(a -> a.value() == EnmasseInstallType.OLM)
-            .orElse(false);
+                .map(a -> a.value() == EnmasseInstallType.OLM)
+                .orElse(false);
     }
 
     public OLMInstallationType getOLMInstallationType() {
@@ -230,7 +230,8 @@ public class TestInfo {
     }
 
     private boolean isSameTestMethod(TestIdentifier test1, ExtensionContext test2) {
-        return test1 != null && test2 != null && test1.getDisplayName().equals(test2.getDisplayName());
+        return test1 != null && test2 != null && test1.getDisplayName().replace("()", "").replaceAll("\\s+", "")
+                .equals(test2.getDisplayName().replace("()", "").replaceAll("\\s+", ""));
     }
 
 }
diff --git a/systemtests/src/main/java/io/enmasse/systemtest/platform/Kubernetes.java b/systemtests/src/main/java/io/enmasse/systemtest/platform/Kubernetes.java
index 2ea116c70..2e31caee2 100644
--- a/systemtests/src/main/java/io/enmasse/systemtest/platform/Kubernetes.java
+++ b/systemtests/src/main/java/io/enmasse/systemtest/platform/Kubernetes.java
@@ -45,7 +45,6 @@ import io.enmasse.systemtest.Endpoint;
 import io.enmasse.systemtest.EnmasseInstallType;
 import io.enmasse.systemtest.Environment;
 import io.enmasse.systemtest.OLMInstallationType;
-import io.enmasse.systemtest.UserCredentials;
 import io.enmasse.systemtest.logs.CustomLogger;
 import io.enmasse.systemtest.platform.cluster.KubeCluster;
 import io.enmasse.systemtest.platform.cluster.MinikubeCluster;
@@ -59,8 +58,6 @@ import io.enmasse.user.model.v1.UserList;
 import io.fabric8.kubernetes.api.model.ConfigMap;
 import io.fabric8.kubernetes.api.model.ConfigMapList;
 import io.fabric8.kubernetes.api.model.Container;
-import io.fabric8.kubernetes.api.model.Event;
-import io.fabric8.kubernetes.api.model.HasMetadata;
 import io.fabric8.kubernetes.api.model.Namespace;
 import io.fabric8.kubernetes.api.model.NamespaceBuilder;
 import io.fabric8.kubernetes.api.model.PersistentVolumeClaim;
@@ -80,27 +77,20 @@ import io.fabric8.kubernetes.client.KubernetesClient;
 import io.fabric8.kubernetes.client.VersionInfo;
 import io.fabric8.kubernetes.client.Watch;
 import io.fabric8.kubernetes.client.Watcher;
-import io.fabric8.kubernetes.client.dsl.ExecListener;
-import io.fabric8.kubernetes.client.dsl.ExecWatch;
 import io.fabric8.kubernetes.client.dsl.LogWatch;
 import io.fabric8.kubernetes.client.dsl.MixedOperation;
 import io.fabric8.kubernetes.client.dsl.Resource;
-import okhttp3.Response;
 import org.slf4j.Logger;
 
-import java.io.ByteArrayOutputStream;
 import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Base64;
 import java.util.HashMap;
-import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
-import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
@@ -336,10 +326,6 @@ public abstract class Kubernetes {
         return new Endpoint(service.getSpec().getClusterIP(), getPort(service, port));
     }
 
-    public Endpoint getOSBEndpoint() {
-        return getEndpoint("service-broker", infraNamespace, "https");
-    }
-
     public abstract Endpoint getMasterEndpoint();
 
     public abstract Endpoint getRestEndpoint();
@@ -356,17 +342,6 @@ public abstract class Kubernetes {
 
     public abstract Endpoint getExternalEndpoint(String name, String namespace);
 
-    public UserCredentials getKeycloakCredentials() {
-        Secret creds = client.secrets().inNamespace(infraNamespace).withName("keycloak-credentials").get();
-        if (creds != null) {
-            String username = new String(Base64.getDecoder().decode(creds.getData().get("admin.username")));
-            String password = new String(Base64.getDecoder().decode(creds.getData().get("admin.password")));
-            return new UserCredentials(username, password);
-        } else {
-            return null;
-        }
-    }
-
     public Map<String, String> getLogsOfTerminatedPods(String namespace) {
         Map<String, String> terminatedPodsLogs = new HashMap<>();
         client.pods().inNamespace(namespace).list().getItems().forEach(pod -> {
@@ -428,24 +403,16 @@ public abstract class Kubernetes {
         client.apps().deployments().inNamespace(namespace).withName(name).scale(numReplicas, true);
     }
 
-    public void setStatefulSetReplicas(String name, int numReplicas) {
-        client.apps().statefulSets().inNamespace(infraNamespace).withName(name).scale(numReplicas, true);
-    }
-
-    public List<Pod> listPods(String namespace, String uuid) {
-        return new ArrayList<>(client.pods().inNamespace(namespace).withLabel("enmasse.io/uuid", uuid).list().getItems());
-    }
-
     public List<Pod> listPods(String namespace) {
         return new ArrayList<>(client.pods().inNamespace(namespace).list().getItems());
     }
 
     public List<Pod> listPods() {
-        return new ArrayList<>(client.pods().inNamespace(infraNamespace).list().getItems());
+        return listPods(infraNamespace);
     }
 
     public List<Pod> listPods(Map<String, String> labelSelector) {
-        return client.pods().inNamespace(infraNamespace).withLabels(labelSelector).list().getItems();
+        return listPods(infraNamespace, labelSelector);
     }
 
     public List<Pod> listPods(String namespace, Map<String, String> labelSelector) {
@@ -463,24 +430,10 @@ public abstract class Kubernetes {
         }).collect(Collectors.toList());
     }
 
-    public int getExpectedPods(String plan) {
-        if (plan.endsWith("with-mqtt")) {
-            return 6;
-        } else if (plan.endsWith("medium") || plan.endsWith("unlimited")) {
-            return 3;
-        } else {
-            return 2;
-        }
-    }
-
     public Watch watchPods(String uuid, Watcher<Pod> podWatcher) {
         return client.pods().withLabel("enmasse.io/infra", uuid).watch(podWatcher);
     }
 
-    public List<Event> listEvents(String namespace) {
-        return client.events().inNamespace(namespace).list().getItems();
-    }
-
     public LogWatch watchPodLog(String name, String container, OutputStream outputStream) {
         return client.pods().withName(name).inContainer(container).watchLog(outputStream);
     }
@@ -495,20 +448,6 @@ public abstract class Kubernetes {
                 .collect(Collectors.toSet());
     }
 
-    public String getKeycloakCA() throws UnsupportedEncodingException {
-        Secret secret = client.secrets().inNamespace(infraNamespace).withName("standard-authservice-cert").get();
-        if (secret == null) {
-            throw new IllegalStateException("Unable to find CA cert for keycloak");
-        }
-        return new String(Base64.getDecoder().decode(secret.getData().get("tls.crt")), StandardCharsets.UTF_8);
-    }
-
-    public List<ConfigMap> listConfigMaps(String type) {
-        Map<String, String> labels = new LinkedHashMap<>();
-        labels.put("type", type);
-        return listConfigMaps(labels);
-    }
-
     public List<ConfigMap> listConfigMaps(Map<String, String> labels) {
         return client.configMaps().inNamespace(infraNamespace).withLabels(labels).list().getItems();
     }
@@ -545,30 +484,18 @@ public abstract class Kubernetes {
         return client.configMaps().inNamespace(namespace).list();
     }
 
-    public ConfigMap getConfigMap(String namespace, String configMapName) {
-        return getAllConfigMaps(namespace).getItems().stream()
-                .filter(configMap -> configMap.getMetadata().getName().equals(configMapName))
-                .findFirst().get();
-    }
-
-    public void replaceConfigMap(String namespace, ConfigMap newConfigMap) {
-        client.configMaps().inNamespace(namespace).createOrReplace(newConfigMap);
-    }
-
     public void createNamespace(String namespace) {
-        log.info("Following namespace will be created = {}", namespace);
-        if (!namespaceExists(namespace)) {
-            Namespace ns = new NamespaceBuilder().withNewMetadata().withName(namespace).endMetadata().build();
-            client.namespaces().create(ns);
-        } else {
-            log.info("Namespace {} already exists", namespace);
-        }
+        createNamespace(namespace, null);
     }
 
     public void createNamespace(String namespace, Map<String, String> labels) {
         log.info("Following namespace will be created = {}", namespace);
         if (!namespaceExists(namespace)) {
-            Namespace ns = new NamespaceBuilder().withNewMetadata().withName(namespace).withLabels(labels).endMetadata().build();
+            var builder = new NamespaceBuilder().withNewMetadata().withName(namespace);
+            if (labels != null) {
+                builder.withLabels(labels);
+            }
+            Namespace ns = builder.endMetadata().build();
             client.namespaces().create(ns);
         } else {
             log.info("Namespace {} already exists", namespace);
@@ -597,21 +524,6 @@ public abstract class Kubernetes {
         client.pods().inNamespace(namespace).withLabels(labels).withPropagationPolicy("Background").delete();
     }
 
-    /***
-     * Creates pod from resources
-     * @param namespace
-     * @param configName
-     * @throws Exception
-     */
-    public void createPodFromTemplate(String namespace, String configName) throws Exception {
-        List<HasMetadata> resources = client.load(getClass().getResourceAsStream(configName)).inNamespace(namespace).get();
-        HasMetadata resource = resources.get(0);
-        Pod podRes = client.pods().inNamespace(namespace).create((Pod) resource);
-        Pod result = client.pods().inNamespace(namespace)
-                .withName(podRes.getMetadata().getName()).waitUntilReady(5, TimeUnit.SECONDS);
-        log.info("Pod created {}", result.getMetadata().getName());
-    }
-
     /***
      * Delete pod by name
      * @param namespace
@@ -623,16 +535,6 @@ public abstract class Kubernetes {
         log.info("Pod {} removed", podName);
     }
 
-    /***
-     * Returns pod ip
-     * @param namespace namespace
-     * @param podName name of pod
-     * @return string ip
-     */
-    public String getPodIp(String namespace, String podName) {
-        return client.pods().inNamespace(namespace).withName(podName).get().getStatus().getPodIP();
-    }
-
     /***
      * Creates application from resources
      * @param namespace namespace
@@ -833,46 +735,6 @@ public abstract class Kubernetes {
         return listPods().get(0).getMetadata().getLabels().get("app");
     }
 
-
-    /**
-     * Run command on kubernetes pod
-     *
-     * @param pod       pod instance
-     * @param container name of container
-     * @param command   command to run
-     * @return stdout
-     */
-    public String runOnPod(Pod pod, String container, String... command) {
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        log.info("Running command on pod {}: {}", pod.getMetadata().getName(), command);
-        CompletableFuture<String> data = new CompletableFuture<>();
-        try (ExecWatch execWatch = client.pods().inNamespace(pod.getMetadata().getNamespace())
-                .withName(pod.getMetadata().getName()).inContainer(container)
-                .readingInput(null)
-                .writingOutput(baos)
-                .usingListener(new ExecListener() {
-                    @Override
-                    public void onOpen(Response response) {
-                        log.info("Reading data...");
-                    }
-
-                    @Override
-                    public void onFailure(Throwable throwable, Response response) {
-                        data.completeExceptionally(throwable);
-                    }
-
-                    @Override
-                    public void onClose(int i, String s) {
-                        data.complete(baos.toString());
-                    }
-                }).exec(command)) {
-            return data.get(10, TimeUnit.SECONDS);
-        } catch (Exception e) {
-            log.warn("Exception running command {} on pod: {} with exception {}", command, pod.getMetadata().getName(), e);
-            return "";
-        }
-    }
-
     public ServiceAccount getServiceAccount(String namespace, String name) {
         return client.serviceAccounts().inNamespace(namespace).withName(name).get();
     }
diff --git a/systemtests/src/main/java/io/enmasse/systemtest/utils/TestUtils.java b/systemtests/src/main/java/io/enmasse/systemtest/utils/TestUtils.java
index bfe2b4f5e..3592ed915 100644
--- a/systemtests/src/main/java/io/enmasse/systemtest/utils/TestUtils.java
+++ b/systemtests/src/main/java/io/enmasse/systemtest/utils/TestUtils.java
@@ -8,11 +8,8 @@ package io.enmasse.systemtest.utils;
 import com.google.common.io.BaseEncoding;
 import io.enmasse.address.model.Address;
 import io.enmasse.address.model.AddressSpace;
-import io.enmasse.address.model.AddressSpaceSchema;
 import io.enmasse.address.model.BrokerState;
 import io.enmasse.address.model.BrokerStatus;
-import io.enmasse.admin.model.v1.AddressPlan;
-import io.enmasse.admin.model.v1.AddressSpacePlan;
 import io.enmasse.systemtest.Endpoint;
 import io.enmasse.systemtest.logs.CustomLogger;
 import io.enmasse.systemtest.logs.GlobalLogCollector;
@@ -23,13 +20,10 @@ import io.enmasse.systemtest.time.SystemtestsOperation;
 import io.enmasse.systemtest.time.TimeMeasuringSystem;
 import io.enmasse.systemtest.time.TimeoutBudget;
 import io.enmasse.systemtest.time.WaitPhase;
-import io.fabric8.kubernetes.api.model.ConfigMap;
 import io.fabric8.kubernetes.api.model.ContainerStatus;
 import io.fabric8.kubernetes.api.model.PersistentVolumeClaim;
 import io.fabric8.kubernetes.api.model.Pod;
-import io.fabric8.kubernetes.api.model.apiextensions.CustomResourceDefinition;
 import io.vertx.core.VertxException;
-import io.vertx.core.json.JsonObject;
 import org.junit.jupiter.api.function.ThrowingSupplier;
 import org.openqa.selenium.Capabilities;
 import org.openqa.selenium.chrome.ChromeOptions;
@@ -475,29 +469,6 @@ public class TestUtils {
         });
     }
 
-    /**
-     * Replace address plan in ConfigMap of already existing address
-     *
-     * @param kubernetes client for manipulation with kubernetes cluster
-     * @param addrSpace  address space which contains ConfigMap
-     * @param dest       destination which will be modified
-     * @param plan       definition of AddressPlan
-     */
-    public static void replaceAddressConfig(Kubernetes kubernetes, AddressSpace addrSpace, Address dest, AddressPlan plan) {
-        String mapKey = "config.json";
-        ConfigMap destConfigMap = kubernetes.getConfigMap(addrSpace.getMetadata().getNamespace(), dest.getSpec().getAddress());
-
-        JsonObject data = new JsonObject(destConfigMap.getData().get(mapKey));
-        log.info(data.toString());
-        data.getJsonObject("spec").remove("plan");
-        data.getJsonObject("spec").put("plan", plan.getMetadata().getName());
-
-        Map<String, String> modifiedData = new LinkedHashMap<>();
-        modifiedData.put(mapKey, data.toString());
-        destConfigMap.setData(modifiedData);
-        kubernetes.replaceConfigMap(addrSpace.getMetadata().getNamespace(), destConfigMap);
-    }
-
     public static void deleteAddressSpaceCreatedBySC(Kubernetes kubernetes, AddressSpace addressSpace, GlobalLogCollector logCollector) throws Exception {
         String operationID = TimeMeasuringSystem.startOperation(SystemtestsOperation.DELETE_ADDRESS_SPACE);
         logCollector.collectEvents();
diff --git a/systemtests/src/test/java/io/enmasse/systemtest/bases/olm/OLMTestBase.java b/systemtests/src/test/java/io/enmasse/systemtest/bases/olm/OLMTestBase.java
index 352cd3b07..1e26ff0d0 100644
--- a/systemtests/src/test/java/io/enmasse/systemtest/bases/olm/OLMTestBase.java
+++ b/systemtests/src/test/java/io/enmasse/systemtest/bases/olm/OLMTestBase.java
@@ -9,6 +9,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Base64;
 import java.util.List;
 import java.util.Set;
@@ -17,9 +18,10 @@ import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
 import org.apache.qpid.proton.message.Message;
+import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.extension.ExtensionContext;
 import org.slf4j.Logger;
 
@@ -51,35 +53,17 @@ public abstract class OLMTestBase extends TestBase implements ITestIsolatedStand
 
     protected abstract String getInstallationNamespace();
 
-    @AfterEach
-    void collectLogs(ExtensionContext context) {
-        if (context.getExecutionException().isPresent()) { //test failed
-            logCollector.collectLogsOfPodsInNamespace(getInstallationNamespace());
-            logCollector.collectEvents(getInstallationNamespace());
-        }
-    }
+    protected abstract String getDifferentAddressSpaceNamespace();
 
-    @BeforeEach
-    void setupExampleResources() {
+    @BeforeAll
+    void setupExampleResources() throws Exception {
         ExecutionResultData result = KubeCMDClient.runOnCluster("get", "csv", "-n", getInstallationNamespace(), "-o", "json", "-l", "app=enmasse");
         JsonObject csvList = new JsonObject(result.getStdOut());
         JsonObject csv = csvList.getJsonArray("items").getJsonObject(0);
         String almExamples = csv.getJsonObject("metadata").getJsonObject("annotations").getString("alm-examples");
         JsonArray examples = new JsonArray(almExamples);
         exampleResources = examples.stream().map(o -> (JsonObject) o).collect(Collectors.toList());
-    }
-
-    @AfterEach
-    void teardownExampleResources() throws IOException {
-        if (!environment.skipCleanup()) {
-            for (JsonObject example : exampleResources) {
-                log.info("Deleting {}", example.toString());
-                KubeCMDClient.deleteCR(getInstallationNamespace(), example.toString(), CR_TIMEOUT_MILLIS);
-            }
-        }
-    }
 
-    protected void doTestCreateExampleResources() throws Exception {
         Set<String> infraKinds = Set.of("StandardInfraConfig", "BrokeredInfraConfig", "AddressPlan", "AddressSpacePlan", "AuthenticationService");
 
         for(JsonObject example : exampleResources) {
@@ -92,56 +76,83 @@ public abstract class OLMTestBase extends TestBase implements ITestIsolatedStand
                 kubernetes.getAuthenticationServiceClient(getInstallationNamespace()).create(authService);
             } else if(infraKinds.contains(kind)) {
                 log.info("Creating {}", example.toString());
-                ExecutionResultData res = KubeCMDClient.createCR(getInstallationNamespace(), example.toString(), CR_TIMEOUT_MILLIS);
-                if(!res.getRetCode()) {
-                    Assertions.fail(res.getStdErr());
-                }
+                createCR(getInstallationNamespace(), example);
             }
         }
         TestUtils.waitUntilDeployed(getInstallationNamespace());
         TestUtils.waitForPodReady("standard-authservice", getInstallationNamespace());
         TestUtils.waitForSchemaInSync("standard-small");
-
         var addressSpacePlanClient = kubernetes.getAddressSpacePlanClient(getInstallationNamespace());
         TestUtils.waitUntilCondition("AddressSpacePlan standard-small visible",
                 phase -> addressSpacePlanClient.withName("standard-small").get() != null,
                 new TimeoutBudget(2, TimeUnit.MINUTES));
-        for(JsonObject example : exampleResources) {
-            String kind = example.getString("kind");
-            if(kind.equals("AddressSpace")) {
-                log.info("Creating {}", kind);
-                ExecutionResultData res = KubeCMDClient.createCR(getInstallationNamespace(), example.toString(), CR_TIMEOUT_MILLIS);
-                if(!res.getRetCode()) {
-                    Assertions.fail(res.getStdErr());
-                }
+    }
+
+    @AfterEach
+    void deleteAddressSpace(ExtensionContext context) throws Exception {
+        if (context.getExecutionException().isPresent()) { //test failed
+            logCollector.collectLogsOfPodsInNamespace(getInstallationNamespace());
+            logCollector.collectEvents(getInstallationNamespace());
+        }
+        for (String namespace : Arrays.asList(getInstallationNamespace(), getDifferentAddressSpaceNamespace())) {
+            AddressSpace addressSpace = kubernetes.getAddressSpaceClient(namespace).withName("myspace").get();
+            if (addressSpace != null) {
+                resourcesManager.deleteAddressSpace(addressSpace);
+            }
+        }
+    }
+
+    @AfterAll
+    void teardownExampleResources() throws Exception {
+        if (!environment.skipCleanup()) {
+            for (JsonObject example : exampleResources) {
+                log.info("Deleting {}", example.toString());
+                KubeCMDClient.deleteCR(getInstallationNamespace(), example.toString(), CR_TIMEOUT_MILLIS);
             }
+            kubernetes.deleteNamespace(getDifferentAddressSpaceNamespace());
         }
-        var client = kubernetes.getAddressSpaceClient(getInstallationNamespace());
+    }
+
+    protected void doTestExampleResourcesSameNamespaceAsOperator() throws Exception {
+        createdUserResources(getInstallationNamespace());
+    }
+
+    protected void doTestExampleResourcesDifferentNamespaceThanOperator() throws Exception {
+        kubernetes.createNamespace(getDifferentAddressSpaceNamespace());
+        createdUserResources(getDifferentAddressSpaceNamespace());
+    }
+
+    private void createdUserResources(String addressSpaceNamespace) throws Exception {
+        JsonObject exampleAddressSpace = exampleResources.stream()
+            .filter(example -> example.getString("kind").equals("AddressSpace"))
+            .findFirst()
+            .orElseThrow(()-> new IllegalStateException("Example resources don't contain an address space"));
+        log.info("Creating {}", exampleAddressSpace.getString("kind"));
+        createCR(addressSpaceNamespace, exampleAddressSpace);
+        var client = kubernetes.getAddressSpaceClient(addressSpaceNamespace);
         TestUtils.waitUntilCondition("Address space visible",
-                phase -> client.withName("myspace").get() != null,
-                new TimeoutBudget(30, TimeUnit.SECONDS));
+                phase -> client.withName("myspace").get() != null,new TimeoutBudget(30, TimeUnit.SECONDS));
         resourcesManager.waitForAddressSpaceReady(client.withName("myspace").get());
+
+
         for(JsonObject example : exampleResources) {
             String kind = example.getString("kind");
             if(kind.equals("Address") || kind.equals("MessagingUser")) {
                 log.info("Creating {}", kind);
-                ExecutionResultData res = KubeCMDClient.createCR(getInstallationNamespace(), example.toString(), CR_TIMEOUT_MILLIS);
-                if(!res.getRetCode()) {
-                    Assertions.fail(res.getStdErr());
-                }
+                createCR(addressSpaceNamespace, example);
             }
         }
         Thread.sleep(10_000);
         TestUtils.waitUntilDeployed(getInstallationNamespace());
-        var addressClient = kubernetes.getAddressClient(getInstallationNamespace());
+        var addressClient = kubernetes.getAddressClient(addressSpaceNamespace);
         TestUtils.waitUntilCondition("Address visible",
                 phase -> addressClient.withName("myspace.myqueue").get() != null,
                 new TimeoutBudget(30, TimeUnit.SECONDS));
         waitForDestinationsReady(addressClient.withName("myspace.myqueue").get());
 
         // Test basic messages
-        AddressSpace exampleSpace = kubernetes.getAddressSpaceClient(getInstallationNamespace()).withName("myspace").get();
-        Address exampleAddress = kubernetes.getAddressClient(getInstallationNamespace()).withName("myspace.myqueue").get();
+        AddressSpace exampleSpace = kubernetes.getAddressSpaceClient(addressSpaceNamespace).withName("myspace").get();
+        Address exampleAddress = kubernetes.getAddressClient(addressSpaceNamespace).withName("myspace.myqueue").get();
 
         AmqpClient amqpClient = resourcesManager.getAmqpClientFactory().createClient(new AmqpConnectOptions()
                 .setTerminusFactory(new QueueTerminusFactory())
@@ -158,4 +169,12 @@ public abstract class OLMTestBase extends TestBase implements ITestIsolatedStand
         Future<List<Message>> received = amqpClient.recvMessages(exampleAddress.getSpec().getAddress(), messageCount);
         assertEquals(messageCount, received.get(1, TimeUnit.MINUTES).size(), "Incorrect count of messages received");
     }
+
+    private void createCR(String namespace, JsonObject cr) throws IOException {
+        ExecutionResultData res = KubeCMDClient.createCR(namespace, cr.toString(), CR_TIMEOUT_MILLIS);
+        if(!res.getRetCode()) {
+            Assertions.fail(res.getStdErr());
+        }
+    }
+
 }
diff --git a/systemtests/src/test/java/io/enmasse/systemtest/olm/OLMDefaultTest.java b/systemtests/src/test/java/io/enmasse/systemtest/olm/OLMDefaultTest.java
index 5a833b560..c289bb04e 100644
--- a/systemtests/src/test/java/io/enmasse/systemtest/olm/OLMDefaultTest.java
+++ b/systemtests/src/test/java/io/enmasse/systemtest/olm/OLMDefaultTest.java
@@ -23,9 +23,19 @@ public class OLMDefaultTest extends OLMTestBase{
         return kubernetes.getOlmNamespace();
     }
 
+    @Override
+    protected String getDifferentAddressSpaceNamespace() {
+        return "systemtests-default-olm";
+    }
+
+    @Test
+    void testExampleResourcesSameNamespaceAsOperator() throws Exception {
+        doTestExampleResourcesSameNamespaceAsOperator();
+    }
+
     @Test
-    void testExampleResources() throws Exception {
-        doTestCreateExampleResources();
+    void testExampleResourcesDifferentNamespaceThanOperator() throws Exception {
+        doTestExampleResourcesDifferentNamespaceThanOperator();
     }
 
 }
diff --git a/systemtests/src/test/java/io/enmasse/systemtest/olm/OLMSpecificTest.java b/systemtests/src/test/java/io/enmasse/systemtest/olm/OLMSpecificTest.java
index fa06edca8..4baaa7cc1 100644
--- a/systemtests/src/test/java/io/enmasse/systemtest/olm/OLMSpecificTest.java
+++ b/systemtests/src/test/java/io/enmasse/systemtest/olm/OLMSpecificTest.java
@@ -23,9 +23,19 @@ public class OLMSpecificTest extends OLMTestBase{
         return kubernetes.getInfraNamespace();
     }
 
+    @Override
+    protected String getDifferentAddressSpaceNamespace() {
+        return "systemtests-specific-olm";
+    }
+
+    @Test
+    void testExampleResourcesSameNamespaceAsOperator() throws Exception {
+        doTestExampleResourcesSameNamespaceAsOperator();
+    }
+
     @Test
-    void testExampleResources() throws Exception {
-        doTestCreateExampleResources();
+    void testExampleResourcesDifferentNamespaceThanOperator() throws Exception {
+        doTestExampleResourcesDifferentNamespaceThanOperator();
     }
 
 }
diff --git a/systemtests/src/test/java/io/enmasse/systemtest/shared/standard/mqtt/ConnectionTest.java b/systemtests/src/test/java/io/enmasse/systemtest/shared/standard/mqtt/ConnectionTest.java
index 55a61150b..dd8b11735 100644
--- a/systemtests/src/test/java/io/enmasse/systemtest/shared/standard/mqtt/ConnectionTest.java
+++ b/systemtests/src/test/java/io/enmasse/systemtest/shared/standard/mqtt/ConnectionTest.java
@@ -13,8 +13,10 @@ import io.enmasse.systemtest.model.addressplan.DestinationPlan;
 import io.enmasse.systemtest.mqtt.MqttConnectionLostCallback;
 import io.enmasse.systemtest.mqtt.MqttDeliveryCompleteCallback;
 import io.enmasse.systemtest.utils.AddressUtils;
+import io.github.artsok.RepeatedIfExceptionsTest;
 import org.eclipse.paho.client.mqttv3.IMqttClient;
 import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
+import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Test;
 
 import java.util.concurrent.CompletableFuture;
@@ -32,7 +34,8 @@ public class ConnectionTest extends TestBase implements ITestSharedWithMqtt {
      * MQTT-3.1.4-2 If the ClientId represents a Client already connected to the Server then the Server MUST
      * disconnect the existing Client [].
      */
-    @Test
+    @DisplayName("newSessionDisconnectsExisting")
+    @RepeatedIfExceptionsTest(repeats = 2, name = "")
     public void newSessionDisconnectsExisting() throws Exception {
         Address dest = new AddressBuilder()
                 .withNewMetadata()
diff --git a/systemtests/src/test/java/io/enmasse/systemtest/shared/standard/mqtt/InteroperabilityTest.java b/systemtests/src/test/java/io/enmasse/systemtest/shared/standard/mqtt/InteroperabilityTest.java
index 28f1cb98a..0fd9d7813 100644
--- a/systemtests/src/test/java/io/enmasse/systemtest/shared/standard/mqtt/InteroperabilityTest.java
+++ b/systemtests/src/test/java/io/enmasse/systemtest/shared/standard/mqtt/InteroperabilityTest.java
@@ -13,12 +13,14 @@ import io.enmasse.systemtest.bases.shared.ITestSharedWithMqtt;
 import io.enmasse.systemtest.model.addressplan.DestinationPlan;
 import io.enmasse.systemtest.mqtt.MqttUtils;
 import io.enmasse.systemtest.utils.AddressUtils;
+import io.github.artsok.RepeatedIfExceptionsTest;
 import org.apache.qpid.proton.amqp.Binary;
 import org.apache.qpid.proton.amqp.messaging.AmqpValue;
 import org.apache.qpid.proton.amqp.messaging.Data;
 import org.apache.qpid.proton.message.Message;
 import org.eclipse.paho.client.mqttv3.IMqttClient;
 import org.eclipse.paho.client.mqttv3.MqttMessage;
+import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Test;
 
 import java.nio.charset.StandardCharsets;
@@ -39,7 +41,8 @@ class InteroperabilityTest extends TestBase implements ITestSharedWithMqtt {
     private static final String MQTT_AMQP_TOPIC = "mqtt-amqp-topic";
     private static final String AMQP_MQTT_TOPIC = "amqp-mqtt-topic";
 
-    @Test
+    @DisplayName("testSendMqttReceiveAmqp")
+    @RepeatedIfExceptionsTest(repeats = 2, name = "")
     void testSendMqttReceiveAmqp() throws Exception {
         Address mqttTopic = new AddressBuilder()
                 .withNewMetadata()
@@ -79,7 +82,8 @@ class InteroperabilityTest extends TestBase implements ITestSharedWithMqtt {
         mqttClient.close();
     }
 
-    @Test
+    @DisplayName("testSendAmqpReceiveMqtt")
+    @RepeatedIfExceptionsTest(repeats = 2, name = "")
     void testSendAmqpReceiveMqtt() throws Exception {
         Address mqttTopic = new AddressBuilder()
                 .withNewMetadata()
diff --git a/systemtests/src/test/java/io/enmasse/systemtest/shared/standard/mqtt/PublishTest.java b/systemtests/src/test/java/io/enmasse/systemtest/shared/standard/mqtt/PublishTest.java
index 535b93a15..b3071a080 100644
--- a/systemtests/src/test/java/io/enmasse/systemtest/shared/standard/mqtt/PublishTest.java
+++ b/systemtests/src/test/java/io/enmasse/systemtest/shared/standard/mqtt/PublishTest.java
@@ -8,7 +8,9 @@ package io.enmasse.systemtest.shared.standard.mqtt;
 import io.enmasse.systemtest.bases.mqtt.MqttPublishTestBase;
 import io.enmasse.systemtest.bases.shared.ITestSharedWithMqtt;
 import io.enmasse.systemtest.model.addressplan.DestinationPlan;
+import io.github.artsok.RepeatedIfExceptionsTest;
 import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Test;
 
 /**
@@ -16,27 +18,31 @@ import org.junit.jupiter.api.Test;
  */
 public class PublishTest extends MqttPublishTestBase implements ITestSharedWithMqtt {
 
-    @Test
+    @DisplayName("testPublishQoS0")
+    @RepeatedIfExceptionsTest(repeats = 2, name = "")
     @Override
     public void testPublishQoS0() throws Exception {
         super.testPublishQoS0();
     }
 
-    @Test
+    @DisplayName("testPublishQoS1")
+    @RepeatedIfExceptionsTest(repeats = 2, name = "")
     @Override
     public void testPublishQoS1() throws Exception {
         super.testPublishQoS1();
     }
 
     @Override
-    @Test
+    @DisplayName("testPublishQoS2")
+    @RepeatedIfExceptionsTest(repeats = 2, name = "")
     @Disabled
     public void testPublishQoS2() throws Exception {
         super.testPublishQoS2();
     }
 
     @Override
-    @Test
+    @DisplayName("testRetainedMessages")
+    @RepeatedIfExceptionsTest(repeats = 2, name = "")
     @Disabled("related issue: #1529")
     public void testRetainedMessages() throws Exception {
         super.testRetainedMessages();
diff --git a/systemtests/src/test/java/io/enmasse/systemtest/shared/standard/mqtt/SessionTest.java b/systemtests/src/test/java/io/enmasse/systemtest/shared/standard/mqtt/SessionTest.java
index b9289a0b4..b4357747a 100644
--- a/systemtests/src/test/java/io/enmasse/systemtest/shared/standard/mqtt/SessionTest.java
+++ b/systemtests/src/test/java/io/enmasse/systemtest/shared/standard/mqtt/SessionTest.java
@@ -13,8 +13,10 @@ import io.enmasse.systemtest.model.addressplan.DestinationPlan;
 import io.enmasse.systemtest.mqtt.MqttDeliveryCompleteCallback;
 import io.enmasse.systemtest.mqtt.MqttMessageArrivedCallback;
 import io.enmasse.systemtest.utils.AddressUtils;
+import io.github.artsok.RepeatedIfExceptionsTest;
 import org.eclipse.paho.client.mqttv3.IMqttClient;
 import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
+import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Test;
 
 import java.util.concurrent.CompletableFuture;
@@ -37,7 +39,8 @@ public class SessionTest extends TestBase implements ITestSharedWithMqtt {
      * [MQTT-3.1.2-4] If CleanSession is set to 1, the Client and Server MUST discard any previous Session and start a
      * new one.
      */
-    @Test
+    @DisplayName("previousPersistentSessionDiscarded")
+    @RepeatedIfExceptionsTest(repeats = 2, name = "")
     public void previousPersistentSessionDiscarded() throws Exception {
         Address dest = new AddressBuilder()
                 .withNewMetadata()