This is an automated email from the ASF dual-hosted git repository.

jonathanhurley pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ambari.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 4c70e1a  [AMBARI-24994] Updates to the SPI for Upgrade Action 
Operations (#2687)
4c70e1a is described below

commit 4c70e1a7ec5609992fa06fca9618f5b112f941a5
Author: Jonathan Hurley <jonathanhur...@apache.org>
AuthorDate: Tue Dec 4 16:11:58 2018 -0500

    [AMBARI-24994] Updates to the SPI for Upgrade Action Operations (#2687)
---
 .../org/apache/ambari/spi/ClusterInformation.java  |  25 +--
 .../apache/ambari/spi/upgrade/UpgradeAction.java   |   7 +-
 .../spi/upgrade/UpgradeActionOperations.java       | 168 +++++++++++++++++++--
 .../ambari/spi/upgrade/UpgradeInformation.java     | 131 ++++++++++++++++
 .../orm/entities/RepositoryVersionEntity.java      |  17 ++-
 .../upgrades/AbstractUpgradeServerAction.java      |  18 +++
 .../upgrades/PluginUpgradeServerAction.java        |  63 +++++++-
 .../stack/upgrade/orchestrate/UpgradeContext.java  |  42 ++++++
 .../ambari/server/state/cluster/ClusterImpl.java   |   7 +-
 .../upgrades/PluginUpgradeServerActionTest.java    | 105 ++++++++++---
 10 files changed, 519 insertions(+), 64 deletions(-)

diff --git 
a/ambari-server-spi/src/main/java/org/apache/ambari/spi/ClusterInformation.java 
b/ambari-server-spi/src/main/java/org/apache/ambari/spi/ClusterInformation.java
index eb9974e..774fe65 100644
--- 
a/ambari-server-spi/src/main/java/org/apache/ambari/spi/ClusterInformation.java
+++ 
b/ambari-server-spi/src/main/java/org/apache/ambari/spi/ClusterInformation.java
@@ -21,8 +21,6 @@ import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 
-import com.google.common.collect.Maps;
-
 /**
  * The {@link ClusterInformation} class is used to pass the state of the 
cluster
  * as simple primitive values and collections. It contains the following types 
of information:
@@ -124,13 +122,17 @@ public class ClusterInformation {
     return hosts;
   }
 
+  /**
+   * Gets the configuration properties for the specified type. If the type does
+   * not exist, this will return {@code null}.
+   *
+   * @param configurationType
+   *          the configuration type to retrieve.
+   * @return the property name and value pairs for the configuration type, or
+   *         {@code null} if no configuration type exists.
+   */
   public Map<String, String> getConfigurationProperties(String 
configurationType) {
-    Map<String, String> properties = m_configurations.get(configurationType);
-    if (null == properties) {
-      return Maps.newHashMap();
-    }
-
-    return properties;
+    return m_configurations.get(configurationType);
   }
 
   /**
@@ -143,7 +145,12 @@ public class ClusterInformation {
    * @return the property value, or {@code null} if it does not exist.
    */
   public String getConfigurationProperty(String configurationType, String 
propertyName) {
-    return getConfigurationProperties(configurationType).get(propertyName);
+    Map<String, String> configType = 
getConfigurationProperties(configurationType);
+    if (null == configType) {
+      return null;
+    }
+
+    return configType.get(propertyName);
   }
 
   /**
diff --git 
a/ambari-server-spi/src/main/java/org/apache/ambari/spi/upgrade/UpgradeAction.java
 
b/ambari-server-spi/src/main/java/org/apache/ambari/spi/upgrade/UpgradeAction.java
index 1e25c34..a34cee6 100644
--- 
a/ambari-server-spi/src/main/java/org/apache/ambari/spi/upgrade/UpgradeAction.java
+++ 
b/ambari-server-spi/src/main/java/org/apache/ambari/spi/upgrade/UpgradeAction.java
@@ -35,13 +35,14 @@ public interface UpgradeAction {
    *
    * @param clusterInformation
    *          the cluster information, such as topology, configurations, etc.
-   *
+   * @param upgradeInformation
+   *          the upgrade type, direction, services, repository versions, etc.
    * @return the changes to perform during the upgrade, such as updating
    *         configurations.
    * @throws UpgradeActionException
    *           if the class is unable to create the operations for the Ambari
    *           Server to execute.
    */
-  UpgradeActionOperations getOperations(ClusterInformation clusterInformation)
-      throws UpgradeActionException;
+  UpgradeActionOperations getOperations(ClusterInformation clusterInformation,
+      UpgradeInformation upgradeInformation) throws UpgradeActionException;
 }
diff --git 
a/ambari-server-spi/src/main/java/org/apache/ambari/spi/upgrade/UpgradeActionOperations.java
 
b/ambari-server-spi/src/main/java/org/apache/ambari/spi/upgrade/UpgradeActionOperations.java
index ddba98c..8a17538 100644
--- 
a/ambari-server-spi/src/main/java/org/apache/ambari/spi/upgrade/UpgradeActionOperations.java
+++ 
b/ambari-server-spi/src/main/java/org/apache/ambari/spi/upgrade/UpgradeActionOperations.java
@@ -18,12 +18,16 @@
 package org.apache.ambari.spi.upgrade;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
+import java.util.Set;
+
+import org.apache.commons.lang.StringUtils;
 
 /**
  * The {@link UpgradeActionOperations} is used to instruct Ambari Server to
  * perform actions during an upgrade. It is returned by
- * {@link UpgradeAction#getOperations()}.
+ * {@link 
UpgradeAction#getOperations(org.apache.ambari.spi.ClusterInformation, 
UpgradeInformation)}
  */
 public class UpgradeActionOperations {
 
@@ -33,13 +37,36 @@ public class UpgradeActionOperations {
   private List<ConfigurationChanges> m_configurationChanges;
 
   /**
+   * Any configuration types which should be completely removed.
+   */
+  private Set<String> m_configurationTypeRemovals;
+
+  /**
    * A buffer that the {@link UpgradeAction} can use to pass messages back to
    * Ambari to display to the user.
    */
-  private StringBuilder m_standardOutput;
+  private String m_standardOutput;
+
+  /**
+   * Sets configuration changes which are a part of the actions to be performed
+   * during the upgrade for a single configuration type only. If multiple
+   * configuration types are being updated, then
+   * {@link #setConfigurationChanges(List)} should be used.
+   *
+   * @param configurationChanges
+   *          the configuration changes to make.
+   * @return an instance of the {@link UpgradeActionOperations} with the value
+   *         set.
+   * @see #setConfigurationChanges(List)
+   */
+  public UpgradeActionOperations setConfigurationChanges(
+      ConfigurationChanges configurationChanges) {
+    setConfigurationChanges(Collections.singletonList(configurationChanges));
+    return this;
+  }
 
   /**
-   * Sets configuration changes which are a part of the actions to be perfomred
+   * Sets configuration changes which are a part of the actions to be performed
    * during the upgrade.
    *
    * @param configurationChanges
@@ -54,14 +81,48 @@ public class UpgradeActionOperations {
   }
 
   /**
-   * Sets a {@link StringBuilder} which is used by the server to display
-   * messages about what the action did.
+   * Adds a single configuration change to the operations which should be
+   * performed. This might be more useful thank the bulk methods, such as
+   * {@link #setConfigurationChanges(List)}.
+   *
+   * @param configurationChanges
+   *          the configuration changes to make.
+   * @return an instance of the {@link UpgradeActionOperations} with the value
+   *         set.
+   */
+  public UpgradeActionOperations addConfigurationChange(ConfigurationChanges 
configurationChanges) {
+    if (null == m_configurationChanges) {
+      m_configurationChanges = new ArrayList<>();
+    }
+
+    m_configurationChanges.add(configurationChanges);
+    return this;
+  }
+
+  /**
+   * Sets any configuration types which should be completely removed.
+   *
+   * @param configurationTypeRemovals
+   *          the configuration types to be removed, if any.
+   * @return an instance of the {@link UpgradeActionOperations} with the value
+   *         set.
+   */
+  public UpgradeActionOperations setConfigurationTypeRemoval(
+      Set<String> configurationTypeRemovals) {
+    m_configurationTypeRemovals = configurationTypeRemovals;
+    return this;
+  }
+
+  /**
+   * Sets the standard output which will be used by the server to display
+   * information about what actions are being performed.
    *
    * @param standardOutput
+   *          the output which will be displayed by the upgrade process.
    * @return an instance of the {@link UpgradeActionOperations} with the value
    *         set.
    */
-  public UpgradeActionOperations setStandardOutput(StringBuilder 
standardOutput) {
+  public UpgradeActionOperations setStandardOutput(String standardOutput) {
     m_standardOutput = standardOutput;
     return this;
   }
@@ -76,13 +137,22 @@ public class UpgradeActionOperations {
   }
 
   /**
+   * Gets the configuration types which should be removed, if any.
+   *
+   * @return the configuration types which should be completely removed, if 
any.
+   */
+  public Set<String> getConfigurationTypeRemovals() {
+    return m_configurationTypeRemovals;
+  }
+
+  /**
    * Gets the standard output, if any, for the server to display as part of the
    * action being run.
    *
    * @return any messages that should be display along with the command's
    *         status.
    */
-  public StringBuilder getStandardOutput() {
+  public String getStandardOutput() {
     return m_standardOutput;
   }
 
@@ -117,20 +187,34 @@ public class UpgradeActionOperations {
     private final List<PropertyChange> m_changes = new ArrayList<>();
 
     /**
+     * {@code true} if the only changes included are removals (or there are no
+     * changes at all), {@code false} if there are additions and/or
+     * modifications as well.
+     */
+    private boolean m_onlyRemovals = true;
+
+    /**
      * Constructor.
      *
      * @param configType
+     *          the name of the configuration type, such as {@code foo-site}.
      */
     public ConfigurationChanges(String configType) {
       m_configType = configType;
     }
 
     /**
+     * Sets either a new or an update to an existing configuration property.
+     *
      * @param propertyName
+     *          the name of the property.
      * @param propertyValue
-     * @return
+     *          the value for the property.
+     * @return an instance of this class with the value set.
      */
     public ConfigurationChanges set(String propertyName, String propertyValue) 
{
+      m_onlyRemovals = false;
+
       PropertyChange propertyChange = new PropertyChange(ChangeType.SET, 
propertyName,
           propertyValue);
 
@@ -139,8 +223,11 @@ public class UpgradeActionOperations {
     }
 
     /**
+     * Marks a configuration property for removal.
+     *
      * @param propertyName
-     * @return
+     *          the name of the property to remove.
+     * @return an instance of this class with the property marked for removal.
      */
     public ConfigurationChanges remove(String propertyName) {
       PropertyChange propertyChange = new PropertyChange(ChangeType.REMOVE, 
propertyName, null);
@@ -161,11 +248,67 @@ public class UpgradeActionOperations {
      * Gets all of the additions, updates, and removals for this configuration
      * type.
      *
-     * @return
+     * @return all of the various property changes, including additions and
+     *         removals.
      */
     public List<PropertyChange> getPropertyChanges() {
       return m_changes;
     }
+
+    /**
+     * Gets whether the only changes included for this configuration type are
+     * removals (or there are no changes at all of any kind). If there are any
+     * additions or modifications, this will be {@code false}.
+     *
+     * @return whether there are only removals in this change request, or if
+     *         there are also additions or modifications to properties.
+     */
+    public boolean isOnlyRemovals() {
+      return m_onlyRemovals;
+    }
+
+    /**
+     * Gets whether there are no changes yet for this configuration change 
type.
+     *
+     * @return {@code true} if there are no changes yet, otherwise
+     *         {@code false}.
+     */
+    public boolean isEmpty() {
+      return null == m_changes || m_changes.isEmpty();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toString() {
+      if(m_changes.isEmpty()) {
+        return "There are no configuration changes";
+      }
+
+      StringBuilder buffer = new StringBuilder(m_configType);
+      buffer.append(System.lineSeparator());
+
+      for( PropertyChange propertyChange : m_changes ) {
+        switch(propertyChange.getChangeType()) {
+          case REMOVE:
+            buffer.append("  Removed " 
).append(propertyChange.getPropertyName());
+            break;
+          case SET:
+            buffer
+              .append("  Set " )
+              .append(propertyChange.getPropertyName())
+              .append(" to " )
+              
.append(StringUtils.abbreviateMiddle(propertyChange.getPropertyValue(), 
"\u2026", 30));
+            break;
+          default:
+            break;
+
+        }
+      }
+
+      return buffer.toString();
+    }
   }
 
   /**
@@ -196,8 +339,7 @@ public class UpgradeActionOperations {
      * @param propertyName
      *          the name of the property being added, updated, or removed.
      * @param propertyValue
-     *          the value to add/update if the type is
-     *          {@link ConfigurationChangeType#SET}.
+     *          the value to add/update if the type is {@link ChangeType#SET}.
      */
     public PropertyChange(ChangeType changeType,
         String propertyName, String propertyValue) {
@@ -226,7 +368,7 @@ public class UpgradeActionOperations {
 
     /**
      * Gets the name of the property value to set if the configuration type is
-     * {@link ConfigurationChangeType#SET}.
+     * {@link ChangeType#SET}
      *
      * @return the property value.
      */
diff --git 
a/ambari-server-spi/src/main/java/org/apache/ambari/spi/upgrade/UpgradeInformation.java
 
b/ambari-server-spi/src/main/java/org/apache/ambari/spi/upgrade/UpgradeInformation.java
new file mode 100644
index 0000000..19de747
--- /dev/null
+++ 
b/ambari-server-spi/src/main/java/org/apache/ambari/spi/upgrade/UpgradeInformation.java
@@ -0,0 +1,131 @@
+/**
+ * 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.spi.upgrade;
+
+import java.util.Map;
+
+import org.apache.ambari.spi.RepositoryVersion;
+
+/**
+ * The {@link UpgradeInformation} class contains information about a running
+ * upgrade or downgrade.
+ */
+public class UpgradeInformation {
+
+  /**
+   * {@code true} if the direction is upgrade, {@code false} if it is a
+   * downgrade.
+   */
+  private final boolean m_isUpgrade;
+
+  /**
+   * The orchestration type of the upgrade.
+   */
+  private final UpgradeType m_upgradeType;
+
+  /**
+   * The target version of the upgrade or downgrade.
+   */
+  private final RepositoryVersion m_targetVersion;
+
+  /**
+   * The source version of every service in the upgrade.
+   */
+  private final Map<String, RepositoryVersion> m_sourceVersions;
+
+  /**
+   * The target version of every service in the upgrade.
+   */
+  private final Map<String, RepositoryVersion> m_targetVersions;
+
+  /**
+   * Constructor.
+   *
+   * @param isUpgrade
+   *          {@code true} if this is an upgrade, {@code false} otherwise.
+   * @param upgradeType
+   *          the orchestration type of the upgrade.
+   * @param targetVersion
+   *          the target version for all services and components in the upgrade
+   *          or downgrade. If this is an upgrade, then this is the version tha
+   *          all services are moving to. If this is a downgrade, then this is
+   *          the version that all services are coming back from.
+   * @param sourceVersions
+   *          the versions that all services and components are coming from.
+   * @param targetVersions
+   *          the versions that all services and components in the upgrade are
+   *          moving to.
+   */
+  public UpgradeInformation(boolean isUpgrade, UpgradeType upgradeType,
+      RepositoryVersion targetVersion, Map<String, RepositoryVersion> 
sourceVersions,
+      Map<String, RepositoryVersion> targetVersions) {
+    m_isUpgrade = isUpgrade;
+    m_upgradeType = upgradeType;
+    m_targetVersion = targetVersion;
+    m_sourceVersions = sourceVersions;
+    m_targetVersions = targetVersions;
+  }
+
+  /**
+   * {@code true} if this is an upgrade, {@code false} otherwise.
+   *
+   * @return the upgrade direction.
+   */
+  public boolean isUpgrade() {
+    return m_isUpgrade;
+  }
+
+  /**
+   * The orchestration type of the upgrade.
+   *
+   * @return the orchestration type.
+   */
+  public UpgradeType getUpgradeType() {
+    return m_upgradeType;
+  }
+
+  /**
+   * The target version for all services and components in the upgrade or
+   * downgrade. If this is an upgrade, then this is the version tha all 
services
+   * are moving to. If this is a downgrade, then this is the version that all
+   * services are coming back from.
+   *
+   * @return the target versions for all services.
+   */
+  public RepositoryVersion getRepositoryVersion() {
+    return m_targetVersion;
+  }
+
+  /**
+   * The versions that all services and components are coming from.
+   *
+   * @return the source versions of all services.
+   */
+  public Map<String, RepositoryVersion> getSourceVersions() {
+    return m_sourceVersions;
+  }
+
+  /**
+   * The versions that all services and components in the upgrade are moving 
to.
+   *
+   * @return the target versions for all services.
+   */
+  public Map<String, RepositoryVersion> getTargetVersions() {
+    return m_targetVersions;
+  }
+}
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/RepositoryVersionEntity.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/RepositoryVersionEntity.java
index 358de55..ef63538 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/RepositoryVersionEntity.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/RepositoryVersionEntity.java
@@ -47,16 +47,14 @@ import javax.persistence.UniqueConstraint;
 import org.apache.ambari.annotations.Experimental;
 import org.apache.ambari.annotations.ExperimentalFeature;
 import org.apache.ambari.server.StaticallyInject;
-import org.apache.ambari.server.stack.upgrade.RepositoryVersionHelper;
 import org.apache.ambari.server.state.StackId;
 import org.apache.ambari.server.state.repository.VersionDefinitionXml;
 import org.apache.ambari.spi.RepositoryType;
+import org.apache.ambari.spi.RepositoryVersion;
 import org.apache.commons.lang.StringUtils;
 
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Objects;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
 
 @Entity
 @Table(name = "repo_version", uniqueConstraints = {
@@ -93,9 +91,6 @@ import com.google.inject.Provider;
         query = "SELECT repositoryVersion FROM RepositoryVersionEntity 
repositoryVersion WHERE repositoryVersion IN (SELECT DISTINCT 
sd1.desiredRepositoryVersion FROM ServiceDesiredStateEntity sd1 WHERE 
sd1.desiredRepositoryVersion IN ?1)") })
 @StaticallyInject
 public class RepositoryVersionEntity {
-  @Inject
-  private static Provider<RepositoryVersionHelper> 
repositoryVersionHelperProvider;
-
   @Id
   @Column(name = "repo_version_id")
   @GeneratedValue(strategy = GenerationType.TABLE, generator = 
"repository_version_id_generator")
@@ -504,4 +499,14 @@ public class RepositoryVersionEntity {
       repoOsEntity.setRepositoryVersionEntity(this);
     }
   }
+
+  /**
+   * Builds a {@link RepositoryVersion} instance type from this entity.
+   *
+   * @return a single POJO to represent this entity.
+   */
+  public RepositoryVersion getRepositoryVersion() {
+    return new RepositoryVersion(getId(), getStackName(), getStackVersion(),
+        getStackId().getStackId(), getVersion(), getType());
+  }
 }
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/AbstractUpgradeServerAction.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/AbstractUpgradeServerAction.java
index ea4d007..52c85e1 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/AbstractUpgradeServerAction.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/AbstractUpgradeServerAction.java
@@ -19,6 +19,7 @@ package org.apache.ambari.server.serveraction.upgrades;
 
 import org.apache.ambari.server.agent.stomp.AgentConfigsHolder;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
+import org.apache.ambari.server.controller.AmbariManagementController;
 import org.apache.ambari.server.orm.dao.UpgradeDAO;
 import org.apache.ambari.server.orm.entities.UpgradeEntity;
 import org.apache.ambari.server.serveraction.AbstractServerAction;
@@ -27,6 +28,7 @@ import 
org.apache.ambari.server.stack.upgrade.orchestrate.UpgradeContextFactory;
 import org.apache.ambari.server.stack.upgrade.orchestrate.UpgradeHelper;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.ConfigHelper;
 import org.apache.ambari.server.state.StackInfo;
 
 import com.google.gson.Gson;
@@ -64,6 +66,9 @@ public abstract class AbstractUpgradeServerAction extends 
AbstractServerAction {
   @Inject
   private UpgradeContextFactory m_upgradeContextFactory;
 
+  /**
+   * Used for updating push data to the agents.
+   */
   @Inject
   protected AgentConfigsHolder agentConfigsHolder;
 
@@ -74,6 +79,19 @@ public abstract class AbstractUpgradeServerAction extends 
AbstractServerAction {
   protected Provider<AmbariMetaInfo> m_metainfoProvider;
 
   /**
+   * Used for manipulting configurations, such as removing entire types and
+   * creating new ones.
+   */
+  @Inject
+  protected ConfigHelper m_configHelper;
+
+  /**
+   * Who knows what this is used for or why it even exists.
+   */
+  @Inject
+  protected AmbariManagementController m_amc;
+
+  /**
    * Gets the injected instance of the {@link Gson} serializer/deserializer.
    *
    * @return the injected {@link Gson} instance.
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/PluginUpgradeServerAction.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/PluginUpgradeServerAction.java
index 839cd94..154352e 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/PluginUpgradeServerAction.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/PluginUpgradeServerAction.java
@@ -21,6 +21,7 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.ConcurrentMap;
 import java.util.stream.Collectors;
 
@@ -28,6 +29,7 @@ import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.actionmanager.HostRoleStatus;
 import org.apache.ambari.server.agent.CommandReport;
 import org.apache.ambari.server.serveraction.ServerAction;
+import org.apache.ambari.server.stack.upgrade.Direction;
 import org.apache.ambari.server.stack.upgrade.UpgradePack;
 import org.apache.ambari.server.stack.upgrade.orchestrate.UpgradeContext;
 import org.apache.ambari.server.state.Cluster;
@@ -98,14 +100,15 @@ public class PluginUpgradeServerAction extends 
AbstractUpgradeServerAction {
     try {
       ClusterInformation clusterInformation = 
cluster.buildClusterInformation();
       UpgradeActionOperations upgradeActionOperations = 
upgradeAction.getOperations(
-          clusterInformation);
+          clusterInformation, upgradeContext.buildUpgradeInformation());
 
       // update configurations
-      changeConfigurations(cluster, 
upgradeActionOperations.getConfigurationChanges());
+      changeConfigurations(cluster, 
upgradeActionOperations.getConfigurationChanges(), upgradeContext);
+      removeConfigurationTypes(cluster, 
upgradeActionOperations.getConfigurationTypeRemovals());
 
       standardOutput = "Successfully executed " + pluginClassName;
       if(null != upgradeActionOperations.getStandardOutput()) {
-        standardOutput = 
upgradeActionOperations.getStandardOutput().toString();
+        standardOutput = upgradeActionOperations.getStandardOutput();
       }
     } catch (UpgradeActionException exception) {
       LOG.error("Unable to run the upgrade action {}", pluginClassName, 
exception);
@@ -124,22 +127,51 @@ public class PluginUpgradeServerAction extends 
AbstractUpgradeServerAction {
   }
 
   /**
-   * Updates configurations in the cluster.
+   * Updates configurations in the cluster. This will create new configuration
+   * types if changes are required for one which does not exist.
    *
    * @param cluster
    *          the cluster used to retrieve the configurations.
    * @param configurationChanges
    *          the changes to make.
+   * @param upgradeContext
+   *          upgrade information for the current upgrade or downgrade.
    * @throws AmbariException
    *           if there was a problem determining what change to make or while
    *           making changes.
    */
   private void changeConfigurations(Cluster cluster,
-      List<ConfigurationChanges> configurationChanges)
+      List<ConfigurationChanges> configurationChanges, UpgradeContext 
upgradeContext)
       throws AmbariException {
+    if (null == configurationChanges) {
+      return;
+    }
+
     for (ConfigurationChanges configTypeChanges : configurationChanges) {
       String configType = configTypeChanges.getConfigType();
+
+      // the configuration could be null, so try to figure out if we're 
creating
+      // it by checking all of the changes being made
       Config config = cluster.getDesiredConfigByType(configType);
+      if (null == config) {
+        // no additions/updates, so just skip it entirely
+        if (configTypeChanges.isOnlyRemovals()) {
+          continue;
+        }
+
+        Direction direction = upgradeContext.getDirection();
+        String serviceVersionNote = String.format("%s %s %s", 
direction.getText(true),
+            direction.getPreposition(), 
upgradeContext.getRepositoryVersion().getVersion());
+
+        m_configHelper.createConfigType(cluster, 
upgradeContext.getRepositoryVersion().getStackId(),
+            m_amc, configType, new HashMap<>(), m_amc.getAuthName(), 
serviceVersionNote);
+
+        config = cluster.getDesiredConfigByType(configType);
+        if (null == config) {
+          throw new AmbariException(
+              String.format("Unable to create the % configuration type", 
configType));
+        }
+      }
 
       List<PropertyChange> propertyChanges = 
configTypeChanges.getPropertyChanges();
       for (PropertyChange propertyChange : propertyChanges) {
@@ -165,6 +197,27 @@ public class PluginUpgradeServerAction extends 
AbstractUpgradeServerAction {
   }
 
   /**
+   * Remove the specified configuration types from the cluster.
+   *
+   * @param cluster
+   *          the cluster to remove the configurations from.
+   * @param configurationTypeRemovals
+   *          the types to remove.
+   * @throws AmbariException
+   *           if there were problems removing the configuration types.
+   */
+  private void removeConfigurationTypes(Cluster cluster, Set<String> 
configurationTypeRemovals)
+      throws AmbariException {
+    if (null == configurationTypeRemovals) {
+      return;
+    }
+
+    for (String configType : configurationTypeRemovals) {
+      m_configHelper.removeConfigsByType(cluster, configType);
+    }
+  }
+
+  /**
    * Gets the fully qualified classname of the {@link UpgradeAction} class 
which
    * will be executed. This will look in the command parameters of the 
execution
    * command for {@link ServerAction#WRAPPED_CLASS_NAME}.
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/stack/upgrade/orchestrate/UpgradeContext.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/stack/upgrade/orchestrate/UpgradeContext.java
index d2e68bb..dc1cf8e 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/stack/upgrade/orchestrate/UpgradeContext.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/stack/upgrade/orchestrate/UpgradeContext.java
@@ -86,7 +86,9 @@ import org.apache.ambari.server.state.StackId;
 import org.apache.ambari.server.state.repository.ClusterVersionSummary;
 import org.apache.ambari.server.state.repository.VersionDefinitionXml;
 import org.apache.ambari.spi.RepositoryType;
+import org.apache.ambari.spi.RepositoryVersion;
 import org.apache.ambari.spi.upgrade.UpgradeCheckStatus;
+import org.apache.ambari.spi.upgrade.UpgradeInformation;
 import org.apache.ambari.spi.upgrade.UpgradeType;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang.StringUtils;
@@ -1457,4 +1459,44 @@ public class UpgradeContext {
 
     return packs.get(upgrade.getUpgradePackage());
   }
+
+  /**
+   * Builds a {@link UpgradeInformation} instance from a {@link Cluster} where
+   * there is an upgrade in progress.
+   *
+   * @return the {@link UpgradeInformation} instance comprised of simple POJOs
+   *         and SPI classes.
+   */
+  public UpgradeInformation buildUpgradeInformation() {
+    RepositoryVersionEntity targetRepositoryVersionEntity = 
m_repositoryVersion;
+
+    Map<String, Service> clusterServices = m_cluster.getServices();
+    Map<String, RepositoryVersion> clusterServiceVersions = new HashMap<>();
+    if (null != clusterServices) {
+      for (Map.Entry<String, Service> serviceEntry : 
clusterServices.entrySet()) {
+        Service service = serviceEntry.getValue();
+        RepositoryVersionEntity desiredRepositoryEntity = 
service.getDesiredRepositoryVersion();
+        RepositoryVersion desiredRepositoryVersion = 
desiredRepositoryEntity.getRepositoryVersion();
+
+        clusterServiceVersions.put(serviceEntry.getKey(), 
desiredRepositoryVersion);
+      }
+    }
+
+    Map<String, RepositoryVersionEntity> sourceVersionEntites = 
getSourceVersions();
+    Map<String, RepositoryVersionEntity> targetVersionEntites = 
getTargetVersions();
+    Map<String, RepositoryVersion> sourceVersions = new HashMap<>();
+    Map<String, RepositoryVersion> targetVersions = new HashMap<>();
+
+    sourceVersionEntites.forEach(
+        (service, repositoryVersion) -> sourceVersions.put(service, 
repositoryVersion.getRepositoryVersion()));
+
+    targetVersionEntites.forEach(
+        (service, repositoryVersion) -> targetVersions.put(service, 
repositoryVersion.getRepositoryVersion()));
+
+    UpgradeInformation upgradeInformation = new UpgradeInformation(
+        getDirection().isUpgrade(), getType(),
+        targetRepositoryVersionEntity.getRepositoryVersion(), sourceVersions, 
targetVersions);
+
+    return upgradeInformation;
+  }
 }
\ No newline at end of file
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
index 897a433..e23c571 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
@@ -2950,12 +2950,7 @@ public class ClusterImpl implements Cluster {
       for (Map.Entry<String, Service> serviceEntry : 
clusterServices.entrySet()) {
         Service service = serviceEntry.getValue();
         RepositoryVersionEntity desiredRepositoryEntity = 
service.getDesiredRepositoryVersion();
-        StackId stackId = desiredRepositoryEntity.getStackId();
-
-        RepositoryVersion desiredRepositoryVersion = new RepositoryVersion(
-            desiredRepositoryEntity.getId(), stackId.getStackName(), 
stackId.getStackVersion(),
-            stackId.getStackId(), desiredRepositoryEntity.getVersion(),
-            desiredRepositoryEntity.getType());
+        RepositoryVersion desiredRepositoryVersion = 
desiredRepositoryEntity.getRepositoryVersion();
 
         clusterServiceVersions.put(serviceEntry.getKey(), 
desiredRepositoryVersion);
       }
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/serveraction/upgrades/PluginUpgradeServerActionTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/serveraction/upgrades/PluginUpgradeServerActionTest.java
index 49b31af..cd88ff9 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/serveraction/upgrades/PluginUpgradeServerActionTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/serveraction/upgrades/PluginUpgradeServerActionTest.java
@@ -17,6 +17,7 @@
  */
 package org.apache.ambari.server.serveraction.upgrades;
 
+import static org.easymock.EasyMock.eq;
 import static org.easymock.EasyMock.expect;
 import static org.easymock.EasyMock.expectLastCall;
 
@@ -28,19 +29,26 @@ import java.util.Map;
 import org.apache.ambari.server.agent.ExecutionCommand;
 import org.apache.ambari.server.agent.stomp.AgentConfigsHolder;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
+import org.apache.ambari.server.controller.AmbariManagementController;
+import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
 import org.apache.ambari.server.serveraction.ServerAction;
+import org.apache.ambari.server.stack.upgrade.Direction;
 import org.apache.ambari.server.stack.upgrade.UpgradePack;
 import org.apache.ambari.server.stack.upgrade.orchestrate.UpgradeContext;
 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.StackId;
 import org.apache.ambari.server.state.StackInfo;
 import org.apache.ambari.spi.ClusterInformation;
+import org.apache.ambari.spi.RepositoryType;
+import org.apache.ambari.spi.RepositoryVersion;
 import org.apache.ambari.spi.exceptions.UpgradeActionException;
 import org.apache.ambari.spi.upgrade.UpgradeAction;
 import org.apache.ambari.spi.upgrade.UpgradeActionOperations;
 import 
org.apache.ambari.spi.upgrade.UpgradeActionOperations.ConfigurationChanges;
+import org.apache.ambari.spi.upgrade.UpgradeInformation;
 import org.easymock.EasyMockSupport;
 import org.junit.After;
 import org.junit.Before;
@@ -59,11 +67,12 @@ public class PluginUpgradeServerActionTest extends 
EasyMockSupport {
 
   private static final String CLUSTER_NAME = "c1";
   private static final String FOO_SITE = "foo-site";
+  private static final String AUTH_USERNAME = "admin";
   private static final String CLASS_NAME = MockUpgradeAction.class.getName();
 
   private final Map<String, String> m_commandParams = new HashMap<>();
 
-  private final StackId m_mockStackId = createNiceMock(StackId.class);
+  private final StackId m_stackId = new StackId("FOO-STACK-1.0");
   private final StackInfo m_mockStackInfo = createNiceMock(StackInfo.class);
 
   private final Clusters m_mockClusters = createNiceMock(Clusters.class);
@@ -74,9 +83,14 @@ public class PluginUpgradeServerActionTest extends 
EasyMockSupport {
   private final UpgradePack m_mockUpgradePack = 
createNiceMock(UpgradePack.class);
   private final ClassLoader m_mockClassLoader = 
createNiceMock(ClassLoader.class);
   private final AmbariMetaInfo m_mockMetaInfo = 
createNiceMock(AmbariMetaInfo.class);
+  private final AmbariManagementController m_mockController = 
createNiceMock(AmbariManagementController.class);
 
-  private final AgentConfigsHolder m_mockAgentConfigsHolder = createNiceMock(
-      AgentConfigsHolder.class);
+  private final RepositoryVersion m_repositoryVersion = new 
RepositoryVersion(1L, "FOO-STACK",
+      "1.0", "FOO-STACK-1.0", "1.0.0.0-b1", RepositoryType.STANDARD);
+
+  private final RepositoryVersionEntity m_mocRepoEntity = 
createNiceMock(RepositoryVersionEntity.class);
+  private final AgentConfigsHolder m_mockAgentConfigsHolder = 
createNiceMock(AgentConfigsHolder.class);
+  private final ConfigHelper m_mockConfigHelper = 
createNiceMock(ConfigHelper.class);
 
   private PluginUpgradeServerAction m_action;
 
@@ -84,7 +98,7 @@ public class PluginUpgradeServerActionTest extends 
EasyMockSupport {
    * @throws Exception
    */
   @Before
-  @SuppressWarnings("rawtypes")
+  @SuppressWarnings({ "rawtypes", "unchecked" })
   public void before() throws Exception {
     m_action = 
PowerMock.createNicePartialMock(PluginUpgradeServerAction.class, 
"getUpgradeContext",
         "createCommandReport", "getClusters");
@@ -98,10 +112,21 @@ public class PluginUpgradeServerActionTest extends 
EasyMockSupport {
 
     m_action.m_clusters = m_mockClusters;
     m_action.m_metainfoProvider = () -> m_mockMetaInfo;
+    m_action.m_amc = m_mockController;
+    m_action.m_configHelper = m_mockConfigHelper;
+
+    expect(m_mockController.getAuthName()).andReturn(AUTH_USERNAME).anyTimes();
+
+    expect(m_mocRepoEntity.getStackId()).andReturn(m_stackId).anyTimes();
+    expect(m_mocRepoEntity.getVersion()).andReturn("1.0.0.0-b1").anyTimes();
+    
expect(m_mocRepoEntity.getRepositoryVersion()).andReturn(m_repositoryVersion).anyTimes();
 
     
expect(m_mockUpgradeContext.getUpgradePack()).andReturn(m_mockUpgradePack).atLeastOnce();
-    
expect(m_mockUpgradePack.getOwnerStackId()).andReturn(m_mockStackId).atLeastOnce();
-    
expect(m_mockMetaInfo.getStack(m_mockStackId)).andReturn(m_mockStackInfo).atLeastOnce();
+    
expect(m_mockUpgradeContext.getDirection()).andReturn(Direction.UPGRADE).anyTimes();
+    
expect(m_mockUpgradeContext.getRepositoryVersion()).andReturn(m_mocRepoEntity).anyTimes();
+
+    
expect(m_mockUpgradePack.getOwnerStackId()).andReturn(m_stackId).atLeastOnce();
+    
expect(m_mockMetaInfo.getStack(m_stackId)).andReturn(m_mockStackInfo).atLeastOnce();
     
expect(m_mockStackInfo.getLibraryClassLoader()).andReturn(m_mockClassLoader).atLeastOnce();
 
     expect(m_action.getClusters()).andReturn(m_mockClusters).anyTimes();
@@ -110,17 +135,6 @@ public class PluginUpgradeServerActionTest extends 
EasyMockSupport {
     Class clazz = MockUpgradeAction.class;
     
expect(m_mockClassLoader.loadClass(CLASS_NAME)).andReturn(clazz).atLeastOnce();
 
-    
expect(m_mockCluster.getDesiredConfigByType(FOO_SITE)).andReturn(m_mockConfig).once();
-
-    Map<String, String> configUpdates = new HashMap<>();
-    configUpdates.put("property-name", "property-value");
-
-    m_mockConfig.updateProperties(configUpdates);
-    expectLastCall().once();
-
-    m_mockConfig.save();
-    expectLastCall().once();
-
     m_action.agentConfigsHolder = m_mockAgentConfigsHolder;
 
     m_commandParams.put("clusterName", CLUSTER_NAME);
@@ -144,6 +158,53 @@ public class PluginUpgradeServerActionTest extends 
EasyMockSupport {
    */
   @Test
   public void testExecute() throws Exception {
+    // mock out the config stuff
+    
expect(m_mockCluster.getDesiredConfigByType(FOO_SITE)).andReturn(m_mockConfig).once();
+
+    Map<String, String> configUpdates = new HashMap<>();
+    configUpdates.put("property-name", "property-value");
+
+    m_mockConfig.updateProperties(configUpdates);
+    expectLastCall().once();
+
+    m_mockConfig.save();
+    expectLastCall().once();
+
+    PowerMock.replay(m_action);
+    replayAll();
+
+    m_action.execute(null);
+
+    // easymock verify
+    verifyAll();
+  }
+
+  /**
+   * Tests that when a new configuration type is specified in the list of
+   * configurattion changes, that the new type is created first.
+   *
+   * @throws Exception
+   */
+  @Test
+  public void testExecuteAddNewConfiguration() throws Exception {
+    // mock two different answers
+    
expect(m_mockCluster.getDesiredConfigByType(FOO_SITE)).andReturn(null).once();
+    
expect(m_mockCluster.getDesiredConfigByType(FOO_SITE)).andReturn(m_mockConfig).once();
+
+    m_mockConfigHelper.createConfigType(eq(m_mockCluster), eq(m_stackId), 
eq(m_mockController),
+        eq(FOO_SITE), eq(new HashMap<>()), eq(AUTH_USERNAME), eq("Upgrade to 
1.0.0.0-b1"));
+
+    expectLastCall();
+
+    Map<String, String> configUpdates = new HashMap<>();
+    configUpdates.put("property-name", "property-value");
+
+    m_mockConfig.updateProperties(configUpdates);
+    expectLastCall().once();
+
+    m_mockConfig.save();
+    expectLastCall().once();
+
     PowerMock.replay(m_action);
     replayAll();
 
@@ -162,18 +223,18 @@ public class PluginUpgradeServerActionTest extends 
EasyMockSupport {
      * {@inheritDoc}
      */
     @Override
-    public UpgradeActionOperations getOperations(ClusterInformation 
clusterInformation)
-        throws UpgradeActionException {
+    public UpgradeActionOperations getOperations(ClusterInformation 
clusterInformation,
+        UpgradeInformation upgradeInformation) throws UpgradeActionException {
 
       List<ConfigurationChanges> allChanges = new ArrayList<>();
       ConfigurationChanges configurationTypeChanges = new 
ConfigurationChanges(FOO_SITE);
       configurationTypeChanges.set( "property-name", "property-value");
       allChanges.add(configurationTypeChanges);
 
-      StringBuilder standardOutput = new StringBuilder("Standard Output");
-
       UpgradeActionOperations upgradeActionOperations = new 
UpgradeActionOperations();
-      
upgradeActionOperations.setConfigurationChanges(allChanges).setStandardOutput(standardOutput);
+      upgradeActionOperations
+        .setConfigurationChanges(allChanges)
+        .setStandardOutput("Standard Output");
 
       return upgradeActionOperations;
     }

Reply via email to