You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by jo...@apache.org on 2015/04/23 04:32:18 UTC

ambari git commit: AMBARI-10673 - RU Finalization Should Set The Correct Stack Versions On Upgrade (jonathanhurley)

Repository: ambari
Updated Branches:
  refs/heads/trunk dc92b6d6b -> cc76c39dd


AMBARI-10673 - RU Finalization Should Set The Correct Stack Versions On Upgrade (jonathanhurley)


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

Branch: refs/heads/trunk
Commit: cc76c39dd0b1ecc8ffbf7220e8ff1986a99525e6
Parents: dc92b6d
Author: Jonathan Hurley <jh...@hortonworks.com>
Authored: Wed Apr 22 17:59:01 2015 -0400
Committer: Jonathan Hurley <jh...@hortonworks.com>
Committed: Wed Apr 22 22:25:37 2015 -0400

----------------------------------------------------------------------
 .../ServiceConfigVersionResponse.java           |  14 ++
 .../HostStackVersionResourceProvider.java       |   4 +-
 .../ServiceConfigVersionResourceProvider.java   |   6 +-
 .../upgrade/HostVersionOutOfSyncListener.java   |  37 ++--
 .../listeners/upgrade/StackVersionListener.java |  10 +-
 .../upgrades/FinalizeUpgradeAction.java         |  83 ++++---
 .../org/apache/ambari/server/state/Cluster.java |   7 -
 .../apache/ambari/server/state/ConfigImpl.java  |  11 +-
 .../server/state/cluster/ClusterImpl.java       | 220 +++++++++++--------
 .../upgrade/StackVersionListenerTest.java       |  22 +-
 .../upgrades/UpgradeActionTest.java             | 217 +++++++++++++-----
 .../server/state/cluster/ClusterTest.java       |  32 +--
 12 files changed, 426 insertions(+), 237 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/cc76c39d/ambari-server/src/main/java/org/apache/ambari/server/controller/ServiceConfigVersionResponse.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/ServiceConfigVersionResponse.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/ServiceConfigVersionResponse.java
index 486d349..9a07a29 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/ServiceConfigVersionResponse.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/ServiceConfigVersionResponse.java
@@ -24,6 +24,7 @@ import java.util.List;
 import org.apache.ambari.server.orm.entities.ClusterEntity;
 import org.apache.ambari.server.orm.entities.ServiceConfigEntity;
 import org.apache.ambari.server.orm.entities.StackEntity;
+import org.apache.ambari.server.state.StackId;
 import org.codehaus.jackson.annotate.JsonProperty;
 import org.codehaus.jackson.map.annotate.JsonSerialize;
 
@@ -56,6 +57,9 @@ public class ServiceConfigVersionResponse {
   @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
   private final String note;
 
+  @JsonProperty("stack_id")
+  private String stackId;
+
   @JsonProperty("is_current")
   private Boolean isCurrent = Boolean.FALSE;
 
@@ -97,6 +101,7 @@ public class ServiceConfigVersionResponse {
     StackEntity clusterStackEntity = clusterEntity.getClusterStateEntity().getCurrentStack();
 
     isCompatibleWithCurrentStack = clusterStackEntity.equals(serviceConfigStackEntity);
+    stackId = new StackId(serviceConfigStackEntity).getStackId();
   }
 
   public String getServiceName() {
@@ -142,6 +147,15 @@ public class ServiceConfigVersionResponse {
     return groupId;
   }
 
+  /**
+   * Gets the Stack ID that this configuration is scoped for.
+   *
+   * @return
+   */
+  public String getStackId() {
+    return stackId;
+  }
+
   public Boolean getIsCurrent() {
     return isCurrent;
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/cc76c39d/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostStackVersionResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostStackVersionResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostStackVersionResourceProvider.java
index 88b9415..9e1c73a 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostStackVersionResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostStackVersionResourceProvider.java
@@ -434,7 +434,9 @@ public class HostStackVersionResourceProvider extends AbstractControllerResource
     try {
       hostVersEntity.setState(RepositoryVersionState.INSTALLING);
       hostVersionDAO.merge(hostVersEntity);
-      cluster.recalculateClusterVersionState(desiredRepoVersion);
+
+      StackId desiredStackId = cluster.getDesiredStackVersion();
+      cluster.recalculateClusterVersionState(desiredStackId, desiredRepoVersion);
       req.persist();
     } catch (AmbariException e) {
       throw new SystemException("Can not persist request", e);

http://git-wip-us.apache.org/repos/asf/ambari/blob/cc76c39d/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ServiceConfigVersionResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ServiceConfigVersionResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ServiceConfigVersionResourceProvider.java
index aa4087d..52113ff 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ServiceConfigVersionResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ServiceConfigVersionResourceProvider.java
@@ -56,6 +56,7 @@ public class ServiceConfigVersionResourceProvider extends
   public static final String SERVICE_CONFIG_VERSION_NOTE_PROPERTY_ID = PropertyHelper.getPropertyId(null, "service_config_version_note");
   public static final String SERVICE_CONFIG_VERSION_GROUP_ID_PROPERTY_ID = PropertyHelper.getPropertyId(null, "group_id");
   public static final String SERVICE_CONFIG_VERSION_GROUP_NAME_PROPERTY_ID = PropertyHelper.getPropertyId(null, "group_name");
+  public static final String SERVICE_CONFIG_VERSION_STACK_ID_PROPERTY_ID = PropertyHelper.getPropertyId(null, "stack_id");
   public static final String SERVICE_CONFIG_VERSION_IS_CURRENT_PROPERTY_ID = PropertyHelper.getPropertyId(null, "is_current");
   public static final String SERVICE_CONFIG_VERSION_IS_COMPATIBLE_PROPERTY_ID = PropertyHelper.getPropertyId(null, "is_cluster_compatible");
   public static final String SERVICE_CONFIG_VERSION_HOSTS_PROPERTY_ID = PropertyHelper.getPropertyId(null, "hosts");
@@ -81,6 +82,7 @@ public class ServiceConfigVersionResourceProvider extends
     PROPERTY_IDS.add(SERVICE_CONFIG_VERSION_NOTE_PROPERTY_ID);
     PROPERTY_IDS.add(SERVICE_CONFIG_VERSION_GROUP_ID_PROPERTY_ID);
     PROPERTY_IDS.add(SERVICE_CONFIG_VERSION_GROUP_NAME_PROPERTY_ID);
+    PROPERTY_IDS.add(SERVICE_CONFIG_VERSION_STACK_ID_PROPERTY_ID);
     PROPERTY_IDS.add(SERVICE_CONFIG_VERSION_IS_CURRENT_PROPERTY_ID);
     PROPERTY_IDS.add(SERVICE_CONFIG_VERSION_HOSTS_PROPERTY_ID);
     PROPERTY_IDS.add(SERVICE_CONFIG_VERSION_CONFIGURATIONS_PROPERTY_ID);
@@ -155,6 +157,7 @@ public class ServiceConfigVersionResourceProvider extends
       resource.setProperty(SERVICE_CONFIG_VERSION_GROUP_ID_PROPERTY_ID, response.getGroupId());
       resource.setProperty(SERVICE_CONFIG_VERSION_GROUP_NAME_PROPERTY_ID, response.getGroupName());
       resource.setProperty(SERVICE_CONFIG_VERSION_HOSTS_PROPERTY_ID, response.getHosts());
+      resource.setProperty(SERVICE_CONFIG_VERSION_STACK_ID_PROPERTY_ID, response.getStackId());
       resource.setProperty(SERVICE_CONFIG_VERSION_IS_CURRENT_PROPERTY_ID, response.getIsCurrent());
       resource.setProperty(SERVICE_CONFIG_VERSION_IS_COMPATIBLE_PROPERTY_ID, response.isCompatibleWithCurrentStack());
 
@@ -183,14 +186,15 @@ public class ServiceConfigVersionResourceProvider extends
     Set<String> unsupportedProperties = new HashSet<String>();
 
     for (String propertyId : propertyIds) {
-
       if (!propertyId.equals("cluster_name") && !propertyId.equals("service_config_version") &&
           !propertyId.equals("service_name") && !propertyId.equals("createtime") &&
           !propertyId.equals("appliedtime") && !propertyId.equals("user") &&
           !propertyId.equals("service_config_version_note") &&
           !propertyId.equals("group_id") &&
           !propertyId.equals("group_name") &&
+          !propertyId.equals("stack_id") &&
           !propertyId.equals("is_current") &&
+          !propertyId.equals("is_cluster_compatible") &&
           !propertyId.equals("hosts")) {
 
         unsupportedProperties.add(propertyId);

http://git-wip-us.apache.org/repos/asf/ambari/blob/cc76c39d/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/HostVersionOutOfSyncListener.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/HostVersionOutOfSyncListener.java b/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/HostVersionOutOfSyncListener.java
index 7a8c4b9..8c8f978 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/HostVersionOutOfSyncListener.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/HostVersionOutOfSyncListener.java
@@ -17,11 +17,12 @@
  */
 package org.apache.ambari.server.events.listeners.upgrade;
 
-import com.google.common.eventbus.Subscribe;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.Singleton;
-import com.google.inject.persist.Transactional;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.EagerSingleton;
 import org.apache.ambari.server.events.HostAddedEvent;
@@ -41,11 +42,11 @@ import org.apache.ambari.server.state.StackId;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import com.google.common.eventbus.Subscribe;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+import com.google.inject.persist.Transactional;
 
 /**
  * The {@link org.apache.ambari.server.events.listeners.upgrade.HostVersionOutOfSyncListener} class
@@ -72,11 +73,8 @@ public class HostVersionOutOfSyncListener {
   @Inject
   private Provider<Clusters> clusters;
 
-  private AmbariEventPublisher ambariEventPublisher;
-
   @Inject
   public HostVersionOutOfSyncListener(AmbariEventPublisher ambariEventPublisher) {
-    this.ambariEventPublisher = ambariEventPublisher;
     ambariEventPublisher.register(this);
   }
 
@@ -86,17 +84,20 @@ public class HostVersionOutOfSyncListener {
     if (LOG.isDebugEnabled()) {
       LOG.debug(event.toString());
     }
+
     try {
       Cluster cluster = clusters.get().getClusterById(event.getClusterId());
       List<HostVersionEntity> hostVersionEntities =
           hostVersionDAO.get().findByClusterAndHost(cluster.getClusterName(), event.getHostName());
+
       StackId currentStackId = cluster.getCurrentStackVersion();
       for (HostVersionEntity hostVersionEntity : hostVersionEntities) {
         if (hostVersionEntity.getRepositoryVersion().getStack().equals(currentStackId.getStackId())
             && hostVersionEntity.getState().equals(RepositoryVersionState.INSTALLED)) {
           hostVersionEntity.setState(RepositoryVersionState.OUT_OF_SYNC);
           hostVersionDAO.get().merge(hostVersionEntity);
-          cluster.recalculateClusterVersionState(hostVersionEntity.getRepositoryVersion().getVersion());
+          cluster.recalculateClusterVersionState(currentStackId,
+              hostVersionEntity.getRepositoryVersion().getVersion());
         }
       }
     } catch (AmbariException e) {
@@ -110,6 +111,7 @@ public class HostVersionOutOfSyncListener {
     if (LOG.isDebugEnabled()) {
       LOG.debug(event.toString());
     }
+
     try {
       Cluster cluster = clusters.get().getClusterById(event.getClusterId());
       Set<String> changedRepositoryVersions = new HashSet<String>();
@@ -135,7 +137,7 @@ public class HostVersionOutOfSyncListener {
         }
       }
       for (String version : changedRepositoryVersions) {
-        cluster.recalculateClusterVersionState(version);
+        cluster.recalculateClusterVersionState(currentStackId, version);
       }
     } catch (AmbariException e) {
       LOG.error("Can not update hosts about out of sync", e);
@@ -148,8 +150,11 @@ public class HostVersionOutOfSyncListener {
     if (LOG.isDebugEnabled()) {
       LOG.debug(event.toString());
     }
+
     try {
       Cluster cluster = clusters.get().getClusterById(event.getClusterId());
+      StackId currentStackId = cluster.getCurrentStackVersion();
+
       Set<String> changedRepositoryVersions = new HashSet<String>();
       Collection<ClusterVersionEntity> allClusterVersions = cluster.getAllClusterVersions();
       for (ClusterVersionEntity clusterVersion : allClusterVersions) {
@@ -163,7 +168,7 @@ public class HostVersionOutOfSyncListener {
         }
       }
       for (String version : changedRepositoryVersions) {
-        cluster.recalculateClusterVersionState(version);
+        cluster.recalculateClusterVersionState(currentStackId, version);
       }
     } catch (AmbariException e) {
       LOG.error("Can not update hosts about out of sync", e);

http://git-wip-us.apache.org/repos/asf/ambari/blob/cc76c39d/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/StackVersionListener.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/StackVersionListener.java b/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/StackVersionListener.java
index b09a273..8a71b40 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/StackVersionListener.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/events/listeners/upgrade/StackVersionListener.java
@@ -25,6 +25,7 @@ import org.apache.ambari.server.events.HostComponentVersionEvent;
 import org.apache.ambari.server.events.publishers.VersionEventPublisher;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.ServiceComponentHost;
+import org.apache.ambari.server.state.StackId;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -71,16 +72,19 @@ public class StackVersionListener {
     LOG.debug("Received event {}", event);
 
     Cluster cluster = event.getCluster();
+    StackId desiredStackId = cluster.getDesiredStackVersion();
+
     ServiceComponentHost sch = event.getServiceComponentHost();
 
     m_stackVersionLock.lock();
 
     try {
       String repoVersion = sch.recalculateHostVersionState();
-      cluster.recalculateClusterVersionState(repoVersion);
+      cluster.recalculateClusterVersionState(desiredStackId, repoVersion);
     } catch (Exception e) {
-      LOG.error("Unable to propagate version for ServiceHostComponent on component: " + sch.getServiceComponentName() +
-          ", host: " + sch.getHostName() + ". Error: " + e.getMessage());
+      LOG.error(
+          "Unable to propagate version for ServiceHostComponent on component: {}, host: {}. Error: {}",
+          sch.getServiceComponentName(), sch.getHostName(), e.getMessage());
     } finally {
       m_stackVersionLock.unlock();
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/cc76c39d/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/FinalizeUpgradeAction.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/FinalizeUpgradeAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/FinalizeUpgradeAction.java
index 472953c..d97ad61 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/FinalizeUpgradeAction.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/FinalizeUpgradeAction.java
@@ -68,7 +68,7 @@ public class FinalizeUpgradeAction extends AbstractServerAction {
    * The Cluster that this ServerAction implementation is executing on
    */
   @Inject
-  private Clusters clusters = null;
+  private Clusters clusters;
 
   @Inject
   private ClusterVersionDAO clusterVersionDAO;
@@ -111,9 +111,6 @@ public class FinalizeUpgradeAction extends AbstractServerAction {
   private CommandReport executeUpgrade(String clusterName, String version)
     throws AmbariException, InterruptedException {
 
-    Set<RepositoryVersionState> allowedStates = new HashSet<RepositoryVersionState>();
-    allowedStates.add(RepositoryVersionState.UPGRADED);
-
     StringBuilder outSB = new StringBuilder();
     StringBuilder errSB = new StringBuilder();
 
@@ -121,22 +118,25 @@ public class FinalizeUpgradeAction extends AbstractServerAction {
       outSB.append(MessageFormat.format("Begin finalizing the upgrade of cluster {0} to version {1}\n", clusterName, version));
 
       Cluster cluster = clusters.getCluster(clusterName);
+      StackId clusterDesiredStackId = cluster.getDesiredStackVersion();
 
-      StackId stackId = cluster.getCurrentStackVersion();
-      ClusterVersionEntity upgradingClusterVersion = clusterVersionDAO.findByClusterAndStackAndVersion(clusterName,
-          stackId, version);
+      ClusterVersionEntity upgradingClusterVersion = clusterVersionDAO.findByClusterAndStackAndVersion(
+          clusterName, clusterDesiredStackId, version);
 
       if (upgradingClusterVersion == null) {
-        throw new AmbariException(String.format("Cluster stack version %s not found", version));
+        throw new AmbariException(MessageFormat.format(
+            "Cluster stack version {0} not found", version));
       }
 
       // Validate that all of the hosts with a version in the cluster have the version being upgraded to, and it is in an allowed state.
-      List<HostVersionEntity> hostVersions = hostVersionDAO.findByClusterStackAndVersion(clusterName, stackId, version);
+      List<HostVersionEntity> hostVersions = hostVersionDAO.findByClusterStackAndVersion(
+          clusterName, clusterDesiredStackId, version);
 
       // Will include hosts whose state is UPGRADED, and potentially INSTALLED
       Set<HostVersionEntity> hostVersionsAllowed = new HashSet<HostVersionEntity>();
       Set<String> hostsWithoutCorrectVersionState = new HashSet<String>();
       Set<String> hostsToUpdate = new HashSet<String>();
+
       // If true, then the cluster version is still in UPGRADING and allowed to transition to UPGRADED, and then CURRENT
       boolean atLeastOneHostInInstalledState = false;
 
@@ -145,15 +145,17 @@ public class FinalizeUpgradeAction extends AbstractServerAction {
       for (HostVersionEntity hostVersion : hostVersions) {
         boolean isStateCorrect = false;
 
-        if (allowedStates.contains(hostVersion.getState())) {
+        if (RepositoryVersionState.UPGRADED == hostVersion.getState()) {
           isStateCorrect = true;
         } else {
           if (hostVersion.getState() == RepositoryVersionState.INSTALLED) {
             // It is possible that the host version has a state of INSTALLED and it never changed if the host only has
             // components that do not advertise a version.
             HostEntity host = hostVersion.getHostEntity();
+
             ServiceComponentHostSummary hostSummary = new ServiceComponentHostSummary(
-                ambariMetaInfo, host, stackId);
+                ambariMetaInfo, host, clusterDesiredStackId);
+
             if (hostSummary.haveAllComponentsFinishedAdvertisingVersion()){
               isStateCorrect = true;
               atLeastOneHostInInstalledState = true;
@@ -179,17 +181,18 @@ public class FinalizeUpgradeAction extends AbstractServerAction {
         throw new AmbariException(message);
       }
 
-      checkHostComponentVesions(cluster, version, stackId);
+      checkHostComponentVersions(cluster, version, clusterDesiredStackId);
 
       // May need to first transition to UPGRADED
       if (atLeastOneHostInInstalledState) {
-        cluster.transitionClusterVersion(stackId, version,
+        cluster.transitionClusterVersion(clusterDesiredStackId, version,
             RepositoryVersionState.UPGRADED);
-        upgradingClusterVersion = clusterVersionDAO.findByClusterAndStackAndVersion(clusterName,
-            stackId, version);
+
+        upgradingClusterVersion = clusterVersionDAO.findByClusterAndStackAndVersion(
+            clusterName, clusterDesiredStackId, version);
       }
 
-      if (!allowedStates.contains(upgradingClusterVersion.getState())) {
+      if (RepositoryVersionState.UPGRADED != upgradingClusterVersion.getState()) {
         throw new AmbariException(String.format("The cluster stack version state %s is not allowed to transition directly into %s",
             upgradingClusterVersion.getState(), RepositoryVersionState.CURRENT.toString()));
       }
@@ -211,7 +214,10 @@ public class FinalizeUpgradeAction extends AbstractServerAction {
       cluster.mapHostVersions(hostsToUpdate, upgradingClusterVersion, RepositoryVersionState.CURRENT);
 
       outSB.append(String.format("Will finalize the version for cluster %s.\n", clusterName));
-      cluster.transitionClusterVersion(stackId, version,
+
+      // transitioning the cluster into CURRENT will update the current/desired
+      // stack values
+      cluster.transitionClusterVersion(clusterDesiredStackId, version,
           RepositoryVersionState.CURRENT);
 
       outSB.append("Upgrade was successful!\n");
@@ -236,7 +242,8 @@ public class FinalizeUpgradeAction extends AbstractServerAction {
 
     try {
       Cluster cluster = clusters.getCluster(clusterName);
-      StackId stackId = cluster.getDesiredStackVersion();
+      StackId desiredClusterStackId = cluster.getDesiredStackVersion();
+      StackId currentClusterStackId = cluster.getCurrentStackVersion();
 
       // !!! find and make sure the cluster_version EXCEPT current are set back
       out.append(String.format("Searching for current version for %s\n", clusterName));
@@ -259,6 +266,7 @@ public class FinalizeUpgradeAction extends AbstractServerAction {
       }
 
       Set<String> badVersions = new HashSet<String>();
+
       // update the cluster version
       for (ClusterVersionEntity cve : clusterVersionDAO.findByCluster(clusterName)) {
         switch (cve.getState()) {
@@ -274,13 +282,14 @@ public class FinalizeUpgradeAction extends AbstractServerAction {
             break;
         }
       }
+
       out.append(String.format("Found %d other version(s) not matching downgrade: %s\n",
           badVersions.size(), StringUtils.join(badVersions, ", ")));
 
       Set<String> badHosts = new HashSet<String>();
       for (String badVersion : badVersions) {
         List<HostVersionEntity> hostVersions = hostVersionDAO.findByClusterStackAndVersion(
-            clusterName, stackId, badVersion);
+            clusterName, desiredClusterStackId, badVersion);
 
         for (HostVersionEntity hostVersion : hostVersions) {
           badHosts.add(hostVersion.getHostName());
@@ -300,6 +309,10 @@ public class FinalizeUpgradeAction extends AbstractServerAction {
         }
       }
 
+      // ensure that when downgrading, we set the desired back to the
+      // original value
+      cluster.setDesiredStackVersion(currentClusterStackId);
+
       return createCommandReport(0, HostRoleStatus.COMPLETED, "{}",
           out.toString(), err.toString());
 
@@ -321,26 +334,30 @@ public class FinalizeUpgradeAction extends AbstractServerAction {
    * @param desiredVersion     the target version of the upgrade
    * @throws AmbariException if any host component has not been updated yet
    */
-  private void checkHostComponentVesions(Cluster cluster, String desiredVersion, StackId stackId)
+  private void checkHostComponentVersions(Cluster cluster, String desiredVersion, StackId targetStackId)
           throws AmbariException {
+
     class InfoTuple {
+      public final String serviceName;
+      public final String componentName;
+      public final String hostName;
+
       public InfoTuple(String serviceName, String componentName, String hostName) {
         this.serviceName = serviceName;
         this.componentName = componentName;
         this.hostName = hostName;
       }
-
-      public final String serviceName;
-      public final String componentName;
-      public final String hostName;
     }
+
     ArrayList<InfoTuple> errors = new ArrayList<InfoTuple>();
     for (Service service : cluster.getServices().values()) {
       for (ServiceComponent serviceComponent : service.getServiceComponents().values()) {
         for (ServiceComponentHost serviceComponentHost : serviceComponent.getServiceComponentHosts().values()) {
-          ComponentInfo componentInfo = ambariMetaInfo.getComponent(stackId.getStackName(),
-                  stackId.getStackVersion(), service.getName(), serviceComponent.getName());
-          if (componentInfo.isVersionAdvertised() && ! serviceComponentHost.getVersion().equals(desiredVersion)) {
+          ComponentInfo componentInfo = ambariMetaInfo.getComponent(targetStackId.getStackName(),
+                  targetStackId.getStackVersion(), service.getName(), serviceComponent.getName());
+
+          if (componentInfo.isVersionAdvertised()
+              && !serviceComponentHost.getVersion().equals(desiredVersion)) {
             errors.add(new InfoTuple(
                     service.getName(), serviceComponent.getName(), serviceComponentHost.getHostName()));
           }
@@ -349,16 +366,18 @@ public class FinalizeUpgradeAction extends AbstractServerAction {
     }
 
     if (! errors.isEmpty()) {
-      StrBuilder messageBuff = new StrBuilder(String.format("The following %d host component(s) " +
-              "have not been upgraded to version %s. Please install and upgrade " +
-              "the Stack Version on those hosts and try again.\nHost components:\n",
+      StrBuilder messageBuff = new StrBuilder(
+          String.format(
+              "The following %d host component(s) "
+                  + "have not been upgraded to version %s. Please install and upgrade "
+                  + "the Stack Version on those hosts and try again.\nHost components:\n",
               errors.size(), desiredVersion));
+
       for (InfoTuple error : errors) {
         messageBuff.append(String.format("%s on host %s\n", error.componentName, error.hostName));
       }
+
       throw new AmbariException(messageBuff.toString());
     }
-
   }
-
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/cc76c39d/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java b/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
index 847e349..64fe765 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java
@@ -166,13 +166,6 @@ public interface Cluster {
   public HostVersionEntity transitionHostVersionState(HostEntity host, final RepositoryVersionEntity repositoryVersion, final StackId stack) throws AmbariException;
 
   /**
-   * Update state of a cluster stack version for cluster based on states of host versions.
-   * @param repositoryVersion repository version (e.g. 2.2.1.0-100)
-   * @throws AmbariException
-   */
-  void recalculateClusterVersionState(String repositoryVersion) throws AmbariException;
-
-  /**
    * Update state of a cluster stack version for cluster based on states of host versions and stackids.
    * @param stackId           the stack id with the version
    * @param repositoryVersion the repository version (e.g. 2.2.1.0-100)

http://git-wip-us.apache.org/repos/asf/ambari/blob/cc76c39d/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigImpl.java
index ff11e70..4a62ff6 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigImpl.java
@@ -60,10 +60,14 @@ public class ConfigImpl implements Config {
   public ConfigImpl(@Assisted Cluster cluster, @Assisted String type, @Assisted Map<String, String> properties,
       @Assisted Map<String, Map<String, String>> propertiesAttributes, Injector injector) {
     this.cluster = cluster;
-    stackId = cluster.getCurrentStackVersion();
     this.type = type;
     this.properties = properties;
     this.propertiesAttributes = propertiesAttributes;
+
+    // when creating a brand new config without a backing entity, use the
+    // cluster's desired stack as the config's stack
+    stackId = cluster.getDesiredStackVersion();
+
     injector.injectMembers(this);
 
   }
@@ -71,10 +75,13 @@ public class ConfigImpl implements Config {
   @AssistedInject
   public ConfigImpl(@Assisted Cluster cluster, @Assisted ClusterConfigEntity entity, Injector injector) {
     this.cluster = cluster;
-    stackId = cluster.getCurrentStackVersion();
     type = entity.getType();
     tag = entity.getTag();
     version = entity.getVersion();
+
+    // when using an existing entity, use the actual value of the entity's stack
+    stackId = new StackId(entity.getStack());
+
     this.entity = entity;
     injector.injectMembers(this);
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/cc76c39d/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java
----------------------------------------------------------------------
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 6055eb8..f62e3f3 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
@@ -18,6 +18,7 @@
 
 package org.apache.ambari.server.state.cluster;
 
+import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -127,10 +128,8 @@ import com.google.inject.persist.Transactional;
 
 public class ClusterImpl implements Cluster {
 
-  private static final Logger LOG =
-    LoggerFactory.getLogger(ClusterImpl.class);
-  private static final Logger configChangeLog =
-    LoggerFactory.getLogger("configchange");
+  private static final Logger LOG = LoggerFactory.getLogger(ClusterImpl.class);
+  private static final Logger configChangeLog = LoggerFactory.getLogger("configchange");
 
   /**
    * Prefix for cluster session attributes name.
@@ -1200,11 +1199,9 @@ public class ClusterImpl implements Cluster {
     return RepositoryVersionState.OUT_OF_SYNC;
   }
 
-  @Override
-  public void recalculateClusterVersionState(String repositoryVersion) throws AmbariException {
-    recalculateClusterVersionState(null, repositoryVersion);
-  }
-
+  /**
+   * {@inheritDoc}
+   */
   @Override
   public void recalculateClusterVersionState(StackId stackId, String repositoryVersion) throws AmbariException {
     if (repositoryVersion == null) {
@@ -1212,12 +1209,8 @@ public class ClusterImpl implements Cluster {
     }
 
     Map<String, Host> hosts = clusters.getHostsForCluster(getClusterName());
-
-    if (null == stackId) {
-      stackId = getCurrentStackVersion();
-    }
-
     clusterGlobalLock.writeLock().lock();
+
     try {
       // Part 1, bootstrap cluster version if necessary.
 
@@ -1461,7 +1454,15 @@ public class ClusterImpl implements Cluster {
   }
 
   /**
-   * Transition an existing cluster version from one state to another.
+   * Transition an existing cluster version from one state to another. The
+   * following are some of the steps that are taken when transitioning between
+   * specific states:
+   * <ul>
+   * <li>UPGRADING/UPGRADED --> CURRENT</lki>: Set the current stack to the
+   * desired stack, ensure all hosts with the desired stack are CURRENT as well.
+   * </ul>
+   * <li>UPGRADING/UPGRADED --> CURRENT</lki>: Set the current stack to the
+   * desired stack. </ul>
    *
    * @param stackId
    *          Stack ID
@@ -1480,6 +1481,7 @@ public class ClusterImpl implements Cluster {
     try {
       ClusterVersionEntity existingClusterVersion = clusterVersionDAO.findByClusterAndStackAndVersion(
           getClusterName(), stackId, version);
+
       if (existingClusterVersion == null) {
         throw new AmbariException(
             "Existing cluster version not found for cluster="
@@ -1487,99 +1489,127 @@ public class ClusterImpl implements Cluster {
                 + version);
       }
 
-      if (existingClusterVersion.getState() != state) {
-        switch (existingClusterVersion.getState()) {
-          case CURRENT:
-            // If CURRENT state is changed here cluster will not have CURRENT
-            // state.
-            // CURRENT state will be changed to INSTALLED when another CURRENT
-            // state is added.
-            // allowedStates.add(RepositoryVersionState.INSTALLED);
-            break;
-          case INSTALLING:
-            allowedStates.add(RepositoryVersionState.INSTALLED);
-            allowedStates.add(RepositoryVersionState.INSTALL_FAILED);
-            allowedStates.add(RepositoryVersionState.OUT_OF_SYNC);
-            break;
-          case INSTALL_FAILED:
-            allowedStates.add(RepositoryVersionState.INSTALLING);
-            break;
-          case INSTALLED:
-            allowedStates.add(RepositoryVersionState.INSTALLING);
-            allowedStates.add(RepositoryVersionState.UPGRADING);
-            allowedStates.add(RepositoryVersionState.OUT_OF_SYNC);
-            break;
-          case OUT_OF_SYNC:
-            allowedStates.add(RepositoryVersionState.INSTALLING);
-            break;
-          case UPGRADING:
-            allowedStates.add(RepositoryVersionState.UPGRADED);
-            allowedStates.add(RepositoryVersionState.UPGRADE_FAILED);
-            if (clusterVersionDAO.findByClusterAndStateCurrent(getClusterName()) == null) {
-              allowedStates.add(RepositoryVersionState.CURRENT);
-            }
-            break;
-          case UPGRADED:
+      // NOOP
+      if (existingClusterVersion.getState() == state) {
+        return;
+      }
+
+      switch (existingClusterVersion.getState()) {
+        case CURRENT:
+          // If CURRENT state is changed here cluster will not have CURRENT
+          // state.
+          // CURRENT state will be changed to INSTALLED when another CURRENT
+          // state is added.
+          // allowedStates.add(RepositoryVersionState.INSTALLED);
+          break;
+        case INSTALLING:
+          allowedStates.add(RepositoryVersionState.INSTALLED);
+          allowedStates.add(RepositoryVersionState.INSTALL_FAILED);
+          allowedStates.add(RepositoryVersionState.OUT_OF_SYNC);
+          break;
+        case INSTALL_FAILED:
+          allowedStates.add(RepositoryVersionState.INSTALLING);
+          break;
+        case INSTALLED:
+          allowedStates.add(RepositoryVersionState.INSTALLING);
+          allowedStates.add(RepositoryVersionState.UPGRADING);
+          allowedStates.add(RepositoryVersionState.OUT_OF_SYNC);
+          break;
+        case OUT_OF_SYNC:
+          allowedStates.add(RepositoryVersionState.INSTALLING);
+          break;
+        case UPGRADING:
+          allowedStates.add(RepositoryVersionState.UPGRADED);
+          allowedStates.add(RepositoryVersionState.UPGRADE_FAILED);
+          if (clusterVersionDAO.findByClusterAndStateCurrent(getClusterName()) == null) {
             allowedStates.add(RepositoryVersionState.CURRENT);
-            break;
-          case UPGRADE_FAILED:
-            allowedStates.add(RepositoryVersionState.UPGRADING);
-            break;
-        }
+          }
+          break;
+        case UPGRADED:
+          allowedStates.add(RepositoryVersionState.CURRENT);
+          break;
+        case UPGRADE_FAILED:
+          allowedStates.add(RepositoryVersionState.UPGRADING);
+          break;
+      }
 
-        if (!allowedStates.contains(state)) {
-          throw new AmbariException("Invalid cluster version transition from "
-              + existingClusterVersion.getState() + " to " + state);
-        }
+      if (!allowedStates.contains(state)) {
+        throw new AmbariException("Invalid cluster version transition from "
+            + existingClusterVersion.getState() + " to " + state);
+      }
 
-        // There must be at most one cluster version whose state is CURRENT at
-        // all times.
-        if (state == RepositoryVersionState.CURRENT) {
-          ClusterVersionEntity currentVersion = clusterVersionDAO.findByClusterAndStateCurrent(getClusterName());
-          if (currentVersion != null) {
-            currentVersion.setState(RepositoryVersionState.INSTALLED);
-            clusterVersionDAO.merge(currentVersion);
-          }
+      // There must be at most one cluster version whose state is CURRENT at
+      // all times.
+      if (state == RepositoryVersionState.CURRENT) {
+        ClusterVersionEntity currentVersion = clusterVersionDAO.findByClusterAndStateCurrent(getClusterName());
+        if (currentVersion != null) {
+          currentVersion.setState(RepositoryVersionState.INSTALLED);
+          clusterVersionDAO.merge(currentVersion);
         }
+      }
 
-        existingClusterVersion.setState(state);
-        existingClusterVersion.setEndTime(System.currentTimeMillis());
-        clusterVersionDAO.merge(existingClusterVersion);
+      existingClusterVersion.setState(state);
+      existingClusterVersion.setEndTime(System.currentTimeMillis());
+      clusterVersionDAO.merge(existingClusterVersion);
 
-        if (RepositoryVersionState.CURRENT == state) {
-          for (HostEntity he : clusterEntity.getHostEntities()) {
-            if (hostHasReportables(existingClusterVersion.getRepositoryVersion(), he)) {
-              continue;
-            }
+      if (state == RepositoryVersionState.CURRENT) {
+        for (HostEntity hostEntity : clusterEntity.getHostEntities()) {
+          if (hostHasReportables(existingClusterVersion.getRepositoryVersion(),
+              hostEntity)) {
+            continue;
+          }
 
-            Collection<HostVersionEntity> versions = hostVersionDAO.findByHost(
-                he.getHostName());
-
-            HostVersionEntity target = null;
-            if (null != versions) {
-              // Set anything that was previously marked CURRENT as INSTALLED, and the matching version as CURRENT
-              for (HostVersionEntity entity : versions) {
-                if (entity.getRepositoryVersion().getId().equals(existingClusterVersion.getRepositoryVersion().getId())) {
-                  target = entity;
-                  target.setState(state);
-                  hostVersionDAO.merge(target);
-                } else if (entity.getState() == RepositoryVersionState.CURRENT) {
-                  entity.setState(RepositoryVersionState.INSTALLED);
-                  hostVersionDAO.merge(entity);
-                }
+          Collection<HostVersionEntity> versions = hostVersionDAO.findByHost(hostEntity.getHostName());
+
+          HostVersionEntity target = null;
+          if (null != versions) {
+            // Set anything that was previously marked CURRENT as INSTALLED, and
+            // the matching version as CURRENT
+            for (HostVersionEntity entity : versions) {
+              if (entity.getRepositoryVersion().getId().equals(
+                  existingClusterVersion.getRepositoryVersion().getId())) {
+                target = entity;
+                target.setState(state);
+                hostVersionDAO.merge(target);
+              } else if (entity.getState() == RepositoryVersionState.CURRENT) {
+                entity.setState(RepositoryVersionState.INSTALLED);
+                hostVersionDAO.merge(entity);
               }
             }
-            if (null == target) {
-              // If no matching version was found, create one with the desired state
-              HostVersionEntity hve = new HostVersionEntity(he, existingClusterVersion.getRepositoryVersion(), state);
-              hostVersionDAO.create(hve);
-            }
+          }
+
+          if (null == target) {
+            // If no matching version was found, create one with the desired
+            // state
+            HostVersionEntity hve = new HostVersionEntity(hostEntity,
+                existingClusterVersion.getRepositoryVersion(), state);
+
+            hostVersionDAO.create(hve);
           }
         }
+
+        // when setting the cluster's state to current, we must also
+        // bring the desired stack and current stack in line with each other
+        StackEntity desiredStackEntity = clusterEntity.getDesiredStack();
+        StackId desiredStackId = new StackId(desiredStackEntity);
+
+        // if the desired stack ID doesn't match the target when setting the
+        // cluster to CURRENT, then there's a problem
+        if (!desiredStackId.equals(stackId)) {
+          String message = MessageFormat.format(
+              "The desired stack ID {0} must match {1} when transitioning the cluster''s state to {2}",
+              desiredStackId, stackId, RepositoryVersionState.CURRENT);
+
+          throw new AmbariException(message);
+        }
+
+        setCurrentStackVersion(stackId);
       }
     } catch (RollbackException e) {
-      String message = "Unable to transition stack " + stackId + " at version "
-          + version + " for cluster " + getClusterName() + " to state " + state;
+      String message = MessageFormat.format(
+          "Unable to transition stack {0} at version {1} for cluster {2} to state {3}",
+          stackId, version, getClusterName(), state);
+
       LOG.warn(message);
       throw new AmbariException(message, e);
     } finally {

http://git-wip-us.apache.org/repos/asf/ambari/blob/cc76c39d/ambari-server/src/test/java/org/apache/ambari/server/events/listeners/upgrade/StackVersionListenerTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/events/listeners/upgrade/StackVersionListenerTest.java b/ambari-server/src/test/java/org/apache/ambari/server/events/listeners/upgrade/StackVersionListenerTest.java
index b44ac30..3e02ff1 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/events/listeners/upgrade/StackVersionListenerTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/events/listeners/upgrade/StackVersionListenerTest.java
@@ -17,18 +17,19 @@
  */
 package org.apache.ambari.server.events.listeners.upgrade;
 
+import static org.easymock.EasyMock.createNiceMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+
 import org.apache.ambari.server.events.HostComponentVersionEvent;
 import org.apache.ambari.server.events.publishers.VersionEventPublisher;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.ServiceComponentHost;
+import org.apache.ambari.server.state.StackId;
+import org.easymock.EasyMock;
 import org.junit.Test;
 
-import static org.easymock.EasyMock.createNiceMock;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.replay;
-import static org.easymock.EasyMock.verify;
-import static org.junit.Assert.*;
-
 /**
  * StackVersionListener tests.
  */
@@ -36,6 +37,7 @@ public class StackVersionListenerTest {
 
   @Test
   public void testOnAmbariEvent() throws Exception {
+    StackId stackId = new StackId("HDP-0.1");
 
     VersionEventPublisher publisher = createNiceMock(VersionEventPublisher.class);
 
@@ -43,14 +45,16 @@ public class StackVersionListenerTest {
     ServiceComponentHost sch = createNiceMock(ServiceComponentHost.class);
 
     expect(cluster.getClusterId()).andReturn(99L);
-    cluster.recalculateClusterVersionState("1.0.0");
+    expect(cluster.getDesiredStackVersion()).andReturn(stackId);
+
+    cluster.recalculateClusterVersionState(stackId, "1.0.0");
+    EasyMock.expectLastCall().atLeastOnce();
 
-    expect(sch.recalculateHostVersionState()).andReturn("1.0.0").anyTimes();
+    expect(sch.recalculateHostVersionState()).andReturn("1.0.0").atLeastOnce();
 
     replay(cluster, sch);
 
     HostComponentVersionEvent event = new HostComponentVersionEvent(cluster, sch);
-
     StackVersionListener listener = new StackVersionListener(publisher);
 
     listener.onAmbariEvent(event);

http://git-wip-us.apache.org/repos/asf/ambari/blob/cc76c39d/ambari-server/src/test/java/org/apache/ambari/server/serveraction/upgrades/UpgradeActionTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/serveraction/upgrades/UpgradeActionTest.java b/ambari-server/src/test/java/org/apache/ambari/server/serveraction/upgrades/UpgradeActionTest.java
index 7d1f920..fc86c7a 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/serveraction/upgrades/UpgradeActionTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/serveraction/upgrades/UpgradeActionTest.java
@@ -25,7 +25,6 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 
-import com.google.inject.Inject;
 import org.apache.ambari.server.actionmanager.ExecutionCommandWrapper;
 import org.apache.ambari.server.actionmanager.HostRoleCommand;
 import org.apache.ambari.server.actionmanager.HostRoleCommandFactory;
@@ -60,6 +59,7 @@ import com.google.gson.JsonArray;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.inject.Guice;
+import com.google.inject.Inject;
 import com.google.inject.Injector;
 import com.google.inject.persist.PersistService;
 
@@ -67,29 +67,31 @@ import com.google.inject.persist.PersistService;
  * Tests upgrade-related server side actions
  */
 public class UpgradeActionTest {
-  private static final String UPGRADE_VERSION = "2.2.1.0-2270";
-  private static final String DOWNGRADE_VERSION = "2.2.0.0-2041";
+  private static final String HDP_2_1_1_0 = "2.1.1.0-118";
+  private static final String HDP_2_2_1_0 = "2.2.1.0-2270";
+  private static final String HDP_2_2_0_0 = "2.2.0.0-2041";
+  private static final StackId HDP_21_STACK = new StackId("HDP-2.1.1");
+  private static final StackId HDP_22_STACK = new StackId("HDP-2.2.0");
 
   private Injector m_injector;
 
   @Inject
-  OrmTestHelper helper;
+  private OrmTestHelper helper;
 
   @Inject
-  RepositoryVersionDAO repoVersionDAO;
+  private RepositoryVersionDAO repoVersionDAO;
 
   @Inject
-  ClusterVersionDAO clusterVersionDAO;
+  private ClusterVersionDAO clusterVersionDAO;
 
   @Inject
-  HostVersionDAO hostVersionDAO;
+  private HostVersionDAO hostVersionDAO;
 
   @Inject
-  HostDAO hostDAO;
+  private HostDAO hostDAO;
 
   @Inject
-  HostRoleCommandFactory hostRoleCommandFactory;
-
+  private HostRoleCommandFactory hostRoleCommandFactory;
 
   @Before
   public void setup() throws Exception {
@@ -114,9 +116,8 @@ public class UpgradeActionTest {
     String clusterName = "c1";
     String hostName = "h1";
 
-    StackId stackId = new StackId("HDP-2.1.1");
     Clusters clusters = m_injector.getInstance(Clusters.class);
-    clusters.addCluster(clusterName, stackId);
+    clusters.addCluster(clusterName, HDP_21_STACK);
 
     Cluster c = clusters.getCluster(clusterName);
 
@@ -131,17 +132,15 @@ public class UpgradeActionTest {
     host.setHostAttributes(hostAttributes);
     host.persist();
 
-    helper.getOrCreateRepositoryVersion(stackId, DOWNGRADE_VERSION);
-    helper.getOrCreateRepositoryVersion(stackId, UPGRADE_VERSION);
+    helper.getOrCreateRepositoryVersion(HDP_21_STACK, HDP_2_2_0_0);
+    helper.getOrCreateRepositoryVersion(HDP_21_STACK, HDP_2_2_1_0);
 
-    c.createClusterVersion(stackId, DOWNGRADE_VERSION, "admin",
-        RepositoryVersionState.UPGRADING);
-    c.createClusterVersion(stackId, UPGRADE_VERSION, "admin",
-        RepositoryVersionState.INSTALLING);
+    c.createClusterVersion(HDP_21_STACK, HDP_2_2_0_0, "admin", RepositoryVersionState.UPGRADING);
+    c.createClusterVersion(HDP_21_STACK, HDP_2_2_1_0, "admin", RepositoryVersionState.INSTALLING);
 
-    c.transitionClusterVersion(stackId, DOWNGRADE_VERSION, RepositoryVersionState.CURRENT);
-    c.transitionClusterVersion(stackId, UPGRADE_VERSION, RepositoryVersionState.INSTALLED);
-    c.transitionClusterVersion(stackId, UPGRADE_VERSION, RepositoryVersionState.UPGRADING);
+    c.transitionClusterVersion(HDP_21_STACK, HDP_2_2_0_0, RepositoryVersionState.CURRENT);
+    c.transitionClusterVersion(HDP_21_STACK, HDP_2_2_1_0, RepositoryVersionState.INSTALLED);
+    c.transitionClusterVersion(HDP_21_STACK, HDP_2_2_1_0, RepositoryVersionState.UPGRADING);
 
     c.mapHostVersions(Collections.singleton(hostName), c.getCurrentClusterVersion(),
         RepositoryVersionState.CURRENT);
@@ -149,7 +148,7 @@ public class UpgradeActionTest {
     HostVersionEntity entity = new HostVersionEntity();
     entity.setHostEntity(hostDAO.findByName(hostName));
     entity.setRepositoryVersion(
-        repoVersionDAO.findByStackAndVersion(stackId, UPGRADE_VERSION));
+        repoVersionDAO.findByStackAndVersion(HDP_21_STACK, HDP_2_2_1_0));
     entity.setState(RepositoryVersionState.UPGRADING);
     hostVersionDAO.create(entity);
   }
@@ -158,18 +157,17 @@ public class UpgradeActionTest {
     String clusterName = "c1";
     String hostName = "h1";
 
-    StackId stackId = new StackId("HDP-2.1.1");
     Clusters clusters = m_injector.getInstance(Clusters.class);
-    clusters.addCluster(clusterName, stackId);
+    clusters.addCluster(clusterName, HDP_21_STACK);
 
     StackDAO stackDAO = m_injector.getInstance(StackDAO.class);
-    StackEntity stackEntity = stackDAO.find(stackId.getStackName(),
-        stackId.getStackVersion());
+    StackEntity stackEntity = stackDAO.find(HDP_21_STACK.getStackName(),
+        HDP_21_STACK.getStackVersion());
 
     assertNotNull(stackEntity);
 
     Cluster c = clusters.getCluster(clusterName);
-    c.setDesiredStackVersion(stackId);
+    c.setDesiredStackVersion(HDP_21_STACK);
 
     // add a host component
     clusters.addHost(hostName);
@@ -186,21 +184,19 @@ public class UpgradeActionTest {
         "{'Repositories/base_url':'http://foo1','Repositories/repo_name':'HDP','Repositories/repo_id':'HDP-2.1.1'}" +
         "], 'OperatingSystems/os_type':'redhat6'}]";
 
-    helper.getOrCreateRepositoryVersion(stackId, DOWNGRADE_VERSION);
-    repoVersionDAO.create(stackEntity, UPGRADE_VERSION,
+    helper.getOrCreateRepositoryVersion(HDP_21_STACK, HDP_2_2_0_0);
+    repoVersionDAO.create(stackEntity, HDP_2_2_1_0,
         String.valueOf(System.currentTimeMillis()), "pack",
           urlInfo);
 
-    c.createClusterVersion(stackId, DOWNGRADE_VERSION, "admin",
-        RepositoryVersionState.UPGRADING);
-    c.createClusterVersion(stackId, UPGRADE_VERSION, "admin",
-        RepositoryVersionState.INSTALLING);
+    c.createClusterVersion(HDP_21_STACK, HDP_2_2_0_0, "admin", RepositoryVersionState.UPGRADING);
+    c.createClusterVersion(HDP_21_STACK, HDP_2_2_1_0, "admin", RepositoryVersionState.INSTALLING);
 
-    c.transitionClusterVersion(stackId, DOWNGRADE_VERSION, RepositoryVersionState.CURRENT);
-    c.transitionClusterVersion(stackId, UPGRADE_VERSION, RepositoryVersionState.INSTALLED);
-    c.transitionClusterVersion(stackId, UPGRADE_VERSION, RepositoryVersionState.UPGRADING);
-    c.transitionClusterVersion(stackId, UPGRADE_VERSION, RepositoryVersionState.UPGRADED);
-    c.setCurrentStackVersion(stackId);
+    c.transitionClusterVersion(HDP_21_STACK, HDP_2_2_0_0, RepositoryVersionState.CURRENT);
+    c.transitionClusterVersion(HDP_21_STACK, HDP_2_2_1_0, RepositoryVersionState.INSTALLED);
+    c.transitionClusterVersion(HDP_21_STACK, HDP_2_2_1_0, RepositoryVersionState.UPGRADING);
+    c.transitionClusterVersion(HDP_21_STACK, HDP_2_2_1_0, RepositoryVersionState.UPGRADED);
+    c.setCurrentStackVersion(HDP_21_STACK);
 
     c.mapHostVersions(Collections.singleton(hostName), c.getCurrentClusterVersion(),
         RepositoryVersionState.CURRENT);
@@ -210,7 +206,67 @@ public class UpgradeActionTest {
     HostVersionEntity entity = new HostVersionEntity();
     entity.setHostEntity(hostDAO.findByName(hostName));
     entity.setRepositoryVersion(
-        repoVersionDAO.findByStackAndVersion(stackId, UPGRADE_VERSION));
+        repoVersionDAO.findByStackAndVersion(HDP_21_STACK, HDP_2_2_1_0));
+    entity.setState(RepositoryVersionState.UPGRADED);
+    hostVersionDAO.create(entity);
+  }
+
+  private void makeCrossStackUpgradeCluster() throws Exception {
+    String clusterName = "c1";
+    String hostName = "h1";
+
+    Clusters clusters = m_injector.getInstance(Clusters.class);
+    clusters.addCluster(clusterName, HDP_21_STACK);
+
+    StackDAO stackDAO = m_injector.getInstance(StackDAO.class);
+    StackEntity stackEntity = stackDAO.find(HDP_21_STACK.getStackName(),
+        HDP_21_STACK.getStackVersion());
+
+    assertNotNull(stackEntity);
+
+    Cluster c = clusters.getCluster(clusterName);
+    c.setCurrentStackVersion(HDP_21_STACK);
+    c.setDesiredStackVersion(HDP_21_STACK);
+
+    // add a host component
+    clusters.addHost(hostName);
+
+    Host host = clusters.getHost(hostName);
+
+    Map<String, String> hostAttributes = new HashMap<String, String>();
+    hostAttributes.put("os_family", "redhat");
+    hostAttributes.put("os_release_version", "6");
+    host.setHostAttributes(hostAttributes);
+    host.persist();
+
+    String urlInfo = "[{'repositories':[" +
+        "{'Repositories/base_url':'http://foo1','Repositories/repo_name':'HDP','Repositories/repo_id':'HDP-2.1.1'}" +
+        "], 'OperatingSystems/os_type':'redhat6'}]";
+
+    helper.getOrCreateRepositoryVersion(HDP_21_STACK, HDP_2_1_1_0);
+    helper.getOrCreateRepositoryVersion(HDP_22_STACK, HDP_2_2_1_0);
+
+    repoVersionDAO.create(stackEntity, HDP_2_2_1_0,
+        String.valueOf(System.currentTimeMillis()), "pack",
+          urlInfo);
+
+    c.createClusterVersion(HDP_21_STACK, HDP_2_1_1_0, "admin", RepositoryVersionState.UPGRADING);
+    c.createClusterVersion(HDP_22_STACK, HDP_2_2_1_0, "admin", RepositoryVersionState.INSTALLING);
+
+    c.transitionClusterVersion(HDP_21_STACK, HDP_2_1_1_0, RepositoryVersionState.CURRENT);
+    c.transitionClusterVersion(HDP_22_STACK, HDP_2_2_1_0, RepositoryVersionState.INSTALLED);
+    c.transitionClusterVersion(HDP_22_STACK, HDP_2_2_1_0, RepositoryVersionState.UPGRADING);
+    c.transitionClusterVersion(HDP_22_STACK, HDP_2_2_1_0, RepositoryVersionState.UPGRADED);
+
+    c.mapHostVersions(Collections.singleton(hostName), c.getCurrentClusterVersion(),
+        RepositoryVersionState.CURRENT);
+
+    HostDAO hostDAO = m_injector.getInstance(HostDAO.class);
+
+    HostVersionEntity entity = new HostVersionEntity();
+    entity.setHostEntity(hostDAO.findByName(hostName));
+    entity.setRepositoryVersion(
+        repoVersionDAO.findByStackAndVersion(HDP_22_STACK, HDP_2_2_1_0));
     entity.setState(RepositoryVersionState.UPGRADED);
     hostVersionDAO.create(entity);
   }
@@ -222,14 +278,16 @@ public class UpgradeActionTest {
 
     Map<String, String> commandParams = new HashMap<String, String>();
     commandParams.put("upgrade_direction", "downgrade");
-    commandParams.put("version", DOWNGRADE_VERSION);
+    commandParams.put("version", HDP_2_2_0_0);
 
     ExecutionCommand executionCommand = new ExecutionCommand();
     executionCommand.setCommandParams(commandParams);
     executionCommand.setClusterName("c1");
 
-    HostRoleCommand hostRoleCommand = hostRoleCommandFactory.create(null, null, null, null);
-    hostRoleCommand.setExecutionCommandWrapper(new ExecutionCommandWrapper(executionCommand));
+    HostRoleCommand hostRoleCommand = hostRoleCommandFactory.create(null, null,
+        null, null);
+    hostRoleCommand.setExecutionCommandWrapper(new ExecutionCommandWrapper(
+        executionCommand));
 
     FinalizeUpgradeAction action = m_injector.getInstance(FinalizeUpgradeAction.class);
     action.setExecutionCommand(executionCommand);
@@ -239,19 +297,21 @@ public class UpgradeActionTest {
     assertNotNull(report);
     assertEquals(HostRoleStatus.COMPLETED.name(), report.getStatus());
 
-
-    for (HostVersionEntity entity : hostVersionDAO.findByClusterAndHost("c1", "h1")) {
-      if (entity.getRepositoryVersion().getVersion().equals(DOWNGRADE_VERSION)) {
+    for (HostVersionEntity entity : hostVersionDAO.findByClusterAndHost("c1",
+        "h1")) {
+      if (entity.getRepositoryVersion().getVersion().equals(HDP_2_2_0_0)) {
         assertEquals(RepositoryVersionState.CURRENT, entity.getState());
-      } else if (entity.getRepositoryVersion().getVersion().equals(UPGRADE_VERSION)) {
+      } else if (entity.getRepositoryVersion().getVersion().equals(
+HDP_2_2_1_0)) {
         assertEquals(RepositoryVersionState.INSTALLED, entity.getState());
       }
     }
 
     for (ClusterVersionEntity entity : clusterVersionDAO.findByCluster("c1")) {
-      if (entity.getRepositoryVersion().getVersion().equals(DOWNGRADE_VERSION)) {
+      if (entity.getRepositoryVersion().getVersion().equals(HDP_2_2_0_0)) {
         assertEquals(RepositoryVersionState.CURRENT, entity.getState());
-      } else if (entity.getRepositoryVersion().getVersion().equals(UPGRADE_VERSION)) {
+      } else if (entity.getRepositoryVersion().getVersion().equals(
+HDP_2_2_1_0)) {
         assertEquals(RepositoryVersionState.INSTALLED, entity.getState());
       }
     }
@@ -263,14 +323,16 @@ public class UpgradeActionTest {
 
     Map<String, String> commandParams = new HashMap<String, String>();
     commandParams.put("upgrade_direction", "upgrade");
-    commandParams.put("version", UPGRADE_VERSION);
+    commandParams.put("version", HDP_2_2_1_0);
 
     ExecutionCommand executionCommand = new ExecutionCommand();
     executionCommand.setCommandParams(commandParams);
     executionCommand.setClusterName("c1");
 
-    HostRoleCommand hostRoleCommand = hostRoleCommandFactory.create(null, null, null, null);
-    hostRoleCommand.setExecutionCommandWrapper(new ExecutionCommandWrapper(executionCommand));
+    HostRoleCommand hostRoleCommand = hostRoleCommandFactory.create(null, null,
+        null, null);
+    hostRoleCommand.setExecutionCommandWrapper(new ExecutionCommandWrapper(
+        executionCommand));
 
     FinalizeUpgradeAction action = m_injector.getInstance(FinalizeUpgradeAction.class);
     action.setExecutionCommand(executionCommand);
@@ -280,15 +342,17 @@ public class UpgradeActionTest {
     assertNotNull(report);
     assertEquals(HostRoleStatus.COMPLETED.name(), report.getStatus());
 
-
-    // !!! verify the metainfo url has not been updated, but an output command has
+    // !!! verify the metainfo url has not been updated, but an output command
+    // has
     AmbariMetaInfo metaInfo = m_injector.getInstance(AmbariMetaInfo.class);
-    RepositoryInfo repo = metaInfo.getRepository("HDP", "2.1.1", "redhat6", "HDP-2.1.1");
-    assertEquals("http://s3.amazonaws.com/dev.hortonworks.com/HDP/centos6/2.x/BUILDS/2.1.1.0-118", repo.getBaseUrl());
+    RepositoryInfo repo = metaInfo.getRepository("HDP", "2.1.1", "redhat6",
+        "HDP-2.1.1");
+    assertEquals(
+        "http://s3.amazonaws.com/dev.hortonworks.com/HDP/centos6/2.x/BUILDS/2.1.1.0-118",
+        repo.getBaseUrl());
 
     // !!! verify that a command will return the correct host info
-    AmbariCustomCommandExecutionHelper helper = m_injector.getInstance(
-        AmbariCustomCommandExecutionHelper.class);
+    AmbariCustomCommandExecutionHelper helper = m_injector.getInstance(AmbariCustomCommandExecutionHelper.class);
     Clusters clusters = m_injector.getInstance(Clusters.class);
     Host host = clusters.getHost("h1");
     Cluster cluster = clusters.getCluster("c1");
@@ -308,5 +372,44 @@ public class UpgradeActionTest {
     assertEquals("http://foo1", o.get("baseUrl").getAsString());
   }
 
+  @Test
+  public void testFinalizeUpgradeAcrossStacks() throws Exception {
+    makeCrossStackUpgradeCluster();
+
+    Clusters clusters = m_injector.getInstance(Clusters.class);
+    Cluster cluster = clusters.getCluster("c1");
+
+    // setup the cluster for the upgrade across stacks
+    cluster.setCurrentStackVersion(HDP_21_STACK);
+    cluster.setDesiredStackVersion(HDP_22_STACK);
+
+    Map<String, String> commandParams = new HashMap<String, String>();
+    commandParams.put("upgrade_direction", "upgrade");
+    commandParams.put("version", HDP_2_2_1_0);
+
+    ExecutionCommand executionCommand = new ExecutionCommand();
+    executionCommand.setCommandParams(commandParams);
+    executionCommand.setClusterName("c1");
+
+    HostRoleCommand hostRoleCommand = hostRoleCommandFactory.create(null, null,
+        null, null);
+
+    hostRoleCommand.setExecutionCommandWrapper(new ExecutionCommandWrapper(
+        executionCommand));
+
+    FinalizeUpgradeAction action = m_injector.getInstance(FinalizeUpgradeAction.class);
+    action.setExecutionCommand(executionCommand);
+    action.setHostRoleCommand(hostRoleCommand);
 
+    CommandReport report = action.execute(null);
+    assertNotNull(report);
+    assertEquals(HostRoleStatus.COMPLETED.name(), report.getStatus());
+
+    StackId currentStackId = cluster.getCurrentStackVersion();
+    StackId desiredStackId = cluster.getDesiredStackVersion();
+
+    assertEquals(desiredStackId, currentStackId);
+    assertEquals(HDP_22_STACK, currentStackId);
+    assertEquals(HDP_22_STACK, desiredStackId);
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/cc76c39d/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterTest.java
index e076d4e..bc17c38 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterTest.java
@@ -499,7 +499,8 @@ public class ClusterTest {
       ServiceComponentHost scHost = svcComp.getServiceComponentHost(hce.getHostName());
 
       scHost.recalculateHostVersionState();
-      cluster.recalculateClusterVersionState(version);
+      cluster.recalculateClusterVersionState(cluster.getDesiredStackVersion(),
+          version);
     }
   }
 
@@ -1263,8 +1264,10 @@ public class ClusterTest {
     assertStateException(stackId, version, RepositoryVersionState.OUT_OF_SYNC,
         RepositoryVersionState.UPGRADED);
 
+    c1.setDesiredStackVersion(stackId);
     c1.transitionClusterVersion(stackId, version,
         RepositoryVersionState.CURRENT);
+
     checkStackVersionState(stackId, version, RepositoryVersionState.CURRENT);
 
     checkStackVersionState(new StackId("HDP", "0.1"), "0.1",
@@ -1387,13 +1390,13 @@ public class ClusterTest {
     HostVersionEntity hv1 = helper.createHostVersion("h1", repositoryVersionEntity, RepositoryVersionState.INSTALLING);
     HostVersionEntity hv2 = helper.createHostVersion("h2", repositoryVersionEntity, RepositoryVersionState.INSTALLING);
 
-    c1.recalculateClusterVersionState(stackVersion);
+    c1.recalculateClusterVersionState(c1.getDesiredStackVersion(), stackVersion);
     //Should remain in its current state
     checkStackVersionState(stackId, stackVersion,
         RepositoryVersionState.INSTALLING);
 
     h2.setState(HostState.UNHEALTHY);
-    c1.recalculateClusterVersionState(stackVersion);
+    c1.recalculateClusterVersionState(c1.getDesiredStackVersion(), stackVersion);
     // In order for the states to be accurately reflected, the host health status should not impact the status
     // of the host_version.
     checkStackVersionState(stackId, stackVersion,
@@ -1405,14 +1408,14 @@ public class ClusterTest {
     h2.setState(HostState.HEALTHY);
     hv2.setState(RepositoryVersionState.INSTALLED);
     hostVersionDAO.merge(hv2);
-    c1.recalculateClusterVersionState(stackVersion);
+    c1.recalculateClusterVersionState(c1.getDesiredStackVersion(), stackVersion);
     checkStackVersionState(stackId, stackVersion,
         RepositoryVersionState.INSTALLING);
 
     // Make one host fail
     hv1.setState(RepositoryVersionState.INSTALL_FAILED);
     hostVersionDAO.merge(hv1);
-    c1.recalculateClusterVersionState(stackVersion);
+    c1.recalculateClusterVersionState(c1.getDesiredStackVersion(), stackVersion);
     checkStackVersionState(stackId, stackVersion,
         RepositoryVersionState.INSTALL_FAILED);
     // Retry by going back to INSTALLING
@@ -1422,26 +1425,26 @@ public class ClusterTest {
     // Now, all hosts are in INSTALLED
     hv1.setState(RepositoryVersionState.INSTALLED);
     hostVersionDAO.merge(hv1);
-    c1.recalculateClusterVersionState(stackVersion);
+    c1.recalculateClusterVersionState(c1.getDesiredStackVersion(), stackVersion);
     checkStackVersionState(stackId, stackVersion,
         RepositoryVersionState.INSTALLED);
 
     // Phase 2: Upgrade stack
     hv1.setState(RepositoryVersionState.UPGRADING);
     hostVersionDAO.merge(hv1);
-    c1.recalculateClusterVersionState(stackVersion);
+    c1.recalculateClusterVersionState(c1.getDesiredStackVersion(), stackVersion);
     checkStackVersionState(stackId, stackVersion,
         RepositoryVersionState.UPGRADING);
 
     hv2.setState(RepositoryVersionState.UPGRADING);
     hostVersionDAO.merge(hv2);
-    c1.recalculateClusterVersionState(stackVersion);
+    c1.recalculateClusterVersionState(c1.getDesiredStackVersion(), stackVersion);
     checkStackVersionState(stackId, stackVersion,
         RepositoryVersionState.UPGRADING);
 
     hv2.setState(RepositoryVersionState.UPGRADE_FAILED);
     hostVersionDAO.merge(hv2);
-    c1.recalculateClusterVersionState(stackVersion);
+    c1.recalculateClusterVersionState(c1.getDesiredStackVersion(), stackVersion);
     checkStackVersionState(stackId, stackVersion,
         RepositoryVersionState.UPGRADE_FAILED);
     // Retry by going back to UPGRADING
@@ -1450,14 +1453,14 @@ public class ClusterTest {
 
     hv2.setState(RepositoryVersionState.UPGRADED);
     hostVersionDAO.merge(hv2);
-    c1.recalculateClusterVersionState(stackVersion);
+    c1.recalculateClusterVersionState(c1.getDesiredStackVersion(), stackVersion);
     checkStackVersionState(stackId, stackVersion,
         RepositoryVersionState.UPGRADING);
 
     // Now both hosts are UPGRADED
     hv1.setState(RepositoryVersionState.UPGRADED);
     hostVersionDAO.merge(hv1);
-    c1.recalculateClusterVersionState(stackVersion);
+    c1.recalculateClusterVersionState(c1.getDesiredStackVersion(), stackVersion);
     checkStackVersionState(stackId, stackVersion,
         RepositoryVersionState.UPGRADED);
 
@@ -1466,7 +1469,7 @@ public class ClusterTest {
     hostVersionDAO.merge(hv1);
     hv2.setState(RepositoryVersionState.CURRENT);
     hostVersionDAO.merge(hv2);
-    c1.recalculateClusterVersionState(stackVersion);
+    c1.recalculateClusterVersionState(c1.getDesiredStackVersion(), stackVersion);
     checkStackVersionState(stackId, stackVersion,
         RepositoryVersionState.CURRENT);
   }
@@ -1565,7 +1568,8 @@ public class ClusterTest {
       ServiceComponentHost scHost = svcComp.getServiceComponentHost(hce.getHostName());
 
       scHost.recalculateHostVersionState();
-      cluster.recalculateClusterVersionState(v1);
+      cluster.recalculateClusterVersionState(cluster.getDesiredStackVersion(),
+          v1);
 
       Collection<ClusterVersionEntity> clusterVersions = cluster.getAllClusterVersions();
 
@@ -1675,7 +1679,7 @@ public class ClusterTest {
       ServiceComponentHost scHost = svcComp.getServiceComponentHost(hce.getHostName());
 
       scHost.recalculateHostVersionState();
-      cluster.recalculateClusterVersionState(v2);
+      cluster.recalculateClusterVersionState(cluster.getDesiredStackVersion(),v2);
 
       Collection<ClusterVersionEntity> clusterVersions = cluster.getAllClusterVersions();