You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by at...@apache.org on 2014/12/17 18:02:15 UTC

ambari git commit: AMBARI-8758 Rolling upgrade process: Integrate API for manually triggered/continuable upgrade item. (atkach)

Repository: ambari
Updated Branches:
  refs/heads/trunk 92815dd58 -> fa294ad77


AMBARI-8758 Rolling upgrade process: Integrate API for manually triggered/continuable upgrade item. (atkach)


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

Branch: refs/heads/trunk
Commit: fa294ad774967de85e777337fdc5c681bdedd97a
Parents: 92815dd
Author: Andrii Tkach <at...@hortonworks.com>
Authored: Wed Dec 17 17:54:14 2014 +0200
Committer: Andrii Tkach <at...@hortonworks.com>
Committed: Wed Dec 17 19:01:47 2014 +0200

----------------------------------------------------------------------
 .../main/admin/stack_and_upgrade_controller.js  |   7 +-
 .../main/admin/stack_upgrade/upgrade_group.hbs  |  12 +-
 .../main/admin/stack_upgrade/upgrade_task.hbs   |   4 +-
 ambari-web/app/utils/ajax/ajax.js               |  14 +
 .../views/main/admin/stack_and_upgrade_view.js  |   8 +
 .../admin/stack_upgrade/upgrade_group_view.js   |  74 ++++-
 .../admin/stack_upgrade/upgrade_task_view.js    |  41 ++-
 .../admin/stack_upgrade/upgrade_wizard_view.js  |   4 +
 .../admin/stack_and_upgrade_controller_test.js  | 235 ++++++++++++++-
 .../stack_upgrade/upgrade_group_view_test.js    | 292 ++++++++++++++++++-
 10 files changed, 650 insertions(+), 41 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/fa294ad7/ambari-web/app/controllers/main/admin/stack_and_upgrade_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/admin/stack_and_upgrade_controller.js b/ambari-web/app/controllers/main/admin/stack_and_upgrade_controller.js
index 2a27e53..7e6a1cb 100644
--- a/ambari-web/app/controllers/main/admin/stack_and_upgrade_controller.js
+++ b/ambari-web/app/controllers/main/admin/stack_and_upgrade_controller.js
@@ -58,10 +58,15 @@ App.MainAdminStackAndUpgradeController = Em.Controller.extend(App.LocalStorage,
   targetVersions: [],
 
   /**
+   * properties that stored to localStorage to resume wizard progress
+   */
+  wizardStorageProperties: ['upgradeId', 'upgradeVersion'],
+
+  /**
    * restore data from localStorage
    */
   init: function () {
-    ['upgradeId', 'upgradeVersion'].forEach(function (property) {
+    this.get('wizardStorageProperties').forEach(function (property) {
       if (this.getDBProperty(property)) {
         this.set(property, this.getDBProperty(property));
       }

http://git-wip-us.apache.org/repos/asf/ambari/blob/fa294ad7/ambari-web/app/templates/main/admin/stack_upgrade/upgrade_group.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/admin/stack_upgrade/upgrade_group.hbs b/ambari-web/app/templates/main/admin/stack_upgrade/upgrade_group.hbs
index 1b2a374..f94ff98 100644
--- a/ambari-web/app/templates/main/admin/stack_upgrade/upgrade_group.hbs
+++ b/ambari-web/app/templates/main/admin/stack_upgrade/upgrade_group.hbs
@@ -82,8 +82,10 @@
         <a href="#" {{action toggleExpanded view.content controller.upgradeData.upgradeGroups target="view"}}>{{view.failedItem.context}}</a>
       </div>
       <div class="button-row">
-        <button class="btn btn-warning">{{t admin.stackUpgrade.dialog.continue}}</button>
-        <button class="btn">{{t common.retry}}</button>
+        {{#if view.ignoreAvailable}}
+          <button class="btn btn-warning" {{action continue view.failedItem target="view"}}>{{t admin.stackUpgrade.dialog.continue}}</button>
+        {{/if}}
+        <button class="btn" {{action retry view.failedItem target="view"}}>{{t common.retry}}</button>
       </div>
     </div>
   {{/if}}
@@ -96,9 +98,9 @@
         {{t admin.stackUpgrade.dialog.manualDone}}
       </div>
       <div class="button-row">
-        <button class="btn btn-danger">{{t admin.stackUpgrade.dialog.stop}}</button>
-        <button
-          class="btn btn-success" {{bindAttr disabled="view.isManualProceedDisabled"}}>{{t common.proceed}}</button>
+        <button class="btn btn-success" {{bindAttr disabled="view.isManualProceedDisabled"}} {{action complete view.manualItem target="view"}}>
+          {{t common.proceed}}
+        </button>
       </div>
     </div>
   {{/if}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/fa294ad7/ambari-web/app/templates/main/admin/stack_upgrade/upgrade_task.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/admin/stack_upgrade/upgrade_task.hbs b/ambari-web/app/templates/main/admin/stack_upgrade/upgrade_task.hbs
index 0630fee..5c9781a 100644
--- a/ambari-web/app/templates/main/admin/stack_upgrade/upgrade_task.hbs
+++ b/ambari-web/app/templates/main/admin/stack_upgrade/upgrade_task.hbs
@@ -33,7 +33,7 @@
             <a title="Click to Copy" {{action copyOutLog view.content target="view"}} class="task-detail-copy">
               <i class="icon-copy"></i> {{t common.copy}}
             </a>
-            <a title="Open in New Window" {{action openLogWindow view.content.stdout target="view"}} class="task-detail-open-dialog">
+            <a title="Open in New Window" {{action openErrorLog target="view"}} class="task-detail-open-dialog">
               <i class="icon-external-link"></i> {{t common.open}}
             </a>
           </div>
@@ -49,7 +49,7 @@
             <a title="Click to Copy" {{action copyErrLog view.content target="view"}} class="task-detail-copy">
               <i class="icon-copy"></i> {{t common.copy}}
             </a>
-            <a title="Open in New Window" {{action openLogWindow view.content.stderr target="view"}} class="task-detail-open-dialog">
+            <a title="Open in New Window" {{action openOutLog target="view"}} class="task-detail-open-dialog">
               <i class="icon-external-link"></i> {{t common.open}}
             </a>
           </div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/fa294ad7/ambari-web/app/utils/ajax/ajax.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/ajax/ajax.js b/ambari-web/app/utils/ajax/ajax.js
index 00b4985..c955217 100644
--- a/ambari-web/app/utils/ajax/ajax.js
+++ b/ambari-web/app/utils/ajax/ajax.js
@@ -1377,6 +1377,20 @@ var urls = {
       }
     }
   },
+  'admin.upgrade.upgradeItem.setState': {
+    'real': '/clusters/{clusterName}/upgrades/{upgradeId}/upgrade_groups/{groupId}/upgrade_items/{itemId}',
+    'mock': '',
+    type: 'PUT',
+    'format': function (data) {
+      return {
+        data: JSON.stringify({
+          "UpgradeItem" : {
+            "status" : data.status
+          }
+        })
+      };
+    }
+  },
   'admin.stack_versions.all': {
     'real': '/clusters/{clusterName}/stack_versions?fields=ClusterStackVersions/*,repository_versions/RepositoryVersions/*&minimal_response=true',
     'mock': '/data/stack_versions/stack_version_all.json'

http://git-wip-us.apache.org/repos/asf/ambari/blob/fa294ad7/ambari-web/app/views/main/admin/stack_and_upgrade_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/admin/stack_and_upgrade_view.js b/ambari-web/app/views/main/admin/stack_and_upgrade_view.js
index b0bcd19..51daa6d 100644
--- a/ambari-web/app/views/main/admin/stack_and_upgrade_view.js
+++ b/ambari-web/app/views/main/admin/stack_and_upgrade_view.js
@@ -46,10 +46,14 @@ App.MainAdminStackAndUpgradeView = Em.View.extend({
     switch (App.get('upgradeState')) {
       case 'INIT':
         return (this.get('controller.targetVersions.length') > 0) ? Em.I18n.t('admin.stackUpgrade.state.available') : "";
+      case 'QUEUED':
       case 'PENDING':
       case 'IN_PROGRESS':
         return Em.I18n.t('admin.stackUpgrade.state.inProgress');
+      case 'TIMED_OUT':
       case 'FAILED':
+      case 'HOLDING_FAILED':
+      case 'HOLDING_TIMED_OUT':
       case 'HOLDING':
         return Em.I18n.t('admin.stackUpgrade.state.paused');
       case 'COMPLETED':
@@ -189,12 +193,16 @@ App.MainAdminStackAndUpgradeView = Em.View.extend({
             method = 'runPreUpgradeCheck';
           }
           break;
+        case 'QUEUED':
         case 'PENDING':
         case 'IN_PROGRESS':
           label = Em.I18n.t('admin.stackUpgrade.state.upgrading');
           method = 'openUpgradeDialog';
           break;
+        case 'TIMED_OUT':
         case 'FAILED':
+        case 'HOLDING_FAILED':
+        case 'HOLDING_TIMED_OUT':
         case 'HOLDING':
           label = Em.I18n.t('admin.stackUpgrade.state.resume');
           method = 'resumeUpgrade';

http://git-wip-us.apache.org/repos/asf/ambari/blob/fa294ad7/ambari-web/app/views/main/admin/stack_upgrade/upgrade_group_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/admin/stack_upgrade/upgrade_group_view.js b/ambari-web/app/views/main/admin/stack_upgrade/upgrade_group_view.js
index a1616f5..11bf0f2 100644
--- a/ambari-web/app/views/main/admin/stack_upgrade/upgrade_group_view.js
+++ b/ambari-web/app/views/main/admin/stack_upgrade/upgrade_group_view.js
@@ -28,11 +28,16 @@ App.upgradeGroupView = Em.View.extend({
   isManualDone: false,
 
   /**
+   * @type {Array}
+   */
+  failedStatuses: ['HOLDING_FAILED', 'HOLDING_TIMED_OUT', 'FAILED', 'TIMED_OUT'],
+
+  /**
    * progress info is a box that show running UpgradeItem
    * @type {boolean}
    */
   showProgressInfo: function () {
-    return this.get('content.isRunning') && this.get('runningItem');
+    return Boolean(this.get('content.isRunning') && this.get('runningItem'));
   }.property('content.isRunning', 'runningItem'),
 
   /**
@@ -46,7 +51,7 @@ App.upgradeGroupView = Em.View.extend({
    * @type {boolean}
    */
   showFailedInfo: function () {
-    return this.get('content.status') === 'FAILED' && this.get('failedItem');
+    return Boolean(this.get('failedStatuses').contains(this.get('content.status')) && this.get('failedItem'));
   }.property('content.status', 'failedItem'),
 
   /**
@@ -62,17 +67,78 @@ App.upgradeGroupView = Em.View.extend({
    * @type {object|undefined}
    */
   failedItem: function () {
-    return this.get('content.upgradeItems').findProperty('status', 'FAILED');
+    return this.get('content.upgradeItems').find(function (item) {
+      return this.get('failedStatuses').contains(item.get('status'));
+    }, this);
+  }.property('content.upgradeItems.@each.status'),
+
+  /**
+   * if upgrade group is manual it should have manual item
+   * @type {object|undefined}
+   */
+  manualItem: function () {
+    return this.get('content.upgradeItems').findProperty('status', 'HOLDING');
   }.property('content.upgradeItems.@each.status'),
 
   /**
    * @type {boolean}
    */
   isManualOpened: function () {
-    return this.get('content.status') === 'HOLDING';
+    return Boolean(this.get('manualItem'));
   }.property('content.status'),
 
   /**
+   * indicate whether failed item can be skipped in order to continue Upgrade
+   * @type {boolean}
+   */
+  ignoreAvailable: function () {
+    return Boolean(this.get('failedItem') && ['HOLDING_FAILED', 'HOLDING_TIMED_OUT'].contains(this.get('failedItem.status')));
+  }.property('failedItem'),
+
+  /**
+   * set status to Upgrade item
+   * @param item
+   * @param status
+   */
+  setUpgradeItemStatus: function(item, status) {
+    App.ajax.send({
+      name: 'admin.upgrade.upgradeItem.setState',
+      sender: this,
+      data: {
+        upgradeId: item.get('request_id'),
+        itemId: item.get('stage_id'),
+        groupId: item.get('group_id'),
+        status: status
+      }
+    });
+  },
+
+  /**
+   * set current upgrade item state to FAILED (for HOLDING_FAILED) or TIMED_OUT (for HOLDING_TIMED_OUT)
+   * in order to ignore fail and continue Upgrade
+   * @param {object} event
+   */
+  continue: function (event) {
+    this.setUpgradeItemStatus(event.context, event.context.get('status').slice(8));
+  },
+
+  /**
+   * set current upgrade item state to PENDING in order to retry Upgrade
+   * @param {object} event
+   */
+  retry: function (event) {
+    this.setUpgradeItemStatus(event.context, 'PENDING');
+  },
+
+  /**
+   * set current upgrade item state to COMPLETED in order to proceed
+   * @param {object} event
+   */
+  complete: function (event) {
+    this.setUpgradeItemStatus(event.context, 'COMPLETED');
+  },
+
+  /**
    * Only one UpgradeGroup or UpgradeItem could be expanded at a time
    * @param {object} event
    */

http://git-wip-us.apache.org/repos/asf/ambari/blob/fa294ad7/ambari-web/app/views/main/admin/stack_upgrade/upgrade_task_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/admin/stack_upgrade/upgrade_task_view.js b/ambari-web/app/views/main/admin/stack_upgrade/upgrade_task_view.js
index 1d77bca..bc5b006 100644
--- a/ambari-web/app/views/main/admin/stack_upgrade/upgrade_task_view.js
+++ b/ambari-web/app/views/main/admin/stack_upgrade/upgrade_task_view.js
@@ -50,6 +50,11 @@ App.upgradeTaskView = Em.View.extend({
   timer: null,
 
   /**
+   * @type {Array}
+   */
+  taskDetailsProperties: ['status', 'stdout', 'stderr', 'error_log', 'host_name', 'output_log'],
+
+  /**
    * poll for task details when task is expanded
    */
   doPolling: function () {
@@ -87,7 +92,7 @@ App.upgradeTaskView = Em.View.extend({
   getTaskDetailsSuccessCallback: function (data) {
     //TODO change request to get only one task when API ready
     var task = data.items[0].upgrade_items[0].tasks[0].Tasks;
-    ['status', 'stdout', 'stderr', 'error_log', 'host_name', 'output_log'].forEach(function (property) {
+    this.get('taskDetailsProperties').forEach(function (property) {
       this.set('content.' + property, task[property]);
     }, this);
   },
@@ -101,21 +106,35 @@ App.upgradeTaskView = Em.View.extend({
   },
 
   /**
-   * open logs in new window
+   * open stdout log in textarea to give ability to cope content
    * @param {object} event
    */
-  openLogWindow: function(event) {
-    var newWindow = window.open();
-    var newDocument = newWindow.document;
-    newDocument.write(event.context);
-    newDocument.close();
+  copyOutLog: function(event) {
+    this.toggleProperty('outputLogOpened');
   },
 
   /**
-   * open stdout log in textarea to give ability to cope content
-   * @param {object} event
+   * open error log in new window
    */
-  copyOutLog: function(event) {
-    this.toggleProperty('outputLogOpened');
+  openErrorLog: function () {
+    this.openLogWindow(this.get('content.stderr'));
+  },
+
+  /**
+   * open stdout log in new window
+   */
+  openOutLog: function () {
+    this.openLogWindow(this.get('content.stdout'));
+  },
+
+  /**
+   * open logs in new window
+   * @param {string} log
+   */
+  openLogWindow: function(log) {
+    var newWindow = window.open();
+    var newDocument = newWindow.document;
+    newDocument.write(log);
+    newDocument.close();
   }
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/fa294ad7/ambari-web/app/views/main/admin/stack_upgrade/upgrade_wizard_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/admin/stack_upgrade/upgrade_wizard_view.js b/ambari-web/app/views/main/admin/stack_upgrade/upgrade_wizard_view.js
index e0cd99a..377287b 100644
--- a/ambari-web/app/views/main/admin/stack_upgrade/upgrade_wizard_view.js
+++ b/ambari-web/app/views/main/admin/stack_upgrade/upgrade_wizard_view.js
@@ -57,12 +57,16 @@ App.upgradeWizardView = Em.View.extend({
    */
   upgradeStatusLabel: function() {
     switch (this.get('controller.upgradeData.Upgrade.request_status')) {
+      case 'QUEUED':
       case 'PENDING':
       case 'IN_PROGRESS':
         return Em.I18n.t('admin.stackUpgrade.state.inProgress');
       case 'COMPLETED':
         return Em.I18n.t('admin.stackUpgrade.state.completed');
+      case 'TIMED_OUT':
       case 'FAILED':
+      case 'HOLDING_FAILED':
+      case 'HOLDING_TIMED_OUT':
       case 'HOLDING':
         return Em.I18n.t('admin.stackUpgrade.state.paused');
       default:

http://git-wip-us.apache.org/repos/asf/ambari/blob/fa294ad7/ambari-web/test/controllers/main/admin/stack_and_upgrade_controller_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/admin/stack_and_upgrade_controller_test.js b/ambari-web/test/controllers/main/admin/stack_and_upgrade_controller_test.js
index 0341265..e7e5392 100644
--- a/ambari-web/test/controllers/main/admin/stack_and_upgrade_controller_test.js
+++ b/ambari-web/test/controllers/main/admin/stack_and_upgrade_controller_test.js
@@ -18,13 +18,13 @@
 
 
 var App = require('app');
-var controller;
 require('controllers/main/admin/stack_and_upgrade_controller');
 
 describe('App.MainAdminStackAndUpgradeController', function() {
 
-  beforeEach(function() {
-    controller = App.MainAdminStackAndUpgradeController.create({});
+  var controller = App.MainAdminStackAndUpgradeController.create({
+    getDBProperty: Em.K,
+    setDBProperty: Em.K
   });
 
   describe("#services", function () {
@@ -183,6 +183,11 @@ describe('App.MainAdminStackAndUpgradeController', function() {
         success: 'loadUpgradeDataSuccessCallback'
       })
     });
+    it("upgrade id is null", function() {
+      controller.set('upgradeId', null);
+      controller.loadUpgradeData();
+      expect(App.ajax.send.called).to.be.false;
+    });
   });
 
   describe("#loadUpgradeDataSuccessCallback()", function() {
@@ -277,5 +282,229 @@ describe('App.MainAdminStackAndUpgradeController', function() {
     });
   });
 
+  describe("#init()", function() {
+    before(function () {
+      sinon.stub(controller, 'getDBProperty', function (prop) {
+        return prop;
+      });
+    });
+    after(function () {
+      controller.getDBProperty.restore();
+    });
+    it("set properties", function () {
+      controller.set('wizardStorageProperties', ['prop1']);
+      controller.init();
+      expect(controller.get('prop1')).to.equal('prop1');
+    });
+  });
+
+  describe("#parseVersionsData()", function() {
+    it("", function() {
+      var data = {
+        items: [
+          {
+            ClusterStackVersions: {},
+            repository_versions: [
+              {
+                RepositoryVersions: {
+                  repository_version: '2.2',
+                  display_name: 'v1',
+                  id: '1'
+                }
+              }
+            ]
+          }
+        ]
+      };
+      expect(controller.parseVersionsData(data)).to.eql([
+        {
+          "repository_name": "v1",
+          "repository_id": "1",
+          "repository_version": "2.2"
+        }
+      ]);
+    });
+  });
+
+  describe("#upgrade()", function() {
+    before(function () {
+      sinon.stub(App.ajax, 'send', Em.K);
+      sinon.stub(controller, 'setDBProperty', Em.K);
+    });
+    after(function () {
+      App.ajax.send.restore();
+      controller.setDBProperty.restore();
+    });
+    it("make ajax call", function() {
+      controller.upgrade({
+        value: '2.2',
+        label: 'HDP-2.2'
+      });
+      expect(App.ajax.send.getCall(0).args[0]).to.eql({
+        name: 'admin.upgrade.start',
+        sender: controller,
+        data: {
+          version: '2.2'
+        },
+        success: 'upgradeSuccessCallback'
+      });
+      expect(controller.get('upgradeVersion')).to.equal('HDP-2.2');
+      expect(controller.setDBProperty.calledWith('upgradeVersion', 'HDP-2.2')).to.be.true;
+    });
+  });
+
+  describe("#upgradeSuccessCallback()", function() {
+    before(function () {
+      sinon.stub(App.clusterStatus, 'setClusterStatus', Em.K);
+      sinon.stub(controller, 'openUpgradeDialog', Em.K);
+      sinon.stub(controller, 'setDBProperty', Em.K);
+    });
+    after(function () {
+      App.clusterStatus.setClusterStatus.restore();
+      controller.openUpgradeDialog.restore();
+      controller.setDBProperty.restore();
+    });
+    it("open upgrade dialog", function() {
+      var data = {
+        resources: [
+          {
+            Upgrade: {
+              request_id: 1
+            }
+          }
+        ]
+      };
+      controller.upgradeSuccessCallback(data);
+      expect(controller.setDBProperty.calledWith('upgradeId', 1)).to.be.true;
+      expect(App.clusterStatus.setClusterStatus.calledOnce).to.be.true;
+      expect(controller.openUpgradeDialog.calledOnce).to.be.true;
+    });
+  });
+
+  describe("#updateUpgradeData()", function() {
+    beforeEach(function () {
+      sinon.stub(controller, 'initUpgradeData', Em.K);
+    });
+    afterEach(function () {
+      controller.initUpgradeData.restore();
+    });
+    it("data loaded first time", function() {
+      controller.set('upgradeData', null);
+      controller.updateUpgradeData({});
+      expect(controller.initUpgradeData.calledWith({})).to.be.true;
+    });
+    it("update loaded data", function() {
+      var oldData = Em.Object.create({
+        upgradeGroups: [
+          Em.Object.create({
+            group_id: 1,
+            upgradeItems: [
+              Em.Object.create({
+                stage_id: 1,
+                tasks: [
+                  Em.Object.create({
+                    id: 1
+                  })
+                ]
+              })
+            ]
+          })
+        ]
+      });
+      var newData = {
+        Upgrade: {
+          request_id: 1
+        },
+        upgrade_groups: [
+          {
+            UpgradeGroup: {
+              group_id: 1,
+              status: 'COMPLETED',
+              progress_percent: 100
+            },
+            upgrade_items: [
+              {
+                UpgradeItem: {
+                  stage_id: 1,
+                  status: 'COMPLETED',
+                  progress_percent: 100
+                },
+                tasks: [
+                  {
+                    Tasks: {
+                      id: 1,
+                      status: 'COMPLETED'
+                    }
+                  }
+                ]
+              }
+            ]
+          }
+        ]
+      };
+      controller.set('upgradeData', oldData);
+      controller.updateUpgradeData(newData);
+      expect(controller.get('upgradeData.upgradeGroups')[0].get('status')).to.equal('COMPLETED');
+      expect(controller.get('upgradeData.upgradeGroups')[0].get('progress_percent')).to.equal(100);
+      expect(controller.get('upgradeData.upgradeGroups')[0].get('upgradeItems')[0].get('status')).to.equal('COMPLETED');
+      expect(controller.get('upgradeData.upgradeGroups')[0].get('upgradeItems')[0].get('progress_percent')).to.equal(100);
+      expect(controller.get('upgradeData.upgradeGroups')[0].get('upgradeItems')[0].get('tasks')[0].get('status')).to.equal('COMPLETED');
+    });
+  });
+
+  describe("#initUpgradeData()", function() {
+    it("", function() {
+      var newData = {
+        Upgrade: {
+          request_id: 1
+        },
+        upgrade_groups: [
+          {
+            UpgradeGroup: {
+              group_id: 1
+            },
+            upgrade_items: [
+              {
+                UpgradeItem: {
+                  stage_id: 1
+                },
+                tasks: [
+                  {
+                    Tasks: {
+                      id: 1
+                    }
+                  }
+                ]
+              }
+            ]
+          }
+        ]
+      };
+      controller.initUpgradeData(newData);
+      expect(controller.get('upgradeData.Upgrade.request_id')).to.equal(1);
+      expect(controller.get('upgradeData.upgradeGroups')[0].get('group_id')).to.equal(1);
+      expect(controller.get('upgradeData.upgradeGroups')[0].get('upgradeItems')[0].get('stage_id')).to.equal(1);
+      expect(controller.get('upgradeData.upgradeGroups')[0].get('upgradeItems')[0].get('tasks')[0].get('id')).to.equal(1);
+    });
+  });
 
+  describe("#finish()", function() {
+    before(function () {
+      sinon.stub(App.clusterStatus, 'setClusterStatus', Em.K);
+      sinon.stub(controller, 'setDBProperty', Em.K);
+    });
+    after(function () {
+      App.clusterStatus.setClusterStatus.restore();
+      controller.setDBProperty.restore();
+    });
+    it("reset upgrade info", function() {
+      controller.finish();
+      expect(controller.get('upgradeId')).to.be.null;
+      expect(controller.setDBProperty.calledWith('upgradeId', undefined)).to.be.true;
+      expect(App.get('upgradeState')).to.equal('INIT');
+      expect(controller.get('upgradeVersion')).to.be.null;
+      expect(controller.setDBProperty.calledWith('upgradeVersion', undefined)).to.be.true;
+      expect(App.clusterStatus.setClusterStatus.calledOnce).to.be.true;
+    });
+  });
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/fa294ad7/ambari-web/test/views/main/admin/stack_upgrade/upgrade_group_view_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/views/main/admin/stack_upgrade/upgrade_group_view_test.js b/ambari-web/test/views/main/admin/stack_upgrade/upgrade_group_view_test.js
index f8154a5..0cabbd2 100644
--- a/ambari-web/test/views/main/admin/stack_upgrade/upgrade_group_view_test.js
+++ b/ambari-web/test/views/main/admin/stack_upgrade/upgrade_group_view_test.js
@@ -22,22 +22,108 @@ require('views/main/admin/stack_upgrade/upgrade_group_view');
 
 describe('App.upgradeGroupView', function () {
   var view = App.upgradeGroupView.create({
-    content: Em.Object.create({})
+    content: Em.Object.create({}),
+    failedStatuses: ['FAILED']
   });
 
-  describe.skip("#isFailed", function () {
+  describe("#runningItem", function () {
+    it("no running item", function () {
+      view.set('content.upgradeItems', []);
+      view.propertyDidChange('runningItem');
+      expect(view.get('runningItem')).to.be.undefined;
+    });
+    it("running item present", function () {
+      view.set('content.upgradeItems', [
+        {status: 'IN_PROGRESS'}
+      ]);
+      view.propertyDidChange('runningItem');
+      expect(view.get('runningItem')).to.be.eql({status: 'IN_PROGRESS'});
+    });
+  });
+
+  describe("#failedItem", function () {
+    it("no running item", function () {
+      view.set('content.upgradeItems', []);
+      view.propertyDidChange('failedItem');
+      expect(view.get('failedItem')).to.be.undefined;
+    });
+    it("running item present", function () {
+      view.set('content.upgradeItems', [Em.Object.create({status: 'FAILED'})]);
+      view.propertyDidChange('failedItem');
+      expect(view.get('failedItem')).to.be.eql(Em.Object.create({status: 'FAILED'}));
+    });
+  });
+
+  describe("#manualItem", function () {
+    it("no running item", function () {
+      view.set('content.upgradeItems', []);
+      view.propertyDidChange('manualItem');
+      expect(view.get('manualItem')).to.be.undefined;
+    });
+    it("running item present", function () {
+      view.set('content.upgradeItems', [Em.Object.create({status: 'HOLDING'})]);
+      view.propertyDidChange('manualItem');
+      expect(view.get('manualItem')).to.be.eql(Em.Object.create({status: 'HOLDING'}));
+    });
+  });
+
+  describe("#showProgressInfo", function () {
     var testCases = [
       {
         data: {
-          failedItem: undefined,
-          status: 'COMPLETED'
+          runningItem: undefined,
+          isRunning: false
+        },
+        result: false
+      },
+      {
+        data: {
+          runningItem: undefined,
+          isRunning: true
         },
         result: false
       },
       {
         data: {
-          failedItem: true,
-          status: 'COMPLETED'
+          runningItem: {},
+          isRunning: false
+        },
+        result: false
+      },
+      {
+        data: {
+          runningItem: {},
+          isRunning: true
+        },
+        result: true
+      }
+    ];
+    testCases.forEach(function (test) {
+      it('runningItem - ' + test.data.runningItem + ', isRunning - ' + test.data.isRunning, function () {
+        view.reopen({
+          runningItem: test.data.runningItem
+        });
+        view.set('content.isRunning', test.data.isRunning);
+        view.propertyDidChange('showProgressInfo');
+        expect(view.get('showProgressInfo')).to.equal(test.result);
+      });
+    });
+  });
+
+  describe("#isManualProceedDisabled", function () {
+    it("", function () {
+      view.set('isManualDone', true);
+      view.propertyDidChange('isManualProceedDisabled');
+      expect(view.get('isManualProceedDisabled')).to.be.false;
+    });
+  });
+
+  describe("#showFailedInfo", function () {
+    var testCases = [
+      {
+        data: {
+          failedItem: undefined,
+          status: 'PENDING'
         },
         result: false
       },
@@ -50,25 +136,201 @@ describe('App.upgradeGroupView', function () {
       },
       {
         data: {
-          failedItem: true,
+          failedItem: {},
+          status: 'PENDING'
+        },
+        result: false
+      },
+      {
+        data: {
+          failedItem: {},
           status: 'FAILED'
         },
         result: true
       }
     ];
+    testCases.forEach(function (test) {
+      it('failedItem - ' + test.data.failedItem + ', status - ' + test.data.status, function () {
+        view.reopen({
+          failedItem: test.data.failedItem
+        });
+        view.set('content.status', test.data.status);
+        view.propertyDidChange('showFailedInfo');
+        expect(view.get('showFailedInfo')).to.equal(test.result);
+      });
+    });
+  });
+
+  describe("#ignoreAvailable", function () {
+    var testCases = [
+      {
+        data: {
+          failedItem: undefined
+        },
+        result: false
+      },
+      {
+        data: {
+          failedItem: {status: 'PENDING'}
+        },
+        result: false
+      },
+      {
+        data: {
+          failedItem: {status: 'HOLDING_FAILED'}
+        },
+        result: true
+      },
+      {
+        data: {
+          failedItem: {status: 'HOLDING_TIMED_OUT'}
+        },
+        result: true
+      }
+    ];
+    testCases.forEach(function (test) {
+      it('failedItem - ' + test.data.failedItem, function () {
+        view.reopen({
+          failedItem: test.data.failedItem
+        });
+        view.propertyDidChange('ignoreAvailable');
+        expect(view.get('ignoreAvailable')).to.equal(test.result);
+      });
+    });
+  });
+
+  describe("#setUpgradeItemStatus()", function () {
+    before(function () {
+      sinon.stub(App.ajax, 'send', Em.K);
+    });
+    after(function () {
+      App.ajax.send.restore();
+    });
+    it("", function () {
+      view.setUpgradeItemStatus(Em.Object.create({request_id: 1, stage_id: 1, group_id: 1}), 'PENDING');
+      expect(App.ajax.send.getCall(0).args[0]).to.eql({
+        name: 'admin.upgrade.upgradeItem.setState',
+        sender: view,
+        data: {
+          upgradeId: 1,
+          itemId: 1,
+          groupId: 1,
+          status: 'PENDING'
+        }
+      })
+    });
+  });
+
+  describe("#continue()", function () {
+    before(function () {
+      sinon.stub(view, 'setUpgradeItemStatus', Em.K);
+    });
+    after(function () {
+      view.setUpgradeItemStatus.restore();
+    });
+    it("", function () {
+      view.continue({context: Em.Object.create({'status': 'HOLDING_FAILED'})});
+      expect(view.setUpgradeItemStatus.calledWith(Em.Object.create({'status': 'HOLDING_FAILED'}), 'FAILED')).to.be.true;
+    });
+  });
+
+  describe("#complete()", function () {
+    before(function () {
+      sinon.stub(view, 'setUpgradeItemStatus', Em.K);
+    });
+    after(function () {
+      view.setUpgradeItemStatus.restore();
+    });
+    it("", function () {
+      view.complete({context: Em.Object.create({'status': 'FAILED'})});
+      expect(view.setUpgradeItemStatus.calledWith(Em.Object.create({'status': 'FAILED'}), 'COMPLETED')).to.be.true;
+    });
+  });
+
+  describe("#retry()", function () {
+    before(function () {
+      sinon.stub(view, 'setUpgradeItemStatus', Em.K);
+    });
+    after(function () {
+      view.setUpgradeItemStatus.restore();
+    });
+    it("", function () {
+      view.retry({context: Em.Object.create({'status': 'FAILED'})});
+      expect(view.setUpgradeItemStatus.calledWith(Em.Object.create({'status': 'FAILED'}), 'PENDING')).to.be.true;
+    });
+  });
+
+  describe("#toggleExpanded()", function () {
+    before(function () {
+      sinon.stub(view, 'collapseLowerLevels', Em.K);
+    });
+    after(function () {
+      view.collapseLowerLevels.restore();
+    });
+    it("", function () {
+      var data = {
+        context: Em.Object.create({
+          isExpanded: true
+        }),
+        contexts: [
+          [],
+          [
+            Em.Object.create({
+              isExpanded: true
+            })
+          ]
+        ]
+      };
+      view.toggleExpanded(data);
+      expect(view.collapseLowerLevels.calledTwice).to.be.true;
+      expect(data.context.get('isExpanded')).to.be.false;
+      expect(data.contexts[1][0].get('isExpanded')).to.be.false;
+    });
+  });
+
+  describe("#collapseLowerLevels()", function () {
     beforeEach(function () {
-      this.mock = sinon.stub(view, 'get');
+      sinon.spy(view, 'collapseLowerLevels');
     });
     afterEach(function () {
-      this.mock.restore();
+      view.collapseLowerLevels.restore();
     });
-    testCases.forEach(function (test) {
-      it('failedItem - ' + test.data.failedItem + ', status - ' + test.data.status, function () {
-        view.get.withArgs('content.status').returns(test.data.status);
-        view.get.withArgs('failedItem').returns(test.data.failedItem);
-        view.propertyDidChange('isFailed');
-        expect(view.get('isFailed')).to.equal(test.result);
+    it("isExpanded false", function () {
+      var data = Em.Object.create({
+        isExpanded: false
+      });
+      view.collapseLowerLevels(data);
+      expect(view.collapseLowerLevels.calledOnce).to.be.true;
+      expect(data.get('isExpanded')).to.be.false;
+    });
+    it("ITEM expanded", function () {
+      var data = Em.Object.create({
+        isExpanded: true,
+        type: 'ITEM',
+        tasks: [
+          Em.Object.create({
+            isExpanded: true
+          })
+        ]
       });
+      view.collapseLowerLevels(data);
+      expect(view.collapseLowerLevels.calledOnce).to.be.true;
+      expect(data.get('tasks')[0].get('isExpanded')).to.be.false;
+    });
+    it("GROUP expanded", function () {
+      var data = Em.Object.create({
+        isExpanded: true,
+        type: 'GROUP',
+        upgradeItems: [
+          Em.Object.create({
+            isExpanded: true
+          })
+        ]
+      });
+      view.collapseLowerLevels(data);
+      expect(view.collapseLowerLevels.calledTwice).to.be.true;
+      expect(data.get('upgradeItems')[0].get('isExpanded')).to.be.false;
     });
   });
+
 });
\ No newline at end of file