Pārlūkot izejas kodu

AMBARI-6281. Provide 'properties_attributes' property on configuration provided by /api/v1/configurations. (srimanth)

Srimanth Gunturi 11 gadi atpakaļ
vecāks
revīzija
96bad666fa
29 mainītis faili ar 812 papildinājumiem un 141 dzēšanām
  1. 11 5
      ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
  2. 17 1
      ambari-server/src/main/java/org/apache/ambari/server/controller/ConfigurationRequest.java
  3. 12 1
      ambari-server/src/main/java/org/apache/ambari/server/controller/ConfigurationResponse.java
  4. 18 2
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractResourceProvider.java
  5. 27 4
      ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ConfigurationResourceProvider.java
  6. 21 4
      ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterConfigEntity.java
  7. 11 0
      ambari-server/src/main/java/org/apache/ambari/server/state/Config.java
  8. 21 2
      ambari-server/src/main/java/org/apache/ambari/server/state/ConfigFactory.java
  9. 20 2
      ambari-server/src/main/java/org/apache/ambari/server/state/ConfigImpl.java
  10. 3 0
      ambari-server/src/main/java/org/apache/ambari/server/state/configgroup/ConfigGroupImpl.java
  11. 1 0
      ambari-server/src/main/java/org/apache/ambari/server/upgrade/SchemaUpgradeHelper.java
  12. 78 0
      ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog170.java
  13. 1 1
      ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
  14. 1 1
      ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
  15. 1 1
      ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
  16. 1 1
      ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql
  17. 7 4
      ambari-server/src/test/java/org/apache/ambari/server/actionmanager/ExecutionCommandWrapperTest.java
  18. 2 2
      ambari-server/src/test/java/org/apache/ambari/server/agent/TestHeartbeatMonitor.java
  19. 177 68
      ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerTest.java
  20. 6 6
      ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AbstractResourceProviderTest.java
  21. 56 6
      ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ConfigurationResourceProviderTest.java
  22. 5 5
      ambari-server/src/test/java/org/apache/ambari/server/controller/internal/JMXHostProviderTest.java
  23. 2 0
      ambari-server/src/test/java/org/apache/ambari/server/orm/dao/ConfigGroupDAOTest.java
  24. 31 2
      ambari-server/src/test/java/org/apache/ambari/server/state/ConfigGroupTest.java
  25. 19 8
      ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterTest.java
  26. 2 2
      ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClustersTest.java
  27. 2 2
      ambari-server/src/test/java/org/apache/ambari/server/state/host/HostTest.java
  28. 130 11
      ambari-server/src/test/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostTest.java
  29. 129 0
      ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog170Test.java

+ 11 - 5
ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java

@@ -78,7 +78,6 @@ import org.apache.ambari.server.agent.ExecutionCommand;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.controller.internal.RequestOperationLevel;
-import org.apache.ambari.server.controller.internal.RequestResourceFilter;
 import org.apache.ambari.server.controller.internal.RequestStageContainer;
 import org.apache.ambari.server.controller.internal.URLStreamProvider;
 import org.apache.ambari.server.customactions.ActionDefinition;
@@ -613,6 +612,12 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
       configs = new HashMap<String, Config>();
     }
 
+    // Configuration attributes are optional. If not present, use empty map
+    Map<String, Map<String, String>> propertiesAttributes = request.getPropertiesAttributes();
+    if (null == propertiesAttributes) {
+      propertiesAttributes = new HashMap<String, Map<String,String>>();
+    }
+
     if (configs.containsKey(request.getVersionTag())) {
       throw new AmbariException(MessageFormat.format("Configuration with tag ''{0}'' exists for ''{1}''",
           request.getVersionTag(),
@@ -620,7 +625,7 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
     }
 
     Config config = configFactory.createNew (cluster, request.getType(),
-        request.getProperties());
+        request.getProperties(), propertiesAttributes);
     config.setVersionTag(request.getVersionTag());
 
     config.persist();
@@ -931,7 +936,7 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
       if (null != config) {
         ConfigurationResponse response = new ConfigurationResponse(
             cluster.getClusterName(), config.getType(), config.getVersionTag(),
-            config.getProperties());
+            config.getProperties(), config.getPropertiesAttributes());
         responses.add(response);
       }
     }
@@ -944,7 +949,8 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
           for (Entry<String, Config> entry : configs.entrySet()) {
             ConfigurationResponse response = new ConfigurationResponse(
                 cluster.getClusterName(), request.getType(),
-                entry.getValue().getVersionTag(), new HashMap<String, String>());
+                entry.getValue().getVersionTag(), new HashMap<String, String>(),
+                new HashMap<String, Map<String,String>>());
             responses.add(response);
           }
         }
@@ -955,7 +961,7 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
         for (Config config : all) {
           ConfigurationResponse response = new ConfigurationResponse(
              cluster.getClusterName(), config.getType(), config.getVersionTag(),
-             new HashMap<String, String>());
+             new HashMap<String, String>(), new HashMap<String, Map<String,String>>());
 
           responses.add(response);
         }

+ 17 - 1
ambari-server/src/main/java/org/apache/ambari/server/controller/ConfigurationRequest.java

@@ -32,21 +32,25 @@ public class ConfigurationRequest {
   private String tag;
   private Map<String, String> configs;
   private boolean selected = true;
+  private Map<String, Map<String, String>> configsAttributes;
 
   public ConfigurationRequest() {
     configs = new HashMap<String, String>();
+    configsAttributes = new HashMap<String, Map<String,String>>();
   }
   
   public ConfigurationRequest(String clusterName,
                               String type,
                               String tag,
-                              Map<String, String> configs) {
+                              Map<String, String> configs,
+                              Map<String, Map<String, String>> configsAttributes) {
 
     this.clusterName = clusterName;
     this.configs = configs;
     this.type = type;
     this.tag = tag;
     this.configs = configs;
+    this.configsAttributes = configsAttributes;
   }
 
   /**
@@ -121,4 +125,16 @@ public class ConfigurationRequest {
   public boolean isSelected() {
     return selected;
   }
+
+  /**
+   * @return Attributes of configs
+   */
+  public Map<String, Map<String, String>> getPropertiesAttributes() {
+    return configsAttributes;
+  }
+
+  public void setPropertiesAttributes(
+      Map<String, Map<String, String>> configsAttributes) {
+    this.configsAttributes = configsAttributes;
+  }
 }

+ 12 - 1
ambari-server/src/main/java/org/apache/ambari/server/controller/ConfigurationResponse.java

@@ -34,16 +34,19 @@ public class ConfigurationResponse {
 
   private Map<String, String> configs;
 
+  private Map<String, Map<String, String>> configAttributes;
 
   public ConfigurationResponse(String clusterName,
                                String type, String versionTag,
-                               Map<String, String> configs) {
+                               Map<String, String> configs,
+                               Map<String, Map<String, String>> configAttributes) {
     super();
     this.clusterName = clusterName;
     this.configs = configs;
     this.type = type;
     this.versionTag = versionTag;
     this.configs = configs;
+    this.configAttributes = configAttributes;
   }
 
 
@@ -75,6 +78,14 @@ public class ConfigurationResponse {
     this.configs = configs;
   }
 
+  public Map<String, Map<String, String>> getConfigAttributes() {
+    return configAttributes;
+  }
+
+  public void setConfigAttributes(Map<String, Map<String, String>> configAttributes) {
+    this.configAttributes = configAttributes;
+  }
+
   /**
    * @return the type
    */

+ 18 - 2
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractResourceProvider.java

@@ -24,6 +24,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.Map.Entry;
+import java.util.regex.Pattern;
 
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.DuplicateResourceException;
@@ -52,8 +53,9 @@ public abstract class AbstractResourceProvider extends BaseProvider implements R
    */
   private final Set<ResourceProviderObserver> observers = new HashSet<ResourceProviderObserver>();
 
-  protected final static Logger LOG =
-      LoggerFactory.getLogger(AbstractResourceProvider.class);
+  protected final static Logger LOG = LoggerFactory.getLogger(AbstractResourceProvider.class);
+  protected final static String PROPERTIES_ATTRIBUTES_REGEX = "properties_attributes/[a-zA-Z][a-zA-Z._-]*$";
+  private static Pattern propertiesAttributesPattern = Pattern.compile(".*/" + PROPERTIES_ATTRIBUTES_REGEX);
 
 
   // ----- Constructors ------------------------------------------------------
@@ -334,6 +336,20 @@ public abstract class AbstractResourceProvider extends BaseProvider implements R
         else if (absCategory.endsWith("/properties")) {
           config.getProperties().put(propName, entry.getValue().toString());
         }
+        else if (propertiesAttributesPattern.matcher(absCategory).matches()) {
+          String attributeName = absCategory.substring(absCategory.lastIndexOf('/') + 1);
+          Map<String, Map<String, String>> configAttributesMap = config.getPropertiesAttributes();
+          if (null == configAttributesMap) {
+            configAttributesMap = new HashMap<String, Map<String,String>>();
+            config.setPropertiesAttributes(configAttributesMap);
+          }
+          Map<String, String> attributesMap = configAttributesMap.get(attributeName);
+          if (null == attributesMap) {
+            attributesMap = new HashMap<String, String>();
+            configAttributesMap.put(attributeName, attributesMap);
+          }
+          attributesMap.put(PropertyHelper.getPropertyName(entry.getKey()), entry.getValue().toString());
+        }
       }
     }
 

+ 27 - 4
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ConfigurationResourceProvider.java

@@ -39,6 +39,7 @@ import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 import java.util.Map.Entry;
+import java.util.regex.Pattern;
 
 /**
  * Resource provider for configuration resources.
@@ -46,6 +47,8 @@ import java.util.Map.Entry;
 public class ConfigurationResourceProvider extends
   AbstractControllerResourceProvider {
 
+  private static Pattern propertiesAttributesPattern = Pattern.compile("^" + PROPERTIES_ATTRIBUTES_REGEX);
+
   // ----- Property ID constants ---------------------------------------------
 
   // Configurations (values are part of query strings and body post, so they don't have defined categories)
@@ -98,15 +101,28 @@ public class ConfigurationResourceProvider extends
       String tag  = (String) map.get(CONFIGURATION_CONFIG_TAG_PROPERTY_ID);
 
       Map<String, String> configMap = new HashMap<String, String>();
+      Map<String, Map<String, String>> configAttributesMap = null;
 
       for (Entry<String, Object> entry : map.entrySet()) {
         String propertyCategory = PropertyHelper.getPropertyCategory(entry.getKey());
         if (propertyCategory != null && propertyCategory.equals("properties") && null != entry.getValue()) {
           configMap.put(PropertyHelper.getPropertyName(entry.getKey()), entry.getValue().toString());
         }
+        if (propertyCategory != null && propertiesAttributesPattern.matcher(propertyCategory).matches() && null != entry.getValue()) {
+          if (null == configAttributesMap) {
+            configAttributesMap = new HashMap<String, Map<String,String>>();
+          }
+          String attributeName = propertyCategory.substring(propertyCategory.lastIndexOf('/') + 1);
+          Map<String, String> attributesMap = configAttributesMap.get(attributeName);
+          if (attributesMap == null) {
+            attributesMap = new HashMap<String, String>();
+            configAttributesMap.put(attributeName, attributesMap);
+          }
+          attributesMap.put(PropertyHelper.getPropertyName(entry.getKey()), entry.getValue().toString());
+        }
       }
 
-      final ConfigurationRequest configRequest = new ConfigurationRequest(cluster, type, tag, configMap);
+      final ConfigurationRequest configRequest = new ConfigurationRequest(cluster, type, tag, configMap, configAttributesMap);
 
       createResources(new Command<Void>() {
         @Override
@@ -152,6 +168,13 @@ public class ConfigurationResourceProvider extends
           resource.setProperty(id, entry.getValue());
         }
       }
+      if (null != response.getConfigAttributes() && response.getConfigAttributes().size() > 0) {
+        Map<String, Map<String, String>> configAttributes = response.getConfigAttributes();
+        for (Entry<String, Map<String, String>> configAttribute : configAttributes.entrySet()) {
+          String id = PropertyHelper.getPropertyId("properties_attributes", configAttribute.getKey());
+          resource.setProperty(id, configAttribute.getValue());
+        }
+      }
       resources.add(resource);
     }
     return resources;
@@ -190,11 +213,11 @@ public class ConfigurationResourceProvider extends
       // for example, the tag property can come here as Config/tag, /tag or tag
       if (!propertyId.equals("tag") && !propertyId.equals("type") &&
           !propertyId.equals("/tag") && !propertyId.equals("/type") &&
-          !propertyId.equals("properties")) {
+          !propertyId.equals("properties") && !propertyId.equals("properties_attributes")) {
 
         String propertyCategory = PropertyHelper.getPropertyCategory(propertyId);
 
-        if (propertyCategory == null || !propertyCategory.equals("properties")) {
+        if (propertyCategory == null || !(propertyCategory.equals("properties") || propertiesAttributesPattern.matcher(propertyCategory).matches())) {
           unsupportedProperties.add(propertyId);
         }
       }
@@ -225,6 +248,6 @@ public class ConfigurationResourceProvider extends
 
     return new ConfigurationRequest(
         (String) properties.get(CONFIGURATION_CLUSTER_NAME_PROPERTY_ID),
-        type, tag, new HashMap<String, String>());
+        type, tag, new HashMap<String, String>(), new HashMap<String, Map<String, String>>());
   }
 }

+ 21 - 4
ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterConfigEntity.java

@@ -38,12 +38,17 @@ public class ClusterConfigEntity {
   @Column(name = "version_tag")
   private String tag;
 
-  @Basic(fetch=FetchType.LAZY)
-  @Column(name = "config_data", nullable = false, insertable = true, updatable = false, length=32000)
+  @Basic(fetch = FetchType.LAZY)
+  @Column(name = "config_data", nullable = false, insertable = true, updatable = false, length = 32000)
   @Lob
   private String configJson;
 
-  @Column(name = "create_timestamp", nullable=false, insertable=true, updatable=false)
+  @Basic(fetch = FetchType.LAZY)
+  @Column(name = "config_attributes", nullable = true, insertable = true, updatable = false, length = 32000)
+  @Lob
+  private String configAttributesJson;
+
+  @Column(name = "create_timestamp", nullable = false, insertable = true, updatable = false)
   private long timestamp;
 
   @ManyToOne
@@ -93,6 +98,14 @@ public class ClusterConfigEntity {
     timestamp = stamp;
   }
 
+  public String getAttributes() {
+    return configAttributesJson;
+  }
+
+  public void setAttributes(String attributes) {
+    this.configAttributesJson = attributes;
+  }
+
   @Override
   public boolean equals(Object o) {
     if (this == o) return true;
@@ -103,14 +116,18 @@ public class ClusterConfigEntity {
     if (clusterId != null ? !clusterId.equals(that.clusterId) : that.clusterId != null) return false;
     if (configJson != null ? !configJson.equals(that.configJson) : that.configJson != null)
       return false;
+    if (configAttributesJson != null ? !configAttributesJson
+        .equals(that.configAttributesJson) : that.configAttributesJson != null)
+      return false;
 
     return true;
   }
 
   @Override
   public int hashCode() {
-    int result = clusterId !=null ? clusterId.intValue() : 0;
+    int result = clusterId != null ? clusterId.intValue() : 0;
     result = 31 * result + (configJson != null ? configJson.hashCode() : 0);
+    result = 31 * result + (configAttributesJson != null ? configAttributesJson.hashCode() : 0);
     return result;
   }
 

+ 11 - 0
ambari-server/src/main/java/org/apache/ambari/server/state/Config.java

@@ -41,6 +41,11 @@ public interface Config {
    */
   public Map<String, String> getProperties();
 
+  /**
+   * @return Map of attributes in this config-type to value per property
+   */
+  public Map<String, Map<String, String>> getPropertiesAttributes();
+
   /**
    * Change the version tag
    * @param versionTag
@@ -53,6 +58,12 @@ public interface Config {
    */
   public void setProperties(Map<String, String> properties);
 
+  /**
+   * Replace property attributes with new provided set
+   * @param propertiesAttributes Property Attributes Map to replace existing one
+   */
+  public void setPropertiesAttributes(Map<String, Map<String, String>> propertiesAttributes);
+
   /**
    * Update provided properties' values.
    * @param properties Property Map with updated values

+ 21 - 2
ambari-server/src/main/java/org/apache/ambari/server/state/ConfigFactory.java

@@ -21,14 +21,33 @@ import java.util.Map;
 
 import org.apache.ambari.server.orm.entities.ClusterConfigEntity;
 
+import com.google.inject.assistedinject.Assisted;
+
 /**
- * @author ncole
+ * Factory for creating configuration objects using {@link Assisted} constructor parameters
  *
+ * @author ncole
  */
 public interface ConfigFactory {
   
-  Config createNew(Cluster cluster, String type, Map<String, String> map);
+  /**
+   * Creates a new {@link Config} object using provided values.
+   *
+   * @param cluster
+   * @param type
+   * @param map
+   * @param mapAttributes
+   * @return
+   */
+  Config createNew(Cluster cluster, String type, Map<String, String> map, Map<String, Map<String, String>> mapAttributes);
   
+  /**
+   * Creates a new {@link Config} object using provided entity
+   *
+   * @param cluster
+   * @param entity
+   * @return
+   */
   Config createExisting(Cluster cluster, ClusterConfigEntity entity);
 
 }

+ 20 - 2
ambari-server/src/main/java/org/apache/ambari/server/state/ConfigImpl.java

@@ -18,7 +18,6 @@
 
 package org.apache.ambari.server.state;
 
-import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
@@ -41,6 +40,7 @@ public class ConfigImpl implements Config {
   private String type;
   private String versionTag;
   private Map<String, String> properties;
+  private Map<String, Map<String, String>> propertiesAttributes;
   private ClusterConfigEntity entity;
 
   @Inject
@@ -49,10 +49,12 @@ public class ConfigImpl implements Config {
   private Gson gson;
 
   @AssistedInject
-  public ConfigImpl(@Assisted Cluster cluster, @Assisted String type, @Assisted Map<String, String> properties, Injector injector) {
+  public ConfigImpl(@Assisted Cluster cluster, @Assisted String type, @Assisted Map<String, String> properties, 
+      @Assisted Map<String, Map<String, String>> propertiesAttributes, Injector injector) {
     this.cluster = cluster;
     this.type = type;
     this.properties = properties;
+    this.propertiesAttributes = propertiesAttributes;
     injector.injectMembers(this);
     
   }
@@ -94,6 +96,14 @@ public class ConfigImpl implements Config {
         : new HashMap<String, String>(properties);
   }
 
+  @Override
+  public synchronized Map<String, Map<String, String>> getPropertiesAttributes() {
+    if (null != entity && null == propertiesAttributes) {
+      propertiesAttributes = gson.<Map<String, Map<String, String>>>fromJson(entity.getAttributes(), Map.class);
+    }
+    return null == propertiesAttributes ? null : new HashMap<String, Map<String, String>>(propertiesAttributes);
+  }
+
   @Override
   public synchronized void setVersionTag(String versionTag) {
     this.versionTag = versionTag;
@@ -104,6 +114,11 @@ public class ConfigImpl implements Config {
     this.properties = properties;
   }
 
+  @Override
+  public void setPropertiesAttributes(Map<String, Map<String, String>> propertiesAttributes) {
+    this.propertiesAttributes = propertiesAttributes;
+  }
+
   @Override
   public synchronized void updateProperties(Map<String, String> properties) {
     this.properties.putAll(properties);
@@ -130,6 +145,9 @@ public class ConfigImpl implements Config {
     entity.setTimestamp(new Date().getTime());
     
     entity.setData(gson.toJson(getProperties()));
+    if (null != getPropertiesAttributes()) {
+      entity.setAttributes(gson.toJson(getPropertiesAttributes()));
+    }
     clusterDAO.createConfig(entity);
 
     clusterEntity.getClusterConfigEntities().add(entity);

+ 3 - 0
ambari-server/src/main/java/org/apache/ambari/server/state/configgroup/ConfigGroupImpl.java

@@ -384,6 +384,9 @@ public class ConfigGroupImpl implements ConfigGroup {
           clusterConfigEntity.setType(config.getType());
           clusterConfigEntity.setTag(config.getVersionTag());
           clusterConfigEntity.setData(gson.toJson(config.getProperties()));
+          if (null != config.getPropertiesAttributes()) {
+            clusterConfigEntity.setAttributes(gson.toJson(config.getPropertiesAttributes()));
+          }
           clusterConfigEntity.setTimestamp(System.currentTimeMillis());
 
           // TODO: Is locking necessary and functional ?

+ 1 - 0
ambari-server/src/main/java/org/apache/ambari/server/upgrade/SchemaUpgradeHelper.java

@@ -166,6 +166,7 @@ public class SchemaUpgradeHelper {
       catalogBinder.addBinding().to(UpgradeCatalog151.class);
       catalogBinder.addBinding().to(UpgradeCatalog160.class);
       catalogBinder.addBinding().to(UpgradeCatalog161.class);
+      catalogBinder.addBinding().to(UpgradeCatalog170.class);
     }
   }
 

+ 78 - 0
ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog170.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.ambari.server.upgrade;
+
+import java.sql.SQLException;
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.orm.DBAccessor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+
+/**
+ * Upgrade catalog for version 1.7.0.
+ */
+public class UpgradeCatalog170 extends AbstractUpgradeCatalog {
+
+  //SourceVersion is only for book-keeping purpos
+  @Override
+  public String getSourceVersion() {
+    return "1.6.1";
+  }
+
+  @Override
+  public String getTargetVersion() {
+    return "1.7.0";
+  }
+
+  /**
+   * Logger.
+   */
+  private static final Logger LOG = LoggerFactory.getLogger
+      (UpgradeCatalog170.class);
+  
+  // ----- Constructors ------------------------------------------------------
+
+  @Inject
+  public UpgradeCatalog170(Injector injector) {
+    super(injector);
+  }
+
+
+  // ----- AbstractUpgradeCatalog --------------------------------------------
+
+  @Override
+  protected void executeDDLUpdates() throws AmbariException, SQLException {
+    DBAccessor.DBColumnInfo clusterConfigAttributesColumn = new DBAccessor.DBColumnInfo(
+        "config_attributes", String.class, 32000, null, true);
+    dbAccessor.addColumn("clusterconfig", clusterConfigAttributesColumn);
+  }
+
+
+  // ----- UpgradeCatalog ----------------------------------------------------
+
+  @Override
+  protected void executeDMLUpdates() throws AmbariException, SQLException {}
+  
+  protected void addMissingConfigs() throws AmbariException {}
+
+}

+ 1 - 1
ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql

@@ -27,7 +27,7 @@ delimiter ;
 # USE @schema;
 
 CREATE TABLE clusters (cluster_id BIGINT NOT NULL, cluster_info VARCHAR(255) NOT NULL, cluster_name VARCHAR(100) NOT NULL UNIQUE, provisioning_state VARCHAR(255) NOT NULL DEFAULT 'INIT', desired_cluster_state VARCHAR(255) NOT NULL, desired_stack_version VARCHAR(255) NOT NULL, PRIMARY KEY (cluster_id));
-CREATE TABLE clusterconfig (version_tag VARCHAR(255) NOT NULL, type_name VARCHAR(255) NOT NULL, cluster_id BIGINT NOT NULL, config_data LONGTEXT NOT NULL, create_timestamp BIGINT NOT NULL, PRIMARY KEY (version_tag, type_name, cluster_id));
+CREATE TABLE clusterconfig (version_tag VARCHAR(255) NOT NULL, type_name VARCHAR(255) NOT NULL, cluster_id BIGINT NOT NULL, config_data LONGTEXT NOT NULL, config_attributes LONGTEXT, create_timestamp BIGINT NOT NULL, PRIMARY KEY (version_tag, type_name, cluster_id));
 CREATE TABLE clusterservices (service_name VARCHAR(255) NOT NULL, cluster_id BIGINT NOT NULL, service_enabled INTEGER NOT NULL, PRIMARY KEY (service_name, cluster_id));
 CREATE TABLE clusterstate (cluster_id BIGINT NOT NULL, current_cluster_state VARCHAR(255) NOT NULL, current_stack_version VARCHAR(255) NOT NULL, PRIMARY KEY (cluster_id));
 CREATE TABLE hostcomponentdesiredstate (cluster_id BIGINT NOT NULL, component_name VARCHAR(255) NOT NULL, desired_stack_version VARCHAR(255) NOT NULL, desired_state VARCHAR(255) NOT NULL, host_name VARCHAR(255) NOT NULL, service_name VARCHAR(255) NOT NULL, admin_state VARCHAR(32), maintenance_state VARCHAR(32) NOT NULL DEFAULT 'ACTIVE', restart_required TINYINT(1) NOT NULL DEFAULT 0, PRIMARY KEY (cluster_id, component_name, host_name, service_name));

+ 1 - 1
ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql

@@ -17,7 +17,7 @@
 --
 
 CREATE TABLE clusters (cluster_id NUMBER(19) NOT NULL, cluster_info VARCHAR2(255) NULL, cluster_name VARCHAR2(100) NOT NULL UNIQUE, provisioning_state VARCHAR2(255) DEFAULT 'INIT' NOT NULL, desired_cluster_state VARCHAR2(255) NULL, desired_stack_version VARCHAR2(255) NULL, PRIMARY KEY (cluster_id));
-CREATE TABLE clusterconfig (version_tag VARCHAR2(255) NOT NULL, type_name VARCHAR2(255) NOT NULL, cluster_id NUMBER(19) NOT NULL, config_data CLOB NOT NULL, create_timestamp NUMBER(19) NOT NULL, PRIMARY KEY (version_tag, type_name, cluster_id));
+CREATE TABLE clusterconfig (version_tag VARCHAR2(255) NOT NULL, type_name VARCHAR2(255) NOT NULL, cluster_id NUMBER(19) NOT NULL, config_data CLOB NOT NULL, config_attributes CLOB NOT NULL, create_timestamp NUMBER(19) NOT NULL, PRIMARY KEY (version_tag, type_name, cluster_id));
 CREATE TABLE clusterservices (service_name VARCHAR2(255) NOT NULL, cluster_id NUMBER(19) NOT NULL, service_enabled NUMBER(10) NOT NULL, PRIMARY KEY (service_name, cluster_id));
 CREATE TABLE clusterstate (cluster_id NUMBER(19) NOT NULL, current_cluster_state VARCHAR2(255) NULL, current_stack_version VARCHAR2(255) NULL, PRIMARY KEY (cluster_id));
 CREATE TABLE hostcomponentdesiredstate (cluster_id NUMBER(19) NOT NULL, component_name VARCHAR2(255) NOT NULL, desired_stack_version VARCHAR2(255) NULL, desired_state VARCHAR2(255) NOT NULL, host_name VARCHAR2(255) NOT NULL, service_name VARCHAR2(255) NOT NULL, admin_state VARCHAR2(32) NULL, maintenance_state VARCHAR2(32) NOT NULL, restart_required NUMBER(1) DEFAULT 0 NOT NULL, PRIMARY KEY (cluster_id, component_name, host_name, service_name));

+ 1 - 1
ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql

@@ -19,7 +19,7 @@
 ------create tables ang grant privileges to db user---------
 CREATE TABLE clusters (cluster_id BIGINT NOT NULL, cluster_info VARCHAR(255) NOT NULL, cluster_name VARCHAR(100) NOT NULL UNIQUE, provisioning_state VARCHAR(255) NOT NULL DEFAULT 'INIT', desired_cluster_state VARCHAR(255) NOT NULL, desired_stack_version VARCHAR(255) NOT NULL, PRIMARY KEY (cluster_id));
 
-CREATE TABLE clusterconfig (version_tag VARCHAR(255) NOT NULL, type_name VARCHAR(255) NOT NULL, cluster_id BIGINT NOT NULL, config_data VARCHAR(32000) NOT NULL, create_timestamp BIGINT NOT NULL, PRIMARY KEY (cluster_id, type_name, version_tag));
+CREATE TABLE clusterconfig (version_tag VARCHAR(255) NOT NULL, type_name VARCHAR(255) NOT NULL, cluster_id BIGINT NOT NULL, config_data VARCHAR(32000) NOT NULL, config_attributes VARCHAR(32000) NOT NULL, config_attributes VARCHAR(32000) NOT NULL, create_timestamp BIGINT NOT NULL, PRIMARY KEY (cluster_id, type_name, version_tag));
 
 CREATE TABLE clusterconfigmapping (cluster_id BIGINT NOT NULL, type_name VARCHAR(255) NOT NULL, version_tag VARCHAR(255) NOT NULL, create_timestamp BIGINT NOT NULL, selected INTEGER NOT NULL DEFAULT 0, user_name VARCHAR(255) NOT NULL DEFAULT '_db', PRIMARY KEY (cluster_id, type_name, create_timestamp));
 

+ 1 - 1
ambari-server/src/main/resources/Ambari-DDL-Postgres-EMBEDDED-CREATE.sql

@@ -31,7 +31,7 @@ ALTER ROLE :username SET search_path TO 'ambari';
 CREATE TABLE ambari.clusters (cluster_id BIGINT NOT NULL, cluster_info VARCHAR(255) NOT NULL, cluster_name VARCHAR(100) NOT NULL UNIQUE, provisioning_state VARCHAR(255) NOT NULL DEFAULT 'INIT', desired_cluster_state VARCHAR(255) NOT NULL, desired_stack_version VARCHAR(255) NOT NULL, PRIMARY KEY (cluster_id));
 GRANT ALL PRIVILEGES ON TABLE ambari.clusters TO :username;
 
-CREATE TABLE ambari.clusterconfig (version_tag VARCHAR(255) NOT NULL, type_name VARCHAR(255) NOT NULL, cluster_id BIGINT NOT NULL, config_data VARCHAR(32000) NOT NULL, create_timestamp BIGINT NOT NULL, PRIMARY KEY (cluster_id, type_name, version_tag));
+CREATE TABLE ambari.clusterconfig (version_tag VARCHAR(255) NOT NULL, type_name VARCHAR(255) NOT NULL, cluster_id BIGINT NOT NULL, config_data VARCHAR(32000) NOT NULL, config_attributes VARCHAR(32000) NOT NULL, create_timestamp BIGINT NOT NULL, PRIMARY KEY (cluster_id, type_name, version_tag));
 GRANT ALL PRIVILEGES ON TABLE ambari.clusterconfig TO :username;
 
 CREATE TABLE ambari.clusterconfigmapping (cluster_id BIGINT NOT NULL, type_name VARCHAR(255) NOT NULL, version_tag VARCHAR(255) NOT NULL, create_timestamp BIGINT NOT NULL, selected INTEGER NOT NULL DEFAULT 0, user_name VARCHAR(255) NOT NULL DEFAULT '_db', PRIMARY KEY (cluster_id, type_name, create_timestamp));

+ 7 - 4
ambari-server/src/test/java/org/apache/ambari/server/actionmanager/ExecutionCommandWrapperTest.java

@@ -85,6 +85,7 @@ public class ExecutionCommandWrapperTest {
   private static Map<String, String> SERVICE_SITE_CLUSTER;
   private static Map<String, String> SERVICE_SITE_SERVICE;
   private static Map<String, String> SERVICE_SITE_HOST;
+  private static Map<String, Map<String, String>> CONFIG_ATTRIBUTES;
   
   private static Injector injector;
   private static Clusters clusters;
@@ -124,23 +125,25 @@ public class ExecutionCommandWrapperTest {
     GLOBAL_CLUSTER.put(GLOBAL_NAME1, GLOBAL_CLUSTER_VAL1);
     GLOBAL_CLUSTER.put(GLOBAL_NAME2, GLOBAL_CLUSTER_VAL2);
     
+    CONFIG_ATTRIBUTES = new HashMap<String, Map<String,String>>();
+    
     //Cluster level global config
-    Config globalConfig = configFactory.createNew(cluster1, GLOBAL_CONFIG, GLOBAL_CLUSTER);
+    Config globalConfig = configFactory.createNew(cluster1, GLOBAL_CONFIG, GLOBAL_CLUSTER, CONFIG_ATTRIBUTES);
     globalConfig.setVersionTag(CLUSTER_VERSION_TAG);
     cluster1.addConfig(globalConfig);
 
     //Cluster level service config
-    Config serviceSiteConfigCluster = configFactory.createNew(cluster1, SERVICE_SITE_CONFIG, SERVICE_SITE_CLUSTER);
+    Config serviceSiteConfigCluster = configFactory.createNew(cluster1, SERVICE_SITE_CONFIG, SERVICE_SITE_CLUSTER, CONFIG_ATTRIBUTES);
     serviceSiteConfigCluster.setVersionTag(CLUSTER_VERSION_TAG);
     cluster1.addConfig(serviceSiteConfigCluster);
     
     //Service level service config
-    Config serviceSiteConfigService = configFactory.createNew(cluster1, SERVICE_SITE_CONFIG, SERVICE_SITE_SERVICE);
+    Config serviceSiteConfigService = configFactory.createNew(cluster1, SERVICE_SITE_CONFIG, SERVICE_SITE_SERVICE, CONFIG_ATTRIBUTES);
     serviceSiteConfigService.setVersionTag(SERVICE_VERSION_TAG);
     cluster1.addConfig(serviceSiteConfigService);
     
     //Host level service config
-    Config serviceSiteConfigHost = configFactory.createNew(cluster1, SERVICE_SITE_CONFIG, SERVICE_SITE_HOST);
+    Config serviceSiteConfigHost = configFactory.createNew(cluster1, SERVICE_SITE_CONFIG, SERVICE_SITE_HOST, CONFIG_ATTRIBUTES);
     serviceSiteConfigHost.setVersionTag(HOST_VERSION_TAG);
     cluster1.addConfig(serviceSiteConfigHost);
     

+ 2 - 2
ambari-server/src/test/java/org/apache/ambari/server/agent/TestHeartbeatMonitor.java

@@ -120,7 +120,7 @@ public class TestHeartbeatMonitor {
     
     ConfigFactory configFactory = injector.getInstance(ConfigFactory.class);
     Config config = configFactory.createNew(cluster, "global",
-        new HashMap<String,String>() {{ put("a", "b"); }});
+        new HashMap<String,String>() {{ put("a", "b"); }}, new HashMap<String, Map<String,String>>());
     config.setVersionTag("version1");
     cluster.addConfig(config);
     cluster.addDesiredConfig("_test", config);
@@ -203,7 +203,7 @@ public class TestHeartbeatMonitor {
     Config config = configFactory.createNew(cluster, "global",
       new HashMap<String, String>() {{
         put("a", "b");
-      }});
+      }}, new HashMap<String, Map<String,String>>());
     config.setVersionTag("version1");
     cluster.addConfig(config);
     cluster.addDesiredConfig("_test", config);

+ 177 - 68
ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerTest.java

@@ -1536,14 +1536,15 @@ public class AmbariManagementControllerTest {
 
     Map<String, Config> configs = new HashMap<String, Config>();
     Map<String, String> properties = new HashMap<String, String>();
+    Map<String, Map<String, String>> propertiesAttributes = new HashMap<String, Map<String,String>>();
     properties.put("a", "a1");
     properties.put("b", "b1");
 
-    Config c1 = new ConfigImpl(cluster, "hdfs-site", properties, injector);
+    Config c1 = new ConfigImpl(cluster, "hdfs-site", properties, propertiesAttributes, injector);
     properties.put("c", "c1");
     properties.put("d", "d1");
-    Config c2 = new ConfigImpl(cluster, "core-site", properties, injector);
-    Config c3 = new ConfigImpl(cluster, "foo-site", properties, injector);
+    Config c2 = new ConfigImpl(cluster, "core-site", properties, propertiesAttributes, injector);
+    Config c3 = new ConfigImpl(cluster, "foo-site", properties, propertiesAttributes, injector);
 
     Map<String, String> mapRequestProps = new HashMap<String, String>();
     mapRequestProps.put("context", "Called from a test");
@@ -2189,7 +2190,7 @@ public class AmbariManagementControllerTest {
 
     ConfigurationRequest cr1;
     cr1 = new ConfigurationRequest(clusterName, "hdfs-site", "version1",
-        configs);
+        configs, null);
     ClusterRequest crReq = new ClusterRequest(null, clusterName, null, null);
     crReq.setDesiredConfig(cr1);
     controller.updateClusters(Collections.singleton(crReq), null);
@@ -3782,13 +3783,13 @@ public class AmbariManagementControllerTest {
     Config config1 = cf.createNew(cluster, "global",
         new HashMap<String, String>() {{
           put("key1", "value1");
-        }});
+        }}, new HashMap<String, Map<String,String>>());
     config1.setVersionTag("version1");
 
     Config config2 = cf.createNew(cluster, "core-site",
         new HashMap<String, String>() {{
           put("key1", "value1");
-        }});
+        }}, new HashMap<String, Map<String,String>>());
     config2.setVersionTag("version1");
 
     cluster.addConfig(config1);
@@ -3913,13 +3914,13 @@ public class AmbariManagementControllerTest {
     Config config1 = cf.createNew(cluster, "global",
       new HashMap<String, String>() {{
         put("key1", "value1");
-      }});
+      }}, new HashMap<String, Map<String,String>>());
     config1.setVersionTag("version1");
 
     Config config2 = cf.createNew(cluster, "core-site",
       new HashMap<String, String>() {{
         put("key1", "value1");
-      }});
+      }}, new HashMap<String, Map<String,String>>());
     config2.setVersionTag("version1");
 
     cluster.addConfig(config1);
@@ -4114,13 +4115,13 @@ public class AmbariManagementControllerTest {
     Config config1 = cf.createNew(cluster, "global",
         new HashMap<String, String>() {{
           put("key1", "value1");
-        }});
+        }}, new HashMap<String, Map<String,String>>());
     config1.setVersionTag("version1");
 
     Config config2 = cf.createNew(cluster, "core-site",
         new HashMap<String, String>() {{
           put("key1", "value1");
-        }});
+        }}, new HashMap<String, Map<String,String>>());
     config2.setVersionTag("version1");
 
     cluster.addConfig(config1);
@@ -4376,11 +4377,11 @@ public class AmbariManagementControllerTest {
 
     ConfigFactory cf = injector.getInstance(ConfigFactory.class);
     Config config1 = cf.createNew(cluster, "global",
-        new HashMap<String, String>(){{ put("key1", "value1"); }});
+        new HashMap<String, String>(){{ put("key1", "value1"); }}, new HashMap<String, Map<String,String>>());
     config1.setVersionTag("version1");
 
     Config config2 = cf.createNew(cluster, "core-site",
-        new HashMap<String, String>(){{ put("key1", "value1"); }});
+        new HashMap<String, String>(){{ put("key1", "value1"); }}, new HashMap<String, Map<String,String>>());
     config2.setVersionTag("version1");
 
     cluster.addConfig(config1);
@@ -4587,7 +4588,7 @@ public class AmbariManagementControllerTest {
 
     ClusterRequest cr = new ClusterRequest(null, clusterName, null, null);
     cr.setDesiredConfig(new ConfigurationRequest(clusterName, "global",
-        "v1", configs));
+        "v1", configs, null));
     controller.updateClusters(Collections.singleton(cr), Collections.<String, String>emptyMap());
 
     Set<ServiceRequest> sReqs = new HashSet<ServiceRequest>();
@@ -4736,14 +4737,14 @@ public class AmbariManagementControllerTest {
     configs.put("a", "b");
 
     ConfigurationRequest cr1, cr2, cr3, cr4, cr5, cr6, cr7, cr8;
-    cr1 = new ConfigurationRequest(clusterName, "typeA","v1", configs);
-    cr2 = new ConfigurationRequest(clusterName, "typeB","v1", configs);
-    cr3 = new ConfigurationRequest(clusterName, "typeC","v1", configs);
-    cr4 = new ConfigurationRequest(clusterName, "typeD","v1", configs);
-    cr5 = new ConfigurationRequest(clusterName, "typeA","v2", configs);
-    cr6 = new ConfigurationRequest(clusterName, "typeB","v2", configs);
-    cr7 = new ConfigurationRequest(clusterName, "typeC","v2", configs);
-    cr8 = new ConfigurationRequest(clusterName, "typeE","v1", configs);
+    cr1 = new ConfigurationRequest(clusterName, "typeA","v1", configs, null);
+    cr2 = new ConfigurationRequest(clusterName, "typeB","v1", configs, null);
+    cr3 = new ConfigurationRequest(clusterName, "typeC","v1", configs, null);
+    cr4 = new ConfigurationRequest(clusterName, "typeD","v1", configs, null);
+    cr5 = new ConfigurationRequest(clusterName, "typeA","v2", configs, null);
+    cr6 = new ConfigurationRequest(clusterName, "typeB","v2", configs, null);
+    cr7 = new ConfigurationRequest(clusterName, "typeC","v2", configs, null);
+    cr8 = new ConfigurationRequest(clusterName, "typeE","v1", configs, null);
     controller.createConfiguration(cr1);
     controller.createConfiguration(cr2);
     controller.createConfiguration(cr3);
@@ -4878,16 +4879,20 @@ public class AmbariManagementControllerTest {
 
     Map<String, String> configs = new HashMap<String, String>();
     configs.put("a", "b");
+    
+    Map<String, Map<String, String>> configAttributes = new HashMap<String, Map<String,String>>();
+    configAttributes.put("final", new HashMap<String, String>());
+    configAttributes.get("final").put("a", "true");
 
     ConfigurationRequest cr1, cr2, cr3, cr4, cr5, cr6, cr7, cr8;
-    cr1 = new ConfigurationRequest(clusterName, "typeA","v1", configs);
-    cr2 = new ConfigurationRequest(clusterName, "typeB","v1", configs);
-    cr3 = new ConfigurationRequest(clusterName, "typeC","v1", configs);
-    cr4 = new ConfigurationRequest(clusterName, "typeD","v1", configs);
-    cr5 = new ConfigurationRequest(clusterName, "typeA","v2", configs);
-    cr6 = new ConfigurationRequest(clusterName, "typeB","v2", configs);
-    cr7 = new ConfigurationRequest(clusterName, "typeC","v2", configs);
-    cr8 = new ConfigurationRequest(clusterName, "typeE","v1", configs);
+    cr1 = new ConfigurationRequest(clusterName, "typeA","v1", configs, configAttributes);
+    cr2 = new ConfigurationRequest(clusterName, "typeB","v1", configs, configAttributes);
+    cr3 = new ConfigurationRequest(clusterName, "typeC","v1", configs, configAttributes);
+    cr4 = new ConfigurationRequest(clusterName, "typeD","v1", configs, configAttributes);
+    cr5 = new ConfigurationRequest(clusterName, "typeA","v2", configs, configAttributes);
+    cr6 = new ConfigurationRequest(clusterName, "typeB","v2", configs, configAttributes);
+    cr7 = new ConfigurationRequest(clusterName, "typeC","v2", configs, configAttributes);
+    cr8 = new ConfigurationRequest(clusterName, "typeE","v1", configs, configAttributes);
     controller.createConfiguration(cr1);
     controller.createConfiguration(cr2);
     controller.createConfiguration(cr3);
@@ -5025,11 +5030,11 @@ public class AmbariManagementControllerTest {
 
     ConfigurationRequest cr1,cr2,cr3;
     cr1 = new ConfigurationRequest(clusterName, "core-site","version1",
-      configs);
+      configs, null);
     cr2 = new ConfigurationRequest(clusterName, "hdfs-site","version1",
-      configs);
+      configs, null);
     cr3 = new ConfigurationRequest(clusterName, "core-site","version122",
-      configs);
+      configs, null);
     controller.createConfiguration(cr1);
     controller.createConfiguration(cr2);
     controller.createConfiguration(cr3);
@@ -5179,9 +5184,9 @@ public class AmbariManagementControllerTest {
 
     ConfigurationRequest cr1,cr2,cr3;
     cr1 = new ConfigurationRequest(clusterName, "core-site","version1",
-      configs);
+      configs, null);
     cr2 = new ConfigurationRequest(clusterName, "hdfs-site","version1",
-      configs);
+      configs, null);
 
     ClusterRequest crReq = new ClusterRequest(null, clusterName, null, null);
     crReq.setDesiredConfig(cr1);
@@ -5208,7 +5213,7 @@ public class AmbariManagementControllerTest {
     configs.clear();
     configs.put("c", "d");
     cr3 = new ConfigurationRequest(clusterName, "core-site","version122",
-      configs);
+      configs, null);
     crReq = new ClusterRequest(null, clusterName, null, null);
     crReq.setDesiredConfig(cr3);
     controller.updateClusters(Collections.singleton(crReq), null);
@@ -5325,9 +5330,9 @@ public class AmbariManagementControllerTest {
 
     ConfigurationRequest cr1,cr2,cr3;
     cr1 = new ConfigurationRequest(clusterName, "core-site","version1",
-      configs);
+      configs, null);
     cr2 = new ConfigurationRequest(clusterName, "hdfs-site","version1",
-      configs);
+      configs, null);
 
     ClusterRequest crReq = new ClusterRequest(null, clusterName, null, null);
     crReq.setDesiredConfig(cr1);
@@ -5354,7 +5359,7 @@ public class AmbariManagementControllerTest {
     configs.clear();
     configs.put("c", "d");
     cr3 = new ConfigurationRequest(clusterName, "core-site","version122",
-      configs);
+      configs, null);
     crReq = new ClusterRequest(null, clusterName, null, null);
     crReq.setDesiredConfig(cr3);
     controller.updateClusters(Collections.singleton(crReq), null);
@@ -5930,7 +5935,7 @@ public class AmbariManagementControllerTest {
 
     ConfigurationRequest cr1;
     cr1 = new ConfigurationRequest(clusterName, "hive-site","version1",
-      configs);
+      configs, null);
     ClusterRequest crReq = new ClusterRequest(null, clusterName, null, null);
     crReq.setDesiredConfig(cr1);
     controller.updateClusters(Collections.singleton(crReq), null);
@@ -6005,7 +6010,7 @@ public class AmbariManagementControllerTest {
 
     ConfigurationRequest cr1;
     cr1 = new ConfigurationRequest(clusterName, "hdfs-site","version1",
-      configs);
+      configs, null);
     ClusterRequest crReq = new ClusterRequest(null, clusterName, null, null);
     crReq.setDesiredConfig(cr1);
     controller.updateClusters(Collections.singleton(crReq), null);
@@ -6146,13 +6151,13 @@ public class AmbariManagementControllerTest {
     Config config1 = cf.createNew(cluster, "global",
       new HashMap<String, String>() {{
         put("key1", "value1");
-      }});
+      }}, new HashMap<String, Map<String,String>>());
     config1.setVersionTag("version1");
 
     Config config2 = cf.createNew(cluster, "core-site",
       new HashMap<String, String>() {{
         put("key1", "value1");
-      }});
+      }}, new HashMap<String, Map<String,String>>());
     config2.setVersionTag("version1");
 
     cluster.addConfig(config1);
@@ -6242,13 +6247,13 @@ public class AmbariManagementControllerTest {
     Config config1 = cf.createNew(cluster, "global",
       new HashMap<String, String>() {{
         put("key1", "value1");
-      }});
+      }}, new HashMap<String, Map<String,String>>());
     config1.setVersionTag("version1");
 
     Config config2 = cf.createNew(cluster, "core-site",
       new HashMap<String, String>() {{
         put("key1", "value1");
-      }});
+      }}, new HashMap<String, Map<String,String>>());
     config2.setVersionTag("version1");
 
     cluster.addConfig(config1);
@@ -6379,9 +6384,9 @@ public class AmbariManagementControllerTest {
 
     ConfigurationRequest cr1,cr2;
     cr1 = new ConfigurationRequest(clusterName, "core-site","version1",
-      configs);
+      configs, null);
     cr2 = new ConfigurationRequest(clusterName, "hdfs-site","version1",
-      configs);
+      configs, null);
 
     ClusterRequest crReq = new ClusterRequest(null, clusterName, null, null);
     crReq.setDesiredConfig(cr1);
@@ -6454,9 +6459,9 @@ public class AmbariManagementControllerTest {
 
     ConfigurationRequest cr1,cr2;
     cr1 = new ConfigurationRequest(clusterName, "core-site","version1",
-      configs);
+      configs, null);
     cr2 = new ConfigurationRequest(clusterName, "hdfs-site","version1",
-      configs);
+      configs, null);
 
     // create, but don't assign
     controller.createConfiguration(cr1);
@@ -6589,11 +6594,11 @@ public class AmbariManagementControllerTest {
 
     ConfigurationRequest cr1,cr2,cr3;
     cr1 = new ConfigurationRequest(clusterName, "core-site","version1",
-      configs);
+      configs, null);
     cr2 = new ConfigurationRequest(clusterName, "hdfs-site","version1",
-      configs);
+      configs, null);
     cr3 = new ConfigurationRequest(clusterName, "mapred-site","version1",
-      configs);
+      configs, null);
 
     ClusterRequest crReq = new ClusterRequest(null, clusterName, null, null);
     crReq.setDesiredConfig(cr1);
@@ -6752,7 +6757,7 @@ public class AmbariManagementControllerTest {
 
     ConfigurationRequest cr1, cr2;
     cr1 = new ConfigurationRequest(clusterName, "hdfs-site", "version1",
-        configs);
+        configs, null);
     ClusterRequest crReq = new ClusterRequest(null, clusterName, null, null);
     crReq.setDesiredConfig(cr1);
     controller.updateClusters(Collections.singleton(crReq), null);
@@ -6852,9 +6857,9 @@ public class AmbariManagementControllerTest {
 
     ConfigurationRequest cr1,cr2;
     cr1 = new ConfigurationRequest(clusterName, "core-site","version1",
-      configs);
+      configs, null);
     cr2 = new ConfigurationRequest(clusterName, "hdfs-site","version1",
-      configs);
+      configs, null);
 
     ClusterRequest crReq = new ClusterRequest(null, clusterName, null, null);
     crReq.setDesiredConfig(cr1);
@@ -8553,7 +8558,7 @@ public class AmbariManagementControllerTest {
         }.getType();
 
         ConfigurationRequest configurationRequest = new ConfigurationRequest("c1", type, tag,
-            gson.<Map<String, String>>fromJson("{ \"fs.default.name\" : \"localhost:8020\"}", confType));
+            gson.<Map<String, String>>fromJson("{ \"fs.default.name\" : \"localhost:8020\"}", confType), null);
         amc.createConfiguration(configurationRequest);
 
         amc.createConfiguration(configurationRequest);
@@ -8619,7 +8624,7 @@ public class AmbariManagementControllerTest {
       amc.createCluster(cr);
 
       ConfigurationRequest configRequest = new ConfigurationRequest(CLUSTER_NAME, "global", "version1",
-          new HashMap<String, String>() {{ put("a", "b"); }});
+          new HashMap<String, String>() {{ put("a", "b"); }}, null);
       cr.setDesiredConfig(configRequest);
       amc.updateClusters(Collections.singleton(cr), new HashMap<String, String>());
 
@@ -8732,17 +8737,17 @@ public class AmbariManagementControllerTest {
       }.getType();
 
       ConfigurationRequest configurationRequest = new ConfigurationRequest("c1", "core-site", "version1",
-          gson.<Map<String, String>>fromJson("{ \"fs.default.name\" : \"localhost:8020\"}", confType)
+          gson.<Map<String, String>>fromJson("{ \"fs.default.name\" : \"localhost:8020\"}", confType), null
       );
       amc.createConfiguration(configurationRequest);
 
       configurationRequest = new ConfigurationRequest("c1", "hdfs-site", "version1",
-          gson.<Map<String, String>>fromJson("{ \"dfs.datanode.data.dir.perm\" : \"750\"}", confType)
+          gson.<Map<String, String>>fromJson("{ \"dfs.datanode.data.dir.perm\" : \"750\"}", confType), null
       );
       amc.createConfiguration(configurationRequest);
 
       configurationRequest = new ConfigurationRequest("c1", "global", "version1",
-          gson.<Map<String, String>>fromJson("{ \"hbase_hdfs_root_dir\" : \"/apps/hbase/\"}", confType)
+          gson.<Map<String, String>>fromJson("{ \"hbase_hdfs_root_dir\" : \"/apps/hbase/\"}", confType), null
       );
       amc.createConfiguration(configurationRequest);
 
@@ -8951,15 +8956,15 @@ public class AmbariManagementControllerTest {
       org.junit.Assert.assertEquals(1, ServiceResourceProviderTest.getServices(amc, serviceRequests).size());
       //Create new configs
       configurationRequest = new ConfigurationRequest("c1", "core-site", "version2",
-          gson.<Map<String, String>>fromJson("{ \"fs.default.name\" : \"localhost:8020\"}", confType)
+          gson.<Map<String, String>>fromJson("{ \"fs.default.name\" : \"localhost:8020\"}", confType), null
       );
       amc.createConfiguration(configurationRequest);
       configurationRequest = new ConfigurationRequest("c1", "hdfs-site", "version2",
-          gson.<Map<String, String>>fromJson("{ \"dfs.datanode.data.dir.perm\" : \"750\"}", confType)
+          gson.<Map<String, String>>fromJson("{ \"dfs.datanode.data.dir.perm\" : \"750\"}", confType), null
       );
       amc.createConfiguration(configurationRequest);
       configurationRequest = new ConfigurationRequest("c1", "global", "version2",
-          gson.<Map<String, String>>fromJson("{ \"hbase_hdfs_root_dir\" : \"/apps/hbase/\"}", confType)
+          gson.<Map<String, String>>fromJson("{ \"hbase_hdfs_root_dir\" : \"/apps/hbase/\"}", confType), null
       );
       amc.createConfiguration(configurationRequest);
       //Add configs to service
@@ -9847,21 +9852,21 @@ public class AmbariManagementControllerTest {
 
     // test null map with no prior
     cr.setDesiredConfig(
-        new ConfigurationRequest(clusterName, "typeA", "v1", null));
+        new ConfigurationRequest(clusterName, "typeA", "v1", null, null));
     controller.updateClusters(Collections.singleton(cr), new HashMap<String, String>());
     Config config = cluster.getDesiredConfigByType("typeA");
     Assert.assertNull(config);
     
     // test empty map with no prior
     cr.setDesiredConfig(
-        new ConfigurationRequest(clusterName, "typeA", "v1", new HashMap<String, String>()));
+        new ConfigurationRequest(clusterName, "typeA", "v1", new HashMap<String, String>(), new HashMap<String, Map<String,String>>()));
     controller.updateClusters(Collections.singleton(cr), new HashMap<String, String>());
     config = cluster.getDesiredConfigByType("typeA");
     Assert.assertNotNull(config);
     
     // test empty properties on a new version
     cr.setDesiredConfig(
-        new ConfigurationRequest(clusterName, "typeA", "v2", new HashMap<String, String>()));
+        new ConfigurationRequest(clusterName, "typeA", "v2", new HashMap<String, String>(), new HashMap<String, Map<String,String>>()));
     controller.updateClusters(Collections.singleton(cr), new HashMap<String, String>());
     config = cluster.getDesiredConfigByType("typeA");
     Assert.assertNotNull(config);
@@ -9871,8 +9876,11 @@ public class AmbariManagementControllerTest {
     Map<String, String> map = new HashMap<String, String>();
     map.clear();
     map.put("c", "d");
+    Map<String, Map<String, String>> attributesMap = new HashMap<String, Map<String,String>>();
+    attributesMap.put("final", new HashMap<String, String>());
+    attributesMap.get("final").put("c", "true");
     cr.setDesiredConfig(
-        new ConfigurationRequest(clusterName, "typeA", "v3", map));
+        new ConfigurationRequest(clusterName, "typeA", "v3", map, attributesMap));
     controller.updateClusters(Collections.singleton(cr), new HashMap<String, String>());
     config = cluster.getDesiredConfigByType("typeA");
     Assert.assertNotNull(config);
@@ -9880,7 +9888,7 @@ public class AmbariManagementControllerTest {
     
     // test reset to v2
     cr.setDesiredConfig(
-        new ConfigurationRequest(clusterName, "typeA", "v2", new HashMap<String, String>()));
+        new ConfigurationRequest(clusterName, "typeA", "v2", new HashMap<String, String>(), new HashMap<String, Map<String,String>>()));
     controller.updateClusters(Collections.singleton(cr), new HashMap<String, String>());
     config = cluster.getDesiredConfigByType("typeA");
     Assert.assertEquals("v2", config.getVersionTag());
@@ -9889,7 +9897,10 @@ public class AmbariManagementControllerTest {
     
     // test v2, but with properties
     cr.setDesiredConfig(
-        new ConfigurationRequest(clusterName, "typeA", "v2", new HashMap<String, String>() {{ put("a", "b"); }}));
+        new ConfigurationRequest(clusterName, "typeA", "v2", new HashMap<String, String>() {{ put("a", "b"); }}, 
+            new HashMap<String, Map<String,String>>(){{put("final", new HashMap<String, String>(){{put("a", "true");}});
+          }
+        }));
     try {
       controller.updateClusters(Collections.singleton(cr), new HashMap<String, String>());
       Assert.fail("Expect failure when creating a config that exists");
@@ -9983,6 +9994,104 @@ public class AmbariManagementControllerTest {
     Assert.assertEquals(null, cmd.getComponentName());
   }
 
+  @Test
+  public void testConfigAttributesStaleConfigFilter() throws AmbariException {
+
+    final String host1 = "h1";
+    final String host2 = "h2";
+    String clusterName = "foo1";
+    setupClusterWithHosts(clusterName, "HDP-2.0.5", new ArrayList<String>() {
+      {
+        add(host1);
+        add(host2);
+      }
+    }, "centos5");
+    String serviceName = "HDFS";
+    createService(clusterName, serviceName, null);
+    String componentName1 = "NAMENODE";
+    String componentName2 = "DATANODE";
+    String componentName3 = "HDFS_CLIENT";
+
+    createServiceComponent(clusterName, serviceName, componentName1, State.INIT);
+    createServiceComponent(clusterName, serviceName, componentName2, State.INIT);
+    createServiceComponent(clusterName, serviceName, componentName3, State.INIT);
+
+    createServiceComponentHost(clusterName, serviceName, componentName1, host1, null);
+    createServiceComponentHost(clusterName, serviceName, componentName2, host1, null);
+    createServiceComponentHost(clusterName, serviceName, componentName3, host1, null);
+    createServiceComponentHost(clusterName, serviceName, componentName2, host2, null);
+    createServiceComponentHost(clusterName, serviceName, componentName3, host2, null);
+
+    // Install
+    installService(clusterName, serviceName, false, false);
+
+    // Create and attach config
+    // hdfs-site will not have config-attributes
+    Map<String, String> hdfsConfigs = new HashMap<String, String>();
+    hdfsConfigs.put("a", "b");
+    Map<String, Map<String, String>> hdfsConfigAttributes = new HashMap<String, Map<String, String>>() {
+      {
+        put("final", new HashMap<String, String>() {{put("a", "true");}});
+      }
+    };
+
+    ConfigurationRequest cr1 = new ConfigurationRequest(clusterName, "hdfs-site", "version1", hdfsConfigs, hdfsConfigAttributes);
+    ClusterRequest crReq1 = new ClusterRequest(null, clusterName, null, null);
+    crReq1.setDesiredConfig(cr1);
+    
+    controller.updateClusters(Collections.singleton(crReq1), null);
+
+    // Start
+    startService(clusterName, serviceName, false, false);
+
+    // Update actual config
+    HashMap<String, Map<String, String>> actualConfig = new HashMap<String, Map<String, String>>() {
+      {
+        put("hdfs-site", new HashMap<String, String>() {{put("tag", "version1");}});
+      }
+    };
+    HashMap<String, Map<String, String>> actualConfigOld = new HashMap<String, Map<String, String>>() {
+      {
+        put("hdfs-site", new HashMap<String, String>() {{put("tag", "version0");}});
+      }
+    };
+
+    Service s1 = clusters.getCluster(clusterName).getService(serviceName);
+    s1.getServiceComponent(componentName1).getServiceComponentHost(host1).updateActualConfigs(actualConfig);
+    s1.getServiceComponent(componentName2).getServiceComponentHost(host1).updateActualConfigs(actualConfig);
+    s1.getServiceComponent(componentName3).getServiceComponentHost(host1).updateActualConfigs(actualConfigOld);
+    s1.getServiceComponent(componentName2).getServiceComponentHost(host2).updateActualConfigs(actualConfigOld);
+    s1.getServiceComponent(componentName3).getServiceComponentHost(host2).updateActualConfigs(actualConfig);
+
+    ServiceComponentHostRequest r = new ServiceComponentHostRequest(clusterName, null, null, null, null);
+    Set<ServiceComponentHostResponse> resps = controller.getHostComponents(Collections.singleton(r));
+    Assert.assertEquals(5, resps.size());
+
+    // Get all host components with stale config = true
+    r = new ServiceComponentHostRequest(clusterName, null, null, null, null);
+    r.setStaleConfig("true");
+    resps = controller.getHostComponents(Collections.singleton(r));
+    Assert.assertEquals(2, resps.size());
+
+    // Get all host components with stale config = false
+    r = new ServiceComponentHostRequest(clusterName, null, null, null, null);
+    r.setStaleConfig("false");
+    resps = controller.getHostComponents(Collections.singleton(r));
+    Assert.assertEquals(3, resps.size());
+
+    // Get all host components with stale config = false and hostname filter
+    r = new ServiceComponentHostRequest(clusterName, null, null, host1, null);
+    r.setStaleConfig("false");
+    resps = controller.getHostComponents(Collections.singleton(r));
+    Assert.assertEquals(2, resps.size());
+
+    // Get all host components with stale config = false and hostname filter
+    r = new ServiceComponentHostRequest(clusterName, null, null, host2, null);
+    r.setStaleConfig("true");
+    resps = controller.getHostComponents(Collections.singleton(r));
+    Assert.assertEquals(1, resps.size());
+  }
+
 }
 
   

+ 6 - 6
ambari-server/src/test/java/org/apache/ambari/server/controller/internal/AbstractResourceProviderTest.java

@@ -264,9 +264,9 @@ public class AbstractResourceProviderTest {
     }
 
     public static ConfigurationRequest getConfigurationRequest(
-        String clusterName, String type, String tag, Map<String, String> configs)
+        String clusterName, String type, String tag, Map<String, String> configs, Map<String, Map<String, String>> configAttributes)
     {
-      EasyMock.reportMatcher(new ConfigurationRequestMatcher(clusterName, type, tag, configs));
+      EasyMock.reportMatcher(new ConfigurationRequestMatcher(clusterName, type, tag, configs, configAttributes));
       return null;
     }
 
@@ -370,8 +370,8 @@ public class AbstractResourceProviderTest {
    */
   public static class ConfigurationRequestMatcher extends ConfigurationRequest implements IArgumentMatcher {
 
-    public ConfigurationRequestMatcher(String clusterName, String type, String tag, Map<String, String> configs) {
-      super(clusterName, type, tag, configs);
+    public ConfigurationRequestMatcher(String clusterName, String type, String tag, Map<String, String> configs, Map<String, Map<String, String>> configsAttributes) {
+      super(clusterName, type, tag, configs, configsAttributes);
     }
 
     @Override
@@ -380,8 +380,8 @@ public class AbstractResourceProviderTest {
           eq(((ConfigurationRequest) o).getClusterName(), getClusterName()) &&
           eq(((ConfigurationRequest) o).getType(), getType()) &&
           eq(((ConfigurationRequest) o).getVersionTag(), getVersionTag()) &&
-          eq(((ConfigurationRequest) o).getProperties(), getProperties());
-
+          eq(((ConfigurationRequest) o).getProperties(), getProperties()) &&
+          eq(((ConfigurationRequest) o).getPropertiesAttributes(), getPropertiesAttributes());
     }
 
     @Override

+ 56 - 6
ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ConfigurationResourceProviderTest.java

@@ -50,7 +50,7 @@ public class ConfigurationResourceProviderTest {
     RequestStatusResponse response = createNiceMock(RequestStatusResponse.class);
 
     managementController.createConfiguration(AbstractResourceProviderTest.Matcher.getConfigurationRequest(
-        "Cluster100", "type", "tag", new HashMap<String, String>()));
+        "Cluster100", "type", "tag", new HashMap<String, String>(), null));
 
     // replay
     replay(managementController, response);
@@ -79,6 +79,56 @@ public class ConfigurationResourceProviderTest {
     verify(managementController, response);
   }
 
+  @Test
+  public void testCreateAttributesResources() throws Exception {
+
+    AmbariManagementController managementController = createMock(AmbariManagementController.class);
+    RequestStatusResponse response = createNiceMock(RequestStatusResponse.class);
+
+    managementController.createConfiguration(AbstractResourceProviderTest.Matcher.getConfigurationRequest(
+        "Cluster100", "type", "tag", new HashMap<String, String>(){
+          {
+            put("a", "b");
+          }
+        }, new HashMap<String, Map<String,String>>(){
+          {
+            put("final", new HashMap<String, String>(){
+              {
+                put("a", "true");
+              }
+            });
+          }
+        }));
+
+    // replay
+    replay(managementController, response);
+
+    ConfigurationResourceProvider provider = new ConfigurationResourceProvider(
+        PropertyHelper.getPropertyIds(Resource.Type.Configuration ),
+        PropertyHelper.getKeyPropertyIds(Resource.Type.Configuration),
+        managementController);
+
+    Set<Map<String, Object>> propertySet = new LinkedHashSet<Map<String, Object>>();
+
+    Map<String, Object> properties = new LinkedHashMap<String, Object>();
+
+    properties.put(ConfigurationResourceProvider.CONFIGURATION_CLUSTER_NAME_PROPERTY_ID, "Cluster100");
+    properties.put(ConfigurationResourceProvider.CONFIGURATION_CONFIG_TAG_PROPERTY_ID, "tag");
+    properties.put(ConfigurationResourceProvider.CONFIGURATION_CONFIG_TYPE_PROPERTY_ID, "type");
+    properties.put("properties/a", "b");
+    properties.put("properties_attributes/final/a", "true");
+
+    propertySet.add(properties);
+
+    // create the request
+    Request request = PropertyHelper.getCreateRequest(propertySet, null);
+
+    provider.createResources(request);
+
+    // verify
+    verify(managementController, response);
+  }
+
   @Test
   public void testGetResources() throws Exception {
     Resource.Type type = Resource.Type.Configuration;
@@ -86,13 +136,13 @@ public class ConfigurationResourceProviderTest {
     AmbariManagementController managementController = createMock(AmbariManagementController.class);
 
     Set<ConfigurationResponse> allResponse = new HashSet<ConfigurationResponse>();
-    allResponse.add(new ConfigurationResponse("Cluster100", "type", "tag1", null));
-    allResponse.add(new ConfigurationResponse("Cluster100", "type", "tag2", null));
-    allResponse.add(new ConfigurationResponse("Cluster100", "type", "tag3", null));
+    allResponse.add(new ConfigurationResponse("Cluster100", "type", "tag1", null, null));
+    allResponse.add(new ConfigurationResponse("Cluster100", "type", "tag2", null, null));
+    allResponse.add(new ConfigurationResponse("Cluster100", "type", "tag3", null, null));
 
     Set<ConfigurationResponse> orResponse = new HashSet<ConfigurationResponse>();
-    orResponse.add(new ConfigurationResponse("Cluster100", "type", "tag1", null));
-    orResponse.add(new ConfigurationResponse("Cluster100", "type", "tag2", null));
+    orResponse.add(new ConfigurationResponse("Cluster100", "type", "tag1", null, null));
+    orResponse.add(new ConfigurationResponse("Cluster100", "type", "tag2", null, null));
 
     Capture<Set<ConfigurationRequest>> configRequestCapture1 = new Capture<Set<ConfigurationRequest>>();
     Capture<Set<ConfigurationRequest>> configRequestCapture2 = new Capture<Set<ConfigurationRequest>>();

+ 5 - 5
ambari-server/src/test/java/org/apache/ambari/server/controller/internal/JMXHostProviderTest.java

@@ -178,7 +178,7 @@ public class JMXHostProviderTest {
       configs.put("ambari.dfs.datanode.http.port", "70070");
       
       ConfigurationRequest cr = new ConfigurationRequest(clusterName,
-        "hdfs-site", "version1", configs);
+        "hdfs-site", "version1", configs, null);
       ClusterRequest crequest = new ClusterRequest(null, clusterName, null, null);
       crequest.setDesiredConfig(cr);
       controller.updateClusters(Collections.singleton(crequest), new HashMap<String,String>());
@@ -189,7 +189,7 @@ public class JMXHostProviderTest {
       configs.put(DATANODE_PORT, "localhost:70075");
 
       ConfigurationRequest cr = new ConfigurationRequest(clusterName,
-        "hdfs-site", "version2", configs);
+        "hdfs-site", "version2", configs, null);
       
       ClusterRequest crequest = new ClusterRequest(null, clusterName, null, null);
       crequest.setDesiredConfig(cr);
@@ -261,7 +261,7 @@ public class JMXHostProviderTest {
     yarnConfigs.put(NODEMANAGER_PORT, "8042");
 
     ConfigurationRequest cr1 = new ConfigurationRequest(clusterName,
-      "hdfs-site", "versionN", configs);
+      "hdfs-site", "versionN", configs, null);
 
     ClusterRequest crReq = new ClusterRequest(null, clusterName, null, null);
     crReq.setDesiredConfig(cr1);
@@ -271,7 +271,7 @@ public class JMXHostProviderTest {
       .getVersionTag());
 
     ConfigurationRequest cr2 = new ConfigurationRequest(clusterName,
-      "yarn-site", "versionN", yarnConfigs);
+      "yarn-site", "versionN", yarnConfigs, null);
     crReq.setDesiredConfig(cr2);
     controller.updateClusters(Collections.singleton(crReq), null);
 
@@ -360,7 +360,7 @@ public class JMXHostProviderTest {
     yarnConfigs.put(RESOURCEMANAGER_PORT, "localhost:50030");
     yarnConfigs.put(NODEMANAGER_PORT, "localhost:11111");
     ConfigurationRequest cr2 = new ConfigurationRequest("c1",
-      "yarn-site", "versionN+1", yarnConfigs);
+      "yarn-site", "versionN+1", yarnConfigs, null);
 
     ClusterRequest crReq = new ClusterRequest(null, "c1", null, null);
     crReq.setDesiredConfig(cr2);

+ 2 - 0
ambari-server/src/test/java/org/apache/ambari/server/orm/dao/ConfigGroupDAOTest.java

@@ -211,6 +211,7 @@ public class ConfigGroupDAOTest {
     configEntity.setType("core-site");
     configEntity.setTag("version1");
     configEntity.setData("someData");
+    configEntity.setAttributes("someAttributes");
 
     List<ClusterConfigEntity> configEntities = new
       ArrayList<ClusterConfigEntity>();
@@ -231,5 +232,6 @@ public class ConfigGroupDAOTest {
     Assert.assertEquals("core-site", configEntities.get(0).getType());
     Assert.assertEquals("version1", configEntities.get(0).getTag());
     Assert.assertEquals("someData", configEntities.get(0).getData());
+    Assert.assertEquals("someAttributes", configEntities.get(0).getAttributes());
   }
 }

+ 31 - 2
ambari-server/src/test/java/org/apache/ambari/server/state/ConfigGroupTest.java

@@ -17,11 +17,15 @@
  */
 package org.apache.ambari.server.state;
 
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.google.inject.persist.PersistService;
 import com.google.inject.persist.Transactional;
+
 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;
@@ -42,6 +46,7 @@ import org.junit.Test;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
@@ -96,7 +101,11 @@ public class ConfigGroupTest {
     Map<String, String> properties = new HashMap<String, String>();
     properties.put("a", "b");
     properties.put("c", "d");
-    Config config = configFactory.createNew(cluster, "hdfs-site", properties);
+    Map<String, Map<String, String>> propertiesAttributes = new HashMap<String, Map<String,String>>();
+    Map<String, String> attributes = new HashMap<String, String>();
+    attributes.put("a", "true");
+    propertiesAttributes.put("final", attributes);
+    Config config = configFactory.createNew(cluster, "hdfs-site", properties, propertiesAttributes);
     config.setVersionTag("testversion");
 
     Host host = clusters.getHost("h1");
@@ -130,6 +139,8 @@ public class ConfigGroupTest {
     Assert.assertNotNull(configMappingEntity.getClusterConfigEntity());
     Assert.assertTrue(configMappingEntity
       .getClusterConfigEntity().getData().contains("a"));
+    Assert.assertEquals("{\"final\":{\"a\":\"true\"}}", configMappingEntity
+        .getClusterConfigEntity().getAttributes());
     ConfigGroupHostMappingEntity hostMappingEntity = configGroupEntity
       .getConfigGroupHostMappingEntities().iterator().next();
     Assert.assertNotNull(hostMappingEntity);
@@ -154,8 +165,13 @@ public class ConfigGroupTest {
     // Create a new config
     Map<String, String> properties = new HashMap<String, String>();
     properties.put("key1", "value1");
+    Map<String, Map<String, String>> propertiesAttributes = new HashMap<String, Map<String,String>>();
+    Map<String, String> attributes = new HashMap<String, String>();
+    attributes.put("key1", "true");
+    propertiesAttributes.put("final", attributes);
     Config config = new ConfigImpl("test-site");
     config.setProperties(properties);
+    config.setPropertiesAttributes(propertiesAttributes);
     config.setVersionTag("version100");
 
     configGroup.addConfiguration(config);
@@ -177,8 +193,21 @@ public class ConfigGroupTest {
       .getConfigGroupConfigMappingEntities().size());
     Assert.assertEquals("NewTag", configGroupEntity.getTag());
     Assert.assertEquals("NewDesc", configGroupEntity.getDescription());
-
     Assert.assertNotNull(cluster.getConfig("test-site", "version100"));
+    
+    ConfigGroupConfigMappingEntity configMappingEntity = null;
+    Object[] array = configGroupEntity.getConfigGroupConfigMappingEntities().toArray();
+    for(Object o: array) {
+      if("test-site".equals(((ConfigGroupConfigMappingEntity)o).getConfigType())){
+        configMappingEntity = (ConfigGroupConfigMappingEntity) o;
+        break;
+      }
+    }
+    Assert.assertNotNull(configMappingEntity);
+    Assert.assertTrue(configMappingEntity
+        .getClusterConfigEntity().getData().contains("{\"key1\":\"value1\"}"));
+      Assert.assertEquals("{\"final\":{\"key1\":\"true\"}}", configMappingEntity
+          .getClusterConfigEntity().getAttributes());
   }
 
   @Test

+ 19 - 8
ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterTest.java

@@ -305,16 +305,23 @@ public class ClusterTest {
 
   @Test
   public void testGetAndSetConfigs() {
+    
+    Map<String, Map<String, String>> c1PropAttributes = new HashMap<String, Map<String,String>>();
+    c1PropAttributes.put("final", new HashMap<String, String>());
+    c1PropAttributes.get("final").put("a", "true");
+    Map<String, Map<String, String>> c2PropAttributes = new HashMap<String, Map<String,String>>();
+    c2PropAttributes.put("final", new HashMap<String, String>());
+    c2PropAttributes.get("final").put("x", "true");
     Config config1 = configFactory.createNew(c1, "global",
-        new HashMap<String, String>() {{ put("a", "b"); }});
+        new HashMap<String, String>() {{ put("a", "b"); }}, c1PropAttributes);
     config1.setVersionTag("version1");
     
     Config config2 = configFactory.createNew(c1, "global",
-        new HashMap<String, String>() {{ put("x", "y"); }});
+        new HashMap<String, String>() {{ put("x", "y"); }}, c2PropAttributes);
     config2.setVersionTag("version2");
     
     Config config3 = configFactory.createNew(c1, "core-site",
-        new HashMap<String, String>() {{ put("x", "y"); }});
+        new HashMap<String, String>() {{ put("x", "y"); }}, new HashMap<String, Map<String,String>>());
     config3.setVersionTag("version2");
     
     c1.addConfig(config1);
@@ -324,6 +331,7 @@ public class ClusterTest {
     c1.addDesiredConfig("_test", config1);
     Config res = c1.getDesiredConfigByType("global");
     Assert.assertNotNull("Expected non-null config", res);
+    Assert.assertEquals("true", res.getPropertiesAttributes().get("final").get("a"));
     
     res = c1.getDesiredConfigByType("core-site");
     Assert.assertNull("Expected null config", res);
@@ -331,21 +339,21 @@ public class ClusterTest {
     c1.addDesiredConfig("_test", config2);
     res = c1.getDesiredConfigByType("global");
     Assert.assertEquals("Expected version tag to be 'version2'", "version2", res.getVersionTag());
-    
+    Assert.assertEquals("true", res.getPropertiesAttributes().get("final").get("x"));
   }
   
   @Test
   public void testDesiredConfigs() throws Exception {
     Config config1 = configFactory.createNew(c1, "global",
-        new HashMap<String, String>() {{ put("a", "b"); }});
+        new HashMap<String, String>() {{ put("a", "b"); }}, new HashMap<String, Map<String,String>>());
     config1.setVersionTag("version1");
     
     Config config2 = configFactory.createNew(c1, "global",
-        new HashMap<String, String>() {{ put("x", "y"); }});
+        new HashMap<String, String>() {{ put("x", "y"); }}, new HashMap<String, Map<String,String>>());
     config2.setVersionTag("version2");
     
     Config config3 = configFactory.createNew(c1, "core-site",
-        new HashMap<String, String>() {{ put("x", "y"); }});
+        new HashMap<String, String>() {{ put("x", "y"); }}, new HashMap<String, Map<String,String>>());
     config3.setVersionTag("version2");
     
     c1.addConfig(config1);
@@ -531,9 +539,12 @@ public class ClusterTest {
   public void testGetHostsDesiredConfigs() throws Exception {
     Host host1 = clusters.getHost("h1");
 
+    Map<String, Map<String, String>> propAttributes = new HashMap<String, Map<String,String>>();
+    propAttributes.put("final", new HashMap<String, String>());
+    propAttributes.get("final").put("test", "true");
     Config config = configFactory.createNew(c1, "hdfs-site", new HashMap<String, String>(){{
       put("test", "test");
-    }});
+    }}, propAttributes);
     config.setVersionTag("1");
 
     host1.addDesiredConfig(c1.getClusterId(), true, "test", config);

+ 2 - 2
ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClustersTest.java

@@ -318,14 +318,14 @@ public class ClustersTest {
     final Config config1 = injector.getInstance(ConfigFactory.class).createNew(cluster, "t1",
         new HashMap<String, String>() {{
           put("prop1", "val1");
-        }});
+        }}, new HashMap<String, Map<String,String>>());
     config1.setVersionTag("1");
     config1.persist();
     
     Config config2 = injector.getInstance(ConfigFactory.class).createNew(cluster, "t1",
         new HashMap<String, String>() {{
           put("prop2", "val2");
-        }});
+        }}, new HashMap<String, Map<String,String>>());
     config2.setVersionTag("2");
     config2.persist();
     

+ 2 - 2
ambari-server/src/test/java/org/apache/ambari/server/state/host/HostTest.java

@@ -376,7 +376,7 @@ public class HostTest {
     
     ConfigFactory configFactory = injector.getInstance(ConfigFactory.class);
     Config config = configFactory.createNew(c1, "global",
-        new HashMap<String,String>() {{ put("a", "b"); put("x", "y"); }});
+        new HashMap<String,String>() {{ put("a", "b"); put("x", "y"); }}, new HashMap<String, Map<String,String>>());
     
     try {
       host.addDesiredConfig(c1.getClusterId(), true, "_test", config);
@@ -403,7 +403,7 @@ public class HostTest {
     Assert.assertEquals("Expect global user to be '_test'", "_test", map.get("global").getUser());
     
     config = configFactory.createNew(c1, "global",
-        new HashMap<String,String>() {{ put("c", "d"); }});
+        new HashMap<String,String>() {{ put("c", "d"); }}, new HashMap<String, Map<String,String>>());
     config.setVersionTag("v2");
     host.addDesiredConfig(c1.getClusterId(), true, "_test1", config);
     

+ 130 - 11
ambari-server/src/test/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostTest.java

@@ -185,7 +185,7 @@ public class ServiceComponentHostTest {
     Cluster c = clusters.getCluster("C1");
     if (c.getConfig("time", String.valueOf(timestamp)) == null) {
       Config config = configFactory.createNew (c, "time",
-          new HashMap<String, String>());
+          new HashMap<String, String>(), new HashMap<String, Map<String,String>>());
       config.setVersionTag(String.valueOf(timestamp));
       c.addConfig(config);
       config.persist();
@@ -669,7 +669,7 @@ public class ServiceComponentHostTest {
           put("a", "b");
           put("dfs_namenode_name_dir", "/foo1"); // HDFS only
           put("mapred_log_dir_prefix", "/foo2"); // MR2 only
-        }});
+        }}, new HashMap<String, Map<String,String>>());
 
     Map<String, Map<String, String>> actual = new HashMap<String, Map<String, String>>() {{
       put("global", new HashMap<String,String>() {{ put("tag", "version1"); }});
@@ -680,14 +680,14 @@ public class ServiceComponentHostTest {
     sch3.updateActualConfigs(actual);
 
     makeConfig(cluster, "foo", "version1",
-        new HashMap<String,String>() {{ put("a", "c"); }});
+        new HashMap<String,String>() {{ put("a", "c"); }}, new HashMap<String, Map<String,String>>());
 
     // HDP-x/HDFS does not define type 'foo', so changes do not count to stale
     Assert.assertFalse(sch1.convertToResponse().isStaleConfig());
     Assert.assertFalse(sch2.convertToResponse().isStaleConfig());
     
     makeConfig(cluster, "hdfs-site", "version1",
-        new HashMap<String,String>() {{ put("a", "b"); }});
+        new HashMap<String,String>() {{ put("a", "b"); }}, new HashMap<String, Map<String,String>>());
 
     // HDP-x/HDFS/hdfs-site is not on the actual, but it is defined, so it is stale
     Assert.assertTrue(sch1.convertToResponse().isStaleConfig());
@@ -716,7 +716,8 @@ public class ServiceComponentHostTest {
     Assert.assertFalse(sch2.convertToResponse().isStaleConfig());
     
     makeConfig(cluster, "hdfs-site", "version2",
-        new HashMap<String, String>() {{ put("dfs.journalnode.http-address", "http://foo"); }});
+        new HashMap<String, String>() {{ put("dfs.journalnode.http-address", "http://foo"); }}, 
+        new HashMap<String, Map<String,String>>());
 
     // HDP-x/HDFS/hdfs-site updated to changed property
     Assert.assertTrue(sch1.convertToResponse().isStaleConfig());
@@ -738,7 +739,8 @@ public class ServiceComponentHostTest {
     Assert.assertNotNull(host);
     
     final Config c = configFactory.createNew(cluster, "hdfs-site",
-        new HashMap<String, String>() {{ put("dfs.journalnode.http-address", "http://goo"); }});
+        new HashMap<String, String>() {{ put("dfs.journalnode.http-address", "http://goo"); }}, 
+        new HashMap<String, Map<String,String>>());
     c.setVersionTag("version3");
     c.persist();
     cluster.addConfig(c);
@@ -780,7 +782,7 @@ public class ServiceComponentHostTest {
         put("a", "b");
         put("dfs_namenode_name_dir", "/foo3"); // HDFS only
         put("mapred_log_dir_prefix", "/foo2"); // MR2 only
-      }});
+      }}, new HashMap<String, Map<String,String>>());
     
     Assert.assertTrue(sch1.convertToResponse().isStaleConfig());
     Assert.assertTrue(sch2.convertToResponse().isStaleConfig());
@@ -791,7 +793,7 @@ public class ServiceComponentHostTest {
       new HashMap<String,String>() {{
         put("a", "b");
         put("fs.trash.interval", "360"); // HDFS only
-      }});
+      }}, new HashMap<String, Map<String,String>>());
 
     Assert.assertTrue(sch1.convertToResponse().isStaleConfig());
     Assert.assertTrue(sch2.convertToResponse().isStaleConfig());
@@ -804,7 +806,8 @@ public class ServiceComponentHostTest {
     sch1.updateActualConfigs(actual);
 
     final Config c1 = configFactory.createNew(cluster, "core-site",
-      new HashMap<String, String>() {{ put("fs.trash.interval", "400"); }});
+      new HashMap<String, String>() {{ put("fs.trash.interval", "400"); }}, 
+      new HashMap<String, Map<String,String>>());
     c1.setVersionTag("version2");
     c1.persist();
     cluster.addConfig(c1);
@@ -848,6 +851,122 @@ public class ServiceComponentHostTest {
     sch3.setRestartRequired(false);
     Assert.assertFalse(sch3.convertToResponse().isStaleConfig());
   }
+  
+  @Test
+  public void testStaleConfigsAttributes() throws Exception {
+    String stackVersion="HDP-2.0.6";
+    String clusterName = "c2";
+    String hostName = "h3";
+    
+    clusters.addCluster(clusterName);
+    clusters.addHost(hostName);
+    setOsFamily(clusters.getHost(hostName), "redhat", "5.9");
+    clusters.getHost(hostName).persist();
+    clusters.getCluster(clusterName).setDesiredStackVersion(
+        new StackId(stackVersion));
+    metaInfo.init();
+    clusters.mapHostToCluster(hostName, clusterName);    
+    
+    Cluster cluster = clusters.getCluster(clusterName);
+    
+    ServiceComponentHost sch1 = createNewServiceComponentHost(cluster, "HDFS", "NAMENODE", hostName);
+    ServiceComponentHost sch2 = createNewServiceComponentHost(cluster, "HDFS", "DATANODE", hostName);
+    ServiceComponentHost sch3 = createNewServiceComponentHost(cluster, "MAPREDUCE2", "HISTORYSERVER", hostName);
+    
+    sch1.setDesiredState(State.INSTALLED);
+    sch1.setState(State.INSTALLING);
+    sch1.setStackVersion(new StackId(stackVersion));
+
+    sch2.setDesiredState(State.INSTALLED);
+    sch2.setState(State.INSTALLING);
+    sch2.setStackVersion(new StackId(stackVersion));
+    
+    sch3.setDesiredState(State.INSTALLED);
+    sch3.setState(State.INSTALLING);
+    sch3.setStackVersion(new StackId(stackVersion));    
+
+    Assert.assertFalse(sch1.convertToResponse().isStaleConfig());
+    Assert.assertFalse(sch2.convertToResponse().isStaleConfig());
+
+    makeConfig(cluster, "global", "version1",
+        new HashMap<String,String>() {{
+          put("a", "b");
+          put("dfs_namenode_name_dir", "/foo1"); // HDFS only
+          put("mapred_log_dir_prefix", "/foo2"); // MR2 only
+        }}, new HashMap<String, Map<String,String>>());
+    makeConfig(cluster, "hdfs-site", "version1",
+        new HashMap<String,String>() {{
+          put("hdfs1", "hdfs1value1");
+        }}, new HashMap<String, Map<String,String>>());
+    Map<String, Map<String, String>> actual = new HashMap<String, Map<String, String>>() {{
+      put("global", new HashMap<String,String>() {{ put("tag", "version1"); }});
+      put("hdfs-site", new HashMap<String,String>() {{ put("tag", "version1"); }});
+    }};
+    
+    sch1.updateActualConfigs(actual);
+    sch2.updateActualConfigs(actual);
+    sch3.updateActualConfigs(actual);
+
+    makeConfig(cluster, "mapred-site", "version1",
+      new HashMap<String,String>() {{ put("a", "c"); }},new HashMap<String, Map<String,String>>(){{
+       put("final", new HashMap<String, String>(){{
+         put("a", "true");
+       }}); 
+      }});
+    // HDP-x/HDFS does not define type 'foo', so changes do not count to stale
+    Assert.assertFalse(sch1.convertToResponse().isStaleConfig());
+    Assert.assertFalse(sch2.convertToResponse().isStaleConfig());
+    Assert.assertTrue(sch3.convertToResponse().isStaleConfig());
+    actual = new HashMap<String, Map<String, String>>() {{
+      put("global", new HashMap<String,String>() {{ put("tag", "version1"); }});
+      put("mapred-site", new HashMap<String,String>() {{ put("tag", "version1"); }});
+    }};
+    sch3.setRestartRequired(false);
+    sch3.updateActualConfigs(actual);
+    Assert.assertFalse(sch3.convertToResponse().isStaleConfig());
+    
+    // Now add config-attributes
+    Map<String, Map<String, String>> c1PropAttributes = new HashMap<String, Map<String,String>>();
+    c1PropAttributes.put("final", new HashMap<String, String>());
+    c1PropAttributes.get("final").put("hdfs1", "true");
+    makeConfig(cluster, "hdfs-site", "version2",
+        new HashMap<String,String>() {{
+          put("hdfs1", "hdfs1value1");
+        }}, c1PropAttributes);
+    sch1.setRestartRequired(false);
+    sch2.setRestartRequired(false);
+    sch3.setRestartRequired(false);
+    Assert.assertTrue(sch1.convertToResponse().isStaleConfig());
+    Assert.assertTrue(sch2.convertToResponse().isStaleConfig());
+    Assert.assertFalse(sch3.convertToResponse().isStaleConfig());
+    
+    // Now change config-attributes
+    Map<String, Map<String, String>> c2PropAttributes = new HashMap<String, Map<String,String>>();
+    c2PropAttributes.put("final", new HashMap<String, String>());
+    c2PropAttributes.get("final").put("hdfs1", "false");
+    makeConfig(cluster, "hdfs-site", "version3",
+        new HashMap<String,String>() {{
+          put("hdfs1", "hdfs1value1");
+        }}, c2PropAttributes);
+    sch1.setRestartRequired(false);
+    sch2.setRestartRequired(false);
+    sch3.setRestartRequired(false);
+    Assert.assertTrue(sch1.convertToResponse().isStaleConfig());
+    Assert.assertTrue(sch2.convertToResponse().isStaleConfig());
+    Assert.assertFalse(sch3.convertToResponse().isStaleConfig());
+    
+    // Now change config-attributes
+    makeConfig(cluster, "hdfs-site", "version4",
+        new HashMap<String,String>() {{
+          put("hdfs1", "hdfs1value1");
+        }}, new HashMap<String, Map<String,String>>());
+    sch1.setRestartRequired(false);
+    sch2.setRestartRequired(false);
+    sch3.setRestartRequired(false);
+    Assert.assertTrue(sch1.convertToResponse().isStaleConfig());
+    Assert.assertTrue(sch2.convertToResponse().isStaleConfig());
+    Assert.assertFalse(sch3.convertToResponse().isStaleConfig());
+  }
 
   /**
    * Helper method to create a configuration
@@ -856,8 +975,8 @@ public class ServiceComponentHostTest {
    * @param tag the config tag
    * @param values the values for the config
    */
-  private void makeConfig(Cluster cluster, String type, String tag, Map<String, String> values) {
-    Config config = configFactory.createNew(cluster, type, values);
+  private void makeConfig(Cluster cluster, String type, String tag, Map<String, String> values, Map<String, Map<String, String>> attributes) {
+    Config config = configFactory.createNew(cluster, type, values, attributes);
     config.setVersionTag(tag);
     config.persist();
     cluster.addConfig(config);

+ 129 - 0
ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog170Test.java

@@ -0,0 +1,129 @@
+/*
+ * 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.ambari.server.upgrade;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.capture;
+import static org.easymock.EasyMock.createNiceMock;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+
+import java.lang.reflect.Field;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.apache.ambari.server.configuration.Configuration;
+import org.apache.ambari.server.orm.DBAccessor;
+import org.easymock.Capture;
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.google.inject.Binder;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Module;
+
+/**
+ * UpgradeCatalog170 unit tests.
+ */
+public class UpgradeCatalog170Test {
+
+  @Test
+  public void testExecuteDDLUpdates() throws Exception {
+
+    final DBAccessor dbAccessor = createNiceMock(DBAccessor.class);
+    Configuration configuration = createNiceMock(Configuration.class);
+    ResultSet resultSet = createNiceMock(ResultSet.class);
+    expect(configuration.getDatabaseUrl()).andReturn(Configuration.JDBC_IN_MEMORY_URL).anyTimes();
+
+    Capture<DBAccessor.DBColumnInfo> clusterConfigAttributesColumnCapture = new Capture<DBAccessor.DBColumnInfo>();
+
+    setClusterConfigExpectations(dbAccessor, clusterConfigAttributesColumnCapture);
+    dbAccessor.executeSelect(anyObject(String.class));
+    expectLastCall().andReturn(resultSet).anyTimes();
+    resultSet.next();
+    expectLastCall().andReturn(false).anyTimes();
+    resultSet.close();
+    expectLastCall().anyTimes();
+
+    replay(dbAccessor, configuration, resultSet);
+    AbstractUpgradeCatalog upgradeCatalog = getUpgradeCatalog(dbAccessor);
+    Class<?> c = AbstractUpgradeCatalog.class;
+    Field f = c.getDeclaredField("configuration");
+    f.setAccessible(true);
+    f.set(upgradeCatalog, configuration);
+
+    upgradeCatalog.executeDDLUpdates();
+    verify(dbAccessor, configuration, resultSet);
+
+    assertClusterConfigColumns(clusterConfigAttributesColumnCapture);
+  }
+
+  @Test
+  public void testExecuteDMLUpdates() throws Exception {
+  }
+
+
+  @Test
+  public void testGetTargetVersion() throws Exception {
+    final DBAccessor dbAccessor     = createNiceMock(DBAccessor.class);
+    UpgradeCatalog   upgradeCatalog = getUpgradeCatalog(dbAccessor);
+
+    Assert.assertEquals("1.7.0", upgradeCatalog.getTargetVersion());
+  }
+
+  private AbstractUpgradeCatalog getUpgradeCatalog(final DBAccessor dbAccessor) {
+    Module module = new Module() {
+      @Override
+      public void configure(Binder binder) {
+        binder.bind(DBAccessor.class).toInstance(dbAccessor);
+      }
+    };
+    Injector injector = Guice.createInjector(module);
+    return injector.getInstance(UpgradeCatalog170.class);
+  }
+  
+  private void assertClusterConfigColumns(Capture<DBAccessor.DBColumnInfo> clusterConfigAttributesColumnCapture) {
+    DBAccessor.DBColumnInfo column = clusterConfigAttributesColumnCapture.getValue();
+    assertEquals("config_attributes", column.getName());
+    assertEquals(32000, (int) column.getLength());
+    assertEquals(String.class, column.getType());
+    assertEquals(null, column.getDefaultValue());
+    assertTrue(column.isNullable());
+  }
+
+  private void setClusterConfigExpectations(DBAccessor dbAccessor,
+                                   Capture<DBAccessor.DBColumnInfo> clusterConfigAttributesColumnCapture)
+      throws SQLException {
+    dbAccessor.addColumn(eq("clusterconfig"),
+        capture(clusterConfigAttributesColumnCapture));
+  }
+  
+  @Test
+  public void testGetSourceVersion() {
+    final DBAccessor dbAccessor     = createNiceMock(DBAccessor.class);
+    UpgradeCatalog upgradeCatalog = getUpgradeCatalog(dbAccessor);
+    Assert.assertEquals("1.6.1", upgradeCatalog.getSourceVersion());
+  }   
+}