This is an automated email from the ASF dual-hosted git repository. dsen pushed a commit to branch trunk in repository https://gitbox.apache.org/repos/asf/ambari.git
The following commit(s) were added to refs/heads/trunk by this push: new 50ea420 [AMBARI-24914] Block Ozone datanode co-location with HDFS Datanode when added though the API (dsen) (#2623) 50ea420 is described below commit 50ea4200d389a5ddb41f15484cdddf0ae072e3e3 Author: Dmitry Sen <d...@apache.org> AuthorDate: Thu Nov 22 11:33:50 2018 +0200 [AMBARI-24914] Block Ozone datanode co-location with HDFS Datanode when added though the API (dsen) (#2623) * [AMBARI-24914] Block Ozone datanode co-location with HDFS Datanode when added though the API - initial (dsen) * [AMBARI-24914] Block Ozone datanode co-location with HDFS Datanode when added though the API - changes according to review + few fixes (dsen) * [AMBARI-24914] Block Ozone datanode co-location with HDFS Datanode when added though the API - fixed stack advisor (dsen) --- .../stackadvisor/commands/StackAdvisorCommand.java | 1 + .../controller/AmbariManagementControllerImpl.java | 57 ++++++++++++++++++++ .../internal/StackDependencyResourceProvider.java | 4 ++ .../apache/ambari/server/state/DependencyInfo.java | 20 ++++++- .../server/topology/BlueprintValidatorImpl.java | 61 ++++++++++++++-------- .../src/main/resources/stacks/stack_advisor.py | 36 +++++++------ .../controller/AmbariManagementControllerTest.java | 43 ++++++++++++++- .../topology/BlueprintValidatorImplTest.java | 41 +++++++++++++++ .../stacks/HDP/0.2/services/HDFS/metainfo.xml | 18 +++++++ 9 files changed, 243 insertions(+), 38 deletions(-) diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommand.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommand.java index a7d45fa..0028872 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommand.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/stackadvisor/commands/StackAdvisorCommand.java @@ -86,6 +86,7 @@ public abstract class StackAdvisorCommand<T extends StackAdvisorResponse> extend + "?fields=Versions/stack_name,Versions/stack_version,Versions/parent_stack_version" + ",services/StackServices/service_name,services/StackServices/service_version" + ",services/components/StackServiceComponents,services/components/dependencies/Dependencies/scope" + + ",services/components/dependencies/Dependencies/type" + ",services/components/dependencies/Dependencies/conditions,services/components/auto_deploy" + ",services/configurations/StackConfigurations/property_depends_on" + ",services/configurations/dependencies/StackConfigurationDependency/dependency_name" diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java index 9b5f74b..038c29c 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java @@ -196,6 +196,7 @@ import org.apache.ambari.server.state.ComponentInfo; import org.apache.ambari.server.state.Config; import org.apache.ambari.server.state.ConfigFactory; import org.apache.ambari.server.state.ConfigHelper; +import org.apache.ambari.server.state.DependencyInfo; import org.apache.ambari.server.state.DesiredConfig; import org.apache.ambari.server.state.ExtensionInfo; import org.apache.ambari.server.state.Host; @@ -752,6 +753,8 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle throw new DuplicateResourceException(msg + names); } + validateExclusiveDependencies(hostComponentNames); + // set restartRequired flag for monitoring services setMonitoringServicesRestartRequired(requests); // now doing actual work @@ -759,6 +762,60 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle m_topologyHolder.get().updateData(getAddedComponentsTopologyEvent(requests)); } + /** + * For all components that will be added validate the exclusive components dependencies using the services metainfo + * and respecting already installed components + * + * @throws AmbariException is thrown if the exclusive dependency is violated or if the data is invalid + */ + private void validateExclusiveDependencies(Map<String, Map<String, Map<String, Set<String>>>> hostComponentNames) throws AmbariException { + List<String> validationIssues = new ArrayList<>(); + + for (Entry<String, Map<String, Map<String, Set<String>>>> clusterEntry : hostComponentNames.entrySet()) { + for (Entry<String, Map<String, Set<String>>> serviceEntry : clusterEntry.getValue().entrySet()) { + for (Entry<String, Set<String>> componentEntry : serviceEntry.getValue().entrySet()) { + Set<String> hostnames = componentEntry.getValue(); + if (hostnames != null && !hostnames.isEmpty()) { + //get dependency info + ServiceComponent sc = clusters.getCluster(clusterEntry.getKey()).getService(serviceEntry.getKey()).getServiceComponent(componentEntry.getKey()); + StackId stackId = sc.getDesiredStackId(); + List<DependencyInfo> dependencyInfos = ambariMetaInfo.getComponentDependencies(stackId.getStackName(), + stackId.getStackVersion(), serviceEntry.getKey(), componentEntry.getKey()); + + for (DependencyInfo dependencyInfo : dependencyInfos) { + if ("host".equals(dependencyInfo.getScope()) && "exclusive".equals(dependencyInfo.getType())) { + Service depService = clusters.getCluster(clusterEntry.getKey()).getService(dependencyInfo.getServiceName()); + if (depService != null && depService.getServiceComponents().containsKey(dependencyInfo.getComponentName())) { + ServiceComponent dependentSC = depService.getServiceComponent(dependencyInfo.getComponentName()); + if (dependentSC != null) { + //get cluster dependent component hosts + Set<String> dependentComponentHosts = new HashSet<>(dependentSC.getServiceComponentHosts().keySet()); + //get request dependent component hosts + if (clusterEntry.getValue().containsKey(dependentSC.getServiceName()) && + clusterEntry.getValue().get(dependentSC.getServiceName()).containsKey(dependentSC.getName())) { + dependentComponentHosts.addAll(clusterEntry.getValue(). + get(dependentSC.getServiceName()).get(dependentSC.getName())); + } + //get the intersection + dependentComponentHosts.retainAll(hostnames); + if (!dependentComponentHosts.isEmpty()) { + validationIssues.add("Component " + componentEntry.getKey() + " can't be co-hosted with component " + + dependencyInfo.getComponentName() + " on hosts " + dependentComponentHosts + " due to exclusive dependency"); + } + } + } + } + } + } + } + } + } + + if (!validationIssues.isEmpty()) { + throw new AmbariException("The components exclusive dependencies are not respected: " + validationIssues); + } + } + void persistServiceComponentHosts(Set<ServiceComponentHostRequest> requests, boolean isBlueprintProvisioned) throws AmbariException { Multimap<Cluster, ServiceComponentHost> schMap = ArrayListMultimap.create(); diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackDependencyResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackDependencyResourceProvider.java index f12a37c..d431970 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackDependencyResourceProvider.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/StackDependencyResourceProvider.java @@ -65,6 +65,8 @@ public class StackDependencyResourceProvider extends AbstractResourceProvider { PropertyHelper.getPropertyId("Dependencies", "component_name"); protected static final String SCOPE_ID = PropertyHelper.getPropertyId("Dependencies", "scope"); + protected static final String TYPE_ID = + PropertyHelper.getPropertyId("Dependencies", "type"); protected static final String CONDITIONS_ID = PropertyHelper .getPropertyId("Dependencies","conditions"); protected static final String AUTO_DEPLOY_ENABLED_ID = PropertyHelper @@ -94,6 +96,7 @@ public class StackDependencyResourceProvider extends AbstractResourceProvider { SERVICE_NAME_ID, COMPONENT_NAME_ID, SCOPE_ID, + TYPE_ID, CONDITIONS_ID, AUTO_DEPLOY_ENABLED_ID, AUTO_DEPLOY_LOCATION_ID); @@ -256,6 +259,7 @@ public class StackDependencyResourceProvider extends AbstractResourceProvider { setResourceProperty(resource, DEPENDENT_SERVICE_NAME_ID, dependentService, requestedIds); setResourceProperty(resource, DEPENDENT_COMPONENT_NAME_ID, dependentComponent, requestedIds); setResourceProperty(resource, SCOPE_ID, dependency.getScope(), requestedIds); + setResourceProperty(resource, TYPE_ID, dependency.getType(), requestedIds); AutoDeployInfo autoDeployInfo = dependency.getAutoDeploy(); if (autoDeployInfo != null) { diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/DependencyInfo.java b/ambari-server/src/main/java/org/apache/ambari/server/state/DependencyInfo.java index 2c4ddcc..f8408d9 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/DependencyInfo.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/DependencyInfo.java @@ -44,6 +44,13 @@ public class DependencyInfo { private String scope; /** + * The type of the dependency. Either "inclusive" or "exclusive". + * "inclusive" means the dependent component MUST be co-hosted or installed on the same cluster + * "exclusive" means the dependent component CAN'T be co-hosted or installed on the same cluster + */ + private String type = "inclusive"; + + /** * Service name of the dependency. */ private String serviceName; @@ -173,11 +180,21 @@ public class DependencyInfo { return !CollectionUtils.isEmpty(dependencyConditions); } + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + @Override public String toString() { + String autoDeployString = m_autoDeploy == null? "false" : String.valueOf(m_autoDeploy.isEnabled()); return "DependencyInfo[name=" + getName() + ", scope=" + getScope() + - ", auto-deploy=" + m_autoDeploy.isEnabled() + + ", type=" + getType() + + ", auto-deploy=" + autoDeployString + "]"; } @@ -192,6 +209,7 @@ public class DependencyInfo { if (m_autoDeploy != null ? !m_autoDeploy.equals(that.m_autoDeploy) : that.m_autoDeploy != null) return false; if (name != null ? !name.equals(that.name) : that.name != null) return false; if (scope != null ? !scope.equals(that.scope) : that.scope != null) return false; + if (type != null ? !type.equals(that.type) : that.type != null) return false; if (serviceName != null ? !serviceName.equals(that.serviceName) : that.serviceName != null) return false; return true; diff --git a/ambari-server/src/main/java/org/apache/ambari/server/topology/BlueprintValidatorImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/topology/BlueprintValidatorImpl.java index 33c80e4..2253f61 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/topology/BlueprintValidatorImpl.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/topology/BlueprintValidatorImpl.java @@ -65,12 +65,15 @@ public class BlueprintValidatorImpl implements BlueprintValidator { public void validateTopology() throws InvalidTopologyException { LOGGER.info("Validating topology for blueprint: [{}]", blueprint.getName()); Collection<HostGroup> hostGroups = blueprint.getHostGroups().values(); - Map<String, Map<String, Collection<DependencyInfo>>> missingDependencies = new HashMap<>(); + Map<String, Map<String, Collection<DependencyInfo>>> dependenciesValidationIssues = new HashMap<>(); for (HostGroup group : hostGroups) { - Map<String, Collection<DependencyInfo>> missingGroupDependencies = validateHostGroup(group); - if (!missingGroupDependencies.isEmpty()) { - missingDependencies.put(group.getName(), missingGroupDependencies); + Map<String, Collection<DependencyInfo>> groupDependenciesValidationIssues = + validateHostGroup(group, "inclusive"); + + groupDependenciesValidationIssues.putAll(validateHostGroup(group, "exclusive")); + if (!groupDependenciesValidationIssues.isEmpty()) { + dependenciesValidationIssues.put(group.getName(), groupDependenciesValidationIssues); } } @@ -90,8 +93,8 @@ public class BlueprintValidatorImpl implements BlueprintValidator { } } - if (!missingDependencies.isEmpty() || !cardinalityFailures.isEmpty()) { - generateInvalidTopologyException(missingDependencies, cardinalityFailures); + if (!dependenciesValidationIssues.isEmpty() || !cardinalityFailures.isEmpty()) { + generateInvalidTopologyException(dependenciesValidationIssues, cardinalityFailures); } } @@ -228,9 +231,9 @@ public class BlueprintValidatorImpl implements BlueprintValidator { return cardinalityFailures; } - private Map<String, Collection<DependencyInfo>> validateHostGroup(HostGroup group) { + private Map<String, Collection<DependencyInfo>> validateHostGroup(HostGroup group, String dependencyValidationType) { LOGGER.info("Validating hostgroup: {}", group.getName()); - Map<String, Collection<DependencyInfo>> missingDependencies = new HashMap<>(); + Map<String, Collection<DependencyInfo>> dependenciesIssues = new HashMap<>(); for (String component : new HashSet<>(group.getComponentNames())) { LOGGER.debug("Processing component: {}", component); @@ -260,10 +263,15 @@ public class BlueprintValidatorImpl implements BlueprintValidator { } String dependencyScope = dependency.getScope(); + String dependencyType = dependency.getType(); String componentName = dependency.getComponentName(); AutoDeployInfo autoDeployInfo = dependency.getAutoDeploy(); boolean resolved = false; + if (dependencyValidationType != null && !dependencyValidationType.equals(dependencyType)) { + continue; + } + //check if conditions are met, if any if(dependency.hasDependencyConditions()) { boolean conditionsSatisfied = true; @@ -282,24 +290,33 @@ public class BlueprintValidatorImpl implements BlueprintValidator { componentName, new Cardinality("1+"), autoDeployInfo); resolved = missingDependencyInfo.isEmpty(); + if (dependencyType.equals("exclusive")) { + resolved = !resolved; + } } else if (dependencyScope.equals("host")) { - if (group.getComponentNames().contains(componentName) || (autoDeployInfo != null && autoDeployInfo.isEnabled())) { - resolved = true; - group.addComponent(componentName); + if (dependencyType.equals("exclusive")) { + if (!group.getComponentNames().contains(componentName)) { + resolved = true; + } + } else { + if (group.getComponentNames().contains(componentName) || (autoDeployInfo != null && autoDeployInfo.isEnabled())) { + resolved = true; + group.addComponent(componentName); + } } } if (! resolved) { - Collection<DependencyInfo> missingCompDependencies = missingDependencies.get(component); - if (missingCompDependencies == null) { - missingCompDependencies = new HashSet<>(); - missingDependencies.put(component, missingCompDependencies); + Collection<DependencyInfo> compDependenciesIssues = dependenciesIssues.get(component); + if (compDependenciesIssues == null) { + compDependenciesIssues = new HashSet<>(); + dependenciesIssues.put(component, compDependenciesIssues); } - missingCompDependencies.add(dependency); + compDependenciesIssues.add(dependency); } } } - return missingDependencies; + return dependenciesIssues; } /** @@ -379,13 +396,15 @@ public class BlueprintValidatorImpl implements BlueprintValidator { /** * Generate an exception for topology validation failure. * - * @param missingDependencies missing dependency information + * @param dependenciesIssues dependency issues information, + * like component needs to be co-hosted, + * or components can't be installed on the same host * @param cardinalityFailures missing service component information * * @throws IllegalArgumentException Always thrown and contains information regarding the topology validation failure * in the msg */ - private void generateInvalidTopologyException(Map<String, Map<String, Collection<DependencyInfo>>> missingDependencies, + private void generateInvalidTopologyException(Map<String, Map<String, Collection<DependencyInfo>>> dependenciesIssues, Collection<String> cardinalityFailures) throws InvalidTopologyException { //todo: encapsulate some of this in exception? @@ -393,8 +412,8 @@ public class BlueprintValidatorImpl implements BlueprintValidator { if (! cardinalityFailures.isEmpty()) { msg += " Invalid service component count: " + cardinalityFailures; } - if (! missingDependencies.isEmpty()) { - msg += " Unresolved component dependencies: " + missingDependencies; + if (! dependenciesIssues.isEmpty()) { + msg += " Component dependencies issues: " + dependenciesIssues; } msg += ". To disable topology validation and create the blueprint, " + "add the following to the end of the url: '?validate_topology=false'"; diff --git a/ambari-server/src/main/resources/stacks/stack_advisor.py b/ambari-server/src/main/resources/stacks/stack_advisor.py index 336ae75..9fa05ed 100644 --- a/ambari-server/src/main/resources/stacks/stack_advisor.py +++ b/ambari-server/src/main/resources/stacks/stack_advisor.py @@ -1172,30 +1172,36 @@ class DefaultStackAdvisor(StackAdvisor): # account for only dependencies that are not conditional conditionsPresent = "conditions" in dependency["Dependencies"] and dependency["Dependencies"]["conditions"] if not conditionsPresent: - requiredComponent = self.getRequiredComponent(services, dependency["Dependencies"]["component_name"]) + dependentComponent = self.getRequiredComponent(services, dependency["Dependencies"]["component_name"]) componentDisplayName = component["StackServiceComponents"]["display_name"] - requiredComponentDisplayName = requiredComponent["display_name"] \ - if requiredComponent is not None else dependency["Dependencies"]["component_name"] - requiredComponentHosts = requiredComponent["hostnames"] if requiredComponent is not None else [] + dependentComponentDisplayName = dependentComponent["display_name"] \ + if dependentComponent is not None else dependency["Dependencies"]["component_name"] + dependentComponentHosts = dependentComponent["hostnames"] if dependentComponent is not None else [] # Client dependencies are not included in validation # Client dependencies are auto-deployed in both UI deployements and blueprint deployments - if (requiredComponent is None) or \ - (requiredComponent["component_category"] != "CLIENT"): + if (dependentComponent is None) or \ + (dependentComponent["component_category"] != "CLIENT"): scope = "cluster" if "scope" not in dependency["Dependencies"] else dependency["Dependencies"]["scope"] + type = "inclusive" if "type" not in dependency["Dependencies"] else dependency["Dependencies"]["type"] if scope == "host": componentHosts = component["StackServiceComponents"]["hostnames"] - requiredComponentHostsAbsent = [] - for componentHost in componentHosts: - if componentHost not in requiredComponentHosts: - requiredComponentHostsAbsent.append(componentHost) - if requiredComponentHostsAbsent: - message = "{0} requires {1} to be co-hosted on following host(s): {2}.".format(componentDisplayName, - requiredComponentDisplayName, ', '.join(requiredComponentHostsAbsent)) + hostsWithIssues = [] + notMessagePart = "" + if type == "exclusive": + hostsWithIssues = list(set(componentHosts) & set(dependentComponentHosts)) + notMessagePart = "not" + else: + for componentHost in componentHosts: + if componentHost not in dependentComponentHosts: + hostsWithIssues.append(componentHost) + if hostsWithIssues: + message = "{0} requires {1} {2} to be co-hosted on following host(s): {3}.".format(componentDisplayName, + dependentComponentDisplayName, notMessagePart, ', '.join(hostsWithIssues)) items.append({ "type": 'host-component', "level": 'ERROR', "message": message, "component-name": component["StackServiceComponents"]["component_name"]}) - elif scope == "cluster" and not requiredComponentHosts: - message = "{0} requires {1} to be present in the cluster.".format(componentDisplayName, requiredComponentDisplayName) + elif scope == "cluster" and not dependentComponentHosts: + message = "{0} requires {1} to be present in the cluster.".format(componentDisplayName, dependentComponentDisplayName) items.append({ "type": 'host-component', "level": 'ERROR', "message": message, "component-name": component["StackServiceComponents"]["component_name"]}) return items diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerTest.java index c7c6360..4926e87 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerTest.java @@ -50,6 +50,7 @@ import java.util.UUID; import javax.persistence.EntityManager; +import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.ClusterNotFoundException; import org.apache.ambari.server.DuplicateResourceException; import org.apache.ambari.server.H2DatabaseCleaner; @@ -194,7 +195,7 @@ public class AmbariManagementControllerTest { private static final int STACK_VERSIONS_CNT = 17; private static final int REPOS_CNT = 3; private static final int STACK_PROPERTIES_CNT = 103; - private static final int STACK_COMPONENTS_CNT = 4; + private static final int STACK_COMPONENTS_CNT = 5; private static final int OS_CNT = 2; private static final String NON_EXT_VALUE = "XXX"; @@ -1526,6 +1527,46 @@ public class AmbariManagementControllerTest { .getServiceComponentHost(host2)); } + @Test(expected=AmbariException.class) + public void testCreateServiceComponentHostExclusiveAmbariException() + throws Exception { + String cluster1 = getUniqueName(); + createCluster(cluster1); + String serviceName = "HDFS"; + createService(cluster1, serviceName, null); + String componentName1 = "NAMENODE"; + String componentName2 = "DATANODE"; + String componentName3 = "EXCLUSIVE_DEPENDENCY_COMPONENT"; + createServiceComponent(cluster1, serviceName, componentName1, + State.INIT); + createServiceComponent(cluster1, serviceName, componentName2, + State.INIT); + createServiceComponent(cluster1, serviceName, componentName3, + State.INIT); + String host1 = getUniqueName(); + String host2 = getUniqueName(); + addHostToCluster(host1, cluster1); + addHostToCluster(host2, cluster1); + + Set<ServiceComponentHostRequest> set1 = + new HashSet<>(); + ServiceComponentHostRequest r1 = + new ServiceComponentHostRequest(cluster1, serviceName, + componentName1, host1, State.INIT.toString()); + ServiceComponentHostRequest r2 = + new ServiceComponentHostRequest(cluster1, serviceName, + componentName3, host1, State.INIT.toString()); + ServiceComponentHostRequest r3 = + new ServiceComponentHostRequest(cluster1, serviceName, + componentName2, host1, State.INIT.toString()); + + set1.add(r1); + set1.add(r2); + set1.add(r3); + + controller.createHostComponents(set1); + } + @Test public void testCreateServiceComponentHostWithInvalidRequest() throws Exception, AuthorizationException { diff --git a/ambari-server/src/test/java/org/apache/ambari/server/topology/BlueprintValidatorImplTest.java b/ambari-server/src/test/java/org/apache/ambari/server/topology/BlueprintValidatorImplTest.java index 75a9d6b..aec3903 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/topology/BlueprintValidatorImplTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/topology/BlueprintValidatorImplTest.java @@ -180,6 +180,43 @@ public class BlueprintValidatorImplTest { verify(group1); } + @Test(expected=InvalidTopologyException.class) + public void testValidateTopology_exclusiveDependency() throws Exception { + group1Components.add("component2"); + group1Components.add("component3"); + dependencies1.add(dependency1); + services.addAll(Collections.singleton("service1")); + + expect(blueprint.getHostGroupsForComponent("component1")).andReturn(Arrays.asList(group1, group2)).anyTimes(); + expect(blueprint.getHostGroupsForComponent("component2")).andReturn(Arrays.asList(group1, group2)).anyTimes(); + expect(blueprint.getHostGroupsForComponent("component3")).andReturn(Arrays.asList(group1, group2)).anyTimes(); + + expect(stack.getComponents("service1")).andReturn(Arrays.asList("component1", "component2")).anyTimes(); + expect(stack.getComponents("service2")).andReturn(Collections.singleton("component3")).anyTimes(); + expect(stack.getAutoDeployInfo("component1")).andReturn(autoDeploy).anyTimes(); + + AutoDeployInfo dependencyAutoDeploy = new AutoDeployInfo(); + dependencyAutoDeploy.setEnabled(true); + dependencyAutoDeploy.setCoLocate("service1/component1"); + + expect(dependency1.getScope()).andReturn("host").anyTimes(); + expect(dependency1.getType()).andReturn("exclusive").anyTimes(); + expect(dependency1.getAutoDeploy()).andReturn(dependencyAutoDeploy).anyTimes(); + expect(dependency1.getComponentName()).andReturn("component3").anyTimes(); + expect(dependency1.getServiceName()).andReturn("service1").anyTimes(); + expect(dependency1.getName()).andReturn("dependency1").anyTimes(); + + expect(dependencyComponentInfo.isClient()).andReturn(true).anyTimes(); + expect(stack.getComponentInfo("component3")).andReturn(dependencyComponentInfo).anyTimes(); + + replay(blueprint, stack, group1, group2, dependency1, dependencyComponentInfo); + + BlueprintValidator validator = new BlueprintValidatorImpl(blueprint); + validator.validateTopology(); + + verify(group1); + } + @Test public void testValidateTopology_autoDeploy_hasDependency() throws Exception { group1Components.add("component2"); @@ -199,6 +236,7 @@ public class BlueprintValidatorImplTest { dependencyAutoDeploy.setCoLocate("service1/component1"); expect(dependency1.getScope()).andReturn("host").anyTimes(); + expect(dependency1.getType()).andReturn("inclusive").anyTimes(); expect(dependency1.getAutoDeploy()).andReturn(dependencyAutoDeploy).anyTimes(); expect(dependency1.getComponentName()).andReturn("component3").anyTimes(); expect(dependency1.getServiceName()).andReturn("service1").anyTimes(); @@ -343,6 +381,7 @@ public class BlueprintValidatorImplTest { AutoDeployInfo dependencyAutoDeploy = null; expect(dependency1.getScope()).andReturn("host").anyTimes(); + expect(dependency1.getType()).andReturn("inclusive").anyTimes(); expect(dependency1.getAutoDeploy()).andReturn(dependencyAutoDeploy).anyTimes(); expect(dependency1.getComponentName()).andReturn("component-d").anyTimes(); expect(dependency1.getServiceName()).andReturn("service-d").anyTimes(); @@ -393,6 +432,7 @@ public class BlueprintValidatorImplTest { AutoDeployInfo dependencyAutoDeploy = null; expect(dependency1.getScope()).andReturn("host").anyTimes(); + expect(dependency1.getType()).andReturn("inclusive").anyTimes(); expect(dependency1.getAutoDeploy()).andReturn(dependencyAutoDeploy).anyTimes(); expect(dependency1.getComponentName()).andReturn("component-d").anyTimes(); expect(dependency1.getServiceName()).andReturn("service-d").anyTimes(); @@ -400,6 +440,7 @@ public class BlueprintValidatorImplTest { expect(dependency1.hasDependencyConditions()).andReturn(true).anyTimes(); expect(dependency1.getDependencyConditions()).andReturn(dependenciesConditionInfos1).anyTimes(); expect(dependency2.getScope()).andReturn("host").anyTimes(); + expect(dependency2.getType()).andReturn("inclusive").anyTimes(); expect(dependency2.getAutoDeploy()).andReturn(dependencyAutoDeploy).anyTimes(); expect(dependency2.getComponentName()).andReturn("component-d").anyTimes(); expect(dependency2.getServiceName()).andReturn("service-d").anyTimes(); diff --git a/ambari-server/src/test/resources/stacks/HDP/0.2/services/HDFS/metainfo.xml b/ambari-server/src/test/resources/stacks/HDP/0.2/services/HDFS/metainfo.xml index bfe941c..0f5d35c 100644 --- a/ambari-server/src/test/resources/stacks/HDP/0.2/services/HDFS/metainfo.xml +++ b/ambari-server/src/test/resources/stacks/HDP/0.2/services/HDFS/metainfo.xml @@ -52,6 +52,24 @@ <scriptType>PYTHON</scriptType> <timeout>600</timeout> </commandScript> + <dependencies> + <dependency> + <name>HDFS/EXCLUSIVE_DEPENDENCY_COMPONENT</name> + <scope>host</scope> + <type>exclusive</type> + </dependency> + </dependencies> + </component> + + <component> + <name>EXCLUSIVE_DEPENDENCY_COMPONENT</name> + <category>SLAVE</category> + <cardinality>1+</cardinality> + <commandScript> + <script>scripts/datanode.py</script> + <scriptType>PYTHON</scriptType> + <timeout>600</timeout> + </commandScript> </component> <component>