You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by dm...@apache.org on 2015/03/06 19:57:02 UTC

[2/6] ambari git commit: AMBARI-9964. Rolling Upgrade: Expose FinalizeUpgradeAction to API or force transition from UPGRADED to CURRENT for cluster_version via API request (dlysnichenko)

AMBARI-9964. Rolling Upgrade: Expose FinalizeUpgradeAction to API or force transition from UPGRADED to CURRENT for cluster_version via API request (dlysnichenko)


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

Branch: refs/heads/trunk
Commit: 8775d8846da2ecaf82a16a92712d47d8bc5f5745
Parents: 4894eab
Author: Lisnichenko Dmitro <dl...@hortonworks.com>
Authored: Fri Mar 6 20:53:27 2015 +0200
Committer: Lisnichenko Dmitro <dl...@hortonworks.com>
Committed: Fri Mar 6 20:55:39 2015 +0200

----------------------------------------------------------------------
 .../services/ClusterStackVersionService.java    |  17 +++
 .../ClusterStackVersionResourceProvider.java    | 102 ++++++++++++++-
 .../upgrades/FinalizeUpgradeAction.java         |  10 +-
 ambari-server/src/main/python/upgradeHelper.py  |  47 +++++--
 ...ClusterStackVersionResourceProviderTest.java | 123 ++++++++++++++++++-
 .../src/test/python/TestUpgradeHelper.py        |  53 ++++++++
 6 files changed, 336 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/8775d884/ambari-server/src/main/java/org/apache/ambari/server/api/services/ClusterStackVersionService.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ClusterStackVersionService.java b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ClusterStackVersionService.java
index eb8a461..8fe1499 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/services/ClusterStackVersionService.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/services/ClusterStackVersionService.java
@@ -22,6 +22,7 @@ import java.util.Map;
 
 import javax.ws.rs.GET;
 import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
@@ -120,6 +121,22 @@ public class ClusterStackVersionService extends BaseService {
     return handleRequest(headers, body, ui, Request.Type.POST, createResource(null));
   }
 
+
+  /**
+   * Handles: POST /{clustername}/stack_versions requests
+   * triggering Finalize during manual Rolling Upgrade
+   *
+   * @param body        http body
+   * @param headers     http headers
+   * @param ui          uri info
+   * @return information regarding the created services
+   */
+  @PUT
+  @Produces("text/plain")
+  public Response updateRequests(String body, @Context HttpHeaders headers, @Context UriInfo ui) {
+    return handleRequest(headers, body, ui, Request.Type.PUT, createResource(null));
+  }
+
   /**
    * Create a cluster stack version resource instance.
    *

http://git-wip-us.apache.org/repos/asf/ambari/blob/8775d884/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterStackVersionResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterStackVersionResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterStackVersionResourceProvider.java
index 0cf89a3..e93e653 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterStackVersionResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterStackVersionResourceProvider.java
@@ -27,13 +27,21 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 
+import com.google.inject.Injector;
 import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.Role;
 import org.apache.ambari.server.StaticallyInject;
 import org.apache.ambari.server.actionmanager.ActionManager;
+import org.apache.ambari.server.actionmanager.HostRoleCommand;
+import org.apache.ambari.server.actionmanager.HostRoleStatus;
 import org.apache.ambari.server.actionmanager.RequestFactory;
 import org.apache.ambari.server.actionmanager.Stage;
 import org.apache.ambari.server.actionmanager.StageFactory;
+import org.apache.ambari.server.agent.CommandReport;
+import org.apache.ambari.server.agent.ExecutionCommand;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.controller.ActionExecutionContext;
@@ -58,6 +66,7 @@ import org.apache.ambari.server.orm.entities.HostVersionEntity;
 import org.apache.ambari.server.orm.entities.OperatingSystemEntity;
 import org.apache.ambari.server.orm.entities.RepositoryEntity;
 import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
+import org.apache.ambari.server.serveraction.upgrades.FinalizeUpgradeAction;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Host;
 import org.apache.ambari.server.state.RepositoryVersionState;
@@ -109,7 +118,6 @@ public class ClusterStackVersionResourceProvider extends AbstractControllerResou
       add(CLUSTER_STACK_VERSION_CLUSTER_NAME_PROPERTY_ID);
       add(CLUSTER_STACK_VERSION_STACK_PROPERTY_ID);
       add(CLUSTER_STACK_VERSION_VERSION_PROPERTY_ID);
-      add(CLUSTER_STACK_VERSION_STATE_PROPERTY_ID);
       add(CLUSTER_STACK_VERSION_HOST_STATES_PROPERTY_ID);
       add(CLUSTER_STACK_VERSION_STATE_PROPERTY_ID);
       add(CLUSTER_STACK_VERSION_REPOSITORY_VERSION_PROPERTY_ID);
@@ -150,6 +158,16 @@ public class ClusterStackVersionResourceProvider extends AbstractControllerResou
   @Inject
   private static Configuration configuration;
 
+  @Inject
+  private static Injector injector;
+
+  /**
+   * We have to include such a hack here, because if we
+   * make finalizeUpgradeAction field static and request injection
+   * for it, there will be a circle dependency error
+   */
+  private FinalizeUpgradeAction finalizeUpgradeAction = injector.getInstance(FinalizeUpgradeAction.class);
+
   /**
    * Constructor.
    */
@@ -410,12 +428,90 @@ public class ClusterStackVersionResourceProvider extends AbstractControllerResou
     return requestStages;
   }
 
-
+  /**
+   * The only appliance of this method is triggering Finalize during
+   * manual Rolling Upgrade
+   */
   @Override
   public RequestStatus updateResources(Request request, Predicate predicate)
       throws SystemException, UnsupportedPropertyException,
       NoSuchResourceException, NoSuchParentResourceException {
-    throw new SystemException("Method not supported");
+    try {
+      Iterator<Map<String, Object>> iterator = request.getProperties().iterator();
+      String clName;
+      final String desiredRepoVersion;
+      if (request.getProperties().size() != 1) {
+        throw new UnsupportedOperationException("Multiple requests cannot be executed at the same time.");
+      }
+      Map<String, Object> propertyMap = iterator.next();
+
+      Set<String> requiredProperties = new HashSet<String>() {{
+        add(CLUSTER_STACK_VERSION_CLUSTER_NAME_PROPERTY_ID);
+        add(CLUSTER_STACK_VERSION_REPOSITORY_VERSION_PROPERTY_ID);
+        add(CLUSTER_STACK_VERSION_STATE_PROPERTY_ID);
+      }};
+
+      for (String requiredProperty : requiredProperties) {
+        if (!propertyMap.containsKey(requiredProperty)) {
+          throw new IllegalArgumentException(
+                  String.format("The required property %s is not defined",
+                          requiredProperty));
+        }
+      }
+
+      clName = (String) propertyMap.get(CLUSTER_STACK_VERSION_CLUSTER_NAME_PROPERTY_ID);
+      String desiredDisplayRepoVersion = (String) propertyMap.get(CLUSTER_STACK_VERSION_REPOSITORY_VERSION_PROPERTY_ID);
+      RepositoryVersionEntity rve = repositoryVersionDAO.findByDisplayName(desiredDisplayRepoVersion);
+      if (rve == null) {
+        throw new IllegalArgumentException(
+                  String.format("Repository version with display name %s does not exist",
+                          desiredDisplayRepoVersion));
+      }
+      desiredRepoVersion = rve.getVersion();
+      String newStateStr = (String) propertyMap.get(CLUSTER_STACK_VERSION_STATE_PROPERTY_ID);
+
+      LOG.info("Initiating finalization for manual upgrade to version {} for cluster {}",
+              desiredRepoVersion, clName);
+
+      Map<String, String> args = new HashMap<String, String>();
+      if (newStateStr.equals(RepositoryVersionState.CURRENT.toString())) {
+        // Finalize upgrade workflow
+        args.put(FinalizeUpgradeAction.UPGRADE_DIRECTION_KEY, "upgrade");
+      } else if (newStateStr.equals(RepositoryVersionState.INSTALLED.toString())) {
+        // Finalize downgrade workflow
+        args.put(FinalizeUpgradeAction.UPGRADE_DIRECTION_KEY, "downgrade");
+      } else {
+        throw new IllegalArgumentException(
+          String.format("Invalid desired state %s. Should be either CURRENT or INSTALLED",
+                  newStateStr));
+      }
+      args.put(FinalizeUpgradeAction.VERSION_KEY, desiredRepoVersion);
+      args.put(FinalizeUpgradeAction.CLUSTER_NAME_KEY, clName);
+
+      ExecutionCommand command = new ExecutionCommand();
+      command.setCommandParams(args);
+      command.setClusterName(clName);
+      finalizeUpgradeAction.setExecutionCommand(command);
+      HostRoleCommand hostRoleCommand = new HostRoleCommand("none",
+              Role.AMBARI_SERVER_ACTION, null, null);
+      finalizeUpgradeAction.setHostRoleCommand(hostRoleCommand);
+
+      CommandReport report = finalizeUpgradeAction.execute(null);
+
+      LOG.info("Finalize output:");
+      LOG.info("STDERR: {}", report.getStdErr());
+      LOG.info("STDOUT: {}", report.getStdOut());
+
+      if (report.getStatus().equals(HostRoleStatus.COMPLETED.toString())) {
+        return getRequestStatus(null);
+      } else {
+        throw new SystemException("Finalization failed");
+      }
+    } catch (AmbariException e) {
+      throw new SystemException("Can not perform request", e);
+    } catch (InterruptedException e) {
+      throw new SystemException("Can not perform request", e);
+    }
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/ambari/blob/8775d884/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 d8e5e92..01bd9c7 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
@@ -54,6 +54,9 @@ import com.google.inject.Inject;
  */
 public class FinalizeUpgradeAction extends AbstractServerAction {
 
+  public static final String CLUSTER_NAME_KEY = "cluster_name";
+  public static final String UPGRADE_DIRECTION_KEY = "upgrade_direction";
+  public static final String VERSION_KEY = "version";
 
   /**
    * The Cluster that this ServerAction implementation is executing on
@@ -79,10 +82,11 @@ public class FinalizeUpgradeAction extends AbstractServerAction {
 
     Map<String, String> commandParams = getExecutionCommand().getCommandParams();
 
-    boolean isDowngrade = commandParams.containsKey("upgrade_direction") &&
-        "downgrade".equals(commandParams.get("upgrade_direction").toLowerCase());
+    boolean isDowngrade = commandParams.containsKey(UPGRADE_DIRECTION_KEY) &&
+        "downgrade".equals(commandParams.get(UPGRADE_DIRECTION_KEY).toLowerCase());
+
+    String version = commandParams.get(VERSION_KEY);
 
-    String version = commandParams.get("version");
     String clusterName = getExecutionCommand().getClusterName();
 
     if (isDowngrade) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/8775d884/ambari-server/src/main/python/upgradeHelper.py
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/python/upgradeHelper.py b/ambari-server/src/main/python/upgradeHelper.py
index 11292e9..2426d69 100644
--- a/ambari-server/src/main/python/upgradeHelper.py
+++ b/ambari-server/src/main/python/upgradeHelper.py
@@ -165,6 +165,7 @@ class Options(Const):
   MODIFY_CONFIG_ACTION = "update-configs"
   BACKUP_CONFIG_ACTION = "backup-configs"
   INSTALL_YARN_MR2_ACTION = "install-yarn-mr2"
+  FINALIZE_RU_ACTION = "finalize-ru"
 
   MR_MAPPING_FILE = "mr_mapping"
   CAPACITY_SCHEDULER_TAG = "capacity-scheduler"
@@ -196,6 +197,9 @@ class Options(Const):
   # for verify action
   REPORT_FILE = None
 
+  # for finalize action
+  REPO_VERSION = None
+
   API_TOKENS = {
     "user": None,
     "pass": None
@@ -897,7 +901,9 @@ def install_services():
                          "the status of the install requests."
 
 
-def validate_response(response, expect_body):
+def validate_response(response, expect_body, http_code):
+  if http_code is not None and http_code < 200 or http_code >= 300:
+    return 1, "HTTP code {0}\n".format(http_code) + response
   if expect_body:
     if "\"href\" : \"" not in response:
       return 1, response
@@ -910,16 +916,13 @@ def validate_response(response, expect_body):
 
 
 def curl(url, tokens=None, headers=None, request_type="GET", data=None, parse=False,
-         simulate=None, validate=False, validate_expect_body=False):
+         simulate=None, validate=False, validate_expect_body=False, request_http_code=False):
 
   simulate_only = Options.CURL_PRINT_ONLY is not None or (simulate is not None and simulate is True)
   print_url = Options.CURL_PRINT_ONLY is not None and simulate is not None
 
   curl_path = '/usr/bin/curl'
-  curl_list = [curl_path]
-
-  curl_list.append('-X')
-  curl_list.append(request_type)
+  curl_list = [curl_path, '-X', request_type]
 
   if tokens is not None:
     curl_list.append('-u')
@@ -928,6 +931,9 @@ def curl(url, tokens=None, headers=None, request_type="GET", data=None, parse=Fa
     curl_list.append('-u')
     curl_list.append("%s:%s" % (Options.API_TOKENS["user"], Options.API_TOKENS["pass"]))
 
+  if request_http_code:
+    curl_list += ['-w', '\n%{http_code}']
+
   if request_type in Options.POST_REQUESTS:
     curl_list.append(url)
 
@@ -949,12 +955,17 @@ def curl(url, tokens=None, headers=None, request_type="GET", data=None, parse=Fa
   if print_url:
     Options.logger.info(" ".join(curl_list))
 
+  http_code = None
   if not simulate_only:
     osStat = subprocess.Popen(
       curl_list,
       stderr=subprocess.PIPE,
       stdout=subprocess.PIPE)
     out, err = osStat.communicate()
+    if request_http_code:
+      out_lines = out.splitlines()
+      http_code = int(out_lines[-1])
+      out = '\n'.join(out_lines[0:-1])
     if 0 != osStat.returncode:
       error = "curl call failed. out: " + out + " err: " + err
       Options.logger.error(error)
@@ -965,7 +976,7 @@ def curl(url, tokens=None, headers=None, request_type="GET", data=None, parse=Fa
     out = "{}"
 
   if validate and not simulate_only:
-    retcode, errdata = validate_response(out, validate_expect_body)
+    retcode, errdata = validate_response(out, validate_expect_body, http_code)
     if not retcode == 0:
       raise FatalException(retcode, errdata)
 
@@ -1134,6 +1145,18 @@ def verify_configuration():
       Options.logger.error("Report file close error: %s" % e.message)
 
 
+def finalize_ru():
+  TARGET_URL = Options.CLUSTER_URL + '/stack_versions'
+  request = {
+    "ClusterStackVersions": {
+       "repository_version": Options.REPO_VERSION,
+       "state": "CURRENT"
+    }
+  }
+  curl(TARGET_URL, request_type="PUT", data=request,
+       validate=True, validate_expect_body=False, request_http_code=True)
+
+
 def report_formatter(report_file, config_item, analyzed_list_item):
   prefix = "Configuration item %s" % config_item
   if analyzed_list_item["fail"]["count"] > 0:
@@ -1155,7 +1178,8 @@ def main():
                    Options.MODIFY_CONFIG_ACTION: modify_configs,
                    Options.INSTALL_YARN_MR2_ACTION: install_services,
                    Options.BACKUP_CONFIG_ACTION: backup_configs,
-                   Options.VERIFY_ACTION: verify_configuration
+                   Options.VERIFY_ACTION: verify_configuration,
+                   Options.FINALIZE_RU_ACTION: finalize_ru
   }
 
   parser = optparse.OptionParser(usage="usage: %prog [options] action\n  Valid actions: "
@@ -1179,6 +1203,8 @@ def main():
   parser.add_option('--password', default=None, help="Ambari admin password", dest="password")
   parser.add_option('--clustername', default=None, help="Cluster name", dest="clustername")
 
+  parser.add_option('--repository-version', default=None, help="Repository version", dest="repo_version")
+
   (options, args) = parser.parse_args()
   Options.initialize_logger(options.logfile)
   options.warnings = []
@@ -1211,6 +1237,10 @@ def main():
     if options.report is None:
       options.warnings.append("Should be provided report option")
 
+  if action == Options.FINALIZE_RU_ACTION:
+    if options.repo_version is None:
+      options.warnings.append("Should be provided repository-version option")
+
   if len(options.warnings) != 0:
     print parser.print_help()
     for warning in options.warnings:
@@ -1231,6 +1261,7 @@ def main():
     "pass": options.password
   }
   Options.REPORT_FILE = options.report
+  Options.REPO_VERSION = options.repo_version
 
   if action in action_list:
     Options.initialize()

http://git-wip-us.apache.org/repos/asf/ambari/blob/8775d884/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ClusterStackVersionResourceProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ClusterStackVersionResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ClusterStackVersionResourceProviderTest.java
index e05405c..29677fc 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ClusterStackVersionResourceProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ClusterStackVersionResourceProviderTest.java
@@ -27,6 +27,7 @@ import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.actionmanager.ActionManager;
 import org.apache.ambari.server.actionmanager.HostRoleCommand;
 import org.apache.ambari.server.agent.AgentEnv;
+import org.apache.ambari.server.agent.CommandReport;
 import org.apache.ambari.server.agent.DiskInfo;
 import org.apache.ambari.server.agent.HostInfo;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
@@ -45,6 +46,8 @@ import org.apache.ambari.server.orm.InMemoryDefaultTestModule;
 import org.apache.ambari.server.orm.dao.RepositoryVersionDAO;
 import org.apache.ambari.server.orm.entities.HostVersionEntity;
 import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
+import org.apache.ambari.server.serveraction.upgrades.FinalizeUpgradeAction;
+import org.apache.ambari.server.stack.StackManager;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.ConfigHelper;
@@ -59,9 +62,11 @@ import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.powermock.api.easymock.PowerMock;
 import org.powermock.core.classloader.annotations.PrepareForTest;
 import org.powermock.modules.junit4.PowerMockRunner;
 
+import java.lang.reflect.Field;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
@@ -89,8 +94,6 @@ import static org.junit.Assert.fail;
 /**
  * ClusterStackVersionResourceProvider tests.
  */
-//@RunWith(PowerMockRunner.class)
-@PrepareForTest(AmbariManagementControllerImpl.class)
 public class ClusterStackVersionResourceProviderTest {
 
   private Injector injector;
@@ -121,6 +124,7 @@ public class ClusterStackVersionResourceProviderTest {
     // Create instances of mocks
     repositoryVersionDAOMock = createNiceMock(RepositoryVersionDAO.class);
     configHelper = createNiceMock(ConfigHelper.class);
+
     // Initialize injector
     InMemoryDefaultTestModule module = new InMemoryDefaultTestModule();
     injector = Guice.createInjector(Modules.override(module).with(new MockModule()));
@@ -234,11 +238,126 @@ public class ClusterStackVersionResourceProviderTest {
     verify(managementController, response, clusters);
   }
 
+
+  /**
+   * Tests manual finalization scenario
+   * @throws Exception
+   */
+  @Test
+  public void testUpdateResources() throws Exception {
+    Resource.Type type = Resource.Type.ClusterStackVersion;
+
+    AmbariManagementController managementController = createMock(AmbariManagementController.class);
+    Clusters clusters = createNiceMock(Clusters.class);
+    Cluster cluster = createNiceMock(Cluster.class);
+    StackId stackId = new StackId("HDP", "2.0.1");
+
+    final Host host1 = createNiceMock("host1", Host.class);
+    final Host host2 = createNiceMock("host2", Host.class);
+    expect(host1.getHostName()).andReturn("host1").anyTimes();
+    expect(host1.getOsFamily()).andReturn("redhat6").anyTimes();
+    expect(host2.getHostName()).andReturn("host2").anyTimes();
+    expect(host2.getOsFamily()).andReturn("redhat6").anyTimes();
+    replay(host1, host2);
+    Map<String, Host> hostsForCluster = new HashMap<String, Host>() {{
+      put(host1.getHostName(), host1);
+      put(host2.getHostName(), host2);
+    }};
+
+    ServiceComponentHost sch = createMock(ServiceComponentHost.class);
+    List<ServiceComponentHost> schs = Collections.singletonList(sch);
+
+    RepositoryVersionEntity repoVersion = new RepositoryVersionEntity();
+    repoVersion.setOperatingSystems(operatingSystemsJson);
+
+    ServiceOsSpecific.Package hivePackage = new ServiceOsSpecific.Package();
+    hivePackage.setName("hive");
+    List<ServiceOsSpecific.Package> packages = Collections.singletonList(hivePackage);
+
+    ActionManager actionManager = createNiceMock(ActionManager.class);
+
+    RequestStatusResponse response = createNiceMock(RequestStatusResponse.class);
+    ResourceProviderFactory resourceProviderFactory = createNiceMock(ResourceProviderFactory.class);
+    ResourceProvider csvResourceProvider = createNiceMock(ClusterStackVersionResourceProvider.class);
+
+    CommandReport report = createNiceMock(CommandReport.class);
+    FinalizeUpgradeAction finalizeUpgradeAction = createNiceMock(FinalizeUpgradeAction.class);
+
+    AbstractControllerResourceProvider.init(resourceProviderFactory);
+
+    Map<String, Map<String, String>> hostConfigTags = new HashMap<String, Map<String, String>>();
+    expect(configHelper.getEffectiveDesiredTags(anyObject(ClusterImpl.class), anyObject(String.class))).andReturn(hostConfigTags);
+
+    expect(managementController.getClusters()).andReturn(clusters).anyTimes();
+    expect(managementController.getAmbariMetaInfo()).andReturn(ambariMetaInfo).anyTimes();
+    expect(managementController.getAuthName()).andReturn("admin").anyTimes();
+    expect(managementController.getActionManager()).andReturn(actionManager).anyTimes();
+    expect(managementController.getJdkResourceUrl()).andReturn("/JdkResourceUrl").anyTimes();
+    expect(managementController.getPackagesForServiceHost(anyObject(ServiceInfo.class),
+            (Map<String, String>) anyObject(List.class), anyObject(String.class))).andReturn(packages).anyTimes();
+
+    expect(resourceProviderFactory.getHostResourceProvider(anyObject(Set.class), anyObject(Map.class),
+            eq(managementController))).andReturn(csvResourceProvider).anyTimes();
+
+    expect(clusters.getCluster(anyObject(String.class))).andReturn(cluster);
+    expect(clusters.getHostsForCluster(anyObject(String.class))).andReturn(hostsForCluster);
+
+    expect(cluster.getCurrentStackVersion()).andReturn(stackId);
+    expect(cluster.getServiceComponentHosts(anyObject(String.class))).andReturn(schs).anyTimes();
+
+    expect(sch.getServiceName()).andReturn("HIVE").anyTimes();
+
+    expect(repositoryVersionDAOMock.findByDisplayName(anyObject(String.class))).andReturn(repoVersion);
+
+    expect(actionManager.getRequestTasks(anyLong())).andReturn(Collections.<HostRoleCommand>emptyList()).anyTimes();
+
+    expect(finalizeUpgradeAction.execute(null)).andReturn(report);
+
+    expect(report.getStdOut()).andReturn("Dummy stdout");
+    expect(report.getStdErr()).andReturn("Dummy stderr");
+    expect(report.getStatus()).andReturn("COMPLETED");
+
+    // replay
+    replay(managementController, response, clusters, resourceProviderFactory, csvResourceProvider,
+            cluster, repositoryVersionDAOMock, configHelper, sch, actionManager, finalizeUpgradeAction, report);
+
+    ResourceProvider provider = AbstractControllerResourceProvider.getResourceProvider(
+            type,
+            PropertyHelper.getPropertyIds(type),
+            PropertyHelper.getKeyPropertyIds(type),
+            managementController);
+
+    injector.injectMembers(provider);
+
+    // Have to inject instance manually because injection via DI fails
+    Field field = ClusterStackVersionResourceProvider.class.getDeclaredField("finalizeUpgradeAction");
+    field.setAccessible(true);
+    field.set((ClusterStackVersionResourceProvider)provider, finalizeUpgradeAction);
+
+    // add the property map to a set for the request.  add more maps for multiple creates
+    Map<String, Object> properties = new LinkedHashMap<String, Object>();
+
+    // add properties to the request map
+    properties.put(ClusterStackVersionResourceProvider.CLUSTER_STACK_VERSION_CLUSTER_NAME_PROPERTY_ID, "Cluster100");
+    properties.put(ClusterStackVersionResourceProvider.CLUSTER_STACK_VERSION_STATE_PROPERTY_ID, "CURRENT");
+    properties.put(ClusterStackVersionResourceProvider.CLUSTER_STACK_VERSION_REPOSITORY_VERSION_PROPERTY_ID, "HDP-2.2.2.0-2561");
+
+    // create the request
+    Request request = PropertyHelper.getUpdateRequest(properties, null);
+
+    provider.updateResources(request, null);
+
+    // verify
+    verify(managementController, response);
+  }
+
+
   public class MockModule extends AbstractModule {
     @Override
     protected void configure() {
       bind(RepositoryVersionDAO.class).toInstance(repositoryVersionDAOMock);
       bind(ConfigHelper.class).toInstance(configHelper);
+      //bind(FinalizeUpgradeAction.class).toInstance(finalizeUpgradeAction);
     }
   }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/8775d884/ambari-server/src/test/python/TestUpgradeHelper.py
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/python/TestUpgradeHelper.py b/ambari-server/src/test/python/TestUpgradeHelper.py
index 2f47620..8e88883 100644
--- a/ambari-server/src/test/python/TestUpgradeHelper.py
+++ b/ambari-server/src/test/python/TestUpgradeHelper.py
@@ -78,6 +78,7 @@ class TestUpgradeHelper(TestCase):
       report = "report.txt"
       warnings = []
       printonly = False
+      repo_version = None
 
     args = ["update-configs"]
     modify_action_mock.return_value = MagicMock()
@@ -93,5 +94,57 @@ class TestUpgradeHelper(TestCase):
     self.assertEqual(options.clustername, upgradeHelper.Options.CLUSTER_NAME)
 
 
+  @patch("optparse.OptionParser")
+  @patch("upgradeHelper.finalize_ru")
+  @patch("__builtin__.open")
+  def test_Finalize_options(self, open_mock, finalize_ru_mock, option_parser_mock):
+    class options(object):
+      user = "test_user"
+      hostname = "127.0.0.1"
+      clustername = "test1"
+      password = "test_password"
+      upgrade_json =  None
+      from_stack = None
+      to_stack = None
+      logfile = "test.log"
+      report = None
+      warnings = []
+      printonly = False
+      repo_version = None
+
+    args = ["finalize-ru"]
+    test_mock = MagicMock()
+    test_mock.parse_args = lambda: (options, args)
+    option_parser_mock.return_value = test_mock
+
+    try:
+      upgradeHelper.main()
+    except upgradeHelper.FatalException:
+      # Expected
+      pass
+
+    class options(object):
+      user = "test_user"
+      hostname = "127.0.0.1"
+      clustername = "test1"
+      password = "test_password"
+      upgrade_json =  None
+      from_stack = None
+      to_stack = None
+      logfile = "test.log"
+      report = None
+      warnings = []
+      printonly = False
+      repo_version = 'HDP-2.2.2.0-2561'
+
+    args = ["finalize-ru"]
+    test_mock = MagicMock()
+    test_mock.parse_args = lambda: (options, args)
+    option_parser_mock.return_value = test_mock
+
+    upgradeHelper.main()
+    self.assertTrue(finalize_ru_mock.called)
+
+
 if __name__ == "__main__":
   unittest.main()