You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by dm...@apache.org on 2015/12/08 13:45:26 UTC

ambari git commit: AMBARI-14250. Add unit test coverage for ConfigMergeHelper#getConflicts (dlysnichenko)

Repository: ambari
Updated Branches:
  refs/heads/trunk 139764681 -> 4bd35dfba


AMBARI-14250. Add unit test coverage for ConfigMergeHelper#getConflicts (dlysnichenko)


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

Branch: refs/heads/trunk
Commit: 4bd35dfbab90d85965caf03e7808fcd5b6054e27
Parents: 1397646
Author: Lisnichenko Dmitro <dl...@hortonworks.com>
Authored: Tue Dec 8 14:46:03 2015 +0200
Committer: Lisnichenko Dmitro <dl...@hortonworks.com>
Committed: Tue Dec 8 14:46:03 2015 +0200

----------------------------------------------------------------------
 .../ambari/server/state/ConfigMergeHelper.java  |  34 ++-
 .../server/state/ConfigMergeHelperTest.java     | 245 +++++++++++++++++++
 2 files changed, 272 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/4bd35dfb/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigMergeHelper.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigMergeHelper.java b/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigMergeHelper.java
index 0610137..b799bc7 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigMergeHelper.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigMergeHelper.java
@@ -55,9 +55,10 @@ public class ConfigMergeHelper {
     Cluster cluster = m_clusters.get().getCluster(clusterName);
     StackId oldStack = cluster.getCurrentStackVersion();
 
-    Map<String, Map<String, String>> oldMap = new HashMap<String, Map<String, String>>();
-    Map<String, Map<String, String>> newMap = new HashMap<String, Map<String, String>>();
+    Map<String, Map<String, String>> oldMap = new HashMap<>();
+    Map<String, Map<String, String>> newMap = new HashMap<>();
 
+    // Collect service-level properties for old and new stack
     for (String serviceName : cluster.getServices().keySet()) {
       Set<PropertyInfo> oldStackProperties = m_ambariMetaInfo.get().getServiceProperties(
           oldStack.getStackName(), oldStack.getStackVersion(), serviceName);
@@ -68,6 +69,7 @@ public class ConfigMergeHelper {
       addToMap(newMap, newStackProperties);
     }
 
+    // Collect stack-level properties defined for old and new stack
     Set<PropertyInfo> set = m_ambariMetaInfo.get().getStackProperties(
         oldStack.getStackName(), oldStack.getStackVersion());
     addToMap(oldMap, set);
@@ -79,6 +81,7 @@ public class ConfigMergeHelper {
     Map<String, Map<String, ThreeWayValue>> result =
         new HashMap<String, Map<String, ThreeWayValue>>();
 
+    // For every property of every config type, see if it is going to be merged:
     for (Entry<String, Map<String, String>> entry : oldMap.entrySet()) {
       if (!newMap.containsKey(entry.getKey())) {
         LOG.info("Stack {} does not have an equivalent config type {} in {}",
@@ -91,6 +94,10 @@ public class ConfigMergeHelper {
       Collection<String> customValueKeys = null;
 
       Config config = cluster.getDesiredConfigByType(entry.getKey());
+
+      // The conflict occurs, if current property is not defined at stack
+      // metainfo, but is present in current cluster configs,
+      // and a new value is different from current value
       if (null != config) {
         Set<String> valueKeys = config.getProperties().keySet();
 
@@ -116,6 +123,13 @@ public class ConfigMergeHelper {
         }
       }
 
+      // Also, conflict occurs if all four coditions are met:
+      // a) property is defined both in old and in new stack
+      // b) property value in new stack differs from current
+      // the property value
+      // c) current property value is not the same as in new stack
+      // d) current property value is not the same as in old stack
+
       Collection<String> common = CollectionUtils.intersection(newPairs.keySet(),
           oldPairs.keySet());
 
@@ -127,10 +141,7 @@ public class ConfigMergeHelper {
           savedVal = config.getProperties().get(prop);
         }
 
-        // If values are not defined in stack (null), we skip them
-        // Or if values in old stack and in new stack are the same, and value
-        // in current config is different, skip it
-        if (!(newStackVal == null && oldStackVal == null)
+        if (!(newStackVal == null || oldStackVal == null)
                 && !newStackVal.equals(savedVal) &&
             (!oldStackVal.equals(newStackVal) || !oldStackVal.equals(savedVal))) {
           ThreeWayValue twv = new ThreeWayValue();
@@ -161,6 +172,7 @@ public class ConfigMergeHelper {
     }
 
   }
+
   /**
    * Represents the three different config values for merging.
    */
@@ -179,7 +191,15 @@ public class ConfigMergeHelper {
     public String savedValue;
   }
 
-  private static String normalizeValue(String templateValue, String newRawValue) {
+  /**
+   * If template looks like Java heap size, then format raw value to comply
+   * with size unit specified in template
+   * @param templateValue
+   * @param newRawValue
+   * @return raw value formatted like template (if template looks like Java
+   * heap size), raw value as is otherwise.
+   */
+  static String normalizeValue(String templateValue, String newRawValue) {
     if (null == templateValue) {
       return newRawValue;
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/4bd35dfb/ambari-server/src/test/java/org/apache/ambari/server/state/ConfigMergeHelperTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/ConfigMergeHelperTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/ConfigMergeHelperTest.java
new file mode 100644
index 0000000..ba9eb3c
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/ConfigMergeHelperTest.java
@@ -0,0 +1,245 @@
+/**
+ * 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 com.google.inject.Binder;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Module;
+import com.google.inject.persist.PersistService;
+import com.google.inject.util.Modules;
+import org.apache.ambari.server.api.services.AmbariMetaInfo;
+import org.apache.ambari.server.orm.GuiceJpaInitializer;
+import org.apache.ambari.server.orm.InMemoryDefaultTestModule;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static org.easymock.EasyMock.anyString;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.createNiceMock;
+import static org.junit.Assert.assertEquals;
+import static org.apache.ambari.server.state.ConfigMergeHelper.ThreeWayValue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * Tests the {@link ConfigMergeHelper} class
+ */
+public class ConfigMergeHelperTest {
+
+  private static final StackId currentStackId = new StackId("HDP-2.1.1");
+  private static final StackId newStackId = new StackId("HPD-2.2.0");
+
+  private Injector injector;
+  private Clusters clustersMock;
+  private AmbariMetaInfo ambariMetaInfoMock;
+
+  @Before
+  public void before() throws Exception {
+    clustersMock = createNiceMock(Clusters.class);
+    ambariMetaInfoMock = createNiceMock(AmbariMetaInfo.class);
+
+    final InMemoryDefaultTestModule injectorModule = new InMemoryDefaultTestModule() {
+      @Override
+      protected void configure() {
+        super.configure();
+      }
+    };
+
+    MockModule mockModule = new MockModule();
+    // create an injector which will inject the mocks
+    injector = Guice.createInjector(Modules.override(injectorModule).with(mockModule));
+    injector.getInstance(GuiceJpaInitializer.class);
+  }
+
+  @After
+  public void teardown() {
+    injector.getInstance(PersistService.class).stop();
+  }
+
+  @Test
+  public void testGetConflicts() throws Exception {
+    Cluster clusterMock = createNiceMock(Cluster.class);
+
+    expect(clustersMock.getCluster(anyString())).andReturn(clusterMock);
+
+    expect(clusterMock.getCurrentStackVersion()).andReturn(currentStackId);
+    expect(clusterMock.getServices()).andReturn(new HashMap<String, Service>() {{
+      put("HDFS", createNiceMock(Service.class));
+      put("ZK", createNiceMock(Service.class));
+    }});
+
+
+    Set<PropertyInfo> currentHDFSProperties = new HashSet<PropertyInfo>() {{
+      add(createPropertyInfo("hdfs-env.xml", "equal.key", "equal-value"));
+    }};
+
+    Set<PropertyInfo> currentZKProperties = new HashSet<PropertyInfo>() {{
+      add(createPropertyInfo("zk-env.xml", "different.key", "different-value-1"));
+    }};
+
+    Set<PropertyInfo> currentStackProperties = new HashSet<PropertyInfo>() {{
+      add(createPropertyInfo("hadoop-env.xml", "equal.key", "modified.value"));
+    }};
+
+    expect(ambariMetaInfoMock.getServiceProperties(currentStackId.getStackName(),
+      currentStackId.getStackVersion(), "HDFS")).andReturn(currentHDFSProperties);
+    expect(ambariMetaInfoMock.getServiceProperties(currentStackId.getStackName(),
+      currentStackId.getStackVersion(), "ZK")).andReturn(currentZKProperties);
+    expect(ambariMetaInfoMock.getStackProperties(currentStackId.getStackName(),
+      currentStackId.getStackVersion())).andReturn(currentStackProperties);
+
+    Set<PropertyInfo> newHDFSProperties = new HashSet<PropertyInfo>() {{
+      add(createPropertyInfo("hdfs-env.xml", "equal.key", "equal-value"));
+      add(createPropertyInfo("new-hdfs-config.xml", "equal.key", "equal-value"));
+    }};
+
+    Set<PropertyInfo> newZKProperties = new HashSet<PropertyInfo>() {{
+      add(createPropertyInfo("zk-env.xml", "equal.key", "different-value-2"));
+      add(createPropertyInfo("zk-env.xml", "new.key", "new-value-2"));
+    }};
+
+    Set<PropertyInfo> newStackProperties = new HashSet<PropertyInfo>() {{
+      add(createPropertyInfo("hadoop-env.xml", "equal.key", "another.value"));
+    }};
+
+    expect(ambariMetaInfoMock.getServiceProperties(newStackId.getStackName(),
+      newStackId.getStackVersion(), "HDFS")).andReturn(newHDFSProperties);
+    expect(ambariMetaInfoMock.getServiceProperties(newStackId.getStackName(),
+      newStackId.getStackVersion(), "ZK")).andReturn(newZKProperties);
+    expect(ambariMetaInfoMock.getStackProperties(newStackId.getStackName(),
+      newStackId.getStackVersion())).andReturn(newStackProperties);
+
+    // desired config of hdfs-env.xml
+    Map<String, String> desiredHdfsEnvProperties = new HashMap<String, String>() {{
+
+    }};
+    expect(clusterMock.getDesiredConfigByType("hdfs-env.xml")).andReturn(
+      createConfigMock(desiredHdfsEnvProperties)
+    );
+
+    // desired config of zk-env.xml
+    Map<String, String> desiredZkEnvProperties = new HashMap<String, String>() {{
+
+    }};
+    expect(clusterMock.getDesiredConfigByType("hdfs-env.xml")).andReturn(
+      createConfigMock(desiredZkEnvProperties)
+    );
+
+    // desired config of hadoop-env.xml
+    Map<String, String> desiredHadoopEnvProperties = new HashMap<String, String>() {{
+
+    }};
+    expect(clusterMock.getDesiredConfigByType("hadoop-env.xml")).andReturn(
+      createConfigMock(desiredHadoopEnvProperties)
+    );
+
+    replay(clusterMock, clustersMock, ambariMetaInfoMock);
+
+    ConfigMergeHelper configMergeHelper = injector.getInstance(ConfigMergeHelper.class);
+
+    Map<String, Map<String, ThreeWayValue>> conflicts = configMergeHelper.getConflicts(
+      "clustername", newStackId);
+
+    assertNotNull(conflicts);
+    assertEquals(2, conflicts.size());
+    for (String key : conflicts.keySet()) {
+      if (key.equals("hdfs-env")) {
+        Map<String, ThreeWayValue> stringThreeWayValueMap = conflicts.get(key);
+        assertEquals(1, stringThreeWayValueMap.size());
+        assertEquals("equal-value", stringThreeWayValueMap.get("equal.key").oldStackValue);
+        assertEquals("equal-value", stringThreeWayValueMap.get("equal.key").newStackValue);
+        assertEquals("", stringThreeWayValueMap.get("equal.key").savedValue);
+      } else if (key.equals("hadoop-env")) {
+        Map<String, ThreeWayValue> stringThreeWayValueMap = conflicts.get(key);
+        assertEquals(1, stringThreeWayValueMap.size());
+        assertEquals("modified.value", stringThreeWayValueMap.get("equal.key").oldStackValue);
+        assertEquals("another.value", stringThreeWayValueMap.get("equal.key").newStackValue);
+        assertEquals("", stringThreeWayValueMap.get("equal.key").savedValue);
+      } else {
+        fail("Unexpected key");
+      }
+    }
+    assertEquals(2, conflicts.size());
+  }
+
+  private PropertyInfo createPropertyInfo(String fileName, String name, String value) {
+    PropertyInfo result = new PropertyInfo();
+    result.setFilename(fileName);
+    result.setName(name);
+    result.setValue(value);
+    return result;
+  }
+
+  /**
+   * Generates config that returns properties
+   * @param properties properties that should be returned by config mock
+   * @return mock
+   */
+  private Config createConfigMock(Map<String, String> properties) {
+    Config result = createNiceMock(Config.class);
+    expect(result.getProperties()).andReturn(properties);
+    return result;
+  }
+
+  @Test
+  public void testNormalizeValue() throws Exception{
+    // If template not defined
+    String normalizedValue = ConfigMergeHelper.normalizeValue(null, "2048m");
+    assertEquals("2048m", normalizedValue);
+
+    // Template does not define heap
+    normalizedValue = ConfigMergeHelper.normalizeValue("3k", "2048");
+    assertEquals("2048", normalizedValue);
+
+    // Template - megabytes
+    normalizedValue = ConfigMergeHelper.normalizeValue("1024m", "2048");
+    assertEquals("2048m", normalizedValue);
+
+    normalizedValue = ConfigMergeHelper.normalizeValue("1024M", "2048");
+    assertEquals("2048M", normalizedValue);
+
+    // Template - gigabytes
+    normalizedValue = ConfigMergeHelper.normalizeValue("4g", "2");
+    assertEquals("2g", normalizedValue);
+
+    normalizedValue = ConfigMergeHelper.normalizeValue("4G", "2");
+    assertEquals("2G", normalizedValue);
+  }
+
+
+
+  private class MockModule implements Module {
+
+    @Override
+    public void configure(Binder binder) {
+      binder.bind(Clusters.class).toInstance(clustersMock);
+      binder.bind(AmbariMetaInfo.class).toInstance(ambariMetaInfoMock);
+    }
+  }
+}