You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by nc...@apache.org on 2017/01/18 15:58:10 UTC

[37/50] [abbrv] ambari git commit: AMBARI-19574 - Add upgrade logic for the heap dump control option added in HDP 2.6 stack (jonathanhurley)

AMBARI-19574 - Add upgrade logic for the heap dump control option added in HDP 2.6 stack (jonathanhurley)


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

Branch: refs/heads/branch-dev-patch-upgrade
Commit: 410f2943d5babf6b9bd3e2f225fbbe9ac5761ee2
Parents: 4dac278
Author: Jonathan Hurley <jh...@hortonworks.com>
Authored: Mon Jan 16 20:26:24 2017 -0500
Committer: Jonathan Hurley <jh...@hortonworks.com>
Committed: Tue Jan 17 18:51:50 2017 -0500

----------------------------------------------------------------------
 .../serveraction/upgrades/ConfigureAction.java  | 142 +++++++++++---
 .../upgrade/ConfigUpgradeChangeDefinition.java  | 194 +++++++++++++------
 .../state/stack/upgrade/ConfigureTask.java      |  45 ++---
 .../stacks/HDP/2.5/upgrades/config-upgrade.xml  |  27 +++
 .../HDP/2.5/upgrades/nonrolling-upgrade-2.6.xml |  19 +-
 .../stacks/HDP/2.5/upgrades/upgrade-2.6.xml     |  14 ++
 .../src/main/resources/upgrade-config.xsd       |  16 ++
 .../upgrades/ConfigureActionTest.java           | 147 +++++++++++---
 8 files changed, 455 insertions(+), 149 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/410f2943/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 97280ee..a42e667 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
@@ -46,6 +46,7 @@ import org.apache.ambari.server.state.DesiredConfig;
 import org.apache.ambari.server.state.PropertyInfo;
 import org.apache.ambari.server.state.StackId;
 import org.apache.ambari.server.state.stack.upgrade.ConfigUpgradeChangeDefinition.ConfigurationKeyValue;
+import org.apache.ambari.server.state.stack.upgrade.ConfigUpgradeChangeDefinition.Insert;
 import org.apache.ambari.server.state.stack.upgrade.ConfigUpgradeChangeDefinition.Masked;
 import org.apache.ambari.server.state.stack.upgrade.ConfigUpgradeChangeDefinition.Replace;
 import org.apache.ambari.server.state.stack.upgrade.ConfigUpgradeChangeDefinition.Transfer;
@@ -185,7 +186,7 @@ public class ConfigureAction extends AbstractServerAction {
     // such as hdfs-site or hbase-env
     String configType = commandParameters.get(ConfigureTask.PARAMETER_CONFIG_TYPE);
 
-    // extract transfers
+    // extract setters
     List<ConfigurationKeyValue> keyValuePairs = Collections.emptyList();
     String keyValuePairJson = commandParameters.get(ConfigureTask.PARAMETER_KEY_VALUE_PAIRS);
     if (null != keyValuePairJson) {
@@ -212,14 +213,22 @@ public class ConfigureAction extends AbstractServerAction {
       replacements = getAllowedReplacements(cluster, configType, replacements);
     }
 
+    // extract insertions
+    List<Insert> insertions = Collections.emptyList();
+    String insertJson = commandParameters.get(ConfigureTask.PARAMETER_INSERTIONS);
+    if (null != insertJson) {
+      insertions = m_gson.fromJson(
+          insertJson, new TypeToken<List<Insert>>(){}.getType());
+    }
+
     // if there is nothing to do, then skip the task
-    if (keyValuePairs.isEmpty() && transfers.isEmpty() && replacements.isEmpty()) {
-      String message = "cluster={0}, type={1}, transfers={2}, replacements={3}, configurations={4}";
+    if (keyValuePairs.isEmpty() && transfers.isEmpty() && replacements.isEmpty() && insertions.isEmpty()) {
+      String message = "cluster={0}, type={1}, transfers={2}, replacements={3}, insertions={4}, configurations={5}";
       message = MessageFormat.format(message, clusterName, configType, transfers, replacements,
-          keyValuePairs);
+          insertions, keyValuePairs);
 
       StringBuilder buffer = new StringBuilder(
-          "Skipping this configuration task since none of the conditions were met and there are no transfers or replacements").append("\n");
+          "Skipping this configuration task since none of the conditions were met and there are no transfers, replacements, or insertions.").append("\n");
 
       buffer.append(message);
 
@@ -229,9 +238,12 @@ public class ConfigureAction extends AbstractServerAction {
     // if only 1 of the required properties was null and no transfer properties,
     // then something went wrong
     if (null == clusterName || null == configType
-        || (keyValuePairs.isEmpty() && transfers.isEmpty() && replacements.isEmpty())) {
-      String message = "cluster={0}, type={1}, transfers={2}, replacements={3}, configurations={4}";
-      message = MessageFormat.format(message, clusterName, configType, transfers, replacements, keyValuePairs);
+        || (keyValuePairs.isEmpty() && transfers.isEmpty() && replacements.isEmpty() && insertions.isEmpty())) {
+      String message = "cluster={0}, type={1}, transfers={2}, replacements={3}, insertions={4}, configurations={5}";
+
+      message = MessageFormat.format(message, clusterName, configType, transfers, replacements,
+          insertions, keyValuePairs);
+
       return createCommandReport(0, HostRoleStatus.FAILED, "{}", "", message);
     }
 
@@ -251,7 +263,7 @@ public class ConfigureAction extends AbstractServerAction {
 
     // !!! initial reference values
     Map<String, String> base = config.getProperties();
-    Map<String, String> newValues = new HashMap<String, String>(base);
+    Map<String, String> newValues = new HashMap<>(base);
 
     boolean changedValues = false;
 
@@ -287,7 +299,7 @@ public class ConfigureAction extends AbstractServerAction {
                 case YAML_ARRAY: {
                   // turn c6401,c6402 into ['c6401',c6402']
                   String[] splitValues = StringUtils.split(valueToCopy, ',');
-                  List<String> quotedValues = new ArrayList<String>(splitValues.length);
+                  List<String> quotedValues = new ArrayList<>(splitValues.length);
                   for (String splitValue : splitValues) {
                     quotedValues.add("'" + StringUtils.trim(splitValue) + "'");
                   }
@@ -306,7 +318,8 @@ public class ConfigureAction extends AbstractServerAction {
             newValues.put(transfer.toKey, valueToCopy);
 
             // append standard output
-            outputBuffer.append(MessageFormat.format("Created {0}/{1} = \"{2}\"\n", configType,
+            updateBufferWithMessage(outputBuffer, MessageFormat.format("Created {0}/{1} = \"{2}\"",
+                configType,
                 transfer.toKey, mask(transfer, valueToCopy)));
           }
           break;
@@ -319,15 +332,17 @@ public class ConfigureAction extends AbstractServerAction {
             changedValues = true;
 
             // append standard output
-            outputBuffer.append(MessageFormat.format("Renamed {0}/{1} to {2}/{3}\n", configType,
+            updateBufferWithMessage(outputBuffer,
+                MessageFormat.format("Renamed {0}/{1} to {2}/{3}", configType,
                 transfer.fromKey, configType, transfer.toKey));
+
           } else if (StringUtils.isNotBlank(transfer.defaultValue)) {
             newValues.put(transfer.toKey, transfer.defaultValue);
             changedValues = true;
 
             // append standard output
-            outputBuffer.append(MessageFormat.format(
-                "Created {0}/{1} with default value \"{2}\"\n",
+            updateBufferWithMessage(outputBuffer,
+                MessageFormat.format("Created {0}/{1} with default value \"{2}\"",
                 configType, transfer.toKey, mask(transfer, transfer.defaultValue)));
           }
 
@@ -337,15 +352,16 @@ public class ConfigureAction extends AbstractServerAction {
             newValues.clear();
 
             // append standard output
-            outputBuffer.append(MessageFormat.format("Deleted all keys from {0}\n", configType));
+            updateBufferWithMessage(outputBuffer,
+                MessageFormat.format("Deleted all keys from {0}", configType));
 
             for (String keeper : transfer.keepKeys) {
               if (base.containsKey(keeper) && base.get(keeper) != null) {
                 newValues.put(keeper, base.get(keeper));
 
                 // append standard output
-                outputBuffer.append(MessageFormat.format("Preserved {0}/{1} after delete\n",
-                  configType, keeper));
+                updateBufferWithMessage(outputBuffer,
+                    MessageFormat.format("Preserved {0}/{1} after delete", configType, keeper));
               }
             }
 
@@ -358,7 +374,8 @@ public class ConfigureAction extends AbstractServerAction {
                 newValues.put(changed, base.get(changed));
 
                 // append standard output
-                outputBuffer.append(MessageFormat.format("Preserved {0}/{1} after delete\n",
+                updateBufferWithMessage(outputBuffer,
+                    MessageFormat.format("Preserved {0}/{1} after delete",
                     configType, changed));
               }
             }
@@ -369,7 +386,8 @@ public class ConfigureAction extends AbstractServerAction {
             changedValues = true;
 
             // append standard output
-            outputBuffer.append(MessageFormat.format("Deleted {0}/{1}\n", configType,
+            updateBufferWithMessage(outputBuffer,
+                MessageFormat.format("Deleted {0}/{1}", configType,
                 transfer.deleteKey));
           }
 
@@ -389,7 +407,8 @@ public class ConfigureAction extends AbstractServerAction {
           // !!! values are not changing, so make this a no-op
           if (null != oldValue && value.equals(oldValue)) {
             if (currentStack.equals(targetStack) && !changedValues) {
-              outputBuffer.append(MessageFormat.format(
+              updateBufferWithMessage(outputBuffer,
+                  MessageFormat.format(
                   "{0}/{1} for cluster {2} would not change, skipping setting", configType, key,
                   clusterName));
 
@@ -409,40 +428,91 @@ public class ConfigureAction extends AbstractServerAction {
           if (StringUtils.isEmpty(value)) {
             message = MessageFormat.format("{0}/{1} changed to an empty value", configType, key);
           } else {
-            message = MessageFormat.format("{0}/{1} changed to \"{2}\"\n", configType, key,
+            message = MessageFormat.format("{0}/{1} changed to \"{2}\"", configType, key,
                 mask(keyValuePair, value));
           }
 
-          outputBuffer.append(message);
+          updateBufferWithMessage(outputBuffer, message);
         }
       }
     }
 
-    // !!! string replacements happen only on the new values.
+    // replacements happen only on the new values (as they are initialized from
+    // the existing pre-upgrade values)
     for (Replace replacement : replacements) {
       // the key might exist but might be null, so we need to check this
       // condition when replacing a part of the value
       String toReplace = newValues.get(replacement.key);
       if (StringUtils.isNotBlank(toReplace)) {
         if (!toReplace.contains(replacement.find)) {
-          outputBuffer.append(MessageFormat.format("String \"{0}\" was not found in {1}/{2}\n",
+          updateBufferWithMessage(outputBuffer,
+              MessageFormat.format("String \"{0}\" was not found in {1}/{2}",
               replacement.find, configType, replacement.key));
         } else {
           String replaced = StringUtils.replace(toReplace, replacement.find, replacement.replaceWith);
 
           newValues.put(replacement.key, replaced);
 
-          outputBuffer.append(
+          updateBufferWithMessage(outputBuffer,
               MessageFormat.format("Replaced {0}/{1} containing \"{2}\" with \"{3}\"", configType,
                   replacement.key, replacement.find, replacement.replaceWith));
-
-          outputBuffer.append(System.lineSeparator());
         }
       } else {
-        outputBuffer.append(MessageFormat.format(
+        updateBufferWithMessage(outputBuffer, MessageFormat.format(
             "Skipping replacement for {0}/{1} because it does not exist or is empty.",
             configType, replacement.key));
-        outputBuffer.append(System.lineSeparator());
+      }
+    }
+
+    // insertions happen only on the new values (as they are initialized from
+    // the existing pre-upgrade values)
+    for (Insert insert : insertions) {
+      String valueToInsertInto = newValues.get(insert.key);
+
+      // if the key doesn't exist, then do no work
+      if (StringUtils.isNotBlank(valueToInsertInto)) {
+        // make this insertion idempotent - don't do it if the value already
+        // contains the content
+        if (StringUtils.contains(valueToInsertInto, insert.value)) {
+          updateBufferWithMessage(outputBuffer,
+              MessageFormat.format("Skipping insertion for {0}/{1} because it already contains {2}",
+                  configType, insert.key, insert.value));
+
+          continue;
+        }
+
+        // new line work
+        String valueToInsert = insert.value;
+        if (insert.newlineBefore) {
+          valueToInsert = System.lineSeparator() + valueToInsert;
+        }
+
+        // new line work
+        if (insert.newlineAfter) {
+          valueToInsert = valueToInsert + System.lineSeparator();
+        }
+
+        switch (insert.insertType) {
+          case APPEND:
+            valueToInsertInto = valueToInsertInto + valueToInsert;
+            break;
+          case PREPEND:
+            valueToInsertInto = valueToInsert + valueToInsertInto;
+            break;
+          default:
+            LOG.error("Unable to insert {0}/{1} with unknown insertion type of {2}", configType,
+                insert.key, insert.insertType);
+            break;
+        }
+
+        newValues.put(insert.key, valueToInsertInto);
+
+        updateBufferWithMessage(outputBuffer, MessageFormat.format(
+            "Updated {0}/{1} by inserting {2}", configType, insert.key, insert.value));
+      } else {
+        updateBufferWithMessage(outputBuffer, MessageFormat.format(
+            "Skipping insertion for {0}/{1} because it does not exist or is empty.", configType,
+            insert.key));
       }
     }
 
@@ -492,7 +562,7 @@ public class ConfigureAction extends AbstractServerAction {
    */
   private List<String> findValuesToPreserve(String clusterName, Config config)
       throws AmbariException {
-    List<String> result = new ArrayList<String>();
+    List<String> result = new ArrayList<>();
 
     Map<String, Map<String, ThreeWayValue>> conflicts =
         m_mergeHelper.getConflicts(clusterName, config.getStackId());
@@ -519,7 +589,7 @@ public class ConfigureAction extends AbstractServerAction {
     // iterate over all properties for every cluster service; if the property
     // has the correct config type (ie oozie-site or hdfs-site) then add it to
     // the list of original stack propertiess
-    Set<String> stackPropertiesForType = new HashSet<String>(50);
+    Set<String> stackPropertiesForType = new HashSet<>(50);
     for (String serviceName : cluster.getServices().keySet()) {
       Set<PropertyInfo> serviceProperties = m_ambariMetaInfo.get().getServiceProperties(
           oldStack.getStackName(), oldStack.getStackVersion(), serviceName);
@@ -700,4 +770,14 @@ public class ConfigureAction extends AbstractServerAction {
 
     return config.getProperties().get(propertyKey);
   }
+
+  /**
+   * Appends the buffer with the message as well as a newline.
+   *
+   * @param buffer
+   * @param message
+   */
+  private void updateBufferWithMessage(StringBuilder buffer, String message) {
+    buffer.append(message).append(System.lineSeparator());
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/410f2943/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ConfigUpgradeChangeDefinition.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ConfigUpgradeChangeDefinition.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ConfigUpgradeChangeDefinition.java
index 31df790..5c0fba7 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ConfigUpgradeChangeDefinition.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ConfigUpgradeChangeDefinition.java
@@ -28,6 +28,8 @@ import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlAttribute;
 import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlEnum;
+import javax.xml.bind.annotation.XmlEnumValue;
 import javax.xml.bind.annotation.XmlRootElement;
 import javax.xml.bind.annotation.XmlType;
 
@@ -36,7 +38,7 @@ import org.apache.ambari.server.state.Config;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.gson.Gson;
+import com.google.common.base.Objects;
 
 /**
  * The {@link ConfigUpgradeChangeDefinition} represents a configuration change. This change can be
@@ -109,10 +111,6 @@ public class ConfigUpgradeChangeDefinition {
 
   public static final Float DEFAULT_PRIORITY = 1.0f;
 
-  /**
-   * Gson
-   */
-  private Gson m_gson = new Gson();
 
   /**
    * An optional brief description of config changes.
@@ -137,6 +135,17 @@ public class ConfigUpgradeChangeDefinition {
 
   @XmlElement(name="regex-replace")
   private List<RegexReplace> regexReplacements;
+  /**
+   * Insert new content into an existing value by either prepending or
+   * appending. Each {@link Insert} will only run if:
+   * <ul>
+   * <li>The key specified by {@link Insert#key} exists.
+   * <li>The content specified by {@link Insert#value} is not found in the key's
+   * existing content.
+   * </ul>
+   */
+  @XmlElement(name = "insert")
+  private List<Insert> inserts;
 
   /**
    * @return the config type
@@ -250,6 +259,19 @@ public class ConfigUpgradeChangeDefinition {
 
 
   /**
+   * Gets the insertion directives.
+   *
+   * @return the inserts, or an empty list (never {@code null}).
+   */
+  public List<Insert> getInsertions() {
+    if (null == inserts) {
+      return Collections.emptyList();
+    }
+
+    return inserts;
+  }
+
+  /**
    * Used for configuration updates that should mask their values from being
    * printed in plain text.
    */
@@ -298,14 +320,12 @@ public class ConfigUpgradeChangeDefinition {
 
     @Override
     public String toString() {
-      return "Set{" +
-              ", key='" + key + '\'' +
-              ", value='" + value + '\'' +
-              ", ifKey='" + ifKey + '\'' +
-              ", ifType='" + ifType + '\'' +
-              ", ifValue='" + ifValue + '\'' +
-              ", ifKeyState='" + ifKeyState + '\'' +
-              '}';
+      return Objects.toStringHelper("Set").add("key", key)
+          .add("value", value)
+          .add("ifKey", ifKey)
+          .add("ifType", ifType)
+          .add("ifValue",ifValue)
+          .add("ifKeyState", ifKeyState).omitNullValues().toString();
     }
   }
 
@@ -370,26 +390,24 @@ public class ConfigUpgradeChangeDefinition {
      * The keys to keep when the action is {@link TransferOperation#DELETE}.
      */
     @XmlElement(name = "keep-key")
-    public List<String> keepKeys = new ArrayList<String>();
+    public List<String> keepKeys = new ArrayList<>();
 
 
     @Override
     public String toString() {
-      return "Transfer{" +
-              "operation=" + operation +
-              ", fromType='" + fromType + '\'' +
-              ", fromKey='" + fromKey + '\'' +
-              ", toKey='" + toKey + '\'' +
-              ", deleteKey='" + deleteKey + '\'' +
-              ", preserveEdits=" + preserveEdits +
-              ", defaultValue='" + defaultValue + '\'' +
-              ", coerceTo=" + coerceTo +
-              ", ifKey='" + ifKey + '\'' +
-              ", ifType='" + ifType + '\'' +
-              ", ifValue='" + ifValue + '\'' +
-              ", ifKeyState='" + ifKeyState + '\'' +
-              ", keepKeys=" + keepKeys +
-              '}';
+      return Objects.toStringHelper(this).add("operation", operation)
+          .add("fromType", fromType)
+          .add("fromKey", fromKey)
+          .add("toKey", toKey)
+          .add("deleteKey", deleteKey)
+          .add("preserveEdits",preserveEdits)
+          .add("defaultValue", defaultValue)
+          .add("coerceTo", coerceTo)
+          .add("ifKey", ifKey)
+          .add("ifType", ifType)
+          .add("ifValue", ifValue)
+          .add("ifKeyState", ifKeyState)
+          .add("keepKeys", keepKeys).omitNullValues().toString();
     }
   }
 
@@ -420,15 +438,13 @@ public class ConfigUpgradeChangeDefinition {
 
     @Override
     public String toString() {
-      return "Replace{" +
-              "key='" + key + '\'' +
-              ", find='" + find + '\'' +
-              ", replaceWith='" + replaceWith + '\'' +
-              ", ifKey='" + ifKey + '\'' +
-              ", ifType='" + ifType + '\'' +
-              ", ifValue='" + ifValue + '\'' +
-              ", ifKeyState='" + ifKeyState + '\'' +
-              '}';
+      return Objects.toStringHelper(this).add("key", key)
+          .add("find", find)
+          .add("replaceWith", replaceWith)
+          .add("ifKey", ifKey)
+          .add("ifType", ifType)
+          .add("ifValue", ifValue)
+          .add("ifKeyState", ifKeyState).omitNullValues().toString();
     }
   }
 
@@ -459,15 +475,13 @@ public class ConfigUpgradeChangeDefinition {
 
     @Override
     public String toString() {
-      return "RegexReplace{" +
-              "key='" + key + '\'' +
-              ", find='" + find + '\'' +
-              ", replaceWith='" + replaceWith + '\'' +
-              ", ifKey='" + ifKey + '\'' +
-              ", ifType='" + ifType + '\'' +
-              ", ifValue='" + ifValue + '\'' +
-              ", ifKeyState='" + ifKeyState + '\'' +
-              '}';
+      return Objects.toStringHelper(this).add("key", key)
+          .add("find", find)
+          .add("replaceWith",replaceWith)
+          .add("ifKey", ifKey)
+          .add("ifType", ifType)
+          .add("ifValue", ifValue)
+          .add("ifKeyState", ifKeyState).omitNullValues().toString();
     }
 
     /***
@@ -476,15 +490,85 @@ public class ConfigUpgradeChangeDefinition {
      */
     public Replace copyToReplaceObject(){
       Replace rep = new Replace();
-      rep.find = this.find;
-      rep.key = this.key;
-      rep.replaceWith = this.replaceWith;
-      rep.ifKey = this.ifKey;
-      rep.ifType = this.ifType;
-      rep.ifValue = this.ifValue;
-      rep.ifKeyState = this.ifKeyState;
+      rep.find = find;
+      rep.key = key;
+      rep.replaceWith = replaceWith;
+      rep.ifKey = ifKey;
+      rep.ifType = ifType;
+      rep.ifValue = ifValue;
+      rep.ifKeyState = ifKeyState;
 
       return rep;
     }
   }
-}
+
+  /**
+   * Used to replace strings in a key with other strings. More complex scenarios
+   * will be possible with regex (when needed). If the value specified in
+   * {@link Insert#value} already exists, then it is not inserted again.
+   */
+  @XmlAccessorType(XmlAccessType.FIELD)
+  @XmlType(name = "insert")
+  public static class Insert {
+    /**
+     * The key name
+     */
+    @XmlAttribute(name = "key", required = true)
+    public String key;
+
+    /**
+     * The value to insert.
+     */
+    @XmlAttribute(name = "value", required = true)
+    public String value;
+
+    /**
+     * The value to insert.
+     */
+    @XmlAttribute(name = "insert-type", required = true)
+    public InsertType insertType = InsertType.APPEND;
+
+    /**
+     * {@code true} to insert a new line before inserting the {@link #value}.
+     */
+    @XmlAttribute(name = "newline-before")
+    public boolean newlineBefore = false;
+
+    /**
+     * {@code true} to insert a new line after inserting the {@link #value}.
+     */
+    @XmlAttribute(name = "newline-after")
+    public boolean newlineAfter = false;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toString() {
+      return Objects.toStringHelper(this).add("insertType", insertType)
+          .add("key", key)
+          .add("value",value)
+          .add("newlineBefore", newlineBefore)
+          .add("newlineAfter", newlineAfter).omitNullValues().toString();
+    }
+  }
+
+  /**
+   * The {@link InsertType} defines how to use the {@link Insert} directive.
+   */
+  @XmlEnum
+  public enum InsertType {
+    /**
+     * Prepend the content.
+     */
+    @XmlEnumValue("prepend")
+    PREPEND,
+
+    /**
+     * Append the content.
+     */
+    @XmlEnumValue("append")
+    APPEND
+  }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/410f2943/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 f256eb0..b7be2ec 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
@@ -30,10 +30,9 @@ import javax.xml.bind.annotation.XmlType;
 
 import org.apache.ambari.server.serveraction.upgrades.ConfigureAction;
 import org.apache.ambari.server.state.Cluster;
-import org.apache.ambari.server.state.Config;
-import org.apache.ambari.server.state.DesiredConfig;
 import org.apache.ambari.server.state.stack.ConfigUpgradePack;
 import org.apache.ambari.server.state.stack.upgrade.ConfigUpgradeChangeDefinition.ConfigurationKeyValue;
+import org.apache.ambari.server.state.stack.upgrade.ConfigUpgradeChangeDefinition.Insert;
 import org.apache.ambari.server.state.stack.upgrade.ConfigUpgradeChangeDefinition.Replace;
 import org.apache.ambari.server.state.stack.upgrade.ConfigUpgradeChangeDefinition.Transfer;
 import org.apache.commons.lang.StringUtils;
@@ -86,6 +85,12 @@ public class ConfigureTask extends ServerSideActionTask {
    */
   public static final String PARAMETER_REPLACEMENTS = "configure-task-replacements";
 
+  /**
+   * Insertions can be several per task, so they're passed in as a json-ified
+   * list of objects.
+   */
+  public static final String PARAMETER_INSERTIONS = "configure-task-insertions";
+
   public static final String actionVerb = "Configuring";
 
   /**
@@ -222,6 +227,12 @@ public class ConfigureTask extends ServerSideActionTask {
       configParameters.put(ConfigureTask.PARAMETER_REPLACEMENTS, m_gson.toJson(allowedReplacements));
     }
 
+    // inserts
+    List<Insert> insertions = definition.getInsertions();
+    if (!insertions.isEmpty()) {
+      configParameters.put(ConfigureTask.PARAMETER_INSERTIONS, m_gson.toJson(insertions));
+    }
+
     return configParameters;
   }
 
@@ -296,32 +307,4 @@ public class ConfigureTask extends ServerSideActionTask {
 
     return isValid;
   }
-
-  /**
-   * Gets the value of the specified cluster property.
-   *
-   * @param cluster
-   *          the cluster (not {@code null}).
-   * @param configType
-   *          the configuration type (ie hdfs-site) (not {@code null}).
-   * @param propertyKey
-   *          the key to retrieve (not {@code null}).
-   * @return the value or {@code null} if it does not exist.
-   */
-  private String getDesiredConfigurationValue(Cluster cluster,
-      String configType, String propertyKey) {
-
-    Map<String, DesiredConfig> desiredConfigs = cluster.getDesiredConfigs();
-    DesiredConfig desiredConfig = desiredConfigs.get(configType);
-    if (null == desiredConfig) {
-      return null;
-    }
-
-    Config config = cluster.getConfig(configType, desiredConfig.getTag());
-    if (null == config) {
-      return null;
-    }
-
-    return config.getProperties().get(propertyKey);
-  }
-}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/410f2943/ambari-server/src/main/resources/stacks/HDP/2.5/upgrades/config-upgrade.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/stacks/HDP/2.5/upgrades/config-upgrade.xml b/ambari-server/src/main/resources/stacks/HDP/2.5/upgrades/config-upgrade.xml
index d5dec43..73e3c38 100644
--- a/ambari-server/src/main/resources/stacks/HDP/2.5/upgrades/config-upgrade.xml
+++ b/ambari-server/src/main/resources/stacks/HDP/2.5/upgrades/config-upgrade.xml
@@ -256,5 +256,32 @@
     </component>
     </service>
 
+    <service name="HIVE">
+      <component name="HIVE_SERVER">
+        <changes>
+          <definition xsi:type="configure" id="hdp_2_6_0_0_hive_append_heap_dump_options" summary="Appending optional Java heap dump parameters" >
+            <type>hive-env</type>
+            <insert key="content" value="export HADOOP_CLIENT_OPTS=&quot;$HADOOP_CLIENT_OPTS{{heap_dump_opts}}&quot;" insert-type="append" newline-before="true" newline-after="true" />
+          </definition>
+          <definition xsi:type="configure" id="hdp_2_6_0_0_tez_append_heap_dump_options">
+            <type>tez-site</type>
+            <insert key="tez.task.launch.cmd-opts" value="{{heap_dump_opts}}" insert-type="append" newline-before="false" newline-after="false" />
+          </definition>
+        </changes>
+      </component>
+      <component name="HIVE_SERVER_INTERACTIVE">
+        <changes>
+          <definition xsi:type="configure" id="hdp_2_6_0_0_hive_llap_append_heap_dump_options" summary="Appending optional Java heap dump parameters" >
+            <type>hive-interactive-env</type>
+            <insert key="content" value="export HADOOP_CLIENT_OPTS=&quot;$HADOOP_CLIENT_OPTS{{heap_dump_opts}}&quot;" insert-type="append" newline-before="true" newline-after="true" />
+          </definition>
+          <definition xsi:type="configure" id="hdp_2_6_0_0_hive_llap_append_java_heap_dump_options">
+            <type>hive-interactive-env</type>
+            <insert key="llap_java_opts" value="{{heap_dump_opts}}" insert-type="append" newline-before="false" newline-after="false" />
+          </definition>
+        </changes>
+      </component>
+    </service>
+
   </services>
 </upgrade-config-changes>

http://git-wip-us.apache.org/repos/asf/ambari/blob/410f2943/ambari-server/src/main/resources/stacks/HDP/2.5/upgrades/nonrolling-upgrade-2.6.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/stacks/HDP/2.5/upgrades/nonrolling-upgrade-2.6.xml b/ambari-server/src/main/resources/stacks/HDP/2.5/upgrades/nonrolling-upgrade-2.6.xml
index 5ef959b..6c4da04 100644
--- a/ambari-server/src/main/resources/stacks/HDP/2.5/upgrades/nonrolling-upgrade-2.6.xml
+++ b/ambari-server/src/main/resources/stacks/HDP/2.5/upgrades/nonrolling-upgrade-2.6.xml
@@ -269,8 +269,7 @@
     <!-- After processing this group, will change the effective Stack of the UpgradeContext object. -->
     <group xsi:type="update-stack" name="UPDATE_DESIRED_STACK_ID" title="Update Target Stack">
       <execute-stage title="Update Target Stack">
-        <task xsi:type="server_action" class="org.apache.ambari.server.serveraction.upgrades.UpdateDesiredStackAction">
-        </task>
+        <task xsi:type="server_action" class="org.apache.ambari.server.serveraction.upgrades.UpdateDesiredStackAction"/>
       </execute-stage>
     </group>
 
@@ -402,6 +401,22 @@
       <execute-stage service="STORM" component="NIMBUS" title="Apply config changes for Nimbus">
         <task xsi:type="configure" id="increase_storm_zookeeper_timeouts"/>
       </execute-stage>
+      
+      <execute-stage service="HIVE" component="HIVE_SERVER" title="Appending heap dump options for Hive">
+        <task xsi:type="configure" id="hdp_2_6_0_0_hive_append_heap_dump_options"/>
+      </execute-stage>
+
+      <execute-stage service="HIVE" component="HIVE_SERVER" title="Appending heap dump options for Tez">
+        <task xsi:type="configure" id="hdp_2_6_0_0_tez_append_heap_dump_options"/>
+      </execute-stage>
+
+      <execute-stage service="HIVE" component="HIVE_SERVER_INTERACTIVE" title="Appending heap dump options for HiveSever2 Interactive">
+        <task xsi:type="configure" id="hdp_2_6_0_0_hive_llap_append_heap_dump_options"/>
+      </execute-stage>
+
+      <execute-stage service="HIVE" component="HIVE_SERVER_INTERACTIVE" title="Appending Java heap dump options for HiveSever2 Interactive">
+        <task xsi:type="configure" id="hdp_2_6_0_0_hive_llap_append_java_heap_dump_options"/>
+      </execute-stage>      
     </group>
 
     <!--

http://git-wip-us.apache.org/repos/asf/ambari/blob/410f2943/ambari-server/src/main/resources/stacks/HDP/2.5/upgrades/upgrade-2.6.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/stacks/HDP/2.5/upgrades/upgrade-2.6.xml b/ambari-server/src/main/resources/stacks/HDP/2.5/upgrades/upgrade-2.6.xml
index b13a6f0..7f9e986 100644
--- a/ambari-server/src/main/resources/stacks/HDP/2.5/upgrades/upgrade-2.6.xml
+++ b/ambari-server/src/main/resources/stacks/HDP/2.5/upgrades/upgrade-2.6.xml
@@ -764,12 +764,26 @@
       </component>
 
       <component name="HIVE_SERVER">
+        <pre-upgrade>
+          <task xsi:type="configure" id="hdp_2_6_0_0_tez_append_heap_dump_options"/>
+          <task xsi:type="configure" id="hdp_2_6_0_0_hive_append_heap_dump_options"/>
+        </pre-upgrade>
+        
+        <pre-downgrade />
+        
         <upgrade>
           <task xsi:type="restart-task" />
         </upgrade>
       </component>
 
       <component name="HIVE_SERVER_INTERACTIVE">
+        <pre-upgrade>
+          <task xsi:type="configure" id="hdp_2_6_0_0_hive_llap_append_java_heap_dump_options"/>
+          <task xsi:type="configure" id="hdp_2_6_0_0_hive_llap_append_heap_dump_options"/>
+        </pre-upgrade>
+        
+        <pre-downgrade />
+      
         <upgrade>
           <task xsi:type="restart-task" />
         </upgrade>

http://git-wip-us.apache.org/repos/asf/ambari/blob/410f2943/ambari-server/src/main/resources/upgrade-config.xsd
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/upgrade-config.xsd b/ambari-server/src/main/resources/upgrade-config.xsd
index 805b472..cee7c85 100644
--- a/ambari-server/src/main/resources/upgrade-config.xsd
+++ b/ambari-server/src/main/resources/upgrade-config.xsd
@@ -43,6 +43,13 @@
       <xs:enumeration value="yaml-array"/>
     </xs:restriction>
   </xs:simpleType>
+
+  <xs:simpleType name="insertion-type">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="prepend"/>
+      <xs:enumeration value="append"/>
+    </xs:restriction>
+  </xs:simpleType>
   
   <xs:complexType name="configure">
     <xs:sequence>
@@ -103,6 +110,15 @@
             <xs:attribute name="mask" use="optional" type="xs:boolean"/>
           </xs:complexType>
         </xs:element>
+        <xs:element name="insert" minOccurs="0" maxOccurs="unbounded">
+          <xs:complexType>
+            <xs:attribute name="key" use="required" type="xs:string"/>
+            <xs:attribute name="value" use="required" type="xs:string"/>
+            <xs:attribute name="insert-type" use="required" type="insertion-type"/>
+            <xs:attribute name="newline-before" use="optional" type="xs:boolean"/>
+            <xs:attribute name="newline-after" use="optional" type="xs:boolean"/>            
+          </xs:complexType>
+        </xs:element>
       </xs:choice>
     </xs:sequence>
     <xs:attribute name="id" use="required" type="xs:string"/>

http://git-wip-us.apache.org/repos/asf/ambari/blob/410f2943/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 92fa084..cd8327b 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
@@ -38,7 +38,6 @@ import org.apache.ambari.server.agent.ExecutionCommand;
 import org.apache.ambari.server.orm.GuiceJpaInitializer;
 import org.apache.ambari.server.orm.InMemoryDefaultTestModule;
 import org.apache.ambari.server.orm.OrmTestHelper;
-import org.apache.ambari.server.orm.dao.ClusterVersionDAO;
 import org.apache.ambari.server.orm.dao.HostDAO;
 import org.apache.ambari.server.orm.dao.HostVersionDAO;
 import org.apache.ambari.server.orm.dao.RepositoryVersionDAO;
@@ -57,6 +56,8 @@ import org.apache.ambari.server.state.Service;
 import org.apache.ambari.server.state.ServiceFactory;
 import org.apache.ambari.server.state.StackId;
 import org.apache.ambari.server.state.stack.upgrade.ConfigUpgradeChangeDefinition.ConfigurationKeyValue;
+import org.apache.ambari.server.state.stack.upgrade.ConfigUpgradeChangeDefinition.Insert;
+import org.apache.ambari.server.state.stack.upgrade.ConfigUpgradeChangeDefinition.InsertType;
 import org.apache.ambari.server.state.stack.upgrade.ConfigUpgradeChangeDefinition.Replace;
 import org.apache.ambari.server.state.stack.upgrade.ConfigUpgradeChangeDefinition.Transfer;
 import org.apache.ambari.server.state.stack.upgrade.ConfigureTask;
@@ -100,8 +101,6 @@ public class ConfigureActionTest {
   @Inject
   private Clusters clusters;
   @Inject
-  private ClusterVersionDAO clusterVersionDAO;
-  @Inject
   private ConfigFactory cf;
   @Inject
   private ConfigureAction action;
@@ -139,13 +138,13 @@ public class ConfigureActionTest {
     c.addDesiredConfig("user", Collections.singleton(config));
     assertEquals(2, c.getConfigsByType("zoo.cfg").size());
 
-    List<ConfigurationKeyValue> configurations = new ArrayList<ConfigurationKeyValue>();
+    List<ConfigurationKeyValue> configurations = new ArrayList<>();
     ConfigurationKeyValue keyValue = new ConfigurationKeyValue();
     configurations.add(keyValue);
     keyValue.key = "initLimit";
     keyValue.value = "11";
 
-    Map<String, String> commandParams = new HashMap<String, String>();
+    Map<String, String> commandParams = new HashMap<>();
     commandParams.put("upgrade_direction", "upgrade");
     commandParams.put("version", HDP_2_2_0_1);
     commandParams.put("clusterName", "c1");
@@ -203,7 +202,7 @@ public class ConfigureActionTest {
     c.addDesiredConfig("user", Collections.singleton(config));
     assertEquals(2, c.getConfigsByType("zoo.cfg").size());
 
-    Map<String, String> commandParams = new HashMap<String, String>();
+    Map<String, String> commandParams = new HashMap<>();
     commandParams.put("upgrade_direction", "upgrade");
     commandParams.put("version", HDP_2_2_0_1);
     commandParams.put("clusterName", "c1");
@@ -272,7 +271,7 @@ public class ConfigureActionTest {
     keyValue.key = "initLimit";
     keyValue.value = "11";
 
-    Map<String, String> commandParams = new HashMap<String, String>();
+    Map<String, String> commandParams = new HashMap<>();
     commandParams.put("upgrade_direction", "upgrade");
     commandParams.put("version", HDP_2_2_0_1);
     commandParams.put("clusterName", "c1");
@@ -402,14 +401,14 @@ public class ConfigureActionTest {
     c.addDesiredConfig("user", Collections.singleton(config));
     assertEquals(2, c.getConfigsByType("zoo.cfg").size());
 
-    Map<String, String> commandParams = new HashMap<String, String>();
+    Map<String, String> commandParams = new HashMap<>();
     commandParams.put("upgrade_direction", "upgrade");
     commandParams.put("version", HDP_2_2_0_1);
     commandParams.put("clusterName", "c1");
     commandParams.put(ConfigureTask.PARAMETER_CONFIG_TYPE, "zoo.cfg");
 
     // copy with coerce
-    List<Transfer> transfers = new ArrayList<Transfer>();
+    List<Transfer> transfers = new ArrayList<>();
     Transfer transfer = new Transfer();
     transfer.operation = TransferOperation.COPY;
     transfer.coerceTo = TransferCoercionType.YAML_ARRAY;
@@ -466,14 +465,14 @@ public class ConfigureActionTest {
     c.addDesiredConfig("user", Collections.singleton(config));
     assertEquals(2, c.getConfigsByType("zoo.cfg").size());
 
-    Map<String, String> commandParams = new HashMap<String, String>();
+    Map<String, String> commandParams = new HashMap<>();
     commandParams.put("upgrade_direction", "upgrade");
     commandParams.put("version", HDP_2_2_0_1);
     commandParams.put("clusterName", "c1");
     commandParams.put(ConfigureTask.PARAMETER_CONFIG_TYPE, "zoo.cfg");
 
     // Replacement task
-    List<Replace> replacements = new ArrayList<Replace>();
+    List<Replace> replacements = new ArrayList<>();
     Replace replace = new Replace();
     replace.key = "key_to_replace";
     replace.find = "New Cat";
@@ -538,14 +537,14 @@ public class ConfigureActionTest {
     c.addDesiredConfig("user", Collections.singleton(config));
     assertEquals(2, c.getConfigsByType("zoo.cfg").size());
 
-    Map<String, String> commandParams = new HashMap<String, String>();
+    Map<String, String> commandParams = new HashMap<>();
     commandParams.put("upgrade_direction", "upgrade");
     commandParams.put("version", HDP_2_2_0_1);
     commandParams.put("clusterName", "c1");
     commandParams.put(ConfigureTask.PARAMETER_CONFIG_TYPE, "zoo.cfg");
 
     // Replacement task
-    List<Replace> replacements = new ArrayList<Replace>();
+    List<Replace> replacements = new ArrayList<>();
     Replace replace = new Replace();
     replace.key = "missing";
     replace.find = "foo";
@@ -596,7 +595,7 @@ public class ConfigureActionTest {
     assertEquals(2, c.getConfigsByType("zoo.cfg").size());
 
     // create several configurations
-    List<ConfigurationKeyValue> configurations = new ArrayList<ConfigurationKeyValue>();
+    List<ConfigurationKeyValue> configurations = new ArrayList<>();
     ConfigurationKeyValue fooKey2 = new ConfigurationKeyValue();
     configurations.add(fooKey2);
     fooKey2.key = "fooKey2";
@@ -608,7 +607,7 @@ public class ConfigureActionTest {
     fooKey3.value = "barValue3";
     fooKey3.mask = true;
 
-    Map<String, String> commandParams = new HashMap<String, String>();
+    Map<String, String> commandParams = new HashMap<>();
     commandParams.put("upgrade_direction", "upgrade");
     commandParams.put("version", HDP_2_2_0_1);
     commandParams.put("clusterName", "c1");
@@ -662,7 +661,7 @@ public class ConfigureActionTest {
     assertEquals(2, c.getConfigsByType("zoo.cfg").size());
 
     // create several configurations
-    List<ConfigurationKeyValue> configurations = new ArrayList<ConfigurationKeyValue>();
+    List<ConfigurationKeyValue> configurations = new ArrayList<>();
     ConfigurationKeyValue fooKey1 = new ConfigurationKeyValue();
     configurations.add(fooKey1);
     fooKey1.key = "fooKey1";
@@ -698,7 +697,7 @@ public class ConfigureActionTest {
     fooKey5.ifKeyState= PropertyKeyState.ABSENT;
 
 
-    Map<String, String> commandParams = new HashMap<String, String>();
+    Map<String, String> commandParams = new HashMap<>();
     commandParams.put("upgrade_direction", "upgrade");
     commandParams.put("version", HDP_2_2_0_1);
     commandParams.put("clusterName", "c1");
@@ -756,7 +755,7 @@ public class ConfigureActionTest {
     assertEquals(2, c.getConfigsByType("zoo.cfg").size());
 
     // create several configurations
-    List<ConfigurationKeyValue> configurations = new ArrayList<ConfigurationKeyValue>();
+    List<ConfigurationKeyValue> configurations = new ArrayList<>();
     ConfigurationKeyValue fooKey3 = new ConfigurationKeyValue();
     configurations.add(fooKey3);
     fooKey3.key = "fooKey3";
@@ -782,7 +781,7 @@ public class ConfigureActionTest {
     fooKey5.ifKeyState= PropertyKeyState.PRESENT;
 
 
-    Map<String, String> commandParams = new HashMap<String, String>();
+    Map<String, String> commandParams = new HashMap<>();
     commandParams.put("upgrade_direction", "upgrade");
     commandParams.put("version", HDP_2_2_0_1);
     commandParams.put("clusterName", "c1");
@@ -839,7 +838,7 @@ public class ConfigureActionTest {
     assertEquals(2, c.getConfigsByType("zoo.cfg").size());
 
     // create several configurations
-    List<Replace> replacements = new ArrayList<Replace>();
+    List<Replace> replacements = new ArrayList<>();
     Replace replace = new Replace();
     replace.key = "replace.key.3";
     replace.find = "a";
@@ -876,7 +875,7 @@ public class ConfigureActionTest {
     replace4.ifKeyState = PropertyKeyState.ABSENT;
     replacements.add(replace4);
 
-    Map<String, String> commandParams = new HashMap<String, String>();
+    Map<String, String> commandParams = new HashMap<>();
     commandParams.put("upgrade_direction", "upgrade");
     commandParams.put("version", HDP_2_2_0_1);
     commandParams.put("clusterName", "c1");
@@ -931,7 +930,7 @@ public class ConfigureActionTest {
     assertEquals(2, c.getConfigsByType("zoo.cfg").size());
 
     // create several configurations
-    List<Replace> replacements = new ArrayList<Replace>();
+    List<Replace> replacements = new ArrayList<>();
 
     Replace replace2 = new Replace();
     replacements.add(replace2);
@@ -963,7 +962,7 @@ public class ConfigureActionTest {
     replace4.ifKeyState = PropertyKeyState.PRESENT;
     replacements.add(replace4);
 
-    Map<String, String> commandParams = new HashMap<String, String>();
+    Map<String, String> commandParams = new HashMap<>();
     commandParams.put("upgrade_direction", "upgrade");
     commandParams.put("version", HDP_2_2_0_1);
     commandParams.put("clusterName", "c1");
@@ -1018,7 +1017,7 @@ public class ConfigureActionTest {
     keyValue.key = "initLimit";
     keyValue.value = "11";
 
-    Map<String, String> commandParams = new HashMap<String, String>();
+    Map<String, String> commandParams = new HashMap<>();
     commandParams.put("upgrade_direction", "upgrade");
     commandParams.put("version", HDP_2_2_0_1);
     commandParams.put("clusterName", "c1");
@@ -1131,7 +1130,7 @@ public class ConfigureActionTest {
     keyValue.key = "initLimit";
     keyValue.value = "11";
 
-    Map<String, String> commandParams = new HashMap<String, String>();
+    Map<String, String> commandParams = new HashMap<>();
     commandParams.put("upgrade_direction", "upgrade");
     commandParams.put("version", HDP_2_2_0_1);
     commandParams.put("clusterName", "c1");
@@ -1226,7 +1225,7 @@ public class ConfigureActionTest {
     keyValue.key = "initLimit";
     keyValue.value = "11";
 
-    Map<String, String> commandParams = new HashMap<String, String>();
+    Map<String, String> commandParams = new HashMap<>();
     commandParams.put("upgrade_direction", "upgrade");
     commandParams.put("version", HDP_2_2_0_1);
     commandParams.put("clusterName", "c1");
@@ -1333,7 +1332,7 @@ public class ConfigureActionTest {
     keyValue.key = "initLimit";
     keyValue.value = "11";
 
-    Map<String, String> commandParams = new HashMap<String, String>();
+    Map<String, String> commandParams = new HashMap<>();
     commandParams.put("upgrade_direction", "upgrade");
     commandParams.put("version", HDP_2_2_0_1);
     commandParams.put("clusterName", "c1");
@@ -1434,7 +1433,7 @@ public class ConfigureActionTest {
     keyValue.key = "initLimit";
     keyValue.value = "11";
 
-    Map<String, String> commandParams = new HashMap<String, String>();
+    Map<String, String> commandParams = new HashMap<>();
     commandParams.put("upgrade_direction", "upgrade");
     commandParams.put("version", HDP_2_2_0_1);
     commandParams.put("clusterName", "c1");
@@ -1532,7 +1531,7 @@ public class ConfigureActionTest {
     keyValue.key = "initLimit";
     keyValue.value = "11";
 
-    Map<String, String> commandParams = new HashMap<String, String>();
+    Map<String, String> commandParams = new HashMap<>();
     commandParams.put("upgrade_direction", "upgrade");
     commandParams.put("version", HDP_2_2_0_1);
     commandParams.put("clusterName", "c1");
@@ -1600,6 +1599,94 @@ public class ConfigureActionTest {
     }
   }
 
+  /**
+   * Tests using the {@code <insert/>} element in a configuration upgrade pack.
+   *
+   * @throws Exception
+   */
+  @Test
+  public void testInsert() throws Exception {
+    makeUpgradeCluster();
+
+    Cluster c = clusters.getCluster("c1");
+    assertEquals(1, c.getConfigsByType("zoo.cfg").size());
+
+    c.setDesiredStackVersion(HDP_220_STACK);
+    Config config = cf.createNew(c, "zoo.cfg", "version2", new HashMap<String, String>() {
+      {
+        put("key_to_append", "append");
+        put("key_to_prepend", "prepend");
+      }
+    }, new HashMap<String, Map<String, String>>());
+
+    c.addDesiredConfig("user", Collections.singleton(config));
+    assertEquals(2, c.getConfigsByType("zoo.cfg").size());
+
+    Map<String, String> commandParams = new HashMap<>();
+    commandParams.put("upgrade_direction", "upgrade");
+    commandParams.put("version", HDP_2_2_0_1);
+    commandParams.put("clusterName", "c1");
+    commandParams.put(ConfigureTask.PARAMETER_CONFIG_TYPE, "zoo.cfg");
+
+    // define the changes
+    final String prependValue = "This should be on a newline";
+    final String appendValue = " this will be after...";
+
+    // insert tasks
+    List<Insert> insertions = new ArrayList<>();
+
+    Insert prepend = new Insert();
+    prepend.insertType = InsertType.PREPEND;
+    prepend.key = "key_to_prepend";
+    prepend.value = prependValue;
+    prepend.newlineBefore = false;
+    prepend.newlineAfter = true;
+
+    Insert append = new Insert();
+    append.insertType = InsertType.APPEND;
+    append.key = "key_to_append";
+    append.value = appendValue;
+    append.newlineBefore = false;
+    append.newlineAfter = false;
+
+    // add them to the list
+    insertions.add(prepend);
+    insertions.add(append);
+
+    // just for fun, add them again - this will test their idempotence
+    insertions.add(prepend);
+    insertions.add(append);
+
+    commandParams.put(ConfigureTask.PARAMETER_INSERTIONS, new Gson().toJson(insertions));
+
+    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));
+    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()));
+
+    // build the expected values
+    String expectedPrepend = prependValue + System.lineSeparator() + "prepend";
+    String expectedAppend = "append" + appendValue;
+
+    assertEquals(expectedPrepend, config.getProperties().get("key_to_prepend"));
+    assertEquals(expectedAppend, config.getProperties().get("key_to_append"));
+  }
+
   private void makeUpgradeCluster() throws Exception {
     String clusterName = "c1";
     String hostName = "h1";
@@ -1632,7 +1719,7 @@ public class ConfigureActionTest {
 
     Host host = clusters.getHost(hostName);
 
-    Map<String, String> hostAttributes = new HashMap<String, String>();
+    Map<String, String> hostAttributes = new HashMap<>();
     hostAttributes.put("os_family", "redhat");
     hostAttributes.put("os_release_version", "6");
     host.setHostAttributes(hostAttributes);