Repository: ambari
Updated Branches:
  refs/heads/trunk ac1ba0e4f -> 524368b4a


AMBARI-10981. Upgrade Framework Must Be Able To Have Configuration Properties 
Renamed (ncole)


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

Branch: refs/heads/trunk
Commit: 524368b4a1ba8297a1462a8f98c65653c0a527ad
Parents: ac1ba0e
Author: Nate Cole <nc...@hortonworks.com>
Authored: Wed May 6 17:27:15 2015 -0400
Committer: Nate Cole <nc...@hortonworks.com>
Committed: Thu May 7 07:05:23 2015 -0400

----------------------------------------------------------------------
 .../internal/UpgradeResourceProvider.java       |  20 ++-
 .../serveraction/upgrades/ConfigureAction.java  | 166 ++++++++++++++++---
 .../state/stack/upgrade/ConfigureTask.java      |  77 +++++++++
 .../state/stack/upgrade/TransferOperation.java  |  40 +++++
 .../stacks/HDP/2.2/upgrades/upgrade-2.3.xml     |   5 +
 .../upgrades/ConfigureActionTest.java           | 112 ++++++++++++-
 .../server/state/stack/UpgradePackTest.java     |  45 +++++
 .../stacks/HDP/2.1.1/upgrades/upgrade_test.xml  |   9 +
 8 files changed, 446 insertions(+), 28 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/524368b4/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java
index 99535c2..bdb2784 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java
@@ -98,6 +98,7 @@ import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.gson.Gson;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 
@@ -190,6 +191,8 @@ public class UpgradeResourceProvider extends 
AbstractControllerResourceProvider
   @Inject
   private static HostDAO s_hostDAO = null;
 
+  private static Gson s_gson = new Gson();
+
 
   /**
    * Used to generated the correct tasks and stages during an upgrade.
@@ -1015,14 +1018,16 @@ public class UpgradeResourceProvider extends 
AbstractControllerResourceProvider
       case CONFIGURE: {
         ConfigureTask ct = (ConfigureTask) task;
         Map<String, String> configProperties = 
ct.getConfigurationProperties(cluster);
+        List<ConfigureTask.Transfer> transfers = ct.getTransfers();
 
         // if the properties are empty it means that the conditions in the
         // task did not pass;
-        if (configProperties.isEmpty()) {
+        if (configProperties.isEmpty() && transfers.isEmpty()) {
           stageText = "No conditions were met for this configuration task.";
           itemDetail = stageText;
         } else {
           commandParams.putAll(configProperties);
+          commandParams.put(ConfigureTask.PARAMETER_TRANSFERS, 
s_gson.toJson(transfers));
 
           // extract the config type, key and value to use to build the
           // summary and detail
@@ -1030,8 +1035,17 @@ public class UpgradeResourceProvider extends 
AbstractControllerResourceProvider
           String key = configProperties.get(ConfigureTask.PARAMETER_KEY);
           String value = configProperties.get(ConfigureTask.PARAMETER_VALUE);
 
-          itemDetail = String.format("Updating config %s/%s to %s", configType,
-              key, value);
+          StringBuilder detail = new StringBuilder(String.format("Updating 
config %s", configType));
+
+          if (null != key && null != value) {
+            detail.append(String.format("/%s to %s", key, value));
+          }
+
+          if (!transfers.isEmpty()) {
+            detail.append(String.format("; transferring %d properties", 
transfers.size()));
+          }
+
+          itemDetail = detail.toString();
 
           if (null != ct.summary) {
             stageText = ct.summary;

http://git-wip-us.apache.org/repos/asf/ambari/blob/524368b4/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/ConfigureAction.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/ConfigureAction.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/ConfigureAction.java
index df9d7be..a812169 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/ConfigureAction.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/ConfigureAction.java
@@ -18,7 +18,10 @@
 package org.apache.ambari.server.serveraction.upgrades;
 
 import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentMap;
 
@@ -34,10 +37,14 @@ import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.Config;
 import org.apache.ambari.server.state.ConfigHelper;
+import org.apache.ambari.server.state.ConfigMergeHelper;
+import org.apache.ambari.server.state.ConfigMergeHelper.ThreeWayValue;
 import org.apache.ambari.server.state.DesiredConfig;
 import org.apache.ambari.server.state.StackId;
 import org.apache.ambari.server.state.stack.upgrade.ConfigureTask;
 
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
 import com.google.inject.Inject;
 
 /**
@@ -70,7 +77,12 @@ public class ConfigureAction extends AbstractServerAction {
    * The Ambari configuration.
    */
   @Inject
-  private Configuration configuration;
+  private Configuration m_configuration;
+
+  @Inject
+  private ConfigMergeHelper m_mergeHelper;
+
+  private Gson m_gson = new Gson();
 
   /**
    * Aside from the normal execution, this method performs the following 
logic, with
@@ -126,24 +138,31 @@ public class ConfigureAction extends AbstractServerAction 
{
     }
 
     String clusterName = commandParameters.get("clusterName");
+    // such as hdfs-site or hbase-env
+    String configType = 
commandParameters.get(ConfigureTask.PARAMETER_CONFIG_TYPE);
+
     String key = commandParameters.get(ConfigureTask.PARAMETER_KEY);
     String value = commandParameters.get(ConfigureTask.PARAMETER_VALUE);
 
-    // such as hdfs-site or hbase-env
-    String configType = 
commandParameters.get(ConfigureTask.PARAMETER_CONFIG_TYPE);
+    List<ConfigureTask.Transfer> transfers = Collections.emptyList();
+    String transferJson = 
commandParameters.get(ConfigureTask.PARAMETER_TRANSFERS);
+    if (null != transferJson) {
+      transfers = m_gson.fromJson(
+        transferJson, new 
TypeToken<List<ConfigureTask.Transfer>>(){}.getType());
+    }
 
-    // if the two required properties are null, then assume that no
-    // conditions were met and let the action complete
-    if (null == configType && null == key) {
+    // if the two required properties are null and no transfer properties, then
+    // assume that no conditions were met and let the action complete
+    if (null == configType && null == key && transfers.isEmpty()) {
       return createCommandReport(0, HostRoleStatus.COMPLETED, "{}", "",
           "Skipping configuration task");
     }
 
-    // if only 1 of the required properties was null, then something went
-    // wrong
-    if (null == clusterName || null == configType || null == key) {
-      String message = "cluster={0}, type={1}, key={2}";
-      message = MessageFormat.format(message, clusterName, configType, key);
+    // if only 1 of the required properties was null and no transfer 
properties,
+    // then something went wrong
+    if (null == clusterName || null == configType || (null == key && 
transfers.isEmpty())) {
+      String message = "cluster={0}, type={1}, key={2}, transfers={3}";
+      message = MessageFormat.format(message, clusterName, configType, key, 
transfers);
       return createCommandReport(0, HostRoleStatus.FAILED, "{}", "", message);
     }
 
@@ -157,25 +176,88 @@ public class ConfigureAction extends AbstractServerAction 
{
     StackId targetStack = cluster.getDesiredStackVersion();
     StackId configStack = config.getStackId();
 
-    String oldValue = config.getProperties().get(key);
-    // !!! values are not changing, so make this a no-op
-    if (null != oldValue && value.equals(oldValue)) {
-      if (currentStack.equals(targetStack)) {
-        return createCommandReport(0, HostRoleStatus.COMPLETED, "{}",
-            MessageFormat.format("{0}/{1} for cluster {2} would not change, 
skipping setting",
-                configType, key, clusterName),
-            "");
+    // !!! initial reference values
+    Map<String, String> base = config.getProperties();
+    Map<String, String> newValues = new HashMap<String, String>(base);
+
+    boolean changedValues = false;
+
+    // !!! do transfers first before setting defined values
+    for (ConfigureTask.Transfer transfer : transfers) {
+      switch (transfer.operation) {
+        case COPY:
+          if (null == transfer.fromType && base.containsKey(transfer.fromKey)) 
{
+            newValues.put(transfer.toKey, base.get(transfer.fromKey));
+            changedValues = true;
+          } else {
+            // !!! copying from another configuration
+            Config other = cluster.getDesiredConfigByType(transfer.fromType);
+
+            if (null != other) {
+              Map<String, String> otherValues = other.getProperties();
+
+              if (otherValues.containsKey(transfer.fromKey)) {
+                newValues.put(transfer.toKey, 
otherValues.get(transfer.fromKey));
+                changedValues = true;
+              }
+            }
+          }
+          break;
+        case MOVE:
+          if (newValues.containsKey(transfer.fromKey)) {
+            newValues.put(transfer.toKey, newValues.remove(transfer.fromKey));
+            changedValues = true;
+          }
+
+          break;
+        case DELETE:
+          if ("*".equals(transfer.deleteKey)) {
+            newValues.clear();
+
+            for (String keeper : transfer.keepKeys) {
+              newValues.put(keeper, base.get(keeper));
+            }
+
+            // !!! with preserved edits, find the values that are different
+            // from the stack-defined and keep them
+            if (transfer.preserveEdits) {
+              List<String> edited = findChangedValues(clusterName, config);
+              for (String changed : edited) {
+                newValues.put(changed, base.get(changed));
+              }
+            }
+            changedValues = true;
+          } else {
+            newValues.remove(transfer.deleteKey);
+            changedValues = true;
+          }
+
+          break;
       }
     }
 
-    Map<String, String> propertiesToChange = new HashMap<String, String>();
-    propertiesToChange.put(key, value);
-    config.updateProperties(propertiesToChange);
+
+    if (null != key) {
+      String oldValue = base.get(key);
+
+      // !!! values are not changing, so make this a no-op
+      if (null != oldValue && value.equals(oldValue)) {
+        if (currentStack.equals(targetStack) && !changedValues) {
+          return createCommandReport(0, HostRoleStatus.COMPLETED, "{}",
+              MessageFormat.format("{0}/{1} for cluster {2} would not change, 
skipping setting",
+                  configType, key, clusterName),
+              "");
+        }
+      }
+    }
+
+    newValues.put(key, value);
 
     // !!! check to see if we're going to a new stack and double check the
     // configs are for the target.  Then simply update the new properties 
instead
     // of creating a whole new history record since it was already done
     if (!targetStack.equals(currentStack) && targetStack.equals(configStack)) {
+      config.setProperties(newValues);
       config.persist(false);
 
       return createCommandReport(0, HostRoleStatus.COMPLETED, "{}",
@@ -191,15 +273,51 @@ public class ConfigureAction extends AbstractServerAction 
{
     String auditName = 
getExecutionCommand().getRoleParams().get(ServerAction.ACTION_USER_NAME);
 
     if (auditName == null) {
-      auditName = configuration.getAnonymousAuditName();
+      auditName = m_configuration.getAnonymousAuditName();
     }
 
     m_configHelper.createConfigType(cluster, m_controller, configType,
-        config.getProperties(), auditName, serviceVersionNote);
+        newValues, auditName, serviceVersionNote);
 
     String message = "Updated ''{0}'' with ''{1}={2}''";
     message = MessageFormat.format(message, configType, key, value);
 
     return createCommandReport(0, HostRoleStatus.COMPLETED, "{}", message, "");
   }
+
+
+  /**
+   * @param clusterName the cluster name
+   * @param config      the config with the tag to find conflicts
+   * @return            the list of changed property keys
+   * @throws AmbariException
+   */
+  private List<String> findChangedValues(String clusterName, Config config)
+      throws AmbariException {
+
+    Map<String, Map<String, ThreeWayValue>> conflicts =
+        m_mergeHelper.getConflicts(clusterName, config.getStackId());
+
+    Map<String, ThreeWayValue> conflictMap = conflicts.get(config.getType());
+
+    if (null == conflictMap || conflictMap.isEmpty()) {
+      return Collections.emptyList();
+    }
+
+    List<String> result = new ArrayList<String>();
+
+    for (Map.Entry<String, ThreeWayValue> entry : conflictMap.entrySet()) {
+      ThreeWayValue twv = entry.getValue();
+      if (null == twv.oldStackValue) {
+        result.add(entry.getKey());
+      } else if (null != twv.savedValue && 
!twv.oldStackValue.equals(twv.savedValue)) {
+        result.add(entry.getKey());
+      }
+    }
+
+    return result;
+  }
+
+
+
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/524368b4/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ConfigureTask.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ConfigureTask.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ConfigureTask.java
index d3cb366..1f921c9 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ConfigureTask.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ConfigureTask.java
@@ -17,6 +17,8 @@
  */
 package org.apache.ambari.server.state.stack.upgrade;
 
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -94,6 +96,12 @@ public class ConfigureTask extends ServerSideActionTask {
   public static final String PARAMETER_VALUE = "configure-task-value";
 
   /**
+   * Transfers can be several per task, so they're passed in as a json-ified 
list of
+   * objects.
+   */
+  public static final String PARAMETER_TRANSFERS = "configure-task-transfers";
+
+  /**
    * Constructor.
    *
    */
@@ -119,12 +127,23 @@ public class ConfigureTask extends ServerSideActionTask {
   @XmlElement(name = "condition")
   private List<Condition> conditions;
 
+  @XmlElement(name = "transfer")
+  private List<Transfer> transfers;
+
   @Override
   public Type getType() {
     return type;
   }
 
   /**
+   * @return the config type
+   */
+  public String getConfigType() {
+    return configType;
+  }
+
+
+  /**
    * A conditional element that will only perform the configuration if the
    * condition is met.
    */
@@ -151,6 +170,64 @@ public class ConfigureTask extends ServerSideActionTask {
   }
 
   /**
+   * A {@code transfer} element will copy, move, or delete the value of one 
type/key to another type/key.
+   */
+  @XmlAccessorType(XmlAccessType.FIELD)
+  @XmlType(name = "transfer")
+  public static class Transfer {
+    @XmlAttribute(name = "operation")
+    public TransferOperation operation;
+
+    @XmlAttribute(name = "from-type")
+    public String fromType;
+
+    @XmlAttribute(name = "from-key")
+    public String fromKey;
+
+    @XmlAttribute(name = "to-key")
+    public String toKey;
+
+    @XmlAttribute(name = "delete-key")
+    public String deleteKey;
+
+    @XmlAttribute(name = "preserve-edits")
+    public boolean preserveEdits = false;
+
+    @XmlElement(name = "keep-key")
+    public List<String> keepKeys = new ArrayList<String>();
+
+  }
+
+  /**
+   * @return the list of transfers, checking for appropriate null fields.
+   */
+  public List<Transfer> getTransfers() {
+    if (null == transfers) {
+      return Collections.<Transfer>emptyList();
+    }
+
+    List<Transfer> list = new ArrayList<Transfer>();
+    for (Transfer t : transfers) {
+      switch (t.operation) {
+        case COPY:
+        case MOVE:
+          if (null != t.fromKey && null != t.toKey) {
+            list.add(t);
+          }
+          break;
+        case DELETE:
+          if (null != t.deleteKey) {
+            list.add(t);
+          }
+
+          break;
+      }
+    }
+
+    return list;
+  }
+
+  /**
    * Gets a map containing the following properties pertaining to the
    * configuration value to change:
    * <ul>

http://git-wip-us.apache.org/repos/asf/ambari/blob/524368b4/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/TransferOperation.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/TransferOperation.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/TransferOperation.java
new file mode 100644
index 0000000..1ee2a35
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/TransferOperation.java
@@ -0,0 +1,40 @@
+/**
+ * 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.stack.upgrade;
+
+/**
+ * Operations valid for a property transfer.
+ */
+public enum TransferOperation {
+  /**
+   * The property should be removed.  Special case "*" to delete all
+   * properties that have NOT been overridden by a user.
+   */
+  DELETE,
+  /**
+   * The property value is moved.  A property may only be moved within the
+   * same config type.  To move across types, perform a {@link #COPY} to the 
target then
+   * a {@link #DELETE} from the source config.
+   */
+  MOVE,
+  /**
+   * The property value is copied from another property.  A property may be 
copied from
+   * another config type.
+   */
+  COPY
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/524368b4/ambari-server/src/main/resources/stacks/HDP/2.2/upgrades/upgrade-2.3.xml
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/resources/stacks/HDP/2.2/upgrades/upgrade-2.3.xml 
b/ambari-server/src/main/resources/stacks/HDP/2.2/upgrades/upgrade-2.3.xml
index 8dccf65..225cc63 100644
--- a/ambari-server/src/main/resources/stacks/HDP/2.2/upgrades/upgrade-2.3.xml
+++ b/ambari-server/src/main/resources/stacks/HDP/2.2/upgrades/upgrade-2.3.xml
@@ -573,6 +573,11 @@
           <task xsi:type="manual">
             <message>Before continuing, please deactivate and kill any 
currently running topologies.</message>
           </task>
+          <task xsi:type="configure" summary="Setting nimbus.seeds from 
nimbus.host">
+            <type>storm-site</type>
+            <transfer operation="COPY" from-key="nimbus.host"
+              to-type="storm-site" to-key="nimbus.seeds" />
+          </task>
         </pre-upgrade>
         <upgrade>
           <task xsi:type="restart" />

http://git-wip-us.apache.org/repos/asf/ambari/blob/524368b4/ambari-server/src/test/java/org/apache/ambari/server/serveraction/upgrades/ConfigureActionTest.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/serveraction/upgrades/ConfigureActionTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/serveraction/upgrades/ConfigureActionTest.java
index 391db55..ecd55db 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/serveraction/upgrades/ConfigureActionTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/serveraction/upgrades/ConfigureActionTest.java
@@ -18,10 +18,14 @@
 package org.apache.ambari.server.serveraction.upgrades;
 
 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 java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 import org.apache.ambari.server.actionmanager.ExecutionCommandWrapper;
@@ -38,6 +42,7 @@ import org.apache.ambari.server.orm.dao.RepositoryVersionDAO;
 import org.apache.ambari.server.orm.dao.StackDAO;
 import org.apache.ambari.server.orm.entities.HostVersionEntity;
 import org.apache.ambari.server.orm.entities.StackEntity;
+import org.apache.ambari.server.serveraction.ServerAction;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.Config;
@@ -46,10 +51,12 @@ import org.apache.ambari.server.state.Host;
 import org.apache.ambari.server.state.RepositoryVersionState;
 import org.apache.ambari.server.state.StackId;
 import org.apache.ambari.server.state.stack.upgrade.ConfigureTask;
+import org.apache.ambari.server.state.stack.upgrade.TransferOperation;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
+import com.google.gson.Gson;
 import com.google.inject.Guice;
 import com.google.inject.Inject;
 import com.google.inject.Injector;
@@ -207,8 +214,111 @@ public class ConfigureActionTest {
     assertNotNull(config);
     assertEquals("version2", config.getTag());
     assertEquals("11", config.getProperties().get("initLimit"));
-
   }
 
+  @Test
+  public void testConfigTransferCopy() throws Exception {
+    makeUpgradeCluster();
+
+    Cluster c = m_injector.getInstance(Clusters.class).getCluster("c1");
+    assertEquals(1, c.getConfigsByType("zoo.cfg").size());
+
+    c.setDesiredStackVersion(HDP_21_STACK);
+    ConfigFactory cf = m_injector.getInstance(ConfigFactory.class);
+    Config config = cf.createNew(c, "zoo.cfg", new HashMap<String, String>() {{
+          put("initLimit", "10");
+          put("copyIt", "10");
+          put("moveIt", "10");
+          put("deleteIt", "10");
+        }}, new HashMap<String, Map<String,String>>());
+    config.setTag("version2");
+    config.persist();
+
+    c.addConfig(config);
+    c.addDesiredConfig("user", Collections.singleton(config));
+    assertEquals(2, c.getConfigsByType("zoo.cfg").size());
+
+
+    Map<String, String> commandParams = new HashMap<String, String>();
+    commandParams.put("upgrade_direction", "upgrade");
+    commandParams.put("version", HDP_2_2_1_0);
+    commandParams.put("clusterName", "c1");
+    commandParams.put(ConfigureTask.PARAMETER_CONFIG_TYPE, "zoo.cfg");
+    commandParams.put(ConfigureTask.PARAMETER_KEY, "initLimit");
+    commandParams.put(ConfigureTask.PARAMETER_VALUE, "11");
+
+    List<ConfigureTask.Transfer> transfers = new 
ArrayList<ConfigureTask.Transfer>();
+    ConfigureTask.Transfer transfer = new ConfigureTask.Transfer();
+    transfer.operation = TransferOperation.COPY;
+    transfer.fromKey = "copyIt";
+    transfer.toKey = "copyKey";
+    transfers.add(transfer);
+
+    transfer = new ConfigureTask.Transfer();
+    transfer.operation = TransferOperation.MOVE;
+    transfer.fromKey = "moveIt";
+    transfer.toKey = "movedKey";
+    transfers.add(transfer);
+
+    transfer = new ConfigureTask.Transfer();
+    transfer.operation = TransferOperation.DELETE;
+    transfer.deleteKey = "deleteIt";
+    transfers.add(transfer);
+
+    commandParams.put(ConfigureTask.PARAMETER_TRANSFERS, new 
Gson().toJson(transfers));
+
+    ExecutionCommand executionCommand = new ExecutionCommand();
+    executionCommand.setCommandParams(commandParams);
+    executionCommand.setClusterName("c1");
+    executionCommand.setRoleParams(new HashMap<String, String>());
+    executionCommand.getRoleParams().put(ServerAction.ACTION_USER_NAME, 
"username");
+
+    HostRoleCommand hostRoleCommand = hostRoleCommandFactory.create(null, null,
+        null, null);
+
+    hostRoleCommand.setExecutionCommandWrapper(new ExecutionCommandWrapper(
+        executionCommand));
+
+    ConfigureAction action = m_injector.getInstance(ConfigureAction.class);
+    action.setExecutionCommand(executionCommand);
+    action.setHostRoleCommand(hostRoleCommand);
+
+    CommandReport report = action.execute(null);
+    assertNotNull(report);
+
+    assertEquals(3, c.getConfigsByType("zoo.cfg").size());
+
+    config = c.getDesiredConfigByType("zoo.cfg");
+    assertNotNull(config);
+    assertFalse("version2".equals(config.getTag()));
+
+    Map<String, String> map = config.getProperties();
+    assertEquals("11", map.get("initLimit"));
+    assertEquals("10", map.get("copyIt"));
+    assertTrue(map.containsKey("copyKey"));
+    assertEquals(map.get("copyIt"), map.get("copyKey"));
+    assertFalse(map.containsKey("moveIt"));
+    assertTrue(map.containsKey("movedKey"));
+    assertFalse(map.containsKey("deletedKey"));
+
+    transfers.clear();
+    transfer = new ConfigureTask.Transfer();
+    transfer.operation = TransferOperation.DELETE;
+    transfer.deleteKey = "*";
+    transfer.preserveEdits = true;
+    transfer.keepKeys.add("copyKey");
+    transfers.add(transfer);
+    commandParams.put(ConfigureTask.PARAMETER_TRANSFERS, new 
Gson().toJson(transfers));
+
+    report = action.execute(null);
+    assertNotNull(report);
+
+    assertEquals(4, c.getConfigsByType("zoo.cfg").size());
+    config = c.getDesiredConfigByType("zoo.cfg");
+    map = config.getProperties();
+    assertEquals(2, map.size());
+    assertTrue(map.containsKey("initLimit")); // it just changed to 11 from 10
+    assertTrue(map.containsKey("copyKey")); // is new
+  }
 
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/524368b4/ambari-server/src/test/java/org/apache/ambari/server/state/stack/UpgradePackTest.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/state/stack/UpgradePackTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/state/stack/UpgradePackTest.java
index 1701a33..cdde452 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/state/stack/UpgradePackTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/state/stack/UpgradePackTest.java
@@ -34,10 +34,13 @@ import 
org.apache.ambari.server.orm.InMemoryDefaultTestModule;
 import org.apache.ambari.server.state.stack.UpgradePack.ProcessingComponent;
 import org.apache.ambari.server.state.stack.upgrade.ClusterGrouping;
 import 
org.apache.ambari.server.state.stack.upgrade.ClusterGrouping.ExecuteStage;
+import org.apache.ambari.server.state.stack.upgrade.ConfigureTask;
+import org.apache.ambari.server.state.stack.upgrade.ConfigureTask.Transfer;
 import org.apache.ambari.server.state.stack.upgrade.Direction;
 import org.apache.ambari.server.state.stack.upgrade.Grouping;
 import org.apache.ambari.server.state.stack.upgrade.RestartTask;
 import org.apache.ambari.server.state.stack.upgrade.Task;
+import org.apache.ambari.server.state.stack.upgrade.TransferOperation;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -140,6 +143,48 @@ public class UpgradePackTest {
     assertEquals(1, pc.postTasks.size());
     assertNotNull(pc.tasks);
     assertEquals(1, pc.tasks.size());
+
+    pc = up.getTasks().get("YARN").get("NODEMANAGER");
+    assertNotNull(pc.preTasks);
+    assertEquals(2, pc.preTasks.size());
+    Task t = pc.preTasks.get(1);
+    assertEquals(ConfigureTask.class, t.getClass());
+    ConfigureTask ct = (ConfigureTask) t;
+    assertEquals("core-site", ct.getConfigType());
+    assertEquals(4, ct.getTransfers().size());
+
+    /*
+            <transfer operation="COPY" from-key="copy-key" 
to-key="copy-key-to" />
+            <transfer operation="COPY" from-type="my-site" 
from-key="my-copy-key" to-key="my-copy-key-to" />
+            <transfer operation="MOVE" from-key="move-key" 
to-key="move-key-to" />
+            <transfer operation="DELETE" delete-key="delete-key">
+              <keep-key>important-key</keep-key>
+            </transfer>
+    */
+    Transfer t1 = ct.getTransfers().get(0);
+    assertEquals(TransferOperation.COPY, t1.operation);
+    assertEquals("copy-key", t1.fromKey);
+    assertEquals("copy-key-to", t1.toKey);
+
+    Transfer t2 = ct.getTransfers().get(1);
+    assertEquals(TransferOperation.COPY, t2.operation);
+    assertEquals("my-site", t2.fromType);
+    assertEquals("my-copy-key", t2.fromKey);
+    assertEquals("my-copy-key-to", t2.toKey);
+    assertTrue(t2.keepKeys.isEmpty());
+
+    Transfer t3 = ct.getTransfers().get(2);
+    assertEquals(TransferOperation.MOVE, t3.operation);
+    assertEquals("move-key", t3.fromKey);
+    assertEquals("move-key-to", t3.toKey);
+
+    Transfer t4 = ct.getTransfers().get(3);
+    assertEquals(TransferOperation.DELETE, t4.operation);
+    assertEquals("delete-key", t4.deleteKey);
+    assertNull(t4.toKey);
+    assertTrue(t4.preserveEdits);
+    assertEquals(1, t4.keepKeys.size());
+    assertEquals("important-key", t4.keepKeys.get(0));
   }
 
   @Test

http://git-wip-us.apache.org/repos/asf/ambari/blob/524368b4/ambari-server/src/test/resources/stacks/HDP/2.1.1/upgrades/upgrade_test.xml
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/test/resources/stacks/HDP/2.1.1/upgrades/upgrade_test.xml 
b/ambari-server/src/test/resources/stacks/HDP/2.1.1/upgrades/upgrade_test.xml
index 088d15f..32c419b 100644
--- 
a/ambari-server/src/test/resources/stacks/HDP/2.1.1/upgrades/upgrade_test.xml
+++ 
b/ambari-server/src/test/resources/stacks/HDP/2.1.1/upgrades/upgrade_test.xml
@@ -183,6 +183,15 @@
           <task xsi:type="execute">
             <command>ls</command>
           </task>
+          <task xsi:type="configure">
+            <type>core-site</type>
+            <transfer operation="COPY" from-key="copy-key" 
to-key="copy-key-to" />
+            <transfer operation="COPY" from-type="my-site" 
from-key="my-copy-key" to-key="my-copy-key-to" />
+            <transfer operation="MOVE" from-key="move-key" 
to-key="move-key-to" />
+            <transfer operation="DELETE" delete-key="delete-key" 
preserve-edits="true">
+              <keep-key>important-key</keep-key>
+            </transfer>
+          </task>
         </pre-upgrade>
       </component>
     </service>

Reply via email to