Repository: EnMasseProject/enmasse
Pull request #2586 (2019-04-05 10:00:44)

Align the Java API with the Go API
It looks like the changes to the addressspace and address configuration are missing on the Java side. As is the `IoTConfigList`, which is required when working with the fabric8 client.


Patch:
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 53bca14da..1fdb876bd 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
@@ -4,12 +4,12 @@
  */
 package io.enmasse.controller;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import io.enmasse.address.model.*;
+import io.enmasse.admin.model.v1.*;
 import io.enmasse.admin.model.v1.AuthenticationService;
 import io.enmasse.admin.model.v1.AuthenticationServiceType;
-import io.enmasse.admin.model.v1.BrokeredInfraConfig;
-import io.enmasse.admin.model.v1.InfraConfig;
-import io.enmasse.admin.model.v1.StandardInfraConfig;
 import io.enmasse.config.AnnotationKeys;
 import io.enmasse.config.LabelKeys;
 import io.enmasse.controller.common.Kubernetes;
@@ -18,6 +18,9 @@ import io.enmasse.k8s.api.AuthenticationServiceRegistry;
 import io.fabric8.kubernetes.api.model.*;
 import io.fabric8.kubernetes.api.model.apps.Deployment;
 import io.fabric8.kubernetes.api.model.apps.StatefulSet;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import java.io.File;
 import java.io.IOException;
 import java.io.UncheckedIOException;
@@ -28,8 +31,10 @@ import static io.enmasse.address.model.KubeUtil.applyPodTemplate;
 import static io.enmasse.address.model.KubeUtil.lookupResource;
 
 public class TemplateInfraResourceFactory implements InfraResourceFactory {
+    private static final Logger log = LoggerFactory.getLogger(TemplateInfraResourceFactory.class);
     private static final String KC_IDP_HINT_NONE = "none";
     private static final String KC_IDP_HINT_OPENSHIFT = "openshift-v3";
+    private static final ObjectMapper mapper = new ObjectMapper();
 
     private final Kubernetes kubernetes;
     private final AuthenticationServiceRegistry authenticationServiceRegistry;
@@ -193,6 +198,15 @@ public class TemplateInfraResourceFactory implements InfraResourceFactory {
                 parameters.put(TemplateParameter.ROUTER_WORKER_THREADS, String.valueOf(standardInfraConfig.getSpec().getRouter().getWorkerThreads()));
             }
 
+            if (standardInfraConfig.getSpec().getRouter().getPolicy() != null) {
+                try {
+                    String vhostPolicyJson = createVhostPolicyJson("$default", standardInfraConfig.getSpec().getRouter().getPolicy());
+                    parameters.put(TemplateParameter.ROUTER_VHOST_POLICY_JSON, vhostPolicyJson);
+                    parameters.put(TemplateParameter.ROUTER_ENABLE_VHOST_POLICY, "true");
+                } catch (Exception e) {
+                    log.warn("Error setting router policy settings, ignoring", e);
+                }
+            }
         }
 
         if (standardInfraConfig.getSpec().getAdmin() != null && standardInfraConfig.getSpec().getAdmin().getResources() != null && standardInfraConfig.getSpec().getAdmin().getResources().getMemory() != null) {
@@ -248,6 +262,45 @@ public class TemplateInfraResourceFactory implements InfraResourceFactory {
         }
     }
 
+    static String createVhostPolicyJson(String vhost, RouterPolicySpec policy) throws JsonProcessingException {
+        Map<String, Object> defaultGroupPolicy = new HashMap<>();
+        defaultGroupPolicy.put("remoteHosts", "*");
+        if (policy.getMaxSessionsPerConnection() != null) {
+            defaultGroupPolicy.put("maxSessions", policy.getMaxSessionsPerConnection());
+        }
+
+        if (policy.getMaxSendersPerConnection() != null) {
+            defaultGroupPolicy.put("maxSenders", policy.getMaxSendersPerConnection());
+        }
+
+        if (policy.getMaxReceiversPerConnection() != null) {
+            defaultGroupPolicy.put("maxReceivers", policy.getMaxReceiversPerConnection());
+        }
+
+        Map<String, Object> defaultVhostPolicy = new HashMap<>();
+        defaultVhostPolicy.put("hostname", vhost);
+        defaultVhostPolicy.put("allowUnknownUser", true);
+
+        if (policy.getMaxConnections() != null) {
+            defaultVhostPolicy.put("maxConnections", policy.getMaxConnections());
+        }
+
+        if (policy.getMaxConnectionsPerHost() != null) {
+            defaultVhostPolicy.put("maxConnectionsPerHost", policy.getMaxConnectionsPerHost());
+        }
+
+        if (policy.getMaxConnectionsPerUser() != null) {
+            defaultVhostPolicy.put("maxConnectionsPerUser", policy.getMaxConnectionsPerUser());
+        }
+
+        defaultVhostPolicy.put("groups", Collections.singletonMap(vhost, defaultGroupPolicy));
+
+        List<Object> values = new ArrayList<>();
+        values.add("vhost");
+        values.add(defaultVhostPolicy);
+        return mapper.writeValueAsString(Collections.singletonList(values));
+    }
+
     private String getAnnotation(Map<String, String> annotations, String key, String defaultValue) {
         return Optional.ofNullable(annotations)
                 .flatMap(m -> Optional.ofNullable(m.get(key)))
diff --git a/address-space-controller/src/main/java/io/enmasse/controller/common/TemplateParameter.java b/address-space-controller/src/main/java/io/enmasse/controller/common/TemplateParameter.java
index 370a488b9..7740fc880 100644
--- a/address-space-controller/src/main/java/io/enmasse/controller/common/TemplateParameter.java
+++ b/address-space-controller/src/main/java/io/enmasse/controller/common/TemplateParameter.java
@@ -37,6 +37,8 @@ public interface TemplateParameter {
     String ROUTER_HANDSHAKE_TIMEOUT = "ROUTER_HANDSHAKE_TIMEOUT";
     String ROUTER_IDLE_TIMEOUT = "ROUTER_IDLE_TIMEOUT";
     String ROUTER_WORKER_THREADS = "ROUTER_WORKER_THREADS";
+    String ROUTER_ENABLE_VHOST_POLICY = "ROUTER_ENABLE_VHOST_POLICY";
+    String ROUTER_VHOST_POLICY_JSON = "ROUTER_VHOST_POLICY_JSON";
     String STANDARD_INFRA_CONFIG_NAME = "STANDARD_INFRA_CONFIG_NAME";
 
     String IMAGE_PULL_POLICY = "IMAGE_PULL_POLICY";
diff --git a/address-space-controller/src/main/resources/templates/standard-space-infra.yaml b/address-space-controller/src/main/resources/templates/standard-space-infra.yaml
index 1271b7adc..e696a5af4 100644
--- a/address-space-controller/src/main/resources/templates/standard-space-infra.yaml
+++ b/address-space-controller/src/main/resources/templates/standard-space-infra.yaml
@@ -47,6 +47,16 @@ objects:
     selector:
       capability: router
       infraUuid: ${INFRA_UUID}
+- apiVersion: v1
+  kind: ConfigMap
+  metadata:
+    name: qdrouterd-policies.${INFRA_UUID}
+    labels:
+      app: enmasse
+      infraType: standard
+      infraUuid: ${INFRA_UUID}
+  data:
+    policies.json: ${ROUTER_VHOST_POLICY_JSON}
 - apiVersion: v1
   kind: ConfigMap
   metadata:
@@ -168,6 +178,11 @@ objects:
         linkCapacity: ${ROUTER_LINK_CAPACITY}
       }
 
+      policy {
+        policyDir: /etc/qpid-dispatch/policies
+        enableVhostPolicy: ${ROUTER_ENABLE_VHOST_POLICY}
+      }
+
       connector {
         host: ragent-${INFRA_UUID}
         port: 5671
@@ -315,6 +330,8 @@ objects:
             readOnly: true
           - mountPath: /etc/qpid-dispatch/config
             name: qdrouterd-config
+          - mountPath: /etc/qpid-dispatch/policies
+            name: qdrouterd-policies
         volumes:
         - name: ssl-certs
           secret:
@@ -328,6 +345,9 @@ objects:
         - name: qdrouterd-config
           configMap:
             name: qdrouterd-config.${INFRA_UUID}
+        - name: qdrouterd-policies
+          configMap:
+            name: qdrouterd-policies.${INFRA_UUID}
 - apiVersion: v1
   data:
     tls.crt: ${AUTHENTICATION_SERVICE_CA_CERT}
@@ -720,6 +740,12 @@ parameters:
 - description: Router worker threads
   name: ROUTER_WORKER_THREADS
   value: '4'
+- description: Enable router vhost policies
+  name: ROUTER_ENABLE_VHOST_POLICY
+  value: 'false'
+- description: Router vhost policy json
+  name: ROUTER_VHOST_POLICY_JSON
+  value: '[]'
 - name: IMAGE_PULL_POLICY
   description: Image Pull Policy
   value: ${env.IMAGE_PULL_POLICY}
diff --git a/api-model/src/main/java/io/enmasse/admin/model/v1/RouterPolicySpec.java b/api-model/src/main/java/io/enmasse/admin/model/v1/RouterPolicySpec.java
new file mode 100644
index 000000000..844f46983
--- /dev/null
+++ b/api-model/src/main/java/io/enmasse/admin/model/v1/RouterPolicySpec.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2019, EnMasse authors.
+ * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html).
+ */
+package io.enmasse.admin.model.v1;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+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;
+
+@Buildable(
+        editableEnabled = false,
+        generateBuilderPackage = false,
+        builderPackage = "io.fabric8.kubernetes.api.builder",
+        refs = {@BuildableReference(AbstractWithAdditionalProperties.class)},
+        inline = @Inline(type = Doneable.class, prefix = "Doneable", value = "done")
+)
+@JsonPropertyOrder({"maxConnections", "maxConnectionsPerUser", "maxConnectionsPerHost", "maxSessionsPerConnection", "maxSendersPerConnection", "maxReceiversPerConnection"})
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class RouterPolicySpec {
+    private Integer maxConnections;
+    private Integer maxConnectionsPerUser;
+    private Integer maxConnectionsPerHost;
+    private Integer maxSessionsPerConnection;
+    private Integer maxSendersPerConnection;
+    private Integer maxReceiversPerConnection;
+
+    public Integer getMaxConnections() {
+        return maxConnections;
+    }
+
+    public void setMaxConnections(Integer maxConnections) {
+        this.maxConnections = maxConnections;
+    }
+
+    public Integer getMaxConnectionsPerUser() {
+        return maxConnectionsPerUser;
+    }
+
+    public void setMaxConnectionsPerUser(Integer maxConnectionsPerUser) {
+        this.maxConnectionsPerUser = maxConnectionsPerUser;
+    }
+
+    public Integer getMaxConnectionsPerHost() {
+        return maxConnectionsPerHost;
+    }
+
+    public void setMaxConnectionsPerHost(Integer maxConnectionsPerHost) {
+        this.maxConnectionsPerHost = maxConnectionsPerHost;
+    }
+
+    public Integer getMaxSessionsPerConnection() {
+        return maxSessionsPerConnection;
+    }
+
+    public void setMaxSessionsPerConnection(Integer maxSessionsPerConnection) {
+        this.maxSessionsPerConnection = maxSessionsPerConnection;
+    }
+
+    public Integer getMaxSendersPerConnection() {
+        return maxSendersPerConnection;
+    }
+
+    public void setMaxSendersPerConnection(Integer maxSendersPerConnection) {
+        this.maxSendersPerConnection = maxSendersPerConnection;
+    }
+
+    public Integer getMaxReceiversPerConnection() {
+        return maxReceiversPerConnection;
+    }
+
+    public void setMaxReceiversPerConnection(Integer maxReceiversPerConnection) {
+        this.maxReceiversPerConnection = maxReceiversPerConnection;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        RouterPolicySpec that = (RouterPolicySpec) o;
+        return Objects.equals(maxConnections, that.maxConnections) &&
+                Objects.equals(maxConnectionsPerUser, that.maxConnectionsPerUser) &&
+                Objects.equals(maxConnectionsPerHost, that.maxConnectionsPerHost) &&
+                Objects.equals(maxSessionsPerConnection, that.maxSessionsPerConnection) &&
+                Objects.equals(maxSendersPerConnection, that.maxSendersPerConnection) &&
+                Objects.equals(maxReceiversPerConnection, that.maxReceiversPerConnection);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(maxConnections, maxConnectionsPerUser, maxConnectionsPerHost, maxSessionsPerConnection, maxSendersPerConnection, maxReceiversPerConnection);
+    }
+
+    @Override
+    public String toString() {
+        return "RouterPolicySpec{" +
+                "maxConnections=" + maxConnections +
+                ", maxConnectionsPerUser=" + maxConnectionsPerUser +
+                ", maxConnectionsPerHost=" + maxConnectionsPerHost +
+                ", maxSessionsPerConnection=" + maxSessionsPerConnection +
+                ", maxSendersPerConnection=" + maxSendersPerConnection +
+                ", maxReceiversPerConnection=" + maxReceiversPerConnection +
+                '}';
+    }
+}
diff --git a/api-model/src/main/java/io/enmasse/admin/model/v1/StandardInfraConfigSpecRouter.java b/api-model/src/main/java/io/enmasse/admin/model/v1/StandardInfraConfigSpecRouter.java
index 26f806973..1aa789a12 100644
--- a/api-model/src/main/java/io/enmasse/admin/model/v1/StandardInfraConfigSpecRouter.java
+++ b/api-model/src/main/java/io/enmasse/admin/model/v1/StandardInfraConfigSpecRouter.java
@@ -19,10 +19,10 @@ import io.sundr.builder.annotations.Inline;
         editableEnabled = false,
         generateBuilderPackage = false,
         builderPackage = "io.fabric8.kubernetes.api.builder",
-        refs = {@BuildableReference(AbstractHasMetadataWithAdditionalProperties.class)},
+        refs = {@BuildableReference(AbstractWithAdditionalProperties.class)},
         inline = @Inline(type = Doneable.class, prefix = "Doneable", value = "done")
 )
-@JsonPropertyOrder({"minReplicas", "resources", "linkCapacity", "handshakeTimeout", "idleTimeout", "workerThreads", "podTemplate"})
+@JsonPropertyOrder({"minReplicas", "resources", "linkCapacity", "handshakeTimeout", "idleTimeout", "workerThreads", "policy", "podTemplate"})
 @JsonInclude(JsonInclude.Include.NON_NULL)
 public class StandardInfraConfigSpecRouter extends AbstractWithAdditionalProperties {
     private StandardInfraConfigSpecRouterResources resources;
@@ -31,6 +31,7 @@ public class StandardInfraConfigSpecRouter extends AbstractWithAdditionalPropert
     private Integer handshakeTimeout;
     private Integer idleTimeout;
     private Integer workerThreads;
+    private RouterPolicySpec policy;
     private PodTemplateSpec podTemplate;
 
     public void setResources(StandardInfraConfigSpecRouterResources resources) {
@@ -89,6 +90,14 @@ public class StandardInfraConfigSpecRouter extends AbstractWithAdditionalPropert
         this.workerThreads = workerThreads;
     }
 
+    public RouterPolicySpec getPolicy() {
+        return policy;
+    }
+
+    public void setPolicy(RouterPolicySpec policy) {
+        this.policy = policy;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
@@ -100,12 +109,13 @@ public class StandardInfraConfigSpecRouter extends AbstractWithAdditionalPropert
                 Objects.equals(idleTimeout, that.idleTimeout) &&
                 Objects.equals(linkCapacity, that.linkCapacity) &&
                 Objects.equals(workerThreads, that.workerThreads) &&
+                Objects.equals(policy, that.policy) &&
                 Objects.equals(podTemplate, that.podTemplate);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(resources, linkCapacity, minReplicas, handshakeTimeout, idleTimeout, workerThreads, podTemplate);
+        return Objects.hash(resources, linkCapacity, minReplicas, handshakeTimeout, idleTimeout, workerThreads, policy, podTemplate);
     }
 
     @Override
@@ -117,6 +127,7 @@ public class StandardInfraConfigSpecRouter extends AbstractWithAdditionalPropert
                 ", handshakeTimeout=" + handshakeTimeout +
                 ", idleTimeout=" + idleTimeout +
                 ", workerThreads=" + workerThreads +
+                ", policy=" + policy +
                 ", podTemplate=" + podTemplate +
                 '}';
     }
diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/AddressConfig.java b/api-model/src/main/java/io/enmasse/iot/model/v1/AddressConfig.java
new file mode 100644
index 000000000..3af696f66
--- /dev/null
+++ b/api-model/src/main/java/io/enmasse/iot/model/v1/AddressConfig.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2019, EnMasse authors.
+ * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html).
+ */
+
+package io.enmasse.iot.model.v1;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+import io.fabric8.kubernetes.api.model.Doneable;
+import io.sundr.builder.annotations.Buildable;
+import io.sundr.builder.annotations.Inline;
+
+@Buildable(
+        editableEnabled = false,
+        generateBuilderPackage = false,
+        builderPackage = "io.fabric8.kubernetes.api.builder",
+        inline = @Inline(
+                type = Doneable.class,
+                prefix = "Doneable",
+                value = "done"))
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class AddressConfig {
+
+    private String plan;
+
+    public AddressConfig() {}
+
+    public AddressConfig(String plan) {
+        this.plan = plan;
+    }
+
+    public String getPlan() {
+        return plan;
+    }
+
+    public void setPlan(String plan) {
+        this.plan = plan;
+    }
+}
diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/AddressSpaceConfig.java b/api-model/src/main/java/io/enmasse/iot/model/v1/AddressSpaceConfig.java
new file mode 100644
index 000000000..c8c87f656
--- /dev/null
+++ b/api-model/src/main/java/io/enmasse/iot/model/v1/AddressSpaceConfig.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019, EnMasse authors.
+ * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html).
+ */
+package io.enmasse.iot.model.v1;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+import io.fabric8.kubernetes.api.model.Doneable;
+import io.sundr.builder.annotations.Buildable;
+import io.sundr.builder.annotations.Inline;
+
+@Buildable(
+        editableEnabled = false,
+        generateBuilderPackage = false,
+        builderPackage = "io.fabric8.kubernetes.api.builder",
+        inline = @Inline(
+                type = Doneable.class,
+                prefix = "Doneable",
+                value = "done"))
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class AddressSpaceConfig {
+    private String name;
+    private String plan;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getPlan() {
+        return plan;
+    }
+
+    public void setPlan(String plan) {
+        this.plan = plan;
+    }
+
+
+}
diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/AddressesConfig.java b/api-model/src/main/java/io/enmasse/iot/model/v1/AddressesConfig.java
new file mode 100644
index 000000000..90ad1a2a9
--- /dev/null
+++ b/api-model/src/main/java/io/enmasse/iot/model/v1/AddressesConfig.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2019, EnMasse authors.
+ * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html).
+ */
+
+package io.enmasse.iot.model.v1;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+import io.fabric8.kubernetes.api.model.Doneable;
+import io.sundr.builder.annotations.Buildable;
+import io.sundr.builder.annotations.Inline;
+
+@Buildable(
+        editableEnabled = false,
+        generateBuilderPackage = false,
+        builderPackage = "io.fabric8.kubernetes.api.builder",
+        inline = @Inline(
+                type = Doneable.class,
+                prefix = "Doneable",
+                value = "done"))
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class AddressesConfig {
+
+    private AddressConfig command;
+    private AddressConfig event;
+    private AddressConfig telemetry;
+
+    public AddressConfig getCommand() {
+        return command;
+    }
+
+    public void setCommand(AddressConfig command) {
+        this.command = command;
+    }
+
+    public AddressConfig getEvent() {
+        return event;
+    }
+
+    public void setEvent(AddressConfig event) {
+        this.event = event;
+    }
+
+    public AddressConfig getTelemetry() {
+        return telemetry;
+    }
+
+    public void setTelemetry(AddressConfig telemetry) {
+        this.telemetry = telemetry;
+    }
+
+}
diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/IoTConfigList.java b/api-model/src/main/java/io/enmasse/iot/model/v1/IoTConfigList.java
new file mode 100644
index 000000000..818fd6eb1
--- /dev/null
+++ b/api-model/src/main/java/io/enmasse/iot/model/v1/IoTConfigList.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2019, EnMasse authors.
+ * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html).
+ */
+package io.enmasse.iot.model.v1;
+
+import io.enmasse.common.model.AbstractList;
+import io.enmasse.common.model.DefaultCustomResource;
+
+@DefaultCustomResource
+@SuppressWarnings("serial")
+public class IoTConfigList extends AbstractList<IoTConfig> {
+
+    public static final String KIND = "IoTConfigList";
+
+    public IoTConfigList() {
+        super(KIND, IoTCrd.API_VERSION);
+    }
+}
diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/IoTCrd.java b/api-model/src/main/java/io/enmasse/iot/model/v1/IoTCrd.java
index 4f4c50d2e..466f7d4ff 100644
--- a/api-model/src/main/java/io/enmasse/iot/model/v1/IoTCrd.java
+++ b/api-model/src/main/java/io/enmasse/iot/model/v1/IoTCrd.java
@@ -15,9 +15,11 @@ public class IoTCrd {
     public static final String API_VERSION = GROUP + "/" + VERSION;
 
     private static final CustomResourceDefinition PROJECT_CRD;
+    private static final CustomResourceDefinition CONFIG_CRD;
 
     static {
         PROJECT_CRD = CustomResources.createCustomResource(GROUP, VERSION, IoTProject.KIND);
+        CONFIG_CRD = CustomResources.createCustomResource(GROUP, VERSION, IoTConfig.KIND);
     }
 
     public static void registerCustomCrds() {
@@ -30,4 +32,8 @@ public class IoTCrd {
         return PROJECT_CRD;
     }
 
+    public static CustomResourceDefinition config() {
+        return CONFIG_CRD;
+    }
+
 }
diff --git a/api-model/src/main/java/io/enmasse/iot/model/v1/ManagedDownstreamStrategy.java b/api-model/src/main/java/io/enmasse/iot/model/v1/ManagedDownstreamStrategy.java
index d67bf9a75..393314619 100644
--- a/api-model/src/main/java/io/enmasse/iot/model/v1/ManagedDownstreamStrategy.java
+++ b/api-model/src/main/java/io/enmasse/iot/model/v1/ManagedDownstreamStrategy.java
@@ -6,7 +6,6 @@ package io.enmasse.iot.model.v1;
 
 import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
 
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import com.fasterxml.jackson.annotation.JsonInclude;
 
 import io.fabric8.kubernetes.api.model.Doneable;
@@ -20,21 +19,27 @@ import io.sundr.builder.annotations.Inline;
         inline = @Inline(
                 type = Doneable.class,
                 prefix = "Doneable",
-                value = "done"
-                )
-        )
-@JsonIgnoreProperties(ignoreUnknown = true)
+                value = "done"))
 @JsonInclude(NON_NULL)
 public class ManagedDownstreamStrategy {
 
-    private String addressSpaceName;
+    private AddressSpaceConfig addressSpace;
+    private AddressesConfig addresses;
 
-    public String getAddressSpaceName() {
-        return addressSpaceName;
+    public AddressSpaceConfig getAddressSpace() {
+        return addressSpace;
     }
 
-    public void setAddressSpaceName(String addressSpaceName) {
-        this.addressSpaceName = addressSpaceName;
+    public void setAddressSpace(AddressSpaceConfig addressSpace) {
+        this.addressSpace = addressSpace;
+    }
+
+    public AddressesConfig getAddresses() {
+        return addresses;
+    }
+
+    public void setAddresses(AddressesConfig addresses) {
+        this.addresses = addresses;
     }
 
 }
diff --git a/api-server/src/main/resources/swagger.json b/api-server/src/main/resources/swagger.json
index a029a8626..060e57442 100644
--- a/api-server/src/main/resources/swagger.json
+++ b/api-server/src/main/resources/swagger.json
@@ -2149,6 +2149,29 @@
                 },
                 "workerThreads": {
                     "type": "integer"
+                },
+                "policy": {
+                    "type": "object",
+                    "properties": {
+                        "maxConnections": {
+                            "type": "integer"
+                        },
+                        "maxConnectionsPerUser": {
+                            "type": "integer"
+                        },
+                        "maxConnectionsPerHost": {
+                            "type": "integer"
+                        },
+                        "maxSessionsPerConnection": {
+                            "type": "integer"
+                        },
+                        "maxSendersPerConnection": {
+                            "type": "integer"
+                        },
+                        "maxReceiversPerConnection": {
+                            "type": "integer"
+                        }
+                    }
                 }
             }
         },
diff --git a/documentation/common/restapi-reference.adoc b/documentation/common/restapi-reference.adoc
index 6e1441ada..a990e326e 100644
--- a/documentation/common/restapi-reference.adoc
+++ b/documentation/common/restapi-reference.adoc
@@ -1634,12 +1634,34 @@ __optional__|integer
 __optional__|integer
 |**podTemplate** +
 __optional__|<<_io_enmasse_admin_v1beta1_infraconfigpodspec,io.enmasse.admin.v1beta1.InfraConfigPodSpec>>
+|**policy** +
+__optional__|<<_io_enmasse_admin_v1beta1_standardinfraconfigspecrouter_policy,policy>>
 |**resources** +
 __optional__|<<_io_enmasse_admin_v1beta1_standardinfraconfigspecrouter_resources,resources>>
 |**workerThreads** +
 __optional__|integer
 |===
 
+[[_io_enmasse_admin_v1beta1_standardinfraconfigspecrouter_policy]]
+**policy**
+
+[options="header", cols=".^3a,.^4a"]
+|===
+|Name|Schema
+|**maxConnections** +
+__optional__|integer
+|**maxConnectionsPerHost** +
+__optional__|integer
+|**maxConnectionsPerUser** +
+__optional__|integer
+|**maxReceiversPerConnection** +
+__optional__|integer
+|**maxSendersPerConnection** +
+__optional__|integer
+|**maxSessionsPerConnection** +
+__optional__|integer
+|===
+
 [[_io_enmasse_admin_v1beta1_standardinfraconfigspecrouter_resources]]
 **resources**
 
diff --git a/documentation/modules/con-standard-infra-config.adoc b/documentation/modules/con-standard-infra-config.adoc
index adcfca945..c54e60621 100644
--- a/documentation/modules/con-standard-infra-config.adoc
+++ b/documentation/modules/con-standard-infra-config.adoc
@@ -30,6 +30,13 @@ spec:
       memory: 256Mi
     linkCapcity: 1000
     minReplicas: 1
+    policy:
+      maxConnections: 1000
+      maxConnectionsPerHost: 1
+      maxConnectionsPerUser: 10
+      maxSessionsPerConnection: 10
+      maxSendersPerConnection: 5
+      maxReceiversPerConnection: 5
     podTemplate:
       spec:
         affinity:
diff --git a/templates/crds/standardinfraconfigs.crd.yaml b/templates/crds/standardinfraconfigs.crd.yaml
index 954243087..6c00e9ecc 100644
--- a/templates/crds/standardinfraconfigs.crd.yaml
+++ b/templates/crds/standardinfraconfigs.crd.yaml
@@ -144,3 +144,18 @@ spec:
                   type: integer
                 workerThreads:
                   type: integer
+                policy:
+                  type: object
+                  properties:
+                    maxConnections:
+                      type: integer
+                    maxConnectionsPerUser:
+                      type: integer
+                    maxConnectionsPerHost:
+                      type: integer
+                    maxSessionsPerConnection:
+                      type: integer
+                    maxSendersPerConnection:
+                      type: integer
+                    maxReceiversPerConnection:
+                      type: integer


Test patch:
diff --git a/address-space-controller/src/test/java/io/enmasse/controller/TemplateInfraResourceFactoryTest.java b/address-space-controller/src/test/java/io/enmasse/controller/TemplateInfraResourceFactoryTest.java
index 7c9cd1481..819416d8e 100644
--- a/address-space-controller/src/test/java/io/enmasse/controller/TemplateInfraResourceFactoryTest.java
+++ b/address-space-controller/src/test/java/io/enmasse/controller/TemplateInfraResourceFactoryTest.java
@@ -16,6 +16,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
 import io.enmasse.admin.model.v1.*;
 import io.fabric8.kubernetes.api.model.*;
 import io.fabric8.kubernetes.api.model.apps.Deployment;
@@ -211,6 +212,23 @@ public class TemplateInfraResourceFactoryTest extends JULInitializingTest {
         }
     }
 
+    @Test
+    public void testVhostPolicyGen() throws JsonProcessingException {
+        String vhost = "$default";
+        RouterPolicySpec policySpec = new RouterPolicySpecBuilder()
+                .withMaxConnections(1000)
+                .withMaxConnectionsPerHost(10)
+                .withMaxConnectionsPerUser(10)
+                .withMaxSendersPerConnection(5)
+                .withMaxReceiversPerConnection(5)
+                .withMaxSessionsPerConnection(5)
+                .build();
+
+        String policyJson = TemplateInfraResourceFactory.createVhostPolicyJson(vhost, policySpec);
+        String expected = "[[\"vhost\",{\"hostname\":\"$default\",\"maxConnectionsPerUser\":10,\"allowUnknownUser\":true,\"groups\":{\"$default\":{\"maxSessions\":5,\"maxSenders\":5,\"remoteHosts\":\"*\",\"maxReceivers\":5}},\"maxConnectionsPerHost\":10,\"maxConnections\":1000}]]";
+        assertEquals(expected, policyJson);
+    }
+
     private <T> T findItem(Class<T> clazz, String kind, String name, List<HasMetadata> items) {
         T found = null;
         for (HasMetadata item : items) {
diff --git a/api-model/src/test/java/io/enmasse/iot/model/v1/IoTProjectTest.java b/api-model/src/test/java/io/enmasse/iot/model/v1/IoTProjectTest.java
index 9a3138b2b..6e20754be 100644
--- a/api-model/src/test/java/io/enmasse/iot/model/v1/IoTProjectTest.java
+++ b/api-model/src/test/java/io/enmasse/iot/model/v1/IoTProjectTest.java
@@ -60,14 +60,16 @@ public class IoTProjectTest {
                 .withNewSpec()
                 .withNewDownstreamStrategy()
                 .withNewManagedStrategy()
-                .withAddressSpaceName("managed")
+                .withNewAddressSpace()
+                .withName("managed")
+                .endAddressSpace()
                 .endManagedStrategy()
                 .endDownstreamStrategy()
                 .endSpec()
                 .build();
 
         assertEquals("proj", project.getMetadata().getName());
-        assertEquals("managed", project.getSpec().getDownstreamStrategy().getManagedStrategy().getAddressSpaceName());
+        assertEquals("managed", project.getSpec().getDownstreamStrategy().getManagedStrategy().getAddressSpace().getName());
         assertNull(project.getSpec().getDownstreamStrategy().getExternalStrategy());
     }
 
diff --git a/systemtests/src/main/java/io/enmasse/systemtest/utils/PlanUtils.java b/systemtests/src/main/java/io/enmasse/systemtest/utils/PlanUtils.java
index 4f3b7af38..349d4eca4 100644
--- a/systemtests/src/main/java/io/enmasse/systemtest/utils/PlanUtils.java
+++ b/systemtests/src/main/java/io/enmasse/systemtest/utils/PlanUtils.java
@@ -208,6 +208,12 @@ public class PlanUtils {
         return new StandardInfraConfigSpecRouterBuilder()
                 .withMinReplicas(minReplicas)
                 .withLinkCapacity(linkCapacity)
+                .withNewPolicy()
+                .withMaxConnections(1000)
+                .withMaxConnectionsPerHost(500)
+                .withMaxConnectionsPerUser(500)
+                .withMaxSendersPerConnection(300)
+                .endPolicy()
                 .withNewResources()
                 .withMemory(memory)
                 .endResources()
diff --git a/systemtests/src/test/java/io/enmasse/systemtest/standard/infra/InfraTest.java b/systemtests/src/test/java/io/enmasse/systemtest/standard/infra/InfraTest.java
index ebddf09e2..1eb4a9cef 100644
--- a/systemtests/src/test/java/io/enmasse/systemtest/standard/infra/InfraTest.java
+++ b/systemtests/src/test/java/io/enmasse/systemtest/standard/infra/InfraTest.java
@@ -149,7 +149,7 @@ class InfraTest extends InfraTestBase implements ITestBaseStandard {
         assertEquals(expectedRouter.getResources().getMemory(), actualRouter.getResources().getMemory());
         assertEquals(expectedRouter.getLinkCapacity(), actualRouter.getLinkCapacity());
         assertEquals(expectedRouter.getMinReplicas(), actualRouter.getMinReplicas());
-
+        assertEquals(expectedRouter.getPolicy(), actualRouter.getPolicy());
     }
 
     private boolean assertInfra(String brokerMemory, String brokerStorage, PodTemplateSpec brokerTemplateSpec, int routerReplicas, String routermemory, PodTemplateSpec routerTemplateSpec, String adminMemory, PodTemplateSpec adminTemplateSpec) {