You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by di...@apache.org on 2016/10/07 17:35:36 UTC

ambari git commit: AMBARI-18355: Introduce conditional dependencies in stack defition to handle blueprint validation gracefully (Amruta Borkar via dili)

Repository: ambari
Updated Branches:
  refs/heads/trunk c153cfb63 -> f6124a056


AMBARI-18355: Introduce conditional dependencies in stack defition to handle blueprint validation gracefully (Amruta Borkar via dili)


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

Branch: refs/heads/trunk
Commit: f6124a056d2a8ed16bec917775b9d3554ab5d74d
Parents: c153cfb
Author: Di Li <di...@apache.org>
Authored: Fri Oct 7 13:34:54 2016 -0400
Committer: Di Li <di...@apache.org>
Committed: Fri Oct 7 13:34:54 2016 -0400

----------------------------------------------------------------------
 .../server/state/DependencyConditionInfo.java   | 102 +++++++++++++++++++
 .../ambari/server/state/DependencyInfo.java     |  36 ++++++-
 .../server/topology/BlueprintValidatorImpl.java |  13 +++
 .../common-services/HDFS/2.1.0.2.0/metainfo.xml |  44 ++++++++
 .../topology/BlueprintValidatorImplTest.java    |  75 +++++++++++++-
 5 files changed, 268 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/f6124a05/ambari-server/src/main/java/org/apache/ambari/server/state/DependencyConditionInfo.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/DependencyConditionInfo.java b/ambari-server/src/main/java/org/apache/ambari/server/state/DependencyConditionInfo.java
new file mode 100644
index 0000000..84e186f
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/DependencyConditionInfo.java
@@ -0,0 +1,102 @@
+/**
+ * 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.state;
+
+import java.util.Map;
+import java.util.Objects;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.adapters.XmlAdapter;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+
+import org.apache.commons.lang.NotImplementedException;
+
+/**
+ * Represents stack component dependency condition information.
+ */
+@XmlJavaTypeAdapter(DependencyConditionAdapter.class)
+public abstract class DependencyConditionInfo {
+    public abstract boolean isResolved(Map<String, Map<String, String>> properties);
+}
+
+class PropertyExistsDependencyCondition extends DependencyConditionInfo{
+
+    protected final String configType;
+    protected final String property;
+    public PropertyExistsDependencyCondition( String configType, String property) {
+        this.configType = Objects.requireNonNull(configType, "Config Type must not be null.");
+        this.property = Objects.requireNonNull(property, "Property Name must not be null.");
+    }
+
+    @Override
+    public boolean isResolved(Map<String, Map<String, String>> properties) {
+        return (properties.get(configType).containsKey(property));
+    }
+}
+
+class PropertyValueEqualsDependencyCondition extends PropertyExistsDependencyCondition {
+
+    protected final String propertyValue;
+    public PropertyValueEqualsDependencyCondition(String configType, String property, String propertyValue) {
+        super(configType, property);
+        this.propertyValue = Objects.requireNonNull(propertyValue, "Property value must not be null.");
+    }
+
+    @Override
+    public boolean isResolved(Map<String, Map<String, String>> properties) {
+            return (super.isResolved(properties) && propertyValue.equals(properties.get(configType).get(property)));
+    }
+}
+
+class DependencyConditionAdapter extends XmlAdapter<DependencyConditionAdapter.AdaptedDependencyCondition, DependencyConditionInfo> {
+
+    static class AdaptedDependencyCondition{
+            @XmlElement
+            private String configType;
+            @XmlElement
+            private String property;
+            @XmlElement
+            private String propertyValue;
+            @XmlElement(name="condition-type")
+            private String conditionType;
+    }
+
+    @Override
+    public AdaptedDependencyCondition marshal(DependencyConditionInfo arg0) throws Exception {
+        throw new NotImplementedException();
+    }
+
+    @Override
+    public DependencyConditionInfo unmarshal(AdaptedDependencyCondition adaptedDependencyCondition) throws Exception {
+        if (null == adaptedDependencyCondition) {
+            return null;
+        }
+        DependencyConditionInfo dependencyConditionInfo = null;
+            switch (adaptedDependencyCondition.conditionType) {
+                case "IF-PROPERTY-EXISTS":
+                    dependencyConditionInfo = new PropertyExistsDependencyCondition(adaptedDependencyCondition.configType, adaptedDependencyCondition.property);
+                    break;
+                case "PROPERTY-VALUE-EQUALS":
+                    dependencyConditionInfo = new PropertyValueEqualsDependencyCondition(adaptedDependencyCondition.configType, adaptedDependencyCondition.property, adaptedDependencyCondition.propertyValue);
+                    break;
+                default:
+                    throw new IllegalArgumentException("Specified condition type is not not supported " + adaptedDependencyCondition.conditionType);
+            }
+        return dependencyConditionInfo;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/f6124a05/ambari-server/src/main/java/org/apache/ambari/server/state/DependencyInfo.java
----------------------------------------------------------------------
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 e3db662..3c88401 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
@@ -20,6 +20,10 @@ package org.apache.ambari.server.state;
 
 
 import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlElements;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Represents stack component dependency information.
@@ -54,7 +58,10 @@ public class DependencyInfo {
   @XmlElement(name="auto-deploy")
   private AutoDeployInfo m_autoDeploy;
 
-
+  /**
+   * Conditions for Component dependency to other components.
+   */
+  private List<DependencyConditionInfo> dependencyConditions = new ArrayList<DependencyConditionInfo>();
   /**
    * Setter for name property.
    *
@@ -135,6 +142,33 @@ public class DependencyInfo {
   public String getServiceName() {
     return serviceName;
   }
+  /**
+   * Get the dependencyConditions list
+   *
+   * @return dependencyConditions
+   */
+  @XmlElementWrapper(name="conditions")
+  @XmlElements(@XmlElement(name="condition"))
+  public List<DependencyConditionInfo> getDependencyConditions() {
+    return dependencyConditions;
+  }
+
+  /**
+   * Set dependencyConditions
+   *
+   * @param dependencyConditions
+   */
+  public void setDependencyConditions(List<DependencyConditionInfo> dependencyConditions) {
+    this.dependencyConditions = dependencyConditions;
+  }
+
+  /**
+   * Confirms if dependency have any condition or not
+   * @return true if dependencies are based on a condition
+   */
+  public boolean hasDependencyConditions(){
+    return !(dependencyConditions == null || dependencyConditions.isEmpty());
+  }
 
   @Override
   public String toString() {

http://git-wip-us.apache.org/repos/asf/ambari/blob/f6124a05/ambari-server/src/main/java/org/apache/ambari/server/topology/BlueprintValidatorImpl.java
----------------------------------------------------------------------
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 a5f33ff..7bbe0db 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
@@ -30,6 +30,7 @@ import java.util.regex.Pattern;
 
 import org.apache.ambari.server.controller.internal.Stack;
 import org.apache.ambari.server.state.AutoDeployInfo;
+import org.apache.ambari.server.state.DependencyConditionInfo;
 import org.apache.ambari.server.state.DependencyInfo;
 import org.apache.ambari.server.utils.SecretReference;
 import org.apache.ambari.server.utils.VersionUtils;
@@ -289,6 +290,18 @@ public class BlueprintValidatorImpl implements BlueprintValidator {
         AutoDeployInfo autoDeployInfo  = dependency.getAutoDeploy();
         boolean        resolved        = false;
 
+        //check if conditions are met, if any
+        if(dependency.hasDependencyConditions()) {
+          boolean conditionsSatisfied = true;
+          for (DependencyConditionInfo dependencyCondition : dependency.getDependencyConditions()) {
+            if (!dependencyCondition.isResolved(blueprint.getConfiguration().getFullProperties())) {
+              conditionsSatisfied = false;
+            }
+          }
+          if(!conditionsSatisfied){
+            continue;
+          }
+        }
         if (dependencyScope.equals("cluster")) {
           Collection<String> missingDependencyInfo = verifyComponentCardinalityCount(
               componentName, new Cardinality("1+"), autoDeployInfo);

http://git-wip-us.apache.org/repos/asf/ambari/blob/f6124a05/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/metainfo.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/metainfo.xml b/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/metainfo.xml
index 65d166a..dfbbd55 100644
--- a/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/metainfo.xml
+++ b/ambari-server/src/main/resources/common-services/HDFS/2.1.0.2.0/metainfo.xml
@@ -32,6 +32,50 @@
           <cardinality>1-2</cardinality>
           <versionAdvertised>true</versionAdvertised>
           <reassignAllowed>true</reassignAllowed>
+          <dependencies>
+            <dependency>
+              <name>HDFS/ZKFC</name>
+              <scope>host</scope>
+              <auto-deploy>
+                <enabled>false</enabled>
+              </auto-deploy>
+              <conditions>
+                <condition>
+                  <condition-type>IF-PROPERTY-EXISTS</condition-type>
+                  <configType>hdfs-site</configType>
+                  <property>dfs.nameservices</property>
+                </condition>
+              </conditions>
+            </dependency>
+            <dependency>
+              <name>ZOOKEEPER/ZOOKEEPER_SERVER</name>
+              <scope>host</scope>
+              <auto-deploy>
+                <enabled>false</enabled>
+              </auto-deploy>
+              <conditions>
+                <condition>
+                  <condition-type>IF-PROPERTY-EXISTS</condition-type>
+                  <configType>hdfs-site</configType>
+                  <property>dfs.nameservices</property>
+                </condition>
+              </conditions>
+            </dependency>
+            <dependency>
+              <name>HDFS/JOURNALNODE</name>
+              <scope>host</scope>
+              <auto-deploy>
+                <enabled>false</enabled>
+              </auto-deploy>
+              <conditions>
+                <condition>
+                  <condition-type>IF-PROPERTY-EXISTS</condition-type>
+                  <configType>hdfs-site</configType>
+                  <property>dfs.nameservices</property>
+                </condition>
+              </conditions>
+            </dependency>
+          </dependencies>
           <commandScript>
             <script>scripts/namenode.py</script>
             <scriptType>PYTHON</scriptType>

http://git-wip-us.apache.org/repos/asf/ambari/blob/f6124a05/ambari-server/src/test/java/org/apache/ambari/server/topology/BlueprintValidatorImplTest.java
----------------------------------------------------------------------
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 b1de8ef..1501c53 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
@@ -34,7 +34,9 @@ import java.util.Map;
 import org.apache.ambari.server.controller.internal.Stack;
 import org.apache.ambari.server.state.AutoDeployInfo;
 import org.apache.ambari.server.state.ComponentInfo;
+import org.apache.ambari.server.state.DependencyConditionInfo;
 import org.apache.ambari.server.state.DependencyInfo;
+import org.easymock.EasyMock;
 import org.easymock.EasyMockRule;
 import org.easymock.Mock;
 import org.easymock.MockType;
@@ -66,15 +68,22 @@ public class BlueprintValidatorImplTest {
 
   @Mock(type = MockType.NICE)
   private DependencyInfo dependency1;
+  @Mock(type = MockType.NICE)
+  private DependencyInfo dependency2;
 
   @Mock(type = MockType.NICE)
   private ComponentInfo dependencyComponentInfo;
+  @Mock(type = MockType.NICE)
+  private DependencyConditionInfo dependencyConditionInfo1;
+  @Mock(type = MockType.NICE)
+  private DependencyConditionInfo dependencyConditionInfo2;
 
   private final Collection<String> group1Components = new ArrayList<String>();
   private final Collection<String> group2Components = new ArrayList<String>();
   private final Collection<String> services = new ArrayList<String>();
 
   private Collection<DependencyInfo> dependencies1 = new ArrayList<DependencyInfo>();
+  private List<DependencyConditionInfo> dependenciesConditionInfos1 = new ArrayList<DependencyConditionInfo>();
   private AutoDeployInfo autoDeploy = new AutoDeployInfo();
   private Map<String, Map<String, String>> configProperties = new HashMap<String, Map<String, String>>();
   private Configuration configuration = new Configuration(configProperties, Collections.<String, Map<String, Map<String, String>>>emptyMap());
@@ -105,13 +114,15 @@ public class BlueprintValidatorImplTest {
     expect(stack.getCardinality("component1")).andReturn(new Cardinality("1"));
     expect(stack.getCardinality("component2")).andReturn(new Cardinality("1+"));
     expect(stack.getCardinality("component3")).andReturn(new Cardinality("1+"));
+    dependenciesConditionInfos1.add(dependencyConditionInfo1);
+    dependenciesConditionInfos1.add(dependencyConditionInfo2);
 
     expect(blueprint.getConfiguration()).andReturn(configuration).anyTimes();
   }
 
   @After
   public void tearDown() {
-    reset(blueprint, stack, group1, group2, dependency1);
+    reset(blueprint, stack, group1, group2, dependency1, dependency2, dependencyConditionInfo1, dependencyConditionInfo2);
   }
 
   @Test
@@ -341,4 +352,66 @@ public class BlueprintValidatorImplTest {
     verify(group1);
 
   }
+  @Test(expected=InvalidTopologyException.class)
+  public void testWhenComponentIsConditionallyDependentAndOnlyOneOfTheConditionsIsSatisfied() throws Exception {
+    // GIVEN
+    hostGroups.clear();
+    hostGroups.put("group1", group1);
+
+    group1Components.add("component-1");
+    dependencies1.add(dependency1);
+    dependencies1.add(dependency2);
+    services.addAll(Collections.singleton("service-1"));
+
+
+    expect(blueprint.getHostGroupsForComponent("component-1")).andReturn(Arrays.asList(group1)).anyTimes();
+    expect(blueprint.getName()).andReturn("blueprint-1").anyTimes();
+    Map<String, Map<String, String>> properties = new HashMap<String, Map<String, String>>();
+    Map<String, String> typeProps = new HashMap<String, String>();
+    typeProps.put("yarn.resourcemanager.hostname", "testhost");
+    properties.put("yarn-site", typeProps);
+
+    Configuration clusterConfig = new Configuration(properties,
+       Collections.<String, Map<String, Map<String, String>>>emptyMap());
+
+    Cardinality cardinality = new Cardinality("1");
+
+    expect(stack.getComponents("service-1")).andReturn(Arrays.asList("component-1")).anyTimes();
+    expect(stack.getAutoDeployInfo("component-1")).andReturn(autoDeploy).anyTimes();
+    expect(stack.getDependenciesForComponent("component-1")).andReturn(dependencies1).anyTimes();
+    expect(stack.getCardinality("component-1")).andReturn(cardinality).anyTimes();
+
+    AutoDeployInfo dependencyAutoDeploy = null;
+
+    expect(dependency1.getScope()).andReturn("host").anyTimes();
+    expect(dependency1.getAutoDeploy()).andReturn(dependencyAutoDeploy).anyTimes();
+    expect(dependency1.getComponentName()).andReturn("component-d").anyTimes();
+    expect(dependency1.getServiceName()).andReturn("service-d").anyTimes();
+    expect(dependency1.getName()).andReturn("dependency-1").anyTimes();
+    expect(dependency1.hasDependencyConditions()).andReturn(true).anyTimes();
+    expect(dependency1.getDependencyConditions()).andReturn(dependenciesConditionInfos1).anyTimes();
+    expect(dependency2.getScope()).andReturn("host").anyTimes();
+    expect(dependency2.getAutoDeploy()).andReturn(dependencyAutoDeploy).anyTimes();
+    expect(dependency2.getComponentName()).andReturn("component-d").anyTimes();
+    expect(dependency2.getServiceName()).andReturn("service-d").anyTimes();
+    expect(dependency2.getName()).andReturn("dependency-2").anyTimes();
+    expect(dependency2.hasDependencyConditions()).andReturn(false).anyTimes();
+
+    expect(dependencyConditionInfo1.isResolved(EasyMock.anyObject(Map.class))).andReturn(true).anyTimes();
+    expect(dependencyConditionInfo2.isResolved(EasyMock.anyObject(Map.class))).andReturn(false).anyTimes();
+
+
+    expect(dependencyComponentInfo.isClient()).andReturn(false).anyTimes();
+    expect(stack.getComponentInfo("component-d")).andReturn(dependencyComponentInfo).anyTimes();
+
+    replay(blueprint, stack, group1, group2, dependency1, dependency2, dependencyComponentInfo,dependencyConditionInfo1,dependencyConditionInfo2);
+
+    // WHEN
+    BlueprintValidator validator = new BlueprintValidatorImpl(blueprint);
+    validator.validateTopology();
+
+    // THEN
+    verify(group1);
+
+  }
 }