Browse Source

AMBARI-6041. Blueprints don't support cluster creation when using existing DB.
Also addresses related issue of MYSQL_SERVER configuration when only default configurations
are provided. Fixed cardinality count for HIVE_SERVER in stack definitions.

John Speidel 11 years ago
parent
commit
191d0a81a0

+ 61 - 12
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseBlueprintProcessor.java

@@ -50,6 +50,8 @@ import java.util.Set;
 /**
  * Base blueprint processing resource provider.
  */
+//todo: this class needs to be refactored to a ClusterTopology class which
+//todo: has hostgroup, stack and configuration state specific to a deployment.
 public abstract class BaseBlueprintProcessor extends AbstractControllerResourceProvider {
 
   /**
@@ -145,12 +147,14 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
     Stack stack = new Stack(blueprint.getStackName(), blueprint.getStackVersion());
     Map<String, HostGroup> hostGroupMap = parseBlueprintHostGroups(blueprint, stack);
     Collection<HostGroup> hostGroups = hostGroupMap.values();
+    Map<String, Map<String, String>> clusterConfig = processBlueprintConfigurations(blueprint, null);
     Map<String, Map<String, Collection<DependencyInfo>>> missingDependencies =
         new HashMap<String, Map<String, Collection<DependencyInfo>>>();
 
     Collection<String> services = getTopologyServices(hostGroups);
     for (HostGroup group : hostGroups) {
-      Map<String, Collection<DependencyInfo>> missingGroupDependencies = group.validateTopology(hostGroups, services);
+      Map<String, Collection<DependencyInfo>> missingGroupDependencies =
+          group.validateTopology(hostGroups, services, clusterConfig);
       if (! missingGroupDependencies.isEmpty()) {
         missingDependencies.put(group.getEntity().getName(), missingGroupDependencies);
       }
@@ -166,7 +170,7 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
               blueprint, hostGroups, component, autoDeploy));
         } else {
           cardinalityFailures.addAll(verifyComponentCardinalityCount(
-              blueprint, hostGroups, component, cardinality, autoDeploy));
+              blueprint, hostGroups, component, cardinality, autoDeploy, stack, clusterConfig));
         }
       }
     }
@@ -284,6 +288,33 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
     return services;
   }
 
+  /**
+   * Determine if a component is managed, meaning that it is running inside of the cluster
+   * topology.  Generally, non-managed dependencies will be database components.
+   *
+   * @param stack          stack instance
+   * @param component      component to determine if it is managed
+   * @param clusterConfig  cluster configuration
+   *
+   * @return true if the specified component managed by the cluster; false otherwise
+   */
+  protected boolean isDependencyManaged(Stack stack, String component, Map<String, Map<String, String>> clusterConfig) {
+    boolean isManaged = true;
+    String externalComponentConfig = stack.getExternalComponentConfig(component);
+    if (externalComponentConfig != null) {
+      String[] toks = externalComponentConfig.split("/");
+      String externalComponentConfigType = toks[0];
+      String externalComponentConfigProp = toks[1];
+      Map<String, String> properties = clusterConfig.get(externalComponentConfigType);
+      if (properties != null && properties.containsKey(externalComponentConfigProp)) {
+        if (properties.get(externalComponentConfigProp).startsWith("Existing")) {
+          isManaged = false;
+        }
+      }
+    }
+    return isManaged;
+  }
+
   /**
    * Verify that a component meets cardinality requirements.  For components that are
    * auto-install enabled, will add component to topology if needed.
@@ -300,20 +331,22 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
                                                              Collection<HostGroup> hostGroups,
                                                              String component,
                                                              Cardinality cardinality,
-                                                             AutoDeployInfo autoDeploy) {
+                                                             AutoDeployInfo autoDeploy,
+                                                             Stack stack,
+                                                             Map<String, Map<String, String>> clusterConfig) {
 
     Collection<String> cardinalityFailures = new HashSet<String>();
 
     int actualCount = getHostGroupsForComponent(component, hostGroups).size();
-    boolean autoDeployed = false;
     if (! cardinality.isValidCount(actualCount)) {
-      if (autoDeploy != null && autoDeploy.isEnabled() && cardinality.cardinality.equals("1")) {
+      boolean validated = ! isDependencyManaged(stack, component, clusterConfig);
+      if (! validated && autoDeploy != null && autoDeploy.isEnabled() && cardinality.cardinality.equals("1")) {
         String coLocateName = autoDeploy.getCoLocate();
         if (coLocateName != null && ! coLocateName.isEmpty()) {
           Collection<HostGroup> coLocateHostGroups = getHostGroupsForComponent(
               coLocateName.split("/")[1], hostGroups);
           if (! coLocateHostGroups.isEmpty()) {
-            autoDeployed = true;
+            validated = true;
             HostGroup group = coLocateHostGroups.iterator().next();
             if (group.addComponent(component)) {
               addComponentToBlueprint(blueprint, group.getEntity().getName(), component);
@@ -321,7 +354,7 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
           }
         }
       }
-      if (! autoDeployed) {
+      if (! validated) {
         cardinalityFailures.add(component + "(actual=" + actualCount + ", required=" +
             cardinality.cardinality + ")");
       }
@@ -425,6 +458,14 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
     private Map<DependencyInfo, String> dependencyConditionalServiceMap =
         new HashMap<DependencyInfo, String>();
 
+    /**
+     * Map of database component name to configuration property which indicates whether
+     * the database in to be managed or if it is an external non-managed instance.
+     * If the value of the config property starts with 'New', the database is determined
+     * to be managed, otherwise it is non-managed.
+     */
+    private Map<String, String> dbDependencyInfo = new HashMap<String, String>();
+
     /**
      * Map of component to required cardinality
      */
@@ -462,7 +503,7 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
         String serviceName = stackService.getServiceName();
         parseComponents(serviceName);
         parseConfigurations(serviceName);
-        recordConditionalDependencies();
+        registerConditionalDependencies();
       }
     }
 
@@ -597,6 +638,10 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
       return dependencyConditionalServiceMap.get(dependency);
     }
 
+    public String getExternalComponentConfig(String component) {
+      return dbDependencyInfo.get(component);
+    }
+
     /**
      * Obtain the required cardinality for the specified component.
      */
@@ -683,7 +728,7 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
      * Register conditional dependencies.
      */
     //todo: This information should be specified in the stack definition.
-    private void recordConditionalDependencies() {
+    private void registerConditionalDependencies() {
       Collection<DependencyInfo> nagiosDependencies = getDependenciesForComponent("NAGIOS_SERVER");
       for (DependencyInfo dependency : nagiosDependencies) {
         if (dependency.getComponentName().equals("HCAT")) {
@@ -692,6 +737,7 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
           dependencyConditionalServiceMap.put(dependency, "OOZIE");
         }
       }
+      dbDependencyInfo.put("MYSQL_SERVER", "global/hive_database");
     }
   }
 
@@ -837,12 +883,15 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
     /**
      * Validate host group topology. This includes ensuring that all component dependencies are satisfied.
      *
-     * @param hostGroups  collection of all host groups
+     * @param hostGroups     collection of all host groups
+     * @param services       set of services in cluster topology
+     * @param clusterConfig  cluster configuration
      *
      * @return map of component to missing dependencies
      */
     public Map<String, Collection<DependencyInfo>> validateTopology(Collection<HostGroup> hostGroups,
-                                                                    Collection<String> services) {
+                                                                    Collection<String> services,
+                                                                    Map<String, Map<String, String>> clusterConfig) {
 
       Map<String, Collection<DependencyInfo>> missingDependencies =
           new HashMap<String, Collection<DependencyInfo>>();
@@ -863,7 +912,7 @@ public abstract class BaseBlueprintProcessor extends AbstractControllerResourceP
 
           if (dependencyScope.equals("cluster")) {
             Collection<String> missingDependencyInfo = verifyComponentCardinalityCount(entity, hostGroups,
-                componentName, new Cardinality("1"), autoDeployInfo);
+                componentName, new Cardinality("1"), autoDeployInfo, stack, clusterConfig);
             resolved = missingDependencyInfo.isEmpty();
           } else if (dependencyScope.equals("host")) {
             if (components.contains(component) || (autoDeployInfo != null && autoDeployInfo.isEnabled())) {

+ 65 - 2
ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java

@@ -860,7 +860,8 @@ public class ClusterResourceProvider extends BaseBlueprintProcessor {
     // HIVE_SERVER
     propertyUpdaters.put("hive.metastore.uris", new SingleHostPropertyUpdater("HIVE_SERVER"));
     propertyUpdaters.put("hive_ambari_host", new SingleHostPropertyUpdater("HIVE_SERVER"));
-    propertyUpdaters.put("javax.jdo.option.ConnectionURL", new SingleHostPropertyUpdater("MYSQL_SERVER"));
+    propertyUpdaters.put("javax.jdo.option.ConnectionURL",
+        new DBPropertyUpdater("MYSQL_SERVER", "global", "hive_database"));
 
     // OOZIE_SERVER
     propertyUpdaters.put("oozie.base.url", new SingleHostPropertyUpdater("OOZIE_SERVER"));
@@ -1012,12 +1013,74 @@ public class ClusterResourceProvider extends BaseBlueprintProcessor {
       if (matchingGroups.size() == 1) {
         return origValue.replace("localhost", matchingGroups.iterator().next().getHostInfo().iterator().next());
       } else {
-        throw new IllegalArgumentException("Unable to update configuration properties with topology information. " +
+        throw new IllegalArgumentException("Unable to update configuration property with topology information. " +
             "Component '" + this.component + "' is not mapped to any host group or is mapped to multiple groups.");
       }
     }
   }
 
+  /**
+   * Topology based updater which replaces the original host name of a database property with the host name
+   * where the DB is deployed in the new cluster.  If an existing database is specified, the original property
+   * value is returned.
+   */
+  private class DBPropertyUpdater extends SingleHostPropertyUpdater {
+    /**
+     * Property type (global, core-site ...) for property which is used to determine if DB is external.
+     */
+    private final String configPropertyType;
+
+    /**
+     * Name of property which is used to determine if DB is new or existing (exernal).
+     */
+    private final String conditionalPropertyName;
+
+    /**
+     * Constructor.
+     *
+     * @param component                component to get hot name if new DB
+     * @param configPropertyType       config type of property used to determine if DB is external
+     * @param conditionalPropertyName  name of property which is used to determine if DB is external
+     */
+    private DBPropertyUpdater(String component, String configPropertyType, String conditionalPropertyName) {
+      super(component);
+      this.configPropertyType = configPropertyType;
+      this.conditionalPropertyName = conditionalPropertyName;
+    }
+
+    /**
+     * If database is a new managed database, update the property with the new host name which
+     * runs the associated component.  If the database is external (non-managed), return the
+     * original value.
+     *
+     * @param hostGroups  host groups                 host groups
+     * @param origValue   original value of property  original property value
+     *
+     * @return updated property value with old host name replaced by new host name or original value
+     *         if the database is exernal
+     */
+    @Override
+    public String update(Map<String, HostGroup> hostGroups, String origValue) {
+      if (isDatabaseManaged()) {
+        return super.update(hostGroups, origValue);
+      } else {
+        return origValue;
+      }
+    }
+
+    /**
+     * Determine if database is managed, meaning that it is a component in the cluster topology.
+     *
+     * @return true if the DB is managed; false otherwise
+     */
+    //todo: use super.isDependencyManaged() and remove this method
+    private boolean isDatabaseManaged() {
+      // conditional property should always exist since it is required to be specified in the stack
+      return mapClusterConfigurations.get(configPropertyType).
+          get(conditionalPropertyName).startsWith("New");
+    }
+  }
+
   /**
    * Topology based updater which replaces original host names (possibly more than one) contained in a property
    * value with the host names which runs the associated component in the new cluster.

+ 4 - 0
ambari-server/src/main/resources/stacks/HDP/1.3.2/services/HIVE/metainfo.xml

@@ -28,6 +28,10 @@
           <name>HIVE_METASTORE</name>
           <category>MASTER</category>
           <cardinality>1</cardinality>
+          <auto-deploy>
+            <enabled>true</enabled>
+            <co-locate>HIVE/HIVE_SERVER</co-locate>
+          </auto-deploy>
           <commandScript>
             <script>scripts/hive_metastore.py</script>
             <scriptType>PYTHON</scriptType>

+ 5 - 1
ambari-server/src/main/resources/stacks/HDP/2.0.6/services/HIVE/metainfo.xml

@@ -28,6 +28,10 @@
           <name>HIVE_METASTORE</name>
           <category>MASTER</category>
           <cardinality>1</cardinality>
+          <auto-deploy>
+            <enabled>true</enabled>
+            <co-locate>HIVE/HIVE_SERVER</co-locate>
+          </auto-deploy>
           <commandScript>
             <script>scripts/hive_metastore.py</script>
             <scriptType>PYTHON</scriptType>
@@ -89,7 +93,7 @@
         <component>
           <name>HIVE_CLIENT</name>
           <category>CLIENT</category>
-          <cardinality>0</cardinality>
+          <cardinality>0+</cardinality>
           <commandScript>
             <script>scripts/hive_client.py</script>
             <scriptType>PYTHON</scriptType>

+ 219 - 2
ambari-server/src/test/java/org/apache/ambari/server/controller/internal/BlueprintResourceProviderTest.java

@@ -22,6 +22,10 @@ import com.google.gson.Gson;
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.controller.AmbariManagementController;
+import org.apache.ambari.server.controller.StackConfigurationRequest;
+import org.apache.ambari.server.controller.StackConfigurationResponse;
+import org.apache.ambari.server.controller.StackServiceComponentRequest;
+import org.apache.ambari.server.controller.StackServiceComponentResponse;
 import org.apache.ambari.server.controller.StackServiceRequest;
 import org.apache.ambari.server.controller.StackServiceResponse;
 import org.apache.ambari.server.controller.predicate.EqualsPredicate;
@@ -42,6 +46,7 @@ import org.apache.ambari.server.orm.entities.HostGroupComponentEntity;
 import org.apache.ambari.server.orm.entities.HostGroupConfigEntity;
 import org.apache.ambari.server.orm.entities.HostGroupEntity;
 import org.apache.ambari.server.state.ComponentInfo;
+import org.apache.ambari.server.state.DependencyInfo;
 import org.apache.ambari.server.state.ServiceInfo;
 import org.easymock.Capture;
 
@@ -311,7 +316,7 @@ public class BlueprintResourceProviderTest {
   }
 
   @Test
-  public void testCreateResource_validate__NoHostGroups() throws AmbariException, ResourceAlreadyExistsException, SystemException,
+  public void testCreateResource_Validate__NoHostGroups() throws AmbariException, ResourceAlreadyExistsException, SystemException,
       UnsupportedPropertyException, NoSuchParentResourceException
   {
     Request request = createMock(Request.class);
@@ -506,6 +511,215 @@ public class BlueprintResourceProviderTest {
     verify(dao, metaInfo, request);
   }
 
+  @Test
+  public void testCreateResource_Validate__Cardinality__ExternalComponent() throws AmbariException, ResourceAlreadyExistsException,
+    SystemException, UnsupportedPropertyException, NoSuchParentResourceException {
+
+    Set<Map<String, Object>> setProperties = getTestProperties();
+    setConfigurationProperties(setProperties);
+    ((Set<Map<String, String>>) setProperties.iterator().next().get("configurations")).
+        add(Collections.singletonMap("global/hive_database", "Existing MySQL Database"));
+
+    Iterator iter = ((HashSet<Map<String, HashSet<Map<String, String>>>>) setProperties.iterator().next().
+        get(BlueprintResourceProvider.HOST_GROUP_PROPERTY_ID)).
+        iterator().next().get("components").iterator();
+    iter.next();
+    iter.remove();
+
+    AmbariManagementController managementController = createMock(AmbariManagementController.class);
+    Capture<Set<StackServiceRequest>> stackServiceRequestCapture = new Capture<Set<StackServiceRequest>>();
+    Capture<Set<StackServiceComponentRequest>> serviceComponentRequestCapture = new Capture<Set<StackServiceComponentRequest>>();
+    Capture<StackConfigurationRequest> stackConfigurationRequestCapture = new Capture<StackConfigurationRequest>();
+    Request request = createMock(Request.class);
+    StackServiceResponse stackServiceResponse = createMock(StackServiceResponse.class);
+    StackServiceComponentResponse stackServiceComponentResponse = createNiceMock(StackServiceComponentResponse.class);
+    StackServiceComponentResponse stackServiceComponentResponse2 = createNiceMock(StackServiceComponentResponse.class);
+    Set<StackServiceComponentResponse> setServiceComponents = new HashSet<StackServiceComponentResponse>();
+    setServiceComponents.add(stackServiceComponentResponse);
+    setServiceComponents.add(stackServiceComponentResponse2);
+
+    Map<String, ServiceInfo> services = new HashMap<String, ServiceInfo>();
+    ServiceInfo service = new ServiceInfo();
+    service.setName("test-service");
+    services.put("test-service", service);
+
+    List<ComponentInfo> serviceComponents = new ArrayList<ComponentInfo>();
+    ComponentInfo component1 = new ComponentInfo();
+    component1.setName("component1");
+    ComponentInfo component2 = new ComponentInfo();
+    component2.setName("MYSQL_SERVER");
+    serviceComponents.add(component1);
+    serviceComponents.add(component2);
+
+    Capture<BlueprintEntity> entityCapture = new Capture<BlueprintEntity>();
+
+    // set expectations
+    expect(managementController.getStackServices(capture(stackServiceRequestCapture))).andReturn(
+        Collections.<StackServiceResponse>singleton(stackServiceResponse));
+    expect(stackServiceResponse.getServiceName()).andReturn("test-service").anyTimes();
+    expect(stackServiceResponse.getStackName()).andReturn("test-stack-name").anyTimes();
+    expect(stackServiceResponse.getStackVersion()).andReturn("test-stack-version").anyTimes();
+
+    expect(managementController.getStackComponents(capture(serviceComponentRequestCapture))).andReturn(setServiceComponents).anyTimes();
+    expect(stackServiceComponentResponse.getCardinality()).andReturn("2").anyTimes();
+    expect(stackServiceComponentResponse.getComponentName()).andReturn("component1").anyTimes();
+    expect(stackServiceComponentResponse.getServiceName()).andReturn("test-service").anyTimes();
+    expect(stackServiceComponentResponse.getStackName()).andReturn("test-stack-name").anyTimes();
+    expect(stackServiceComponentResponse.getStackVersion()).andReturn("test-stack-version").anyTimes();
+    expect(stackServiceComponentResponse2.getCardinality()).andReturn("1").anyTimes();
+    expect(stackServiceComponentResponse2.getComponentName()).andReturn("MYSQL_SERVER").anyTimes();
+    expect(stackServiceComponentResponse2.getServiceName()).andReturn("test-service").anyTimes();
+    expect(stackServiceComponentResponse2.getStackName()).andReturn("test-stack-name").anyTimes();
+    expect(stackServiceComponentResponse2.getStackVersion()).andReturn("test-stack-version").anyTimes();
+
+    expect(managementController.getStackConfigurations(Collections.singleton(capture(stackConfigurationRequestCapture)))).
+        andReturn(Collections.<StackConfigurationResponse>emptySet());
+
+    expect(metaInfo.getComponentDependencies("test-stack-name", "test-stack-version", "test-service", "MYSQL_SERVER")).
+        andReturn(Collections.<DependencyInfo>emptyList()).anyTimes();
+    expect(metaInfo.getComponentDependencies("test-stack-name", "test-stack-version", "test-service", "component1")).
+        andReturn(Collections.<DependencyInfo>emptyList()).anyTimes();
+
+    expect(request.getProperties()).andReturn(setProperties);
+    expect(dao.findByName(BLUEPRINT_NAME)).andReturn(null);
+    expect(metaInfo.getServices("test-stack-name", "test-stack-version")).andReturn(services).anyTimes();
+    expect(metaInfo.getComponentsByService("test-stack-name", "test-stack-version", "test-service")).
+        andReturn(serviceComponents).anyTimes();
+    expect(metaInfo.getComponentToService("test-stack-name", "test-stack-version", "component1")).
+        andReturn("test-service").anyTimes();
+    expect(metaInfo.getComponentToService("test-stack-name", "test-stack-version", "component2")).
+        andReturn("test-service").anyTimes();
+    expect(metaInfo.getRequiredProperties("test-stack-name", "test-stack-version", "test-service")).andReturn(
+        Collections.<String, org.apache.ambari.server.state.PropertyInfo>emptyMap()).anyTimes();
+    dao.create(capture(entityCapture));
+
+    replay(dao, metaInfo, request, managementController, stackServiceResponse,
+        stackServiceComponentResponse, stackServiceComponentResponse2);
+    // end expectations
+
+    ResourceProvider provider = AbstractControllerResourceProvider.getResourceProvider(
+        Resource.Type.Blueprint,
+        PropertyHelper.getPropertyIds(Resource.Type.Blueprint),
+        PropertyHelper.getKeyPropertyIds(Resource.Type.Blueprint),
+        managementController);
+
+    AbstractResourceProviderTest.TestObserver observer = new AbstractResourceProviderTest.TestObserver();
+    ((ObservableResourceProvider)provider).addObserver(observer);
+
+    provider.createResources(request);
+
+    ResourceProviderEvent lastEvent = observer.getLastEvent();
+    assertNotNull(lastEvent);
+    assertEquals(Resource.Type.Blueprint, lastEvent.getResourceType());
+    assertEquals(ResourceProviderEvent.Type.Create, lastEvent.getType());
+    assertEquals(request, lastEvent.getRequest());
+    assertNull(lastEvent.getPredicate());
+
+    verify(dao, metaInfo, request, managementController, stackServiceResponse,
+        stackServiceComponentResponse, stackServiceComponentResponse2);
+  }
+
+  @Test
+  public void testCreateResource_Validate__Cardinality__Negative() throws AmbariException, ResourceAlreadyExistsException,
+      SystemException, UnsupportedPropertyException, NoSuchParentResourceException {
+
+    Set<Map<String, Object>> setProperties = getTestProperties();
+    setConfigurationProperties(setProperties);
+
+    Iterator iter = ((HashSet<Map<String, HashSet<Map<String, String>>>>) setProperties.iterator().next().
+        get(BlueprintResourceProvider.HOST_GROUP_PROPERTY_ID)).
+        iterator().next().get("components").iterator();
+    iter.next();
+    iter.remove();
+
+    AmbariManagementController managementController = createMock(AmbariManagementController.class);
+    Capture<Set<StackServiceRequest>> stackServiceRequestCapture = new Capture<Set<StackServiceRequest>>();
+    Capture<Set<StackServiceComponentRequest>> serviceComponentRequestCapture = new Capture<Set<StackServiceComponentRequest>>();
+    Capture<StackConfigurationRequest> stackConfigurationRequestCapture = new Capture<StackConfigurationRequest>();
+    Request request = createMock(Request.class);
+    StackServiceResponse stackServiceResponse = createMock(StackServiceResponse.class);
+    StackServiceComponentResponse stackServiceComponentResponse = createNiceMock(StackServiceComponentResponse.class);
+    StackServiceComponentResponse stackServiceComponentResponse2 = createNiceMock(StackServiceComponentResponse.class);
+    Set<StackServiceComponentResponse> setServiceComponents = new HashSet<StackServiceComponentResponse>();
+    setServiceComponents.add(stackServiceComponentResponse);
+    setServiceComponents.add(stackServiceComponentResponse2);
+
+    Map<String, ServiceInfo> services = new HashMap<String, ServiceInfo>();
+    ServiceInfo service = new ServiceInfo();
+    service.setName("test-service");
+    services.put("test-service", service);
+
+    List<ComponentInfo> serviceComponents = new ArrayList<ComponentInfo>();
+    ComponentInfo component1 = new ComponentInfo();
+    component1.setName("component1");
+    ComponentInfo component2 = new ComponentInfo();
+    component2.setName("MYSQL_SERVER");
+    serviceComponents.add(component1);
+    serviceComponents.add(component2);
+
+    // set expectations
+    expect(managementController.getStackServices(capture(stackServiceRequestCapture))).andReturn(
+        Collections.<StackServiceResponse>singleton(stackServiceResponse));
+    expect(stackServiceResponse.getServiceName()).andReturn("test-service").anyTimes();
+    expect(stackServiceResponse.getStackName()).andReturn("test-stack-name").anyTimes();
+    expect(stackServiceResponse.getStackVersion()).andReturn("test-stack-version").anyTimes();
+
+    expect(managementController.getStackComponents(capture(serviceComponentRequestCapture))).andReturn(setServiceComponents).anyTimes();
+    expect(stackServiceComponentResponse.getCardinality()).andReturn("2").anyTimes();
+    expect(stackServiceComponentResponse.getComponentName()).andReturn("component1").anyTimes();
+    expect(stackServiceComponentResponse.getServiceName()).andReturn("test-service").anyTimes();
+    expect(stackServiceComponentResponse.getStackName()).andReturn("test-stack-name").anyTimes();
+    expect(stackServiceComponentResponse.getStackVersion()).andReturn("test-stack-version").anyTimes();
+    expect(stackServiceComponentResponse2.getCardinality()).andReturn("1").anyTimes();
+    expect(stackServiceComponentResponse2.getComponentName()).andReturn("MYSQL_SERVER").anyTimes();
+    expect(stackServiceComponentResponse2.getServiceName()).andReturn("test-service").anyTimes();
+    expect(stackServiceComponentResponse2.getStackName()).andReturn("test-stack-name").anyTimes();
+    expect(stackServiceComponentResponse2.getStackVersion()).andReturn("test-stack-version").anyTimes();
+
+    expect(managementController.getStackConfigurations(Collections.singleton(capture(stackConfigurationRequestCapture)))).
+        andReturn(Collections.<StackConfigurationResponse>emptySet());
+
+    expect(metaInfo.getComponentDependencies("test-stack-name", "test-stack-version", "test-service", "MYSQL_SERVER")).
+        andReturn(Collections.<DependencyInfo>emptyList()).anyTimes();
+    expect(metaInfo.getComponentDependencies("test-stack-name", "test-stack-version", "test-service", "component1")).
+        andReturn(Collections.<DependencyInfo>emptyList()).anyTimes();
+
+    expect(request.getProperties()).andReturn(setProperties);
+    expect(dao.findByName(BLUEPRINT_NAME)).andReturn(null);
+    expect(metaInfo.getServices("test-stack-name", "test-stack-version")).andReturn(services).anyTimes();
+    expect(metaInfo.getComponentsByService("test-stack-name", "test-stack-version", "test-service")).
+        andReturn(serviceComponents).anyTimes();
+    expect(metaInfo.getComponentToService("test-stack-name", "test-stack-version", "component1")).
+        andReturn("test-service").anyTimes();
+    expect(metaInfo.getComponentToService("test-stack-name", "test-stack-version", "component2")).
+        andReturn("test-service").anyTimes();
+    expect(metaInfo.getRequiredProperties("test-stack-name", "test-stack-version", "test-service")).andReturn(
+        Collections.<String, org.apache.ambari.server.state.PropertyInfo>emptyMap()).anyTimes();
+
+    replay(dao, metaInfo, request, managementController, stackServiceResponse,
+        stackServiceComponentResponse, stackServiceComponentResponse2);
+    // end expectations
+
+    ResourceProvider provider = AbstractControllerResourceProvider.getResourceProvider(
+        Resource.Type.Blueprint,
+        PropertyHelper.getPropertyIds(Resource.Type.Blueprint),
+        PropertyHelper.getKeyPropertyIds(Resource.Type.Blueprint),
+        managementController);
+
+    AbstractResourceProviderTest.TestObserver observer = new AbstractResourceProviderTest.TestObserver();
+    ((ObservableResourceProvider)provider).addObserver(observer);
+
+    try {
+      provider.createResources(request);
+      fail("Expected validation failure for MYSQL_SERVER");
+    } catch (IllegalArgumentException e) {
+      // expected
+    }
+
+    verify(dao, metaInfo, request, managementController, stackServiceResponse,
+        stackServiceComponentResponse, stackServiceComponentResponse2);
+  }
+
   @Test
   public void testCreateResource_Validate__AmbariServerComponent() throws AmbariException, ResourceAlreadyExistsException,
       SystemException, UnsupportedPropertyException, NoSuchParentResourceException
@@ -602,6 +816,7 @@ public class BlueprintResourceProviderTest {
     setHostGroupProperties.add(mapHostGroupProperties2);
 
     Map<String, Object> mapProperties = new HashMap<String, Object>();
+    mapProperties.put("validate_topology", "true");
     mapProperties.put(BlueprintResourceProvider.BLUEPRINT_NAME_PROPERTY_ID, BLUEPRINT_NAME);
     mapProperties.put(BlueprintResourceProvider.STACK_NAME_PROPERTY_ID, "test-stack-name");
     mapProperties.put(BlueprintResourceProvider.STACK_VERSION_PROPERTY_ID, "test-stack-version");
@@ -617,7 +832,9 @@ public class BlueprintResourceProviderTest {
 
     // single entry in set which was created in getTestProperties
     Map<String, Object> mapProperties = properties.iterator().next();
-    mapProperties.put("configurations", Collections.singleton(clusterProperties));
+    Set<Map<String, String>> configurations = new HashSet<Map<String, String>>();
+    configurations.add(clusterProperties);
+    mapProperties.put("configurations", configurations);
 
     Map<String, Object> hostGroupProperties = new HashMap<String, Object>();
     hostGroupProperties.put("core-site/my.custom.hg.property", "anything");

+ 82 - 22
ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ClusterResourceProviderTest.java

@@ -175,6 +175,7 @@ public class ClusterResourceProviderTest {
     StackServiceComponentResponse stackServiceComponentResponse1 = createNiceMock(StackServiceComponentResponse.class);
     StackServiceComponentResponse stackServiceComponentResponse2 = createNiceMock(StackServiceComponentResponse.class);
     StackServiceComponentResponse stackServiceComponentResponse3 = createNiceMock(StackServiceComponentResponse.class);
+    StackServiceComponentResponse stackServiceComponentResponse4 = createNiceMock(StackServiceComponentResponse.class);
     Capture<Set<StackServiceComponentRequest>> serviceComponentRequestCapture1 = new Capture<Set<StackServiceComponentRequest>>();
     Capture<Set<StackServiceComponentRequest>> serviceComponentRequestCapture2 = new Capture<Set<StackServiceComponentRequest>>();
 
@@ -182,15 +183,18 @@ public class ClusterResourceProviderTest {
     StackConfigurationResponse stackConfigurationResponse2 = createNiceMock(StackConfigurationResponse.class);
     StackConfigurationResponse stackConfigurationResponse3 = createNiceMock(StackConfigurationResponse.class);
     StackConfigurationResponse stackConfigurationResponse4 = createNiceMock(StackConfigurationResponse.class);
+    StackConfigurationResponse stackConfigurationResponse5 = createNiceMock(StackConfigurationResponse.class);
     Capture<Set<StackConfigurationRequest>> serviceConfigurationRequestCapture1 = new Capture<Set<StackConfigurationRequest>>();
     Capture<Set<StackConfigurationRequest>> serviceConfigurationRequestCapture2 = new Capture<Set<StackConfigurationRequest>>();
 
     BlueprintConfigEntity blueprintConfig = createNiceMock(BlueprintConfigEntity.class);
+    BlueprintConfigEntity blueprintConfig2 = createNiceMock(BlueprintConfigEntity.class);
 
     HostGroupEntity hostGroup = createNiceMock(HostGroupEntity.class);
     HostGroupComponentEntity hostGroupComponent1 = createNiceMock(HostGroupComponentEntity.class);
     HostGroupComponentEntity hostGroupComponent2 = createNiceMock(HostGroupComponentEntity.class);
     HostGroupComponentEntity hostGroupComponent3 = createNiceMock(HostGroupComponentEntity.class);
+    HostGroupComponentEntity hostGroupComponent4 = createNiceMock(HostGroupComponentEntity.class);
 
     HostGroupConfigEntity hostGroupConfig = createNiceMock(HostGroupConfigEntity.class);
 
@@ -208,6 +212,8 @@ public class ClusterResourceProviderTest {
     Capture<Map<String, String>> updateClusterPropertyMapCapture2 = new Capture<Map<String, String>>();
     Capture<Set<ClusterRequest>> updateClusterRequestCapture3 = new Capture<Set<ClusterRequest>>();
     Capture<Map<String, String>> updateClusterPropertyMapCapture3 = new Capture<Map<String, String>>();
+    Capture<Set<ClusterRequest>> updateClusterRequestCapture4 = new Capture<Set<ClusterRequest>>();
+    Capture<Map<String, String>> updateClusterPropertyMapCapture4 = new Capture<Map<String, String>>();
 
     Capture<Request> serviceRequestCapture = new Capture<Request>();
     Capture<Request> componentRequestCapture = new Capture<Request>();
@@ -220,18 +226,20 @@ public class ClusterResourceProviderTest {
     stackServiceResponses.add(stackServiceResponse1);
     stackServiceResponses.add(stackServiceResponse2);
 
-    // service1 has 2 components
+    // service1 has 3 components
     Set<StackServiceComponentResponse> stackServiceComponentResponses1 = new LinkedHashSet<StackServiceComponentResponse>();
     stackServiceComponentResponses1.add(stackServiceComponentResponse1);
     stackServiceComponentResponses1.add(stackServiceComponentResponse2);
+    stackServiceComponentResponses1.add(stackServiceComponentResponse4);
 
     // service2 has 1 components
     Set<StackServiceComponentResponse> stackServiceComponentResponses2 = new LinkedHashSet<StackServiceComponentResponse>();
     stackServiceComponentResponses2.add(stackServiceComponentResponse3);
 
-    // service1 has 1 config
+    // service1 has 2 config
     Set<StackConfigurationResponse> stackConfigurationResponses1 = new LinkedHashSet<StackConfigurationResponse>();
     stackConfigurationResponses1.add(stackConfigurationResponse1);
+    stackConfigurationResponses1.add(stackConfigurationResponse5);
 
     // service2 has 3 config
     Set<StackConfigurationResponse> stackConfigurationResponses2 = new LinkedHashSet<StackConfigurationResponse>();
@@ -243,6 +251,7 @@ public class ClusterResourceProviderTest {
     hostGroupComponents.add(hostGroupComponent1);
     hostGroupComponents.add(hostGroupComponent2);
     hostGroupComponents.add(hostGroupComponent3);
+    hostGroupComponents.add(hostGroupComponent4);
 
     // request properties
     Set<Map<String, Object>> propertySet = new LinkedHashSet<Map<String, Object>>();
@@ -266,17 +275,24 @@ public class ClusterResourceProviderTest {
     Map<String, String> mapGroupConfigProperties = new HashMap<String, String>();
     mapGroupConfigProperties.put("myGroupProp", "awesomeValue");
 
-    // blueprint cluster configuration properties
-    Map<String, String> blueprintConfigProperties = new HashMap<String, String>();
-    blueprintConfigProperties.put("property1", "value2");
-    blueprintConfigProperties.put("new.property", "new.property.value");
+    // blueprint core-site cluster configuration properties
+    Map<String, String> blueprintCoreConfigProperties = new HashMap<String, String>();
+    blueprintCoreConfigProperties.put("property1", "value2");
+    blueprintCoreConfigProperties.put("new.property", "new.property.value");
+
+    Map<String, String> blueprintGlobalConfigProperties = new HashMap<String, String>();
+    blueprintGlobalConfigProperties.put("hive_database", "New MySQL Database");
+
+    Collection<BlueprintConfigEntity> configurations = new HashSet<BlueprintConfigEntity>();
+    configurations.add(blueprintConfig);
+    configurations.add(blueprintConfig2);
 
     // expectations
     expect(request.getProperties()).andReturn(propertySet).anyTimes();
     expect(blueprintDAO.findByName(blueprintName)).andReturn(blueprint);
     expect(blueprint.getStackName()).andReturn(stackName);
     expect(blueprint.getStackVersion()).andReturn(stackVersion);
-    expect(blueprint.getConfigurations()).andReturn(Collections.<BlueprintConfigEntity>singletonList(blueprintConfig));
+    expect(blueprint.getConfigurations()).andReturn(configurations);
     expect(blueprint.validateConfigurations(metaInfo, PropertyInfo.PropertyType.PASSWORD)).andReturn(
         Collections.<String, Map<String, Collection<String>>>emptyMap());
 
@@ -284,6 +300,8 @@ public class ClusterResourceProviderTest {
         andReturn(Collections.<DependencyInfo>emptyList());
     expect(metaInfo.getComponentDependencies("test", "1.23", "service1", "component2")).
         andReturn(Collections.<DependencyInfo>emptyList());
+    expect(metaInfo.getComponentDependencies("test", "1.23", "service1", "MYSQL_SERVER")).
+        andReturn(Collections.<DependencyInfo>emptyList());
     expect(metaInfo.getComponentDependencies("test", "1.23", "service2", "component3")).
         andReturn(Collections.<DependencyInfo>emptyList());
 
@@ -295,6 +313,7 @@ public class ClusterResourceProviderTest {
         andReturn(stackServiceComponentResponses1);
     expect(stackServiceComponentResponse1.getComponentName()).andReturn("component1");
     expect(stackServiceComponentResponse2.getComponentName()).andReturn("component2");
+    expect(stackServiceComponentResponse4.getComponentName()).andReturn("MYSQL_SERVER");
 
     expect(managementController.getStackConfigurations(capture(serviceConfigurationRequestCapture1))).
         andReturn(stackConfigurationResponses1);
@@ -320,9 +339,16 @@ public class ClusterResourceProviderTest {
     expect(stackConfigurationResponse4.getPropertyName()).andReturn("property3");
     expect(stackConfigurationResponse4.getPropertyValue()).andReturn("value3");
 
+    expect(stackConfigurationResponse5.getType()).andReturn("hive-site.xml");
+    expect(stackConfigurationResponse5.getPropertyName()).andReturn("javax.jdo.option.ConnectionURL");
+    expect(stackConfigurationResponse5.getPropertyValue()).andReturn("localhost:12345");
+
     expect(blueprintConfig.getBlueprintName()).andReturn("test-blueprint").anyTimes();
     expect(blueprintConfig.getType()).andReturn("core-site").anyTimes();
-    expect(blueprintConfig.getConfigData()).andReturn(new Gson().toJson(blueprintConfigProperties));
+    expect(blueprintConfig.getConfigData()).andReturn(new Gson().toJson(blueprintCoreConfigProperties)).anyTimes();
+    expect(blueprintConfig2.getBlueprintName()).andReturn("test-blueprint").anyTimes();
+    expect(blueprintConfig2.getType()).andReturn("global").anyTimes();
+    expect(blueprintConfig2.getConfigData()).andReturn(new Gson().toJson(blueprintGlobalConfigProperties)).anyTimes();
 
     expect(blueprint.getHostGroups()).andReturn(Collections.singleton(hostGroup)).anyTimes();
     expect(hostGroup.getName()).andReturn("group1").anyTimes();
@@ -330,6 +356,7 @@ public class ClusterResourceProviderTest {
     expect(hostGroupComponent1.getName()).andReturn("component1").anyTimes();
     expect(hostGroupComponent2.getName()).andReturn("component2").anyTimes();
     expect(hostGroupComponent3.getName()).andReturn("component3").anyTimes();
+    expect(hostGroupComponent4.getName()).andReturn("MYSQL_SERVER").anyTimes();
     expect(hostGroup.getConfigurations()).andReturn(
         Collections.<HostGroupConfigEntity>singleton(hostGroupConfig)).anyTimes();
 
@@ -343,6 +370,8 @@ public class ClusterResourceProviderTest {
         capture(updateClusterPropertyMapCapture2))).andReturn(null);
     expect(managementController.updateClusters(capture(updateClusterRequestCapture3),
         capture(updateClusterPropertyMapCapture3))).andReturn(null);
+    expect(managementController.updateClusters(capture(updateClusterRequestCapture4),
+        capture(updateClusterPropertyMapCapture4))).andReturn(null);
 
     expect(serviceResourceProvider.createResources(capture(serviceRequestCapture))).andReturn(null);
     expect(componentResourceProvider.createResources(capture(componentRequestCapture))).andReturn(null);
@@ -359,10 +388,11 @@ public class ClusterResourceProviderTest {
 
     replay(blueprintDAO, managementController, request, response, blueprint, stackServiceResponse1, stackServiceResponse2,
            stackServiceComponentResponse1, stackServiceComponentResponse2, stackServiceComponentResponse3,
-           stackConfigurationResponse1, stackConfigurationResponse2, stackConfigurationResponse3, stackConfigurationResponse4,
-           blueprintConfig, hostGroup, hostGroupComponent1, hostGroupComponent2, hostGroupComponent3, hostGroupConfig,
-           serviceResourceProvider, componentResourceProvider, hostResourceProvider, hostComponentResourceProvider,
-           configGroupResourceProvider, persistKeyValue, metaInfo);
+           stackServiceComponentResponse4, stackConfigurationResponse1, stackConfigurationResponse2,
+           stackConfigurationResponse3, stackConfigurationResponse4, stackConfigurationResponse5, blueprintConfig,
+           blueprintConfig2, hostGroup, hostGroupComponent1, hostGroupComponent2, hostGroupComponent3, hostGroupComponent4,
+           hostGroupConfig, serviceResourceProvider, componentResourceProvider, hostResourceProvider,
+           hostComponentResourceProvider, configGroupResourceProvider, persistKeyValue, metaInfo);
 
     // test
     ClusterResourceProvider.init(blueprintDAO, metaInfo);
@@ -421,32 +451,40 @@ public class ClusterResourceProviderTest {
     Set<ClusterRequest> updateClusterRequest1 = updateClusterRequestCapture.getValue();
     Set<ClusterRequest> updateClusterRequest2 = updateClusterRequestCapture2.getValue();
     Set<ClusterRequest> updateClusterRequest3 = updateClusterRequestCapture3.getValue();
+    Set<ClusterRequest> updateClusterRequest4 = updateClusterRequestCapture4.getValue();
     assertEquals(1, updateClusterRequest1.size());
     assertEquals(1, updateClusterRequest2.size());
     assertEquals(1, updateClusterRequest3.size());
+    assertEquals(1, updateClusterRequest4.size());
     ClusterRequest ucr1 = updateClusterRequest1.iterator().next();
     ClusterRequest ucr2 = updateClusterRequest2.iterator().next();
     ClusterRequest ucr3 = updateClusterRequest3.iterator().next();
+    ClusterRequest ucr4 = updateClusterRequest4.iterator().next();
     assertEquals(clusterName, ucr1.getClusterName());
     assertEquals(clusterName, ucr2.getClusterName());
     assertEquals(clusterName, ucr3.getClusterName());
+    assertEquals(clusterName, ucr4.getClusterName());
     ConfigurationRequest cr1 = ucr1.getDesiredConfig();
     ConfigurationRequest cr2 = ucr2.getDesiredConfig();
     ConfigurationRequest cr3 = ucr3.getDesiredConfig();
+    ConfigurationRequest cr4 = ucr4.getDesiredConfig();
     assertEquals("1", cr1.getVersionTag());
     assertEquals("1", cr2.getVersionTag());
     assertEquals("1", cr3.getVersionTag());
+    assertEquals("1", cr4.getVersionTag());
     Map<String, ConfigurationRequest> mapConfigRequests = new HashMap<String, ConfigurationRequest>();
     mapConfigRequests.put(cr1.getType(), cr1);
     mapConfigRequests.put(cr2.getType(), cr2);
     mapConfigRequests.put(cr3.getType(), cr3);
-    assertEquals(3, mapConfigRequests.size());
+    mapConfigRequests.put(cr4.getType(), cr4);
+    assertEquals(4, mapConfigRequests.size());
     ConfigurationRequest globalConfigRequest = mapConfigRequests.get("global");
-    assertEquals(4, globalConfigRequest.getProperties().size());
+    assertEquals(5, globalConfigRequest.getProperties().size());
     assertEquals("hadoop", globalConfigRequest.getProperties().get("user_group"));
     assertEquals("ambari-qa", globalConfigRequest.getProperties().get("smokeuser"));
     assertEquals("default@REPLACEME.NOWHERE", globalConfigRequest.getProperties().get("nagios_contact"));
     assertEquals("oozie", globalConfigRequest.getProperties().get("oozie_user"));
+    assertEquals("New MySQL Database", globalConfigRequest.getProperties().get("hive_database"));
     ConfigurationRequest hdfsConfigRequest = mapConfigRequests.get("hdfs-site");
     assertEquals(1, hdfsConfigRequest.getProperties().size());
     assertEquals("value2", hdfsConfigRequest.getProperties().get("property2"));
@@ -457,15 +495,20 @@ public class ClusterResourceProviderTest {
     assertEquals("*", coreConfigRequest.getProperties().get("hadoop.proxyuser.oozie.hosts"));
     assertEquals("users", coreConfigRequest.getProperties().get("hadoop.proxyuser.oozie.groups"));
     assertEquals("new.property.value", coreConfigRequest.getProperties().get("new.property"));
+    ConfigurationRequest hiveConfigRequest = mapConfigRequests.get("hive-site");
+    assertEquals(1, hiveConfigRequest.getProperties().size());
+    assertEquals("host.domain:12345", hiveConfigRequest.getProperties().get("javax.jdo.option.ConnectionURL"));
+
     assertNull(updateClusterPropertyMapCapture.getValue());
     assertNull(updateClusterPropertyMapCapture2.getValue());
     assertNull(updateClusterPropertyMapCapture3.getValue());
+    assertNull(updateClusterPropertyMapCapture4.getValue());
 
     Request serviceRequest = serviceRequestCapture.getValue();
     assertEquals(2, serviceRequest.getProperties().size());
     Request componentRequest = componentRequestCapture.getValue();
     Request componentRequest2 = componentRequestCapture2.getValue();
-    assertEquals(2, componentRequest.getProperties().size());
+    assertEquals(3, componentRequest.getProperties().size());
     Set<String> componentRequest1Names = new HashSet<String>();
     for (Map<String, Object> componentRequest1Properties : componentRequest.getProperties()) {
       assertEquals(3, componentRequest1Properties.size());
@@ -473,7 +516,8 @@ public class ClusterResourceProviderTest {
       assertEquals("service1", componentRequest1Properties.get("ServiceComponentInfo/service_name"));
       componentRequest1Names.add((String) componentRequest1Properties.get("ServiceComponentInfo/component_name"));
     }
-    assertTrue(componentRequest1Names.contains("component1") && componentRequest1Names.contains("component2"));
+    assertTrue(componentRequest1Names.contains("component1") && componentRequest1Names.contains("component2")
+        && componentRequest1Names.contains("MYSQL_SERVER"));
     assertEquals(1, componentRequest2.getProperties().size());
     Map<String, Object> componentRequest2Properties = componentRequest2.getProperties().iterator().next();
     assertEquals(clusterName, componentRequest2Properties.get("ServiceComponentInfo/cluster_name"));
@@ -484,7 +528,7 @@ public class ClusterResourceProviderTest {
     assertEquals(clusterName, hostRequest.getProperties().iterator().next().get("Hosts/cluster_name"));
     assertEquals("host.domain", hostRequest.getProperties().iterator().next().get("Hosts/host_name"));
     Request hostComponentRequest = hostComponentRequestCapture.getValue();
-    assertEquals(3, hostComponentRequest.getProperties().size());
+    assertEquals(4, hostComponentRequest.getProperties().size());
     Set<String> componentNames = new HashSet<String>();
     for (Map<String, Object> hostComponentProperties : hostComponentRequest.getProperties()) {
       assertEquals(3, hostComponentProperties.size());
@@ -493,7 +537,7 @@ public class ClusterResourceProviderTest {
       componentNames.add((String) hostComponentProperties.get("HostRoles/component_name"));
     }
     assertTrue(componentNames.contains("component1") && componentNames.contains("component2") &&
-        componentNames.contains("component3"));
+        componentNames.contains("component3") && componentNames.contains("MYSQL_SERVER"));
 
     Set<ConfigGroupRequest> configGroupRequests = configGroupRequestCapture.getValue();
     assertEquals(1, configGroupRequests.size());
@@ -509,10 +553,11 @@ public class ClusterResourceProviderTest {
 
     verify(blueprintDAO, managementController, request, response, blueprint, stackServiceResponse1, stackServiceResponse2,
         stackServiceComponentResponse1, stackServiceComponentResponse2, stackServiceComponentResponse3,
-        stackConfigurationResponse1, stackConfigurationResponse2, stackConfigurationResponse3, stackConfigurationResponse4,
-        blueprintConfig, hostGroup, hostGroupComponent1, hostGroupComponent2, hostGroupComponent3, hostGroupConfig,
-        serviceResourceProvider, componentResourceProvider, hostResourceProvider, hostComponentResourceProvider,
-        configGroupResourceProvider, persistKeyValue);
+        stackServiceComponentResponse4, stackConfigurationResponse1, stackConfigurationResponse2,
+        stackConfigurationResponse3, stackConfigurationResponse4, stackConfigurationResponse5, blueprintConfig,
+        blueprintConfig2, hostGroup, hostGroupComponent1, hostGroupComponent2, hostGroupComponent3, hostGroupComponent4,
+        hostGroupConfig, serviceResourceProvider, componentResourceProvider, hostResourceProvider,
+        hostComponentResourceProvider, configGroupResourceProvider, persistKeyValue, metaInfo);
   }
 
   @Test
@@ -1863,6 +1908,9 @@ public class ClusterResourceProviderTest {
     final Map<String, String> mProperty =
       Collections.singletonMap("namenode_heapsize", "1025");
 
+    final Map<String, String> databaseProperty =
+        Collections.singletonMap("javax.jdo.option.ConnectionURL", "localhost:12345");
+
     final HostGroup hostGroup1 = createNiceMock(HostGroup.class);
     final HostGroup hostGroup2 = createNiceMock(HostGroup.class);
 
@@ -1893,6 +1941,11 @@ public class ClusterResourceProviderTest {
 
     replay(managementController, resourceProvider, hostGroup1, hostGroup2);
 
+    Map<String, Map<String, String>> mapConfigurations;
+    Field configField = ClusterResourceProvider.class.getDeclaredField("mapClusterConfigurations");
+    configField.setAccessible(true);
+    mapConfigurations = (Map<String, Map<String, String>>) configField.get(resourceProvider);
+
     Map<String, PropertyUpdater> propertyUpdaterMap;
     Field f = ClusterResourceProvider.class.getDeclaredField("propertyUpdaters");
     f.setAccessible(true);
@@ -1925,6 +1978,13 @@ public class ClusterResourceProviderTest {
     newValue = propertyUpdaterMap.get(entry.getKey()).update(hostGroups, entry.getValue());
     Assert.assertEquals("1025m", newValue);
 
+    Map<String, String> configs = new HashMap<String, String>();
+    configs.put("hive_database", "External MySQL Database");
+    mapConfigurations.put("global", configs);
+    entry = databaseProperty.entrySet().iterator().next();
+    newValue = propertyUpdaterMap.get(entry.getKey()).update(hostGroups, entry.getValue());
+    Assert.assertEquals("localhost:12345", newValue);
+
     verify(managementController, resourceProvider, hostGroup1, hostGroup2);
   }