You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by sm...@apache.org on 2016/12/09 21:57:13 UTC

[21/51] [abbrv] ambari git commit: AMBARI-19130 - Downgrade Can Create Multiple Mappings For Latest Configs (jonathanhurley)

AMBARI-19130 - Downgrade Can Create Multiple Mappings For Latest Configs (jonathanhurley)


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

Branch: refs/heads/branch-feature-AMBARI-18901
Commit: facfa8cea364370434f9e3596147801d73c0d1f0
Parents: 82f9401
Author: Jonathan Hurley <jh...@hortonworks.com>
Authored: Wed Dec 7 20:55:38 2016 -0500
Committer: Jonathan Hurley <jh...@hortonworks.com>
Committed: Thu Dec 8 17:35:18 2016 -0500

----------------------------------------------------------------------
 .../entities/ClusterConfigMappingEntity.java    |  20 ++-
 .../server/state/cluster/ClusterImpl.java       | 104 +++++++++-----
 .../server/orm/dao/ServiceConfigDAOTest.java    | 144 +++++++++----------
 .../server/state/cluster/ClusterTest.java       |  95 ++++++++++++
 4 files changed, 253 insertions(+), 110 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/facfa8ce/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterConfigMappingEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterConfigMappingEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterConfigMappingEntity.java
index 04c6030..5748dc9 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterConfigMappingEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterConfigMappingEntity.java
@@ -27,15 +27,17 @@ import javax.persistence.NamedQueries;
 import javax.persistence.NamedQuery;
 import javax.persistence.Table;
 
+import com.google.common.base.Objects;
+
 /**
  * Entity that maps to a cluster config mapping.
  */
-@Table(name = "clusterconfigmapping")
 @Entity
+@Table(name = "clusterconfigmapping")
 @IdClass(ClusterConfigMappingEntityPK.class)
-@NamedQueries({
-  @NamedQuery(name = "ClusterConfigMappingEntity.findLatestClusterConfigMappingsByType",
-    query = "SELECT mapping FROM ClusterConfigMappingEntity mapping WHERE mapping.clusterId = :clusterId AND mapping.selectedInd > 0 AND mapping.typeName = :typeName")})
+@NamedQueries({ @NamedQuery(
+    name = "ClusterConfigMappingEntity.findLatestClusterConfigMappingsByType",
+    query = "SELECT mapping FROM ClusterConfigMappingEntity mapping WHERE mapping.clusterId = :clusterId AND mapping.selectedInd > 0 AND mapping.typeName = :typeName") })
 
 public class ClusterConfigMappingEntity {
 
@@ -192,4 +194,14 @@ public class ClusterConfigMappingEntity {
 
     return true;
   }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String toString() {
+    return Objects.toStringHelper(this).add("clusterId", clusterId).add("type", typeName).add("tag",
+        tag).add("selected", selectedInd).add("created", createTimestamp).toString();
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/facfa8ce/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
index 649fe38..b62c834 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
@@ -22,6 +22,7 @@ import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Date;
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -144,8 +145,10 @@ import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Functions;
 import com.google.common.base.Predicate;
 import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Maps;
@@ -3072,6 +3075,7 @@ public class ClusterImpl implements Cluster {
    * {@inheritDoc}
    */
   @Override
+  @Transactional
   public void applyLatestConfigurations(StackId stackId) {
     clusterGlobalLock.writeLock().lock();
 
@@ -3079,36 +3083,33 @@ public class ClusterImpl implements Cluster {
       ClusterEntity clusterEntity = getClusterEntity();
       Collection<ClusterConfigMappingEntity> configMappingEntities = clusterEntity.getConfigMappingEntities();
 
+      // hash them for easier retrieval later - these are the same entity
+      // instances which exist on the cluster entity, so modification of the CCM
+      // entity here will affect the cluster CCM entities as well
+      ImmutableMap<Object, ClusterConfigMappingEntity> ccmMap = Maps.uniqueIndex(configMappingEntities, Functions.identity());
+
       // disable all configs
       for (ClusterConfigMappingEntity e : configMappingEntities) {
         LOG.debug("{} with tag {} is unselected", e.getType(), e.getTag());
         e.setSelected(0);
       }
 
-      List<ClusterConfigMappingEntity> clusterConfigMappingsForStack = clusterDAO.getClusterConfigMappingsByStack(
+      // work through the in-memory list, finding only the most recent mapping per type
+      Collection<ClusterConfigMappingEntity> latestConfigMappingByStack = getLatestConfigMappingsForStack(
           clusterEntity.getClusterId(), stackId);
 
-      Collection<ClusterConfigMappingEntity> latestConfigMappingByStack = getLatestConfigMapping(
-          clusterConfigMappingsForStack);
-
-      // loop through all configs and set the latest to enabled for the
-      // specified stack
-      for(ClusterConfigMappingEntity configMappingEntity: configMappingEntities){
-        String type = configMappingEntity.getType();
-        String tag =  configMappingEntity.getTag();
+      for( ClusterConfigMappingEntity latestConfigMapping : latestConfigMappingByStack ){
+        ClusterConfigMappingEntity mapping = ccmMap.get(latestConfigMapping);
+        mapping.setSelected(1);
 
-        for (ClusterConfigMappingEntity latest : latestConfigMappingByStack) {
-          String latestType = latest.getType();
-          String latestTag = latest.getTag();
-
-          // find the latest config of a given mapping entity
-          if (StringUtils.equals(type, latestType) && StringUtils.equals(tag, latestTag)) {
-            LOG.info("{} with version tag {} is selected for stack {}", type, tag, stackId.toString());
-            configMappingEntity.setSelected(1);
-          }
-        }
+        LOG.info("Settting {} with version tag {} created on {} to selected for stack {}",
+            mapping.getType(), mapping.getTag(), new Date(mapping.getCreateTimestamp()),
+            stackId.toString());
       }
 
+      // since the entities which were modified came from the cluster entity's
+      // list to begin with, we can just save them right back - no need for a
+      // new collection since the CCM entity instances were modified directly
       clusterEntity.setConfigMappingEntities(configMappingEntities);
       clusterEntity = clusterDAO.merge(clusterEntity);
       clusterDAO.mergeConfigMappings(configMappingEntities);
@@ -3130,23 +3131,60 @@ public class ClusterImpl implements Cluster {
     jpaEventPublisher.publish(event);
   }
 
-  public Collection<ClusterConfigMappingEntity> getLatestConfigMapping(List<ClusterConfigMappingEntity> clusterConfigMappingEntities){
-    Map<String, ClusterConfigMappingEntity> temp = new HashMap<String, ClusterConfigMappingEntity>();
-    for (ClusterConfigMappingEntity e : clusterConfigMappingEntities) {
-      String type = e.getType();
-      if(temp.containsKey(type)){
-        ClusterConfigMappingEntity entityStored = temp.get(type);
-        Long timestampStored = entityStored.getCreateTimestamp();
-        Long timestamp = e.getCreateTimestamp();
-        if(timestamp > timestampStored){
-          temp.put(type, e); //find a newer config for the given type
-        }
-      } else {
-        temp.put(type, e); //first time encounter a type, add it
+  /**
+   * Retrieves all of the configuration mappings (selected and unselected) for
+   * the specified stack and then iterates through them, returning the most
+   * recent mapping for every type/tag combination.
+   * <p/>
+   * Because of how configuration revert works, mappings can be created for the
+   * same type/tag combinations. The only difference being that the timestamp
+   * reflects when each mapping was created.
+   * <p/>
+   * JPQL cannot be used directly here easily because some databases cannot
+   * support the necessary grouping and IN clause. For example: <br/>
+   *
+   * <pre>
+   * SELECT mapping FROM clusterconfigmappingentity mapping
+   *   WHERE (mapping.typename, mapping.createtimestamp) IN
+   *     (SELECT latest.typename, MAX(latest.createtimestamp)
+   *      FROM clusterconfigmappingentity latest
+   *      GROUP BY latest.typename)
+   * </pre>
+   *
+   * @param clusterId
+   *          the cluster ID
+   * @param stackId
+   *          the stack to retrieve the mappings for (not {@code null}).
+   * @return the most recent mapping (selected or unselected) for the specified
+   *         stack for every type.
+   */
+  public Collection<ClusterConfigMappingEntity> getLatestConfigMappingsForStack(long clusterId,
+      StackId stackId) {
+
+    // get all mappings for the specified stack (which could include
+    // duplicates since a config revert creates a duplicate mapping with a
+    // different timestamp)
+    List<ClusterConfigMappingEntity> clusterConfigMappingsForStack = clusterDAO.getClusterConfigMappingsByStack(
+        clusterId, stackId);
+
+    Map<String, ClusterConfigMappingEntity> latestMappingsByType = new HashMap<String, ClusterConfigMappingEntity>();
+    for (ClusterConfigMappingEntity mapping : clusterConfigMappingsForStack) {
+      String type = mapping.getType();
+
+      if (!latestMappingsByType.containsKey(type)) {
+        latestMappingsByType.put(type, mapping);
+        continue;
+      }
+
+      ClusterConfigMappingEntity entityStored = latestMappingsByType.get(type);
+      Long timestampStored = entityStored.getCreateTimestamp();
+      Long timestamp = mapping.getCreateTimestamp();
+      if (timestamp > timestampStored) {
+        latestMappingsByType.put(type, mapping);
       }
     }
 
-    return temp.values();
+    return latestMappingsByType.values();
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/ambari/blob/facfa8ce/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/ServiceConfigDAOTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/ServiceConfigDAOTest.java b/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/ServiceConfigDAOTest.java
index 2388c11..aafe557 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/ServiceConfigDAOTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/ServiceConfigDAOTest.java
@@ -17,15 +17,11 @@
  */
 package org.apache.ambari.server.orm.dao;
 
-import static org.easymock.EasyMock.createMockBuilder;
-
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Date;
 import java.util.List;
 
-import junit.framework.Assert;
-
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.orm.GuiceJpaInitializer;
@@ -40,9 +36,12 @@ import org.apache.ambari.server.orm.entities.ResourceTypeEntity;
 import org.apache.ambari.server.orm.entities.ServiceConfigEntity;
 import org.apache.ambari.server.orm.entities.StackEntity;
 import org.apache.ambari.server.security.authorization.ResourceType;
+import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.StackId;
 import org.apache.ambari.server.state.cluster.ClusterImpl;
 import org.junit.After;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -393,7 +392,7 @@ public class ServiceConfigDAOTest {
     serviceConfigs = serviceConfigDAO.getLatestServiceConfigs(clusterId, HDP_02);
     Assert.assertEquals(2, serviceConfigs.size());
   }
-  
+
   @Test
   public void testConfiguration() throws Exception{
     initClusterEntities();
@@ -401,17 +400,17 @@ public class ServiceConfigDAOTest {
 
     Assert.assertTrue(!clusterEntity.getClusterConfigEntities().isEmpty());
     Assert.assertTrue(!clusterEntity.getConfigMappingEntities().isEmpty());
-    
+
     Assert.assertEquals(5, clusterEntity.getClusterConfigEntities().size());
     Assert.assertEquals(3, clusterEntity.getConfigMappingEntities().size());
   }
-  
+
   @Test
   public void testGetClusterConfigMappingByStack() throws Exception{
     initClusterEntities();
-    
+
     ClusterEntity clusterEntity = clusterDAO.findByName("c1");
-    
+
     List<ClusterConfigMappingEntity> clusterConfigMappingEntities = clusterDAO.getClusterConfigMappingsByStack(clusterEntity.getClusterId(), HDP_01);
     Assert.assertEquals(2, clusterConfigMappingEntities .size());
 
@@ -420,14 +419,14 @@ public class ServiceConfigDAOTest {
     Assert.assertEquals("version1", tag1);
     String type1 = e1.getType();
     Assert.assertEquals("oozie-site", type1);
-    
+
     ClusterConfigMappingEntity e2 = clusterConfigMappingEntities.get(1);
     String tag2 = e2.getTag();
     Assert.assertEquals("version2", tag2);
     String type2 = e2.getType();
     Assert.assertEquals("oozie-site", type2);
   }
-  
+
   /**
    * Test the get latest configuration query against clusterconfig table with configuration groups inserted
    * */
@@ -435,9 +434,9 @@ public class ServiceConfigDAOTest {
   public void testGetClusterConfigMappingByStackCG() throws Exception{
     initClusterEntitiesWithConfigGroups();
     ClusterEntity clusterEntity = clusterDAO.findByName("c1");
-    
+
     List<ConfigGroupEntity> configGroupEntities = configGroupDAO.findAllByTag("OOZIE");
-    
+
     Assert.assertNotNull(configGroupEntities);
     ConfigGroupEntity configGroupEntity = configGroupEntities.get(0);
     Assert.assertNotNull(configGroupEntity);
@@ -447,7 +446,7 @@ public class ServiceConfigDAOTest {
     Assert.assertEquals("oozie_server", configGroupEntity.getGroupName());
     Assert.assertEquals("OOZIE", configGroupEntity.getTag());
     Assert.assertEquals("oozie server", configGroupEntity.getDescription());
-    
+
     List<ClusterConfigMappingEntity> clusterConfigMappingEntities = clusterDAO.getClusterConfigMappingsByStack(clusterEntity.getClusterId(), HDP_01);
     Assert.assertEquals(2, clusterConfigMappingEntities .size());
 
@@ -456,97 +455,95 @@ public class ServiceConfigDAOTest {
     Assert.assertEquals("version1", tag1);
     String type1 = e1.getType();
     Assert.assertEquals("oozie-site", type1);
-    
+
     ClusterConfigMappingEntity e2 = clusterConfigMappingEntities.get(1);
     String tag2 = e2.getTag();
     Assert.assertEquals("version2", tag2);
     String type2 = e2.getType();
     Assert.assertEquals("oozie-site", type2);
   }
-  
+
   /**
-   * Test  
+   * Test
    *
-   * When the last configuration of a given configuration type to be stored into the clusterconfig table is 
+   * When the last configuration of a given configuration type to be stored into the clusterconfig table is
    * for a configuration group, there is no corresponding entry generated in the clusterconfigmapping.
    *
    * Therefore, the getlatestconfiguration query should skip configuration groups stored in the clusterconfig table.
    *
-   * Test to determine the latest configuration of a given type whose version_tag 
+   * Test to determine the latest configuration of a given type whose version_tag
    * exists in the clusterconfigmapping table.
    *
    * */
   @Test
-  public void testGetLatestClusterConfigMappingByStack() throws Exception{
-    ClusterImpl cluster =
-        createMockBuilder(ClusterImpl.class).
-          addMockedMethod("getSessionManager").
-          addMockedMethod("getClusterName").
-          addMockedMethod("getSessionAttributes").
-          createMock();
-    
+  public void testGetLatestClusterConfigMappingByStack() throws Exception {
+    Clusters clusters = injector.getInstance(Clusters.class);
+    clusters.addCluster("c1", HDP_01);
+
+    Cluster cluster = clusters.getCluster("c1");
+
     initClusterEntities();
-    ClusterEntity clusterEntity = clusterDAO.findByName("c1");
-    List<ClusterConfigMappingEntity> clusterConfigMappingEntities = clusterDAO.getClusterConfigMappingsByStack(clusterEntity.getClusterId(), HDP_01);
-    Collection<ClusterConfigMappingEntity> latestMapingEntities = cluster.getLatestConfigMapping(clusterConfigMappingEntities);
+
+    Collection<ClusterConfigMappingEntity> latestMapingEntities = ((ClusterImpl) cluster).getLatestConfigMappingsForStack(
+        cluster.getClusterId(), HDP_01);
+
     Assert.assertEquals(1, latestMapingEntities.size());
     for(ClusterConfigMappingEntity e: latestMapingEntities){
       Assert.assertEquals("version2", e.getTag());
       Assert.assertEquals("oozie-site", e.getType());
     }
   }
-  
+
   /**
-   * Test  
+   * Test
    *
-   * When the last configuration of a given configuration type to be stored into the clusterconfig table is 
+   * When the last configuration of a given configuration type to be stored into the clusterconfig table is
    * for a configuration group, there is no corresponding entry generated in the clusterconfigmapping.
    *
    * Therefore, the getlatestconfiguration query should skip configuration groups stored in the clusterconfig table.
    *
-   * Test to determine the latest configuration of a given type whose version_tag 
+   * Test to determine the latest configuration of a given type whose version_tag
    * exists in the clusterconfigmapping table.
    *
    * */
   @Test
   public void testGetLatestClusterConfigMappingByStackCG() throws Exception{
-    ClusterImpl cluster =
-        createMockBuilder(ClusterImpl.class).
-          addMockedMethod("getSessionManager").
-          addMockedMethod("getClusterName").
-          addMockedMethod("getSessionAttributes").
-          createMock();
-    
+    Clusters clusters = injector.getInstance(Clusters.class);
+    clusters.addCluster("c1", HDP_01);
+
+    Cluster cluster = clusters.getCluster("c1");
+
     initClusterEntitiesWithConfigGroups();
-    ClusterEntity clusterEntity = clusterDAO.findByName("c1");
-    List<ClusterConfigMappingEntity> clusterConfigMappingEntities = clusterDAO.getClusterConfigMappingsByStack(clusterEntity.getClusterId(), HDP_01);
-    Collection<ClusterConfigMappingEntity> latestMapingEntities = cluster.getLatestConfigMapping(clusterConfigMappingEntities);
+
+    Collection<ClusterConfigMappingEntity> latestMapingEntities = ((ClusterImpl) cluster).getLatestConfigMappingsForStack(
+        cluster.getClusterId(), HDP_01);
+
     Assert.assertEquals(1, latestMapingEntities.size());
     for(ClusterConfigMappingEntity e: latestMapingEntities){
       Assert.assertEquals("version2", e.getTag());
       Assert.assertEquals("oozie-site", e.getType());
     }
   }
-  
+
   private void initClusterEntities() throws Exception{
     String userName = "admin";
-    
+
     ServiceConfigEntity oozieServiceConfigEntity = createServiceConfig("OOZIE", userName, 1L, 1L, System.currentTimeMillis(), null);
     ClusterEntity  clusterEntity = oozieServiceConfigEntity.getClusterEntity();
-    
+
     Long clusterId = clusterEntity.getClusterId();
-    
+
     if(null == clusterId){
       clusterId = 1L;
       clusterEntity.setClusterId(clusterId);
       clusterEntity = clusterDAO.merge(clusterEntity);
     }
-    
+
     StackEntity stackEntityHDP01 = stackDAO.find(HDP_01.getStackName(),HDP_01.getStackVersion());
     StackEntity stackEntityHDP02 = stackDAO.find(HDP_02.getStackName(),HDP_02.getStackVersion());
-    
+
     String oozieSite = "oozie-site";
-    
+
     for (int i = 1; i < 6; i++){
       ClusterConfigEntity entity = new ClusterConfigEntity();
       entity.setClusterEntity(clusterEntity);
@@ -555,22 +552,23 @@ public class ServiceConfigDAOTest {
       entity.setVersion(Long.valueOf(i));
       entity.setTag("version"+i);
       entity.setTimestamp(new Date().getTime());
-      if(i < 4)
+      if(i < 4) {
         entity.setStack(stackEntityHDP01);
-      else
+      } else {
         entity.setStack(stackEntityHDP02);
+      }
       entity.setData("");
       clusterDAO.createConfig(entity);
       clusterEntity.getClusterConfigEntities().add(entity);
       clusterDAO.merge(clusterEntity);
     }
-    
+
     Collection<ClusterConfigMappingEntity> entities = clusterEntity.getConfigMappingEntities();
     if(null == entities){
       entities = new ArrayList<ClusterConfigMappingEntity>();
       clusterEntity.setConfigMappingEntities(entities);
-    }  
-    
+    }
+
     ClusterConfigMappingEntity e1 = new ClusterConfigMappingEntity();
     e1.setClusterEntity(clusterEntity);
     e1.setClusterId(clusterEntity.getClusterId());
@@ -581,7 +579,7 @@ public class ServiceConfigDAOTest {
     e1.setTag("version1");
     entities.add(e1);
     clusterDAO.merge(clusterEntity);
-    
+
     ClusterConfigMappingEntity e2 = new ClusterConfigMappingEntity();
     e2.setClusterEntity(clusterEntity);
     e2.setClusterId(clusterEntity.getClusterId());
@@ -592,7 +590,7 @@ public class ServiceConfigDAOTest {
     e2.setTag("version2");
     entities.add(e2);
     clusterDAO.merge(clusterEntity);
-    
+
     ClusterConfigMappingEntity e3 = new ClusterConfigMappingEntity();
     e3.setClusterEntity(clusterEntity);
     e3.setClusterId(clusterEntity.getClusterId());
@@ -603,25 +601,25 @@ public class ServiceConfigDAOTest {
     e3.setTag("version4");
     entities.add(e3);
     clusterDAO.merge(clusterEntity);
-  } 
-  
+  }
+
   private void initClusterEntitiesWithConfigGroups() throws Exception{
     String userName = "admin";
-    
+
     ServiceConfigEntity oozieServiceConfigEntity = createServiceConfig("OOZIE", userName, 1L, 1L, System.currentTimeMillis(), null);
     ClusterEntity  clusterEntity = oozieServiceConfigEntity.getClusterEntity();
-    
+
     Long clusterId = clusterEntity.getClusterId();
-    
+
     if(null == clusterId){
       clusterId = 1L;
       clusterEntity.setClusterId(clusterId);
       clusterEntity = clusterDAO.merge(clusterEntity);
     }
-    
+
     StackEntity stackEntityHDP01 = stackDAO.find(HDP_01.getStackName(),HDP_01.getStackVersion());
     String oozieSite = "oozie-site";
-    
+
     int count = 3;
     for (int i = 1; i < count; i++){
       ClusterConfigEntity entity = new ClusterConfigEntity();
@@ -637,13 +635,13 @@ public class ServiceConfigDAOTest {
       clusterEntity.getClusterConfigEntities().add(entity);
       clusterDAO.merge(clusterEntity);
     }
-    
+
     Collection<ClusterConfigMappingEntity> entities = clusterEntity.getConfigMappingEntities();
     if(null == entities){
       entities = new ArrayList<ClusterConfigMappingEntity>();
       clusterEntity.setConfigMappingEntities(entities);
-    }  
-    
+    }
+
     ClusterConfigMappingEntity e1 = new ClusterConfigMappingEntity();
     e1.setClusterEntity(clusterEntity);
     e1.setClusterId(clusterEntity.getClusterId());
@@ -654,7 +652,7 @@ public class ServiceConfigDAOTest {
     e1.setTag("version1");
     entities.add(e1);
     clusterDAO.merge(clusterEntity);
-    
+
     ClusterConfigMappingEntity e2 = new ClusterConfigMappingEntity();
     e2.setClusterEntity(clusterEntity);
     e2.setClusterId(clusterEntity.getClusterId());
@@ -665,7 +663,7 @@ public class ServiceConfigDAOTest {
     e2.setTag("version2");
     entities.add(e2);
     clusterDAO.merge(clusterEntity);
-    
+
     ConfigGroupEntity configGroupEntity = new ConfigGroupEntity();
 
     ResourceTypeEntity resourceTypeEntity = resourceTypeDAO.findById(ResourceType.CLUSTER.getId());
@@ -695,9 +693,9 @@ public class ServiceConfigDAOTest {
     List<ClusterConfigEntity> configEntities = new
       ArrayList<ClusterConfigEntity>();
     configEntities.add(configEntity);
-    
+
     configGroupDAO.create(configGroupEntity);
-    
+
     if (configEntities != null && !configEntities.isEmpty()) {
       List<ConfigGroupConfigMappingEntity> configMappingEntities = new
         ArrayList<ConfigGroupConfigMappingEntity>();
@@ -719,7 +717,7 @@ public class ServiceConfigDAOTest {
         configMappingEntities.add(configMappingEntity);
         configGroupConfigMappingDAO.create(configMappingEntity);
       }
-      
+
       configGroupEntity.setConfigGroupConfigMappingEntities(configMappingEntities);
       configGroupDAO.merge(configGroupEntity);
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/facfa8ce/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterTest.java
index fc3646a..daa3abc 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterTest.java
@@ -2467,6 +2467,101 @@ public class ClusterTest {
   }
 
   /**
+   * Tests that {@link Cluster#applyLatestConfigurations(StackId)} sets the
+   * right configs to enabled when there are duplicate mappings for type/tag.
+   * Only the most recent should be enabled.
+   *
+   * @throws Exception
+   */
+  @Test
+  public void testApplyLatestConfigurationsWithMultipleMappings() throws Exception {
+    createDefaultCluster();
+    Cluster cluster = clusters.getCluster("c1");
+    ClusterEntity clusterEntity = clusterDAO.findByName("c1");
+    StackId stackId = cluster.getCurrentStackVersion();
+
+    StackEntity currentStack = stackDAO.find(stackId.getStackName(), stackId.getStackVersion());
+
+    String configType = "foo-type";
+    String configTag = "version-1";
+
+    // create the config for the mappings
+    ClusterConfigEntity clusterConfig = new ClusterConfigEntity();
+    clusterConfig.setClusterEntity(clusterEntity);
+    clusterConfig.setConfigId(1L);
+    clusterConfig.setStack(currentStack);
+    clusterConfig.setTag(configTag);
+    clusterConfig.setData("{}");
+    clusterConfig.setType(configType);
+    clusterConfig.setTimestamp(1L);
+    clusterConfig.setVersion(1L);
+
+    clusterDAO.createConfig(clusterConfig);
+    clusterEntity.getClusterConfigEntities().add(clusterConfig);
+    clusterEntity = clusterDAO.merge(clusterEntity);
+
+    // create 3 mappings for the same type/tag, each with a different time
+
+    // config mapping 1
+    ClusterConfigMappingEntity configMapping = new ClusterConfigMappingEntity();
+    configMapping.setClusterEntity(clusterEntity);
+    configMapping.setCreateTimestamp(1L);
+    configMapping.setSelected(0);
+    configMapping.setTag(configTag);
+    configMapping.setType(configType);
+    configMapping.setUser("admin");
+    clusterDAO.persistConfigMapping(configMapping);
+    clusterEntity.getConfigMappingEntities().add(configMapping);
+
+    // config mapping 2
+    configMapping = new ClusterConfigMappingEntity();
+    configMapping.setClusterEntity(clusterEntity);
+    configMapping.setCreateTimestamp(2L);
+    configMapping.setSelected(0);
+    configMapping.setTag(configTag);
+    configMapping.setType(configType);
+    configMapping.setUser("admin");
+    clusterDAO.persistConfigMapping(configMapping);
+    clusterEntity.getConfigMappingEntities().add(configMapping);
+
+    // config mapping 3
+    configMapping = new ClusterConfigMappingEntity();
+    configMapping.setClusterEntity(clusterEntity);
+    configMapping.setCreateTimestamp(3L);
+    configMapping.setSelected(0);
+    configMapping.setTag(configTag);
+    configMapping.setType(configType);
+    configMapping.setUser("admin");
+    clusterDAO.persistConfigMapping(configMapping);
+    clusterEntity.getConfigMappingEntities().add(configMapping);
+
+    clusterEntity = clusterDAO.merge(clusterEntity);
+
+    // check all 3 mappings are disabled
+    Collection<ClusterConfigMappingEntity> clusterConfigMappings = clusterEntity.getConfigMappingEntities();
+    Assert.assertEquals(3, clusterConfigMappings.size());
+    for (ClusterConfigMappingEntity clusterConfigMapping : clusterConfigMappings) {
+      Assert.assertEquals(0, clusterConfigMapping.isSelected());
+    }
+
+    // apply configurations and check to see we've set the one with the latest
+    // timestamp ONLY
+    cluster.applyLatestConfigurations(cluster.getCurrentStackVersion());
+    clusterEntity = clusterDAO.findByName("c1");
+
+    // now check that the new config mapping is enabled
+    clusterConfigMappings = clusterEntity.getConfigMappingEntities();
+    Assert.assertEquals(3, clusterConfigMappings.size());
+    for (ClusterConfigMappingEntity clusterConfigMapping : clusterConfigMappings) {
+      if (clusterConfigMapping.getCreateTimestamp() < 3) {
+        Assert.assertEquals(0, clusterConfigMapping.isSelected());
+      } else {
+        Assert.assertEquals(1, clusterConfigMapping.isSelected());
+      }
+    }
+  }
+
+  /**
    * Tests that applying configurations for a given stack correctly sets
    * {@link DesiredConfig}s.
    */