You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@stratos.apache.org by im...@apache.org on 2015/06/23 19:43:47 UTC

stratos git commit: Fixing kubernetes service port duplication issue when multiple applications are deployed

Repository: stratos
Updated Branches:
  refs/heads/master bb6e10298 -> b7667b18b


Fixing kubernetes service port duplication issue when multiple applications are deployed


Project: http://git-wip-us.apache.org/repos/asf/stratos/repo
Commit: http://git-wip-us.apache.org/repos/asf/stratos/commit/b7667b18
Tree: http://git-wip-us.apache.org/repos/asf/stratos/tree/b7667b18
Diff: http://git-wip-us.apache.org/repos/asf/stratos/diff/b7667b18

Branch: refs/heads/master
Commit: b7667b18b5129f2a55763ece7b51448f26dfa501
Parents: bb6e102
Author: Imesh Gunaratne <im...@apache.org>
Authored: Tue Jun 23 23:13:15 2015 +0530
Committer: Imesh Gunaratne <im...@apache.org>
Committed: Tue Jun 23 23:13:41 2015 +0530

----------------------------------------------------------------------
 .../context/CloudControllerContext.java         |  69 ++++++++++++-
 .../controller/domain/ClusterPortMapping.java   |  78 ++++++++++++++
 .../cloud/controller/domain/PortMapping.java    |  21 +---
 .../iaases/kubernetes/KubernetesIaas.java       | 103 +++++++++++++------
 .../messaging/topology/TopologyBuilder.java     |  33 +++++-
 .../impl/CloudControllerServiceImpl.java        |  20 ++--
 .../rest/endpoint/api/StratosApiV41Utils.java   |   7 ++
 7 files changed, 262 insertions(+), 69 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/stratos/blob/b7667b18/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/context/CloudControllerContext.java
----------------------------------------------------------------------
diff --git a/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/context/CloudControllerContext.java b/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/context/CloudControllerContext.java
index 5292255..626f3c8 100644
--- a/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/context/CloudControllerContext.java
+++ b/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/context/CloudControllerContext.java
@@ -62,6 +62,9 @@ public class CloudControllerContext implements Serializable {
     private static final String CC_CARTRIDGE_TYPE_TO_CARTRIDGES_MAP = "CC_CARTRIDGE_TYPE_TO_CARTRIDGES_MAP";
     private static final String CC_SERVICE_GROUP_NAME_TO_SERVICE_GROUP_MAP = "CC_SERVICE_GROUP_NAME_TO_SERVICE_GROUP_MAP";
     private static final String CC_NETWORK_PARTITION_ID_TO_NETWORK_PARTITION_MAP = "CC_NETWORK_PARTITION_ID_TO_NETWORK_PARTITION_MAP";
+    private static final String CC_PARTITION_TO_IAAS_PROVIDER_BY_CARTRIDGE_MAP = "CC_PARTITION_TO_IAAS_PROVIDER_BY_CARTRIDGE_MAP";
+    private static final String CC_CARTRIDGE_TYPE_TO_IAAS_PROVIDER_MAP = "CC_CARTRIDGE_TYPE_TO_IAAS_PROVIDER_MAP";
+    private static final String CC_APPLICATION_ID_TO_CLUSTER_ID_TO_PORT_MAPPING_MAP = "CC_APPLICATION_ID_TO_CLUSTER_ID_TO_PORT_MAPPING_MAP";
 
     private static final String CC_CLUSTER_CTX_WRITE_LOCK = "CC_CLUSTER_CTX_WRITE_LOCK";
     private static final String CC_MEMBER_CTX_WRITE_LOCK = "CC_MEMBER_CTX_WRITE_LOCK";
@@ -71,8 +74,6 @@ public class CloudControllerContext implements Serializable {
     private static final String CC_CARTRIDGES_WRITE_LOCK = "CC_CARTRIDGES_WRITE_LOCK";
     private static final String CC_SERVICE_GROUPS_WRITE_LOCK = "CC_SERVICE_GROUPS_WRITE_LOCK";
 
-    private static final String CC_PARTITION_TO_IAAS_PROVIDER_BY_CARTRIDGE_MAP = "CC_PARTITION_TO_IAAS_PROVIDER_BY_CARTRIDGE_MAP";
-    private static final String CC_CARTRIDGE_TYPE_TO_IAAS_PROVIDER_MAP = "CC_CARTRIDGE_TYPE_TO_IAAS_PROVIDER_MAP";
     private static volatile CloudControllerContext instance;
 
     private final transient DistributedObjectProvider distributedObjectProvider;
@@ -163,6 +164,12 @@ public class CloudControllerContext implements Serializable {
      */
     private Map<String, List<IaasProvider>> cartridgeTypeToIaasProviders;
 
+    /**
+     * Key - Application id
+     * Value - Cluster port mappings against application id, cluster id
+     */
+    private Map<String, Map<String, List<ClusterPortMapping>>> applicationIdToClusterIdToPortMappings;
+
     private String streamId;
     private boolean isPublisherRunning;
     private boolean isTopologySyncRunning;
@@ -194,6 +201,8 @@ public class CloudControllerContext implements Serializable {
         networkPartitionIDToNetworkPartitionMap = distributedObjectProvider.getMap(CC_NETWORK_PARTITION_ID_TO_NETWORK_PARTITION_MAP);
         partitionToIaasProviderByCartridge = distributedObjectProvider.getMap(CC_PARTITION_TO_IAAS_PROVIDER_BY_CARTRIDGE_MAP);
         cartridgeTypeToIaasProviders = distributedObjectProvider.getMap(CC_CARTRIDGE_TYPE_TO_IAAS_PROVIDER_MAP);
+        applicationIdToClusterIdToPortMappings = distributedObjectProvider.getMap(CC_APPLICATION_ID_TO_CLUSTER_ID_TO_PORT_MAPPING_MAP);
+
         // Update context from the registry
         updateContextFromRegistry();
     }
@@ -712,6 +721,8 @@ public class CloudControllerContext implements Serializable {
                                 partitionToIaasProviderByCartridge);
                         copyMap(serializedObj.cartridgeTypeToIaasProviders,
                                 cartridgeTypeToIaasProviders);
+                        copyMap(serializedObj.applicationIdToClusterIdToPortMappings,
+                                applicationIdToClusterIdToPortMappings);
 
                         if (log.isDebugEnabled()) {
                             log.debug("Cloud controller context is read from the registry");
@@ -834,4 +845,58 @@ public class CloudControllerContext implements Serializable {
         List<IaasProvider> iaasProviderList = cartridgeTypeToIaasProviders.get(cartridgeType);
         return iaasProviderList;
     }
+
+    /**
+     * Add a cluster port mapping.
+     * @param portMapping
+     */
+    public void addClusterPortMapping(ClusterPortMapping portMapping) {
+        String applicationId = portMapping.getApplicationId();
+        String clusterId = portMapping.getClusterId();
+
+        List<ClusterPortMapping> portMappings = null;
+        Map<String, List<ClusterPortMapping>> clusterIdToPortMappings =
+                applicationIdToClusterIdToPortMappings.get(applicationId);
+
+        if(clusterIdToPortMappings == null) {
+            clusterIdToPortMappings = new HashMap<String, List<ClusterPortMapping>>();
+            applicationIdToClusterIdToPortMappings.put(applicationId, clusterIdToPortMappings);
+        } else {
+            portMappings = clusterIdToPortMappings.get(portMapping.getClusterId());
+        }
+        if(portMappings == null) {
+            portMappings = new ArrayList<ClusterPortMapping>();
+            clusterIdToPortMappings.put(clusterId, portMappings);
+        }
+
+        if(!portMappings.contains(portMapping)) {
+            portMappings.add(portMapping);
+        }
+    }
+
+    /**
+     * Get cluster port mappings of an application cluster.
+     * @param applicationId
+     * @param clusterId
+     * @return
+     */
+    public List<ClusterPortMapping> getClusterPortMappings(String applicationId, String clusterId) {
+        Map<String, List<ClusterPortMapping>> clusterIdToPortMappings =
+                applicationIdToClusterIdToPortMappings.get(applicationId);
+
+        if(clusterIdToPortMappings != null) {
+            return clusterIdToPortMappings.get(clusterId);
+        }
+        return null;
+    }
+
+    /**
+     * Remove all the cluster port mappings of the given application.
+     * @param applicationId
+     */
+    public void removeClusterPortMappings(String applicationId) {
+        if(applicationIdToClusterIdToPortMappings.containsKey(applicationId)) {
+            applicationIdToClusterIdToPortMappings.remove(applicationId);
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/stratos/blob/b7667b18/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/domain/ClusterPortMapping.java
----------------------------------------------------------------------
diff --git a/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/domain/ClusterPortMapping.java b/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/domain/ClusterPortMapping.java
new file mode 100644
index 0000000..e816459
--- /dev/null
+++ b/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/domain/ClusterPortMapping.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one 
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 
+ * KIND, either express or implied.  See the License for the 
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.stratos.cloud.controller.domain;
+
+import java.io.Serializable;
+
+/**
+ * Cluster port mappings keep track of the generated kubernetes service ports for each
+ * port mapping defined in the corresponding cartridge.
+ */
+public class ClusterPortMapping extends PortMapping implements Serializable {
+
+    private static final long serialVersionUID = -5387564414633460306L;
+
+    private String applicationId;
+    private String clusterId;
+    private boolean kubernetes;
+    private int kubernetesServicePort;
+
+    public ClusterPortMapping() {
+    }
+
+    public ClusterPortMapping(String applicationId, String clusterId, String name, String protocol, int port, int proxyPort) {
+        super(protocol, port, proxyPort);
+        super.setName(name);
+
+        this.applicationId = applicationId;
+        this.clusterId = clusterId;
+    }
+
+    public String getApplicationId() {
+        return applicationId;
+    }
+
+    public String getClusterId() {
+        return clusterId;
+    }
+
+    public int getKubernetesServicePort() {
+        return kubernetesServicePort;
+    }
+
+    public void setKubernetesServicePort(int kubernetesServicePort) {
+        this.kubernetesServicePort = kubernetesServicePort;
+        kubernetes = true;
+    }
+
+    public boolean isKubernetes() {
+        return kubernetes;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        ClusterPortMapping portMappingObj = (ClusterPortMapping) obj;
+        return this.getName().equals(portMappingObj.getName());
+    }
+
+    public String toString() {
+        return "[application-id] " + getApplicationId() + " [cluster-id] " + getClusterId() + ", " +
+                super.toString() + " [kubernetes-service-port] "+ getKubernetesServicePort();
+    }
+}

http://git-wip-us.apache.org/repos/asf/stratos/blob/b7667b18/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/domain/PortMapping.java
----------------------------------------------------------------------
diff --git a/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/domain/PortMapping.java b/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/domain/PortMapping.java
index 81c91a4..b33eb97 100644
--- a/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/domain/PortMapping.java
+++ b/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/domain/PortMapping.java
@@ -28,8 +28,6 @@ public class PortMapping implements Serializable {
     private String protocol;
     private int port;
     private int proxyPort;
-    private int kubernetesServicePort;
-    private boolean isKubernetesServicePortMapping;
 
     public PortMapping() {
     }
@@ -73,23 +71,6 @@ public class PortMapping implements Serializable {
     }
 
     public String toString() {
-
-        return "Protocol: " + protocol + ", Port: " + port + ", Proxy Port: " + proxyPort;
-    }
-
-    public void setKubernetesServicePort(int kubernetesServicePort) {
-        this.kubernetesServicePort = kubernetesServicePort;
-    }
-
-    public int getKubernetesServicePort() {
-        return kubernetesServicePort;
-    }
-
-    public boolean isKubernetesServicePortMapping() {
-        return isKubernetesServicePortMapping;
-    }
-
-    public void setKubernetesServicePortMapping(boolean isKubernetesServicePortMapping) {
-        this.isKubernetesServicePortMapping = isKubernetesServicePortMapping;
+        return "[protocol] " + protocol + " [port] " + port + " [proxy-port] " + proxyPort;
     }
 }

http://git-wip-us.apache.org/repos/asf/stratos/blob/b7667b18/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/kubernetes/KubernetesIaas.java
----------------------------------------------------------------------
diff --git a/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/kubernetes/KubernetesIaas.java b/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/kubernetes/KubernetesIaas.java
index 423eb23..faeed2c 100644
--- a/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/kubernetes/KubernetesIaas.java
+++ b/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/iaases/kubernetes/KubernetesIaas.java
@@ -47,6 +47,7 @@ import org.apache.stratos.messaging.domain.topology.KubernetesService;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.List;
 import java.util.concurrent.locks.Lock;
 
@@ -214,7 +215,8 @@ public class KubernetesIaas extends Iaas {
                     kubernetesPortRange.getLower());
 
             // Generate kubernetes service ports and update port mappings in cartridge
-            generateKubernetesServicePorts(kubernetesClusterContext, clusterContext.getClusterId(), cartridge);
+            generateKubernetesServicePorts(clusterContext.getApplicationId(), clusterContext.getClusterId(),
+                    kubernetesClusterContext, cartridge);
 
             // Create kubernetes services for port mappings
             KubernetesApiClient kubernetesApi = kubernetesClusterContext.getKubApi();
@@ -493,11 +495,14 @@ public class KubernetesIaas extends Iaas {
             log.debug(String.format("Minion private IPs: %s", minionPrivateIPList));
         }
 
-        if (cartridge.getPortMappings() != null) {
-            for (PortMapping portMapping : Arrays.asList(cartridge.getPortMappings())) {
+        Collection<ClusterPortMapping> clusterPortMappings = CloudControllerContext.getInstance()
+                .getClusterPortMappings(clusterContext.getApplicationId(), clusterId);
+
+        if (clusterPortMappings != null) {
+            for (ClusterPortMapping clusterPortMapping : clusterPortMappings) {
 
                 // Skip if already created
-                int containerPort = portMapping.getPort();
+                int containerPort = clusterPortMapping.getPort();
                 if (kubernetesServiceExist(kubernetesServices, containerPort)) {
                     continue;
                 }
@@ -505,19 +510,18 @@ public class KubernetesIaas extends Iaas {
                 // Find next service sequence no
                 long serviceSeqNo = kubernetesClusterContext.getServiceSeqNo().incrementAndGet();
                 String serviceId = KubernetesIaasUtil.fixSpecialCharacters("service" + "-" + (serviceSeqNo));
-
                 String serviceLabel = KubernetesIaasUtil.fixSpecialCharacters(clusterId);
 
                 if (log.isInfoEnabled()) {
                     log.info(String.format("Creating kubernetes service: [cluster] %s [service] %s " +
                                     "[protocol] %s [service-port] %d [container-port] %s", clusterId,
-                            serviceId, portMapping.getProtocol(), portMapping.getKubernetesServicePort(),
-                            containerPort));
+                            serviceId, clusterPortMapping.getProtocol(),
+                            clusterPortMapping.getKubernetesServicePort(), containerPort));
                 }
 
                 // Create kubernetes service for port mapping
-                int servicePort = portMapping.getKubernetesServicePort();
-                String containerPortName = KubernetesIaasUtil.preparePortNameFromPortMapping(portMapping);
+                int servicePort = clusterPortMapping.getKubernetesServicePort();
+                String containerPortName = KubernetesIaasUtil.preparePortNameFromPortMapping(clusterPortMapping);
 
                 try {
                     // Services need to use minions private IP addresses for creating iptable rules
@@ -541,7 +545,7 @@ public class KubernetesIaas extends Iaas {
                 // Expose minions public IP addresses as they need to be accessed by external networks
                 String[] minionPublicIPArray = minionPublicIPList.toArray(new String[minionPublicIPList.size()]);
                 kubernetesService.setPublicIPs(minionPublicIPArray);
-                kubernetesService.setProtocol(portMapping.getProtocol());
+                kubernetesService.setProtocol(clusterPortMapping.getProtocol());
                 kubernetesService.setPort(service.getSpec().getPorts().get(0).getPort());
                 kubernetesService.setContainerPort(containerPort);
                 kubernetesServices.add(kubernetesService);
@@ -549,7 +553,7 @@ public class KubernetesIaas extends Iaas {
                 if (log.isInfoEnabled()) {
                     log.info(String.format("Kubernetes service successfully created: [cluster] %s [service] %s " +
                                     "[protocol] %s [service-port] %d [container-port] %s", clusterId,
-                            serviceId, portMapping.getProtocol(), servicePort, containerPort));
+                            serviceId, clusterPortMapping.getProtocol(), servicePort, containerPort));
                 }
             }
         }
@@ -575,46 +579,79 @@ public class KubernetesIaas extends Iaas {
      * @param clusterId
      * @param cartridge
      */
-    private void generateKubernetesServicePorts(KubernetesClusterContext kubernetesClusterContext, String clusterId,
+    private void generateKubernetesServicePorts(String applicationId, String clusterId,
+                                                KubernetesClusterContext kubernetesClusterContext,
                                                 Cartridge cartridge) {
+
         synchronized (KubernetesIaas.class) {
             if (cartridge != null) {
+
                 StringBuilder portMappingStrBuilder = new StringBuilder();
                 for (PortMapping portMapping : Arrays.asList(cartridge.getPortMappings())) {
-                    if (portMapping.getKubernetesServicePort() == 0) {
-                        int nextServicePort = kubernetesClusterContext.getNextServicePort();
-                        if (nextServicePort == -1) {
-                            throw new RuntimeException(String.format("Could not generate service port: [cluster-id] %s " +
-                                    "[port] %d", clusterId, portMapping.getPort()));
-                        }
-                        portMapping.setKubernetesServicePort(nextServicePort);
-                        portMapping.setKubernetesServicePortMapping(true);
 
-                        // Add port mappings to payload
-                        if (portMappingStrBuilder.toString().length() > 0) {
-                            portMappingStrBuilder.append(";");
-                        }
-                        portMappingStrBuilder.append(String.format("NAME:%s|PROTOCOL:%s|PORT:%d|PROXY_PORT:%d",
-                                portMapping.getName(), portMapping.getProtocol(),
-                                portMapping.getKubernetesServicePort(), portMapping.getProxyPort()));
+                    int nextServicePort = kubernetesClusterContext.getNextServicePort();
+                    if (nextServicePort == -1) {
+                        throw new RuntimeException(String.format("Could not generate service port: [cluster-id] %s " +
+                                "[port] %d", clusterId, portMapping.getPort()));
+                    }
 
-                        if (log.isInfoEnabled()) {
-                            log.info(String.format("Kubernetes service port generated: [cluster-id] %s [port] %d " +
-                                    "[service-port] %d", clusterId, portMapping.getPort(), nextServicePort));
-                        }
+                    Collection<ClusterPortMapping> clusterPortMappings =
+                            CloudControllerContext.getInstance().getClusterPortMappings(applicationId, clusterId);
+                    if(clusterPortMappings == null) {
+                        throw new CloudControllerException(String.format("Cluster port mappings not found: " +
+                                "[application-id] %s [cluster-id] %s", applicationId, clusterId));
+                    }
+
+                    ClusterPortMapping clusterPortMapping = findClusterPortMapping(clusterPortMappings, portMapping);
+                    if(clusterPortMappings == null) {
+                        throw new CloudControllerException(String.format("Cluster port mapping not found: " +
+                                "[application-id] %s [cluster-id] %s [transport] %s", applicationId, clusterId,
+                                portMapping.getName()));
+                    }
+
+                    clusterPortMapping.setKubernetesServicePort(nextServicePort);
+
+                    // Add port mappings to payload
+                    if (portMappingStrBuilder.toString().length() > 0) {
+                        portMappingStrBuilder.append(";");
+                    }
+                    portMappingStrBuilder.append(String.format("NAME:%s|PROTOCOL:%s|PORT:%d|PROXY_PORT:%d",
+                            clusterPortMapping.getName(), clusterPortMapping.getProtocol(),
+                            clusterPortMapping.getKubernetesServicePort(), clusterPortMapping.getProxyPort()));
+
+                    if (log.isInfoEnabled()) {
+                        log.info(String.format("Kubernetes service port generated: [application-id] %s " +
+                                        "[cluster-id] %s [port] %d [service-port] %d",
+                                applicationId, clusterId, clusterPortMapping.getPort(),
+                                clusterPortMapping.getKubernetesServicePort()));
                     }
                 }
 
                 NameValuePair nameValuePair = new NameValuePair(PORT_MAPPINGS, portMappingStrBuilder.toString());
                 payload.add(nameValuePair);
 
-                // Persist service ports added to port mappings
-                CloudControllerContext.getInstance().updateKubernetesClusterContext(kubernetesClusterContext);
+                // Persist service ports added to cluster port mappings
                 CloudControllerContext.getInstance().persist();
             }
         }
     }
 
+
+    /**
+     * Find cluster port mapping that corresponds to cartridge port mapping.
+     * @param clusterPortMappings
+     * @param portMapping
+     * @return
+     */
+    private ClusterPortMapping findClusterPortMapping(Collection<ClusterPortMapping> clusterPortMappings, PortMapping portMapping) {
+        for(ClusterPortMapping clusterPortMapping : clusterPortMappings) {
+            if(clusterPortMapping.getName().equals(portMapping.getName())) {
+                return clusterPortMapping;
+            }
+        }
+        return null;
+    }
+
     /**
      * Terminate all the containers belong to a cluster by cluster id.
      *

http://git-wip-us.apache.org/repos/asf/stratos/blob/b7667b18/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/messaging/topology/TopologyBuilder.java
----------------------------------------------------------------------
diff --git a/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/messaging/topology/TopologyBuilder.java b/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/messaging/topology/TopologyBuilder.java
index 4a78a6e..cfa7732 100644
--- a/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/messaging/topology/TopologyBuilder.java
+++ b/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/messaging/topology/TopologyBuilder.java
@@ -21,10 +21,8 @@ package org.apache.stratos.cloud.controller.messaging.topology;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.stratos.cloud.controller.context.CloudControllerContext;
-import org.apache.stratos.cloud.controller.domain.Cartridge;
-import org.apache.stratos.cloud.controller.domain.ClusterContext;
-import org.apache.stratos.cloud.controller.domain.MemberContext;
-import org.apache.stratos.cloud.controller.domain.PortMapping;
+import org.apache.stratos.cloud.controller.domain.*;
+import org.apache.stratos.cloud.controller.exception.CloudControllerException;
 import org.apache.stratos.cloud.controller.exception.InvalidCartridgeTypeException;
 import org.apache.stratos.cloud.controller.exception.InvalidMemberException;
 import org.apache.stratos.cloud.controller.messaging.publisher.TopologyEventPublisher;
@@ -193,11 +191,32 @@ public class TopologyBuilder {
                     log.info("Application Cluster " + cluster.getClusterId() + " created in CC topology");
                 }
             }
-
             TopologyManager.updateTopology(topology);
         } finally {
             TopologyManager.releaseWriteLock();
         }
+
+        log.debug("Creating cluster port mappings: [appication-id] " + appId);
+        for(Cluster cluster : appClusters) {
+            String cartridgeType = cluster.getServiceName();
+            Cartridge cartridge = CloudControllerContext.getInstance().getCartridge(cartridgeType);
+            if(cartridge == null) {
+                throw new CloudControllerException("Cartridge not found: [cartridge-type] " + cartridgeType);
+            }
+
+            for(PortMapping portMapping : cartridge.getPortMappings()) {
+                ClusterPortMapping clusterPortMapping = new ClusterPortMapping(appId,
+                        cluster.getClusterId(), portMapping.getName(), portMapping.getProtocol(), portMapping.getPort(),
+                        portMapping.getProxyPort());
+                CloudControllerContext.getInstance().addClusterPortMapping(clusterPortMapping);
+                log.debug("Cluster port mapping created: " + clusterPortMapping.toString());
+            }
+        }
+
+        // Persist cluster port mappings
+        CloudControllerContext.getInstance().persist();
+
+        // Send application clusters created event
         TopologyEventPublisher.sendApplicationClustersCreated(appId, appClusters);
     }
 
@@ -235,6 +254,10 @@ public class TopologyBuilder {
             TopologyManager.releaseWriteLock();
         }
 
+        // Remove cluster port mappings of application
+        CloudControllerContext.getInstance().removeClusterPortMappings(appId);
+        CloudControllerContext.getInstance().persist();
+
         TopologyEventPublisher.sendApplicationClustersRemoved(appId, clusterData);
 
     }

http://git-wip-us.apache.org/repos/asf/stratos/blob/b7667b18/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/services/impl/CloudControllerServiceImpl.java
----------------------------------------------------------------------
diff --git a/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/services/impl/CloudControllerServiceImpl.java b/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/services/impl/CloudControllerServiceImpl.java
index dab9f29..4d51cc1 100644
--- a/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/services/impl/CloudControllerServiceImpl.java
+++ b/components/org.apache.stratos.cloud.controller/src/main/java/org/apache/stratos/cloud/controller/services/impl/CloudControllerServiceImpl.java
@@ -1050,23 +1050,25 @@ public class CloudControllerServiceImpl implements CloudControllerService {
             Map<String, List<String>> accessUrls = new HashMap<String, List<String>>();
 
             for (ApplicationClusterContext appClusterCtxt : appClustersContexts) {
+                String clusterId = appClusterCtxt.getClusterId();
                 if (appClusterCtxt.isLbCluster()) {
                     String[] dependencyClusterIDs = appClusterCtxt.getDependencyClusterIds();
                     if (dependencyClusterIDs != null) {
                         for (int i = 0; i < dependencyClusterIDs.length; i++) {
-                            Cartridge cartridge = CloudControllerContext.getInstance().getCartridge(
-                                    appClusterCtxt.getCartridgeType());
+
                             List<String> accessUrlPerCluster = new ArrayList();
-                            List<PortMapping> portMappings = Arrays.asList(cartridge.getPortMappings());
-                            for (PortMapping portMap : portMappings) {
+                            Collection<ClusterPortMapping> clusterPortMappings =
+                                    CloudControllerContext.getInstance().getClusterPortMappings(appId, clusterId);
+
+                            for (ClusterPortMapping clusterPortMapping : clusterPortMappings) {
                                 try {
-                                    if (portMap.isKubernetesServicePortMapping()) {
-                                        URL accessUrl = new URL(portMap.getProtocol(), appClusterCtxt.getHostName(),
-                                                portMap.getKubernetesServicePort(), "");
+                                    if (clusterPortMapping.isKubernetes()) {
+                                        URL accessUrl = new URL(clusterPortMapping.getProtocol(), appClusterCtxt.getHostName(),
+                                                clusterPortMapping.getKubernetesServicePort(), "");
                                         accessUrlPerCluster.add(accessUrl.toString());
                                     } else {
-                                        URL accessUrl = new URL(portMap.getProtocol(), appClusterCtxt.getHostName(),
-                                                portMap.getProxyPort(), "");
+                                        URL accessUrl = new URL(clusterPortMapping.getProtocol(), appClusterCtxt.getHostName(),
+                                                clusterPortMapping.getProxyPort(), "");
                                         accessUrlPerCluster.add(accessUrl.toString());
                                     }
                                 } catch (MalformedURLException e) {

http://git-wip-us.apache.org/repos/asf/stratos/blob/b7667b18/components/org.apache.stratos.rest.endpoint/src/main/java/org/apache/stratos/rest/endpoint/api/StratosApiV41Utils.java
----------------------------------------------------------------------
diff --git a/components/org.apache.stratos.rest.endpoint/src/main/java/org/apache/stratos/rest/endpoint/api/StratosApiV41Utils.java b/components/org.apache.stratos.rest.endpoint/src/main/java/org/apache/stratos/rest/endpoint/api/StratosApiV41Utils.java
index 2211ef3..1a80bc2 100644
--- a/components/org.apache.stratos.rest.endpoint/src/main/java/org/apache/stratos/rest/endpoint/api/StratosApiV41Utils.java
+++ b/components/org.apache.stratos.rest.endpoint/src/main/java/org/apache/stratos/rest/endpoint/api/StratosApiV41Utils.java
@@ -127,6 +127,13 @@ public class StratosApiV41Utils {
                         cartridgeBean.getType()));
             }
 
+            for(PortMappingBean portMapping : cartridgeBean.getPortMapping()) {
+                if(StringUtils.isBlank(portMapping.getName())) {
+                    throw new RestAPIException(String.format("A name is required for each port mapping: " +
+                            "[cartridge] %s", cartridgeBean.getType()));
+                }
+            }
+
             Cartridge cartridgeConfig = createCartridgeConfig(cartridgeBean);
             CloudControllerServiceClient cloudControllerServiceClient = CloudControllerServiceClient.getInstance();
             cloudControllerServiceClient.addCartridge(cartridgeConfig);