Repository: EnMasseProject/enmasse
Pull request #2679 (2019-04-16 11:35:56)

Added metric for RouterCheckFailures


Patch:
diff --git a/Makefile b/Makefile
index 00c83f3c0..ca2c2311d 100644
--- a/Makefile
+++ b/Makefile
@@ -41,13 +41,6 @@ ASCIIDOCTOR_FLAGS       = -v -a EnMasseVersion=$(VERSION) -t -dbook $(ASCIIDOCTO
 ifeq ($(SKIP_TESTS),true)
 	MAVEN_ARGS=-DskipTests -Dmaven.test.skip=true
 endif
-ifneq ($(strip $(PROJECT_DISPLAY_NAME)),)
-	MAVEN_ARGS+="-Dapplication.display.name=$(PROJECT_DISPLAY_NAME)"
-endif
-ifneq ($(strip $(GLOBAL_CONSOLE_DISPLAY_NAME)),)
-        MAVEN_ARGS+="-Dapplication.globalconsole.display.name=$(GLOBAL_CONSOLE_DISPLAY_NAME)"
-endif
-
 
 all: build_java build_go templates
 
diff --git a/Makefile.java.mk b/Makefile.java.mk
index fc71a60c7..fac73f3ea 100644
--- a/Makefile.java.mk
+++ b/Makefile.java.mk
@@ -4,13 +4,6 @@ include $(TOPDIR)/Makefile.common
 ifeq ($(SKIP_TESTS),true)
 MAVEN_ARGS="-DskipTests"
 endif
-ifneq ($(strip $(PROJECT_DISPLAY_NAME)),)
-	MAVEN_ARGS+="-Dapplication.display.name=$(PROJECT_DISPLAY_NAME)"
-endif
-ifneq ($(strip $(GLOBAL_CONSOLE_DISPLAY_NAME)),)
-	MAVEN_ARGS+="-Dapplication.globalconsole.display.name='$(PROJECT_DISPLAY_NAME) Console'"
-endif
-
 
 ifneq ($(FULL_BUILD),true)
 build:
diff --git a/address-space-controller/src/main/java/io/enmasse/controller/TemplateInfraResourceFactory.java b/address-space-controller/src/main/java/io/enmasse/controller/TemplateInfraResourceFactory.java
index 95b99f8ac..8613a1cd4 100644
--- a/address-space-controller/src/main/java/io/enmasse/controller/TemplateInfraResourceFactory.java
+++ b/address-space-controller/src/main/java/io/enmasse/controller/TemplateInfraResourceFactory.java
@@ -6,17 +6,21 @@ package io.enmasse.controller;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
-import io.enmasse.address.model.*;
+import io.enmasse.address.model.AddressSpace;
+import io.enmasse.address.model.CertSpec;
+import io.enmasse.address.model.EndpointSpec;
+import io.enmasse.address.model.KubeUtil;
 import io.enmasse.admin.model.v1.*;
-import io.enmasse.admin.model.v1.AuthenticationService;
-import io.enmasse.admin.model.v1.AuthenticationServiceType;
 import io.enmasse.config.AnnotationKeys;
 import io.enmasse.config.LabelKeys;
 import io.enmasse.controller.common.Kubernetes;
 import io.enmasse.controller.common.TemplateParameter;
 import io.enmasse.k8s.api.AuthenticationServiceRegistry;
 import io.enmasse.k8s.api.SchemaProvider;
-import io.fabric8.kubernetes.api.model.*;
+import io.fabric8.kubernetes.api.model.HasMetadata;
+import io.fabric8.kubernetes.api.model.PersistentVolumeClaim;
+import io.fabric8.kubernetes.api.model.PodTemplateSpec;
+import io.fabric8.kubernetes.api.model.SecretReference;
 import io.fabric8.kubernetes.api.model.apps.Deployment;
 import io.fabric8.kubernetes.api.model.apps.StatefulSet;
 import org.slf4j.Logger;
@@ -25,8 +29,6 @@ import org.slf4j.LoggerFactory;
 import java.io.File;
 import java.io.IOException;
 import java.io.UncheckedIOException;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.util.*;
 
@@ -63,6 +65,22 @@ public class TemplateInfraResourceFactory implements InfraResourceFactory {
             throw new IllegalArgumentException("Authentication service '" + authService.getMetadata().getName() + "' is not yet deployed");
         }
 
+        String authServiceHost = authService.getStatus().getHost();
+        int authServicePort = authService.getStatus().getPort();
+        String authServiceRealm = authService.getSpec().getRealm() != null ? authService.getSpec().getRealm() : addressSpace.getAnnotation(AnnotationKeys.REALM_NAME);
+
+        if (authService.getSpec().getType().equals(AuthenticationServiceType.external) && authService.getSpec().getExternal() != null && authService.getSpec().getExternal().isAllowOverride()) {
+            if (addressSpace.getSpec().getAuthenticationService().getHost() != null) {
+                authServiceHost = addressSpace.getSpec().getAuthenticationService().getHost();
+            }
+            if (addressSpace.getSpec().getAuthenticationService().getPort() != null) {
+                authServicePort = addressSpace.getSpec().getAuthenticationService().getPort();
+            }
+            if (addressSpace.getSpec().getAuthenticationService().getRealm() != null) {
+                authServiceRealm = addressSpace.getSpec().getAuthenticationService().getRealm();
+            }
+        }
+
         Optional<ConsoleService> console = schemaProvider.getSchema().findConsoleService(WELL_KNOWN_CONSOLE_SERVICE_NAME);
         if (console.isEmpty()) {
             log.warn("No ConsoleService found named '{}', address space console service will be unavailable", WELL_KNOWN_CONSOLE_SERVICE_NAME);
@@ -73,8 +91,8 @@ public class TemplateInfraResourceFactory implements InfraResourceFactory {
         parameters.put(TemplateParameter.ADDRESS_SPACE, addressSpace.getMetadata().getName());
         parameters.put(TemplateParameter.INFRA_UUID, infraUuid);
         parameters.put(TemplateParameter.ADDRESS_SPACE_NAMESPACE, addressSpace.getMetadata().getNamespace());
-        parameters.put(TemplateParameter.AUTHENTICATION_SERVICE_HOST, authService.getStatus().getHost());
-        parameters.put(TemplateParameter.AUTHENTICATION_SERVICE_PORT, String.valueOf(authService.getStatus().getPort()));
+        parameters.put(TemplateParameter.AUTHENTICATION_SERVICE_HOST, authServiceHost);
+        parameters.put(TemplateParameter.AUTHENTICATION_SERVICE_PORT, String.valueOf(authServicePort));
         parameters.put(TemplateParameter.ADDRESS_SPACE_PLAN, addressSpace.getSpec().getPlan());
 
         String encodedCaCert = Optional.ofNullable(authService.getStatus().getCaCertSecret())
@@ -94,11 +112,7 @@ public class TemplateInfraResourceFactory implements InfraResourceFactory {
             parameters.put(TemplateParameter.AUTHENTICATION_SERVICE_CLIENT_SECRET, authService.getStatus().getClientCertSecret().getName());
         }
 
-        if (authService.getSpec().getRealm() != null) {
-            parameters.put(TemplateParameter.AUTHENTICATION_SERVICE_SASL_INIT_HOST, authService.getSpec().getRealm());
-        } else {
-            parameters.put(TemplateParameter.AUTHENTICATION_SERVICE_SASL_INIT_HOST, addressSpace.getAnnotation(AnnotationKeys.REALM_NAME));
-        }
+        parameters.put(TemplateParameter.AUTHENTICATION_SERVICE_SASL_INIT_HOST, authServiceRealm);
 
         Map<String, CertSpec> serviceCertMapping = new HashMap<>();
         for (EndpointSpec endpoint : addressSpace.getSpec().getEndpoints()) {
diff --git a/api-model/src/main/java/io/enmasse/address/model/AuthenticationService.java b/api-model/src/main/java/io/enmasse/address/model/AuthenticationService.java
index ca421f199..a51de0ef1 100644
--- a/api-model/src/main/java/io/enmasse/address/model/AuthenticationService.java
+++ b/api-model/src/main/java/io/enmasse/address/model/AuthenticationService.java
@@ -4,21 +4,15 @@
  */
 package io.enmasse.address.model;
 
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-
 import com.fasterxml.jackson.annotation.JsonInclude;
-
 import io.enmasse.admin.model.v1.AbstractWithAdditionalProperties;
-import io.enmasse.common.model.AbstractHasMetadata;
-import io.enmasse.model.validation.AuthenticationServiceDetails;
 import io.fabric8.kubernetes.api.model.Doneable;
 import io.sundr.builder.annotations.Buildable;
 import io.sundr.builder.annotations.BuildableReference;
 import io.sundr.builder.annotations.Inline;
 
+import java.util.Objects;
+
 /**
  * Represents an authentication service for an {@link AddressSpace}.
  */
@@ -34,12 +28,16 @@ import io.sundr.builder.annotations.Inline;
                 )
         )
 @JsonInclude(JsonInclude.Include.NON_NULL)
-@AuthenticationServiceDetails
 public class AuthenticationService extends AbstractWithAdditionalProperties {
 
     private String name;
+
+    private String host;
+    private Integer port;
+    private String realm;
+
+    // TODO: Keep for backwards compatibility
     private AuthenticationServiceType type;
-    private Map<String, Object> details = new HashMap<> ();
 
     public AuthenticationService() {
     }
@@ -52,14 +50,6 @@ public class AuthenticationService extends AbstractWithAdditionalProperties {
         return type;
     }
 
-    public void setDetails(Map<String, Object> details) {
-        this.details = details;
-    }
-
-    public Map<String, Object> getDetails() {
-        return Collections.unmodifiableMap(details);
-    }
-
     public String getName() {
         return name;
     }
@@ -68,6 +58,30 @@ public class AuthenticationService extends AbstractWithAdditionalProperties {
         this.name = name;
     }
 
+    public String getHost() {
+        return host;
+    }
+
+    public void setHost(String host) {
+        this.host = host;
+    }
+
+    public Integer getPort() {
+        return port;
+    }
+
+    public void setPort(Integer port) {
+        this.port = port;
+    }
+
+    public String getRealm() {
+        return realm;
+    }
+
+    public void setRealm(String realm) {
+        this.realm = realm;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
@@ -75,11 +89,13 @@ public class AuthenticationService extends AbstractWithAdditionalProperties {
         final AuthenticationService that = (AuthenticationService) o;
         return Objects.equals(name, that.name) &&
                 Objects.equals(type, that.type) &&
-                Objects.equals(details, that.details);
+                Objects.equals(host, that.host) &&
+                Objects.equals(port, that.port) &&
+                Objects.equals(realm, that.realm);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(name, type, details);
+        return Objects.hash(name, type, host, port, realm);
     }
 }
diff --git a/api-model/src/main/java/io/enmasse/address/model/AuthenticationServiceDetail.java b/api-model/src/main/java/io/enmasse/address/model/AuthenticationServiceDetail.java
deleted file mode 100644
index fe2c45783..000000000
--- a/api-model/src/main/java/io/enmasse/address/model/AuthenticationServiceDetail.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2017-2018, EnMasse authors.
- * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html).
- */
-package io.enmasse.address.model;
-
-public class AuthenticationServiceDetail {
-    private final String name;
-    private final Class<?> type;
-    private final boolean mandatory;
-
-    public AuthenticationServiceDetail(String name, Class<?> type, boolean mandatory) {
-        this.name = name;
-        this.type = type;
-        this.mandatory = mandatory;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public Class<?> getType() {
-        return type;
-    }
-
-    public boolean isMandatory() {
-        return mandatory;
-    }
-}
diff --git a/api-model/src/main/java/io/enmasse/address/model/AuthenticationServiceType.java b/api-model/src/main/java/io/enmasse/address/model/AuthenticationServiceType.java
index 71450642a..b80538ada 100644
--- a/api-model/src/main/java/io/enmasse/address/model/AuthenticationServiceType.java
+++ b/api-model/src/main/java/io/enmasse/address/model/AuthenticationServiceType.java
@@ -4,8 +4,6 @@
  */
 package io.enmasse.address.model;
 
-import java.util.*;
-
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonValue;
 
@@ -15,42 +13,17 @@ import com.fasterxml.jackson.annotation.JsonValue;
 public enum AuthenticationServiceType {
     NONE,
     STANDARD,
-    EXTERNAL(new AuthenticationServiceDetail("host", String.class, true),
-        new AuthenticationServiceDetail("port", Integer.class, true),
-            new AuthenticationServiceDetail("caCertSecretName", String.class, false),
-            new AuthenticationServiceDetail("clientCertSecretName", String.class, false),
-            new AuthenticationServiceDetail("saslInitHost", String.class, true));
-
-    private final Map<String, Class<?>> detailsFields = new HashMap<>();
-    private final Set<String> mandatoryFields = new HashSet<>();
+    EXTERNAL;
 
-    AuthenticationServiceType(AuthenticationServiceDetail ... details) {
-        for (AuthenticationServiceDetail detail : details) {
-            detailsFields.put(detail.getName(), detail.getType());
-
-            if (detail.isMandatory()) {
-                mandatoryFields.add(detail.getName());
-            }
-        }
+    @JsonValue
+    public String getName() {
+        return name().toLowerCase();
     }
 
     public io.enmasse.admin.model.v1.AuthenticationServiceType toAdminType() {
         return io.enmasse.admin.model.v1.AuthenticationServiceType.valueOf(name().toLowerCase());
     }
 
-    public Map<String, Class<?>> getDetailsFields() {
-        return Collections.unmodifiableMap(detailsFields);
-    }
-
-    public Set<String> getMandatoryFields() {
-        return Collections.unmodifiableSet(mandatoryFields);
-    }
-
-    @JsonValue
-    public String getName() {
-        return name().toLowerCase();
-    }
-
     @JsonCreator
     public static AuthenticationServiceType create(String name) {
         return valueOf(name.toUpperCase());
diff --git a/api-model/src/main/java/io/enmasse/admin/model/v1/AuthenticationServiceSpecExternal.java b/api-model/src/main/java/io/enmasse/admin/model/v1/AuthenticationServiceSpecExternal.java
index 8c8be5b7d..5c50c2fa5 100644
--- a/api-model/src/main/java/io/enmasse/admin/model/v1/AuthenticationServiceSpecExternal.java
+++ b/api-model/src/main/java/io/enmasse/admin/model/v1/AuthenticationServiceSpecExternal.java
@@ -11,6 +11,7 @@ import java.util.Objects;
 public class AuthenticationServiceSpecExternal extends AbstractWithAdditionalProperties {
     private String host;
     private int port;
+    private boolean allowOverride = false;
     private SecretReference caCertSecret;
     private SecretReference clientCertSecret;
 
@@ -53,13 +54,14 @@ public class AuthenticationServiceSpecExternal extends AbstractWithAdditionalPro
         AuthenticationServiceSpecExternal that = (AuthenticationServiceSpecExternal) o;
         return port == that.port &&
                 Objects.equals(host, that.host) &&
+                Objects.equals(allowOverride, that.allowOverride) &&
                 Objects.equals(caCertSecret, that.caCertSecret) &&
                 Objects.equals(clientCertSecret, that.clientCertSecret);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(host, port, caCertSecret, clientCertSecret);
+        return Objects.hash(host, port, allowOverride, caCertSecret, clientCertSecret);
     }
 
     @Override
@@ -67,8 +69,17 @@ public class AuthenticationServiceSpecExternal extends AbstractWithAdditionalPro
         return "AuthenticationServiceSpecExternal{" +
                 "host='" + host + '\'' +
                 ", port=" + port +
+                ", allowOverride=" + allowOverride +
                 ", caCertSecret=" + caCertSecret +
                 ", clientCertSecret=" + clientCertSecret +
                 '}';
     }
+
+    public boolean isAllowOverride() {
+        return allowOverride;
+    }
+
+    public void setAllowOverride(boolean allowOverride) {
+        this.allowOverride = allowOverride;
+    }
 }
diff --git a/api-model/src/main/java/io/enmasse/model/validation/AuthenticationServiceDetails.java b/api-model/src/main/java/io/enmasse/model/validation/AuthenticationServiceDetails.java
deleted file mode 100644
index 14c9cd57b..000000000
--- a/api-model/src/main/java/io/enmasse/model/validation/AuthenticationServiceDetails.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2018, EnMasse authors.
- * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html).
- */
-
-package io.enmasse.model.validation;
-
-import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
-import static java.lang.annotation.ElementType.TYPE;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-import javax.validation.Constraint;
-import javax.validation.Payload;
-
-@Target({TYPE, ANNOTATION_TYPE})
-@Retention(RUNTIME)
-@Constraint(validatedBy = {AuthenticationServiceDetailsValidator.class})
-@Documented
-public @interface AuthenticationServiceDetails {
-
-    String message() default "Invalid authentication service details";
-
-    Class<?>[] groups() default {};
-
-    Class<? extends Payload>[] payload() default {};
-
-}
diff --git a/api-model/src/main/java/io/enmasse/model/validation/AuthenticationServiceDetailsValidator.java b/api-model/src/main/java/io/enmasse/model/validation/AuthenticationServiceDetailsValidator.java
deleted file mode 100644
index bab64c88b..000000000
--- a/api-model/src/main/java/io/enmasse/model/validation/AuthenticationServiceDetailsValidator.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2018, EnMasse authors.
- * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html).
- */
-
-package io.enmasse.model.validation;
-
-import java.util.Collections;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
-
-import javax.validation.ConstraintValidator;
-import javax.validation.ConstraintValidatorContext;
-
-import io.enmasse.address.model.AuthenticationService;
-import io.enmasse.address.model.AuthenticationServiceType;
-
-public class AuthenticationServiceDetailsValidator implements ConstraintValidator<AuthenticationServiceDetails, AuthenticationService> {
-
-    @Override
-    public boolean isValid(final AuthenticationService service, final ConstraintValidatorContext context) {
-
-        if (service == null) {
-            // nothing to validate
-            return true;
-        }
-
-        if (service.getType() == null) {
-            // nothing to validate
-            return true;
-        }
-
-        Map<String, Object> details = service.getDetails();
-        if (details == null) {
-            // null map means: no values -> empty map
-            // which may be fine for some services
-            details = Collections.emptyMap();
-        }
-
-        final Set<String> missing = findMissing(service.getType(), service.getDetails());
-        final Set<String> unknown = findUnknown(service.getType(), service.getDetails());
-
-        handleFields(context, "Missing", missing);
-        handleFields(context, "Unknown", unknown);
-
-        return missing.isEmpty() && unknown.isEmpty();
-
-    }
-
-    private void handleFields(final ConstraintValidatorContext context, final String kind, final Set<String> fields) {
-        if (fields.isEmpty()) {
-            return;
-        }
-
-        context.disableDefaultConstraintViolation();
-
-        for (final String field : fields) {
-            context
-                    .buildConstraintViolationWithTemplate(kind + " field: " + field)
-                    .addConstraintViolation();
-        }
-    }
-
-    private Set<String> findUnknown(final AuthenticationServiceType type, final Map<String, Object> details) {
-        // we are using a TreeSet here to get a stable field order
-        final Set<String> fields = new TreeSet<>(details.keySet());
-        fields.removeAll(type.getDetailsFields().keySet());
-        return fields;
-    }
-
-    private Set<String> findMissing(final AuthenticationServiceType type, final Map<String, Object> details) {
-        // we are using a TreeSet here to get a stable field order
-        final Set<String> fields = new TreeSet<>(type.getMandatoryFields());
-        fields.removeAll(details.keySet());
-        return fields;
-    }
-}
-
diff --git a/console/console-init/pom.xml b/console/console-init/pom.xml
index ece85d29c..7ba211f30 100644
--- a/console/console-init/pom.xml
+++ b/console/console-init/pom.xml
@@ -67,6 +67,7 @@
                 <REACT_APP_VERSION>${project.version}</REACT_APP_VERSION>
                 <REACT_APP_NAME>EnMasse Console</REACT_APP_NAME>
                 <REACT_APP_REFRESH_RATE_MILLIS>30000</REACT_APP_REFRESH_RATE_MILLIS>
+                <REACT_APP_DOCS>${application.docs}</REACT_APP_DOCS>
               </environmentVariables>
               <arguments>run-script build</arguments>
             </configuration>
diff --git a/console/console-init/ui/src/components/About/About.js b/console/console-init/ui/src/components/About/About.js
index 8e47c46e7..4f669ba86 100644
--- a/console/console-init/ui/src/components/About/About.js
+++ b/console/console-init/ui/src/components/About/About.js
@@ -1,6 +1,7 @@
 import React, {Component} from 'react';
 import {AboutModal, TextContent, TextList, TextListItem} from '@patternfly/react-core';
 import EmptyImg from '../../assets/images/Empty.svg';
+import Aux from '../../hoc/Aux/Aux';
 
 class About extends Component {
 
@@ -16,6 +17,15 @@ class About extends Component {
       productVersion = '';
       console.log('process.env.REACT_APP_VERSION is not set');
     }
+    let docs = process.env.REACT_APP_DOCS;
+    let docsElement;
+    if (docs) {
+      docsElement=
+        <Aux>
+          <TextListItem component="dt">Documentation</TextListItem>
+          <TextListItem component="dd"><a href={process.env.REACT_APP_DOCS}>{process.env.REACT_APP_DOCS}</a></TextListItem>
+        </Aux>;
+    }
 
     return (
       <AboutModal
@@ -29,6 +39,7 @@ class About extends Component {
           <TextList component="dl">
             <TextListItem component="dt">EnMasse Version</TextListItem>
             <TextListItem component="dd">{productVersion}</TextListItem>
+            {(docsElement)}
           </TextList>
         </TextContent>
       </AboutModal>
diff --git a/console/console-init/ui/webpack.common.js b/console/console-init/ui/webpack.common.js
index 245b910ca..0a6255eea 100644
--- a/console/console-init/ui/webpack.common.js
+++ b/console/console-init/ui/webpack.common.js
@@ -15,7 +15,8 @@ module.exports = {
       {
       'process.env.REACT_APP_VERSION': JSON.stringify(process.env.REACT_APP_VERSION),
       'process.env.REACT_APP_NAME': JSON.stringify(process.env.REACT_APP_NAME),
-      'process.env.REACT_APP_REFRESH_RATE_MILLIS': JSON.stringify(process.env.REACT_APP_REFRESH_RATE_MILLIS)
+      'process.env.REACT_APP_REFRESH_RATE_MILLIS': JSON.stringify(process.env.REACT_APP_REFRESH_RATE_MILLIS),
+      'process.env.REACT_APP_DOCS': JSON.stringify(process.env.REACT_APP_DOCS)
     })
   ],
   module: {
diff --git a/metrics-api/src/main/java/io/enmasse/metrics/api/Metric.java b/metrics-api/src/main/java/io/enmasse/metrics/api/Metric.java
index f47236375..9f7cdf549 100644
--- a/metrics-api/src/main/java/io/enmasse/metrics/api/Metric.java
+++ b/metrics-api/src/main/java/io/enmasse/metrics/api/Metric.java
@@ -49,7 +49,7 @@ public class Metric {
         this.description = updated.getDescription();
         this.type = updated.getType();
 
-        if (MetricType.up.equals(type) || MetricType.gauge.equals(type)) {
+        if (MetricType.up.equals(type) || MetricType.gauge.equals(type) || MetricType.counter.equals(type)) {
             values.putAll(updated.values);
         }
     }
diff --git a/metrics-api/src/main/java/io/enmasse/metrics/api/MetricType.java b/metrics-api/src/main/java/io/enmasse/metrics/api/MetricType.java
index 6b8ad4bcf..f5df9634d 100644
--- a/metrics-api/src/main/java/io/enmasse/metrics/api/MetricType.java
+++ b/metrics-api/src/main/java/io/enmasse/metrics/api/MetricType.java
@@ -6,5 +6,6 @@ package io.enmasse.metrics.api;
 
 public enum MetricType {
     up,
-    gauge
+    gauge,
+    counter
 }
diff --git a/pkg/apis/admin/v1beta1/types.go b/pkg/apis/admin/v1beta1/types.go
index 9453b5431..4b52fd18f 100644
--- a/pkg/apis/admin/v1beta1/types.go
+++ b/pkg/apis/admin/v1beta1/types.go
@@ -96,6 +96,7 @@ type AuthenticationServiceSpecExternal struct {
 	Port             int                     `json:"port"`
 	CaCertSecret     *corev1.SecretReference `json:"caCertSecret,omitempty"`
 	ClientCertSecret *corev1.SecretReference `json:"clientCertSecret,omitempty"`
+	AllowOverride    bool                    `json:"allowOverride,omitempty"`
 }
 
 type AuthenticationServiceStatus struct {
@@ -122,7 +123,7 @@ type AuthenticationServiceList struct {
 type ConsoleService struct {
 	metav1.TypeMeta   `json:",inline"`
 	metav1.ObjectMeta `json:"metadata,omitempty"`
-	Spec              ConsoleServiceSpec `json:"spec"`
+	Spec              ConsoleServiceSpec   `json:"spec"`
 	Status            ConsoleServiceStatus `json:"status"`
 }
 
@@ -137,9 +138,9 @@ type ConsoleServiceSpec struct {
 }
 
 type ConsoleServiceStatus struct {
-	Host             string                  `json:"host,omitempty"`
-	Port             int                     `json:"port,omitempty"`
-	CaCertSecret     *corev1.SecretReference `json:"caCertSecret,omitempty"`
+	Host         string                  `json:"host,omitempty"`
+	Port         int                     `json:"port,omitempty"`
+	CaCertSecret *corev1.SecretReference `json:"caCertSecret,omitempty"`
 }
 
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
@@ -150,5 +151,3 @@ type ConsoleServiceList struct {
 
 	Items []ConsoleService `json:"items"`
 }
-
-
diff --git a/pom.properties b/pom.properties
index 688c36927..c70d66d64 100644
--- a/pom.properties
+++ b/pom.properties
@@ -4,3 +4,4 @@ application.bundle.prefix=enmasse
 license.advice.text=EnMasse comprises the following components:
 release.version=0.28-SNAPSHOT
 maven.version=0.28-SNAPSHOT
+application.docs=https://enmasse.io/documentation/
diff --git a/pom.xml b/pom.xml
index 7297fc406..4917beb62 100644
--- a/pom.xml
+++ b/pom.xml
@@ -979,10 +979,6 @@
                                             <name>applicationDisplayName</name>
                                             <value>${application.display.name}</value>
                                         </parameter>
-					<parameter>
-					    <name>applicationGlobalConsoleDisplayName</name>
-					    <value>${application.globalconsole.display.name}</value>
-					</parameter>
                                         <parameter>
                                             <name>licenseAdviceText</name>
                                             <value>${license.advice.text}</value>
@@ -997,7 +993,6 @@
                         </configuration>
                     </plugin>
                 </plugins>
-
             </build>
         </profile>
         <profile>
diff --git a/service-broker/src/main/java/io/enmasse/osb/api/OSBServiceBase.java b/service-broker/src/main/java/io/enmasse/osb/api/OSBServiceBase.java
index 20a34017e..58d8fcd9f 100644
--- a/service-broker/src/main/java/io/enmasse/osb/api/OSBServiceBase.java
+++ b/service-broker/src/main/java/io/enmasse/osb/api/OSBServiceBase.java
@@ -67,7 +67,6 @@ public abstract class OSBServiceBase {
     protected AddressSpace createAddressSpace(String instanceId, String name, String namespace, String type, String plan, String userId, String userName) throws Exception {
         AuthenticationService authService = new AuthenticationServiceBuilder()
                 .withType(AuthenticationServiceType.STANDARD)
-                .withDetails(Collections.emptyMap())
                 .build();
         AddressSpace addressSpace = new AddressSpaceBuilder()
                 .withNewMetadata()
diff --git a/standard-controller/src/main/java/io/enmasse/controller/standard/AddressController.java b/standard-controller/src/main/java/io/enmasse/controller/standard/AddressController.java
index 3c3d46c5f..3d6d143a8 100644
--- a/standard-controller/src/main/java/io/enmasse/controller/standard/AddressController.java
+++ b/standard-controller/src/main/java/io/enmasse/controller/standard/AddressController.java
@@ -47,6 +47,7 @@ public class AddressController implements Watcher<Address> {
     private final Metrics metrics;
     private final BrokerIdGenerator brokerIdGenerator;
     private final BrokerClientFactory brokerClientFactory;
+    private static int routerCheckFailures;
 
     public AddressController(StandardControllerOptions options, AddressApi addressApi, Kubernetes kubernetes, BrokerSetGenerator clusterGenerator, EventLogger eventLogger, SchemaProvider schemaProvider, Vertx vertx, Metrics metrics, BrokerIdGenerator brokerIdGenerator, BrokerClientFactory brokerClientFactory) {
         this.options = options;
@@ -59,6 +60,7 @@ public class AddressController implements Watcher<Address> {
         this.metrics = metrics;
         this.brokerIdGenerator = brokerIdGenerator;
         this.brokerClientFactory = brokerClientFactory;
+        this.routerCheckFailures = 0;
     }
 
     public void start() throws Exception {
@@ -135,7 +137,12 @@ public class AddressController implements Watcher<Address> {
         long provisionResources = System.nanoTime();
 
         Set<Address> liveAddresses = filterByPhases(addressSet, EnumSet.of(Configuring, Active));
-        checkStatuses(liveAddresses, addressResolver);
+        List<RouterStatus> routerStatusList = checkRouterStatuses();
+
+        if (!routerStatusList.isEmpty()) {
+            checkAddressStatuses(liveAddresses, addressResolver, routerStatusList);
+        }
+
         long checkStatuses = System.nanoTime();
         for (Address address : liveAddresses) {
             if (address.getStatus().isReady()) {
@@ -179,16 +186,16 @@ public class AddressController implements Watcher<Address> {
         }
 
         long replaceAddresses = System.nanoTime();
-        garbageCollectTerminating(filterByPhases(addressSet, EnumSet.of(Terminating)), addressResolver);
+        garbageCollectTerminating(filterByPhases(addressSet, EnumSet.of(Terminating)), addressResolver, routerStatusList);
         long gcTerminating = System.nanoTime();
 
         log.info("total: {} ns, resolvedPlan: {} ns, calculatedUsage: {} ns, checkedQuota: {} ns, listClusters: {} ns, provisionResources: {} ns, checkStatuses: {} ns, deprovisionUnused: {} ns, upgradeClusters: {} ns, replaceAddresses: {} ns, gcTerminating: {} ns", gcTerminating - start, resolvedPlan - start, calculatedUsage - resolvedPlan,  checkedQuota  - calculatedUsage, listClusters - checkedQuota, provisionResources - listClusters, checkStatuses - provisionResources, deprovisionUnused - checkStatuses, upgradeClusters - deprovisionUnused, replaceAddresses - upgradeClusters, gcTerminating - replaceAddresses);
 
-        int ready = 0;
+        float ready = 0;
         for (Address address : addressList) {
             ready += address.getStatus().isReady() ? 1 : 0;
         }
-        int notReady = addressList.size() - ready;
+        float notReady = addressList.size() - ready;
 
         long now = System.currentTimeMillis();
 
@@ -196,6 +203,11 @@ public class AddressController implements Watcher<Address> {
         metrics.reportMetric(new Metric("version", "The version of the standard-controller", MetricType.gauge, new MetricValue(0, now, new MetricLabel("name", componentName), new MetricLabel("version", options.getVersion()))));
 
         MetricLabel [] metricLabels = new MetricLabel[]{new MetricLabel("addressspace", options.getAddressSpace()), new MetricLabel("namespace", options.getAddressSpaceNamespace())};
+        if (routerStatusList.isEmpty()) {
+            ready = Float.NaN;
+            notReady = Float.NaN;
+
+        }
         metrics.reportMetric(new Metric("addresses_ready_total", "Total number of addresses in ready state", MetricType.gauge, new MetricValue(ready, now, metricLabels)));
         metrics.reportMetric(new Metric("addresses_not_ready_total", "Total number of address in a not ready state", MetricType.gauge, new MetricValue(notReady, now, metricLabels)));
         metrics.reportMetric(new Metric("addresses_total", "Total number of addresses", MetricType.gauge, new MetricValue(addressList.size(), now, metricLabels)));
@@ -208,6 +220,9 @@ public class AddressController implements Watcher<Address> {
 
         long totalTime = gcTerminating - start;
         metrics.reportMetric(new Metric("standard_controller_loop_duration_seconds", "Time spent in controller loop", MetricType.gauge, new MetricValue((double) totalTime / 1_000_000_000.0, now, metricLabels)));
+
+        metrics.reportMetric(new Metric("standard_controller_router_check_failures_total", "Number of RouterCheckFailures", MetricType.counter, new MetricValue(routerCheckFailures, now, metricLabels)));
+
     }
 
     private void upgradeClusters(StandardInfraConfig desiredConfig, AddressResolver addressResolver, List<BrokerCluster> clusterList, Set<Address> addresses) throws Exception {
@@ -315,8 +330,8 @@ public class AddressController implements Watcher<Address> {
                 .collect(Collectors.toSet());
     }
 
-    private void garbageCollectTerminating(Set<Address> addresses, AddressResolver addressResolver) throws Exception {
-        Map<Address, Integer> okMap = checkStatuses(addresses, addressResolver);
+    private void garbageCollectTerminating(Set<Address> addresses, AddressResolver addressResolver, List<RouterStatus> routerStatusList) throws Exception {
+        Map<Address, Integer> okMap = checkAddressStatuses(addresses, addressResolver, routerStatusList);
         for (Map.Entry<Address, Integer> entry : okMap.entrySet()) {
             if (entry.getValue() == 0) {
                 log.info("Garbage collecting {}", entry.getKey());
@@ -374,11 +389,8 @@ public class AddressController implements Watcher<Address> {
         }
     }
 
-    private Map<Address, Integer> checkStatuses(Set<Address> addresses, AddressResolver addressResolver) throws Exception {
-        Map<Address, Integer> numOk = new HashMap<>();
-        if (addresses.isEmpty()) {
-            return numOk;
-        }
+    private List<RouterStatus> checkRouterStatuses() throws Exception {
+
         RouterStatusCollector routerStatusCollector = new RouterStatusCollector(vertx, options.getCertDir());
         List<RouterStatus> routerStatusList = new ArrayList<>();
         for (Pod router : kubernetes.listRouters()) {
@@ -391,10 +403,19 @@ public class AddressController implements Watcher<Address> {
                 } catch (Exception e) {
                     log.info("Error requesting router status from {}. Ignoring", router.getMetadata().getName(), e);
                     eventLogger.log(RouterCheckFailed, e.getMessage(), Warning, AddressSpace, options.getAddressSpace());
+                    routerCheckFailures++;
                 }
             }
         }
+        return routerStatusList;
+    }
+
+    private Map<Address, Integer> checkAddressStatuses(Set<Address> addresses, AddressResolver addressResolver, List<RouterStatus> routerStatusList) throws Exception {
 
+        Map<Address, Integer> numOk = new HashMap<>();
+        if (addresses.isEmpty()) {
+            return numOk;
+        }
         Map<String, Integer> clusterOk = new HashMap<>();
         for (Address address : addresses) {
             AddressType addressType = addressResolver.getType(address);
diff --git a/templates/crds/authenticationservices.crd.yaml b/templates/crds/authenticationservices.crd.yaml
index 08809f169..bbad6b3a9 100644
--- a/templates/crds/authenticationservices.crd.yaml
+++ b/templates/crds/authenticationservices.crd.yaml
@@ -161,6 +161,8 @@ spec:
               - host
               - port
               properties:
+                allowOverride:
+                  type: boolean
                 host:
                   type: string
                 port:


Test patch:
diff --git a/api-model/src/test/java/io/enmasse/address/model/AuthenticationServiceTest.java b/api-model/src/test/java/io/enmasse/address/model/AuthenticationServiceTest.java
deleted file mode 100644
index 55ee58685..000000000
--- a/api-model/src/test/java/io/enmasse/address/model/AuthenticationServiceTest.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright 2018, EnMasse authors.
- * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html).
- */
-
-package io.enmasse.address.model;
-
-import org.junit.jupiter.api.Test;
-
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-
-/**
- * Unit tests for {@link AuthenticationService}
- */
-public class AuthenticationServiceTest {
-
-    /**
-     * Test if two empty services are equal
-     */
-    @Test
-    public void testEqualsEmpty() {
-        assertEquality(true,
-                new AuthenticationServiceBuilder().build(),
-                new AuthenticationServiceBuilder().build());
-    }
-
-    /**
-     * Test if two services are equal having the same type
-     */
-    @Test
-    public void testEqualsSameType() {
-        assertEquality(true,
-                new AuthenticationServiceBuilder()
-                        .withType(AuthenticationServiceType.EXTERNAL)
-                        .build(),
-                new AuthenticationServiceBuilder()
-                        .withType(AuthenticationServiceType.EXTERNAL)
-                        .build());
-    }
-
-    /**
-     * Test if two services are not equal having the different types
-     */
-    @Test
-    public void testNonEqualsDifferentType() {
-        assertEquality(false,
-                new AuthenticationServiceBuilder()
-                        .withType(AuthenticationServiceType.NONE)
-                        .build(),
-                new AuthenticationServiceBuilder()
-                        .withType(AuthenticationServiceType.EXTERNAL)
-                        .build());
-    }
-
-    /**
-     * Test if two services are equal having the same type and the same details
-     */
-    @Test
-    public void testEqualsSameTypeSameDetails() {
-        final Map<String, Object> details1 = new HashMap<>();
-        final Map<String, Object> details2 = new HashMap<>();
-
-        for (final Map<String, Object> map : Arrays.asList(details1, details2)) {
-            map.put("string", "foo");
-            map.put("int", 1);
-            map.put("bool", true);
-        }
-
-        assertEquality(true,
-                new AuthenticationServiceBuilder()
-                        .withType(AuthenticationServiceType.EXTERNAL)
-                        .withDetails(details1)
-                        .build(),
-                new AuthenticationServiceBuilder()
-                        .withType(AuthenticationServiceType.EXTERNAL)
-                        .withDetails(details2)
-                        .build());
-    }
-
-    /**
-     * Test for equality
-     * 
-     * @param isEqual are values to be expected equal?
-     * @param v1 value 1
-     * @param v2 value 2
-     */
-    protected void assertEquality(final boolean isEqual, final Object v1, final Object v2) {
-        assertEquals(isEqual, v1.equals(v2));
-        assertEquals(isEqual, v2.equals(v1));
-        assertEquals(isEqual, v1.hashCode() == v2.hashCode());
-    }
-}
diff --git a/api-model/src/test/java/io/enmasse/address/model/v1/address/SerializationTest.java b/api-model/src/test/java/io/enmasse/address/model/v1/address/SerializationTest.java
index 5fcc93881..a76640372 100644
--- a/api-model/src/test/java/io/enmasse/address/model/v1/address/SerializationTest.java
+++ b/api-model/src/test/java/io/enmasse/address/model/v1/address/SerializationTest.java
@@ -192,12 +192,7 @@ public class SerializationTest {
                 .endEndpoint()
 
                 .withNewAuthenticationService()
-                    .withType(AuthenticationServiceType.EXTERNAL)
-                    .addToDetails("host", "my.example.com")
-                    .addToDetails("port", 5671)
-                    .addToDetails("caCertSecretName", "authservicesecret")
-                    .addToDetails("clientCertSecretName", "clientcertsecret")
-                    .addToDetails("saslInitHost", "my.example.com")
+                    .withName("myservice")
                 .endAuthenticationService()
 
                 .endSpec()
@@ -242,8 +237,7 @@ public class SerializationTest {
         assertThat(deserialized.getSpec().getEndpoints().get(0).getService(), is(addressSpace.getSpec().getEndpoints().get(0).getService()));
         assertThat(deserialized.getSpec().getEndpoints().get(0).getCert().getProvider(), is(addressSpace.getSpec().getEndpoints().get(0).getCert().getProvider()));
         assertThat(deserialized.getSpec().getEndpoints().get(0).getCert().getSecretName(), is(addressSpace.getSpec().getEndpoints().get(0).getCert().getSecretName()));
-        assertThat(deserialized.getSpec().getAuthenticationService().getType(), is(addressSpace.getSpec().getAuthenticationService().getType()));
-        assertThat(deserialized.getSpec().getAuthenticationService().getDetails(), is(addressSpace.getSpec().getAuthenticationService().getDetails()));
+        assertThat(deserialized.getSpec().getAuthenticationService().getName(), is(addressSpace.getSpec().getAuthenticationService().getName()));
         assertThat(addressSpace, is(deserialized));
     }
 
@@ -613,14 +607,20 @@ public class SerializationTest {
                 "  \"type\":\"standard\"," +
                 "  \"plan\":\"myplan\"," +
                 "  \"authenticationService\": {" +
-                "     \"type\": \"external\"" +
+                "     \"name\": \"external\"," +
+                "     \"host\": \"override.example.com\"," +
+                "     \"port\": 1234," +
+                "     \"realm\": \"override\"" +
                 "  }" +
                 "}" +
                 "}";
 
         ObjectMapper mapper = new ObjectMapper();
-        ConstraintViolationException exception = assertThrows(ConstraintViolationException.class, () -> validate(mapper.readValue(json, AddressSpace.class)));
-        assertEquals(3, exception.getConstraintViolations().size());
+        AddressSpace addressSpace = mapper.readValue(json, AddressSpace.class);
+        assertThat(addressSpace.getSpec().getAuthenticationService().getName(), is("external"));
+        assertThat(addressSpace.getSpec().getAuthenticationService().getHost(), is("override.example.com"));
+        assertThat(addressSpace.getSpec().getAuthenticationService().getPort(), is(1234));
+        assertThat(addressSpace.getSpec().getAuthenticationService().getRealm(), is("override"));
     }
 
     @Test
@@ -635,12 +635,7 @@ public class SerializationTest {
                 "  \"type\":\"standard\"," +
                 "  \"plan\":\"myplan\"," +
                 "  \"authenticationService\": {" +
-                "     \"type\": \"external\"," +
-                "     \"details\": {" +
-                "       \"host\": \"my.example.com\"," +
-                "       \"saslInitHost\": \"localhost\"," +
-                "       \"port\": 1234" +
-                "     }" +
+                "     \"name\": \"myservice\"" +
                 "  }" +
                 "}" +
                 "}";
@@ -648,34 +643,6 @@ public class SerializationTest {
         ObjectMapper mapper = new ObjectMapper();
         AddressSpace addressSpace= mapper.readValue(json, AddressSpace.class);
         validate(addressSpace);
-        assertEquals("my.example.com", addressSpace.getSpec().getAuthenticationService().getDetails().get("host"));
-        assertEquals("localhost", addressSpace.getSpec().getAuthenticationService().getDetails().get("saslInitHost"));
-        assertEquals(1234, addressSpace.getSpec().getAuthenticationService().getDetails().get("port"));
-    }
-
-    @Test
-    public void testDeserializeAddressSpaceWithExtraAuthServiceValues() throws IOException {
-        String json = "{" +
-                "\"apiVersion\":\"enmasse.i/v1beta1\"," +
-                "\"kind\":\"AddressSpace\"," +
-                "\"metadata\":{" +
-                "  \"name\":\"myspace\"" +
-                "}," +
-                "\"spec\": {" +
-                "  \"type\":\"standard\"," +
-                "  \"plan\":\"myplan\"," +
-                "  \"authenticationService\": {" +
-                "     \"type\": \"standard\"," +
-                "     \"details\": {" +
-                "       \"host\": \"my.example.com\"" +
-                "     }" +
-                "  }" +
-                "}" +
-                "}";
-
-        ObjectMapper mapper = new ObjectMapper();
-        ConstraintViolationException exception = assertThrows(ConstraintViolationException.class, () -> validate(mapper.readValue(json, AddressSpace.class)));
-        assertEquals(1, exception.getConstraintViolations().size());
     }
 
     @Test
diff --git a/systemtests/src/test/java/io/enmasse/systemtest/common/api/UserApiTest.java b/systemtests/src/test/java/io/enmasse/systemtest/common/api/UserApiTest.java
index 73d9d4ff0..b8f0ca556 100644
--- a/systemtests/src/test/java/io/enmasse/systemtest/common/api/UserApiTest.java
+++ b/systemtests/src/test/java/io/enmasse/systemtest/common/api/UserApiTest.java
@@ -13,12 +13,8 @@ import io.enmasse.systemtest.amqp.UnauthorizedAccessException;
 import io.enmasse.systemtest.bases.TestBase;
 import io.enmasse.systemtest.cmdclients.KubeCMDClient;
 import io.enmasse.systemtest.executor.ExecutionResultData;
-import io.enmasse.systemtest.selenium.SeleniumManagement;
-import io.enmasse.systemtest.selenium.SeleniumProvider;
-import io.enmasse.systemtest.selenium.page.ConsoleWebPage;
 import io.enmasse.systemtest.utils.AddressSpaceUtils;
 import io.enmasse.systemtest.utils.AddressUtils;
-import io.enmasse.systemtest.utils.TestUtils;
 import io.enmasse.systemtest.utils.UserUtils;
 import io.enmasse.user.model.v1.DoneableUser;
 import io.enmasse.user.model.v1.Operation;
@@ -30,6 +26,8 @@ import org.slf4j.Logger;
 
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 
@@ -45,6 +43,7 @@ class UserApiTest extends TestBase {
     private static Logger log = CustomLogger.getLogger();
     private AddressSpace brokered = AddressSpaceUtils.createAddressSpaceObject("user-api-address-space-brokered", AddressSpaceType.BROKERED, AuthenticationServiceType.STANDARD);
     private AddressSpace standard = AddressSpaceUtils.createAddressSpaceObject("user-api-address-space-standard", AddressSpaceType.STANDARD, AddressSpacePlans.STANDARD_SMALL, AuthenticationServiceType.STANDARD);
+    private Map<AddressSpace, User> users = new HashMap<>();
 
     @BeforeEach
     void deployAddressSpaces() throws Exception {
@@ -54,6 +53,18 @@ class UserApiTest extends TestBase {
         if (!AddressSpaceUtils.existAddressSpace(addressApiClient, standard.getMetadata().getName())) {
             createAddressSpace(standard);
         }
+        users.clear();
+    }
+
+    @AfterEach
+    void cleanUsers() {
+        users.forEach((addressSpace, user) -> {
+            try {
+                removeUser(addressSpace, user.getSpec().getUsername());
+            } catch (Exception e) {
+                log.info("Clean: User not exists {}", user.getSpec().getUsername());
+            }
+        });
     }
 
     @BeforeAll
@@ -77,6 +88,7 @@ class UserApiTest extends TestBase {
                         .withOperations(Operation.send, Operation.recv).build()));
 
         JsonObject userDefinitionPayload = UserUtils.userToJson(brokered.getMetadata().getName(), testUser);
+        users.put(brokered, testUser);
 
         //create user
         assertThat(KubeCMDClient.createCR(kubernetes.getNamespace(), userDefinitionPayload.toString()).getRetCode(), is(true));
@@ -100,6 +112,7 @@ class UserApiTest extends TestBase {
 
         JsonObject userDefinitionPayload = UserUtils.userToJson(brokered.getMetadata().getName(), testUser);
         userDefinitionPayload.getJsonObject("spec").getJsonArray("authorization").getJsonObject(0).getJsonArray("operations").add("unknown");
+        users.put(brokered, testUser);
 
         //create user
         ExecutionResultData createUserResponse = KubeCMDClient.createCR(kubernetes.getNamespace(), userDefinitionPayload.toString());
@@ -131,6 +144,7 @@ class UserApiTest extends TestBase {
 
         JsonObject userDefinitionPayload = UserUtils.userToJson(brokered.getMetadata().getName(), testUser);
         userDefinitionPayload.getJsonObject("spec").getJsonArray("authorization").getJsonObject(0).getJsonArray("operations").add("posilani");
+        users.put(brokered, testUser);
 
         Throwable exception = assertThrows(ExecutionException.class, () -> getUserApiClient().createUser(userDefinitionPayload, HTTP_INTERNAL_ERROR));
         assertTrue(exception.getMessage().contains("value not one of declared Enum instance names: [send, view, recv, manage]"));
@@ -154,6 +168,7 @@ class UserApiTest extends TestBase {
                         .withOperations(Operation.send).build()));
 
         JsonObject userDefinitionPayload = UserUtils.userToJson(brokered.getMetadata().getName(), testUser);
+        users.put(brokered, testUser);
 
         //create user
         assertThat(KubeCMDClient.createCR(kubernetes.getNamespace(), userDefinitionPayload.toString()).getRetCode(), is(true));
@@ -193,6 +208,7 @@ class UserApiTest extends TestBase {
                         .withAddresses(queue.getSpec().getAddress())
                         .withOperations(Operation.send).build()));
 
+        users.put(brokered, testUser);
         createUser(standard, testUser);
 
         AmqpClient client = amqpClientFactory.createQueueClient(standard);
@@ -219,6 +235,7 @@ class UserApiTest extends TestBase {
                         .withAddresses("*")
                         .withOperations(Operation.send).build()));
 
+        users.put(brokered, testUser);
         createUser(brokered, testUser);
 
         testUser = new DoneableUser(testUser).editSpec().withUsername("").endSpec().done();
@@ -255,6 +272,8 @@ class UserApiTest extends TestBase {
                         .withAddresses("*")
                         .withOperations(Operation.send, Operation.recv).build()));
 
+        users.put(brokered, testUser);
+        users.put(brokered, testUser2);
         createUser(brokered, testUser);
         createUser(brokered, testUser2);
 
@@ -281,6 +300,7 @@ class UserApiTest extends TestBase {
                 new UserAuthorizationBuilder()
                         .withAddresses("*")
                         .withOperations(Operation.send).build()));
+        users.put(brokered, testUser);
         getUserApiClient().createUser(UserUtils.userToJson(brokered.getMetadata().getName(), "userpepinator", testUser), HTTP_CREATED);
 
         //first char of username must be a-z0-9 (other symbols respond with HTTP:400)
@@ -298,6 +318,7 @@ class UserApiTest extends TestBase {
                 new UserAuthorizationBuilder()
                         .withAddresses("*")
                         .withOperations(Operation.send).build()));
+        users.put(brokered, testUser3);
         getUserApiClient().createUser(UserUtils.userToJson(brokered.getMetadata().getName(), "userpepinator", testUser3), HTTP_CREATED);
 
         //underscore is also allowed in user name (response HTTP:201)
@@ -306,7 +327,7 @@ class UserApiTest extends TestBase {
                 new UserAuthorizationBuilder()
                         .withAddresses("*")
                         .withOperations(Operation.send).build()));
-
+        users.put(brokered, testUser4);
         getUserApiClient().createUser(UserUtils.userToJson(brokered.getMetadata().getName(), "userpepinator", testUser4), HTTP_CREATED);
 
         //last char must also be a-z (other symbols respond with HTTP:400)
@@ -324,7 +345,7 @@ class UserApiTest extends TestBase {
                 new UserAuthorizationBuilder()
                         .withAddresses("*")
                         .withOperations(Operation.send).build()));
-
+        users.put(brokered, testUser6);
         getUserApiClient().createUser(UserUtils.userToJson(brokered.getMetadata().getName(), "userpepinator", testUser6), HTTP_CREATED);
 
         //Foreign symbols may not be used in username (response HTTP:400)
@@ -342,21 +363,23 @@ class UserApiTest extends TestBase {
         Address queue = AddressUtils.createQueueAddressObject("test-queue", DestinationPlan.STANDARD_SMALL_QUEUE);
         setAddresses(standard, queue);
         UserCredentials serviceAccount = new UserCredentials("test-service-account", "");
-        createUserServiceAccount(standard, serviceAccount, environment.namespace());
-        UserCredentials messagingUser = new UserCredentials("@@serviceaccount@@",
-                kubernetes.getServiceaccountToken(serviceAccount.getUsername(), environment.namespace()));
-        log.info("username: {}, password: {}", messagingUser.getUsername(), messagingUser.getPassword());
-
-        assertCanConnect(standard, messagingUser, Collections.singletonList(queue));
-
-        //delete user
-        assertThat("User deleting failed using oc cmd",
-                KubeCMDClient.deleteUser(kubernetes.getNamespace(), standard.getMetadata().getName(), serviceAccount.getUsername()).getRetCode(), is(true));
-        assertThat("User is still present",
-                KubeCMDClient.getUser(kubernetes.getNamespace(), standard.getMetadata().getName(), serviceAccount.getUsername()).getRetCode(), is(false));
-
-        assertCannotConnect(standard, messagingUser, Collections.singletonList(queue));
-
-        kubernetes.deleteServiceAccount("test-service-account", environment.namespace());
+        try {
+            createUserServiceAccount(standard, serviceAccount, environment.namespace());
+            UserCredentials messagingUser = new UserCredentials("@@serviceaccount@@",
+                    kubernetes.getServiceaccountToken(serviceAccount.getUsername(), environment.namespace()));
+            log.info("username: {}, password: {}", messagingUser.getUsername(), messagingUser.getPassword());
+
+            assertCanConnect(standard, messagingUser, Collections.singletonList(queue));
+
+            //delete user
+            assertThat("User deleting failed using oc cmd",
+                    KubeCMDClient.deleteUser(kubernetes.getNamespace(), standard.getMetadata().getName(), serviceAccount.getUsername()).getRetCode(), is(true));
+            assertThat("User is still present",
+                    KubeCMDClient.getUser(kubernetes.getNamespace(), standard.getMetadata().getName(), serviceAccount.getUsername()).getRetCode(), is(false));
+
+            assertCannotConnect(standard, messagingUser, Collections.singletonList(queue));
+        } finally {
+            kubernetes.deleteServiceAccount("test-service-account", environment.namespace());
+        }
     }
 }