You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by ab...@apache.org on 2016/02/05 11:41:12 UTC

[1/2] ambari git commit: AMBARI-14933 ranger audit db password is required even when audit to db is off.(ababiichuk)

Repository: ambari
Updated Branches:
  refs/heads/trunk dc8e5c3c6 -> 315f502b0


AMBARI-14933 ranger audit db password is required even when audit to db is off.(ababiichuk)


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

Branch: refs/heads/trunk
Commit: 94932c99bb8ff5ad309c15c6a0ecf382f428c73b
Parents: dc8e5c3
Author: ababiichuk <ab...@hortonworks.com>
Authored: Fri Feb 5 09:35:47 2016 +0200
Committer: ababiichuk <ab...@hortonworks.com>
Committed: Fri Feb 5 11:15:13 2016 +0200

----------------------------------------------------------------------
 .../services/RANGER/themes/theme_version_1.json | 20 +++++++++++++++++++-
 .../services/RANGER/themes/theme_version_2.json | 20 +++++++++++++++++++-
 .../configs/widgets/config_widget_view.js       |  3 +++
 3 files changed, 41 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/94932c99/ambari-server/src/main/resources/stacks/HDP/2.2/services/RANGER/themes/theme_version_1.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/stacks/HDP/2.2/services/RANGER/themes/theme_version_1.json b/ambari-server/src/main/resources/stacks/HDP/2.2/services/RANGER/themes/theme_version_1.json
index e6724cd..cb5aa78 100644
--- a/ambari-server/src/main/resources/stacks/HDP/2.2/services/RANGER/themes/theme_version_1.json
+++ b/ambari-server/src/main/resources/stacks/HDP/2.2/services/RANGER/themes/theme_version_1.json
@@ -525,7 +525,25 @@
         },
         {
           "config": "admin-properties/audit_db_password",
-          "subsection-name": "subsection-ranger-audit-db-row2-col2"
+          "subsection-name": "subsection-ranger-audit-db-row2-col2",
+          "depends-on": [
+            {
+              "configs":[
+                "ranger-env/xasecure.audit.destination.db"
+              ],
+              "if": "${ranger-env/xasecure.audit.destination.db}",
+              "then": {
+                "property_value_attributes": {
+                  "visible": true
+                }
+              },
+              "else": {
+                "property_value_attributes": {
+                  "visible": false
+                }
+              }
+            }
+          ]
         }
       ]
     },

http://git-wip-us.apache.org/repos/asf/ambari/blob/94932c99/ambari-server/src/main/resources/stacks/HDP/2.3/services/RANGER/themes/theme_version_2.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/stacks/HDP/2.3/services/RANGER/themes/theme_version_2.json b/ambari-server/src/main/resources/stacks/HDP/2.3/services/RANGER/themes/theme_version_2.json
index fff50fb..2c469a9 100644
--- a/ambari-server/src/main/resources/stacks/HDP/2.3/services/RANGER/themes/theme_version_2.json
+++ b/ambari-server/src/main/resources/stacks/HDP/2.3/services/RANGER/themes/theme_version_2.json
@@ -904,7 +904,25 @@
         },
         {
           "config": "admin-properties/audit_db_password",
-          "subsection-name": "subsection-ranger-audit-db-row2-col2"
+          "subsection-name": "subsection-ranger-audit-db-row2-col2",
+          "depends-on": [
+            {
+              "configs":[
+                "ranger-env/xasecure.audit.destination.db"
+              ],
+              "if": "${ranger-env/xasecure.audit.destination.db}",
+              "then": {
+                "property_value_attributes": {
+                  "visible": true
+                }
+              },
+              "else": {
+                "property_value_attributes": {
+                  "visible": false
+                }
+              }
+            }
+          ]
         },
         {
           "config": "ranger-env/xasecure.audit.destination.solr",

http://git-wip-us.apache.org/repos/asf/ambari/blob/94932c99/ambari-web/app/views/common/configs/widgets/config_widget_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/common/configs/widgets/config_widget_view.js b/ambari-web/app/views/common/configs/widgets/config_widget_view.js
index 9858b75..e9eaed2 100644
--- a/ambari-web/app/views/common/configs/widgets/config_widget_view.js
+++ b/ambari-web/app/views/common/configs/widgets/config_widget_view.js
@@ -434,6 +434,9 @@ App.ConfigWidgetView = Em.View.extend(App.SupportsDependentConfigs, App.WidgetPo
         var conditionalConfig = serviceConfigs.filterProperty('filename',conditionalConfigFileName).findProperty('name', conditionalConfigName);
         if (conditionalConfig) {
           conditionalConfig.set(valueAttribute, valueAttributes[key]);
+          if (valueAttribute === 'isVisible') {
+            conditionalConfig.set('hiddenBySection', !valueAttributes[key]);
+          }
         }
       }
     }


[2/2] ambari git commit: AMBARI-14934 Can't remove YARN or MapReduce2.(ababiichuk)

Posted by ab...@apache.org.
AMBARI-14934 Can't remove YARN or MapReduce2.(ababiichuk)


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

Branch: refs/heads/trunk
Commit: 315f502b0e7fed166f7d91387fa41f85e8dc891a
Parents: 94932c9
Author: ababiichuk <ab...@hortonworks.com>
Authored: Fri Feb 5 12:37:25 2016 +0200
Committer: ababiichuk <ab...@hortonworks.com>
Committed: Fri Feb 5 12:37:25 2016 +0200

----------------------------------------------------------------------
 ambari-web/app/controllers/main/service/item.js | 107 ++++++++++++----
 ambari-web/app/messages.js                      |   4 +
 ambari-web/app/models/stack_service.js          |   2 +-
 ambari-web/app/utils/ajax/ajax.js               |   5 -
 .../test/controllers/main/service/item_test.js  | 122 +++++++++++++++----
 5 files changed, 189 insertions(+), 51 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/315f502b/ambari-web/app/controllers/main/service/item.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/service/item.js b/ambari-web/app/controllers/main/service/item.js
index a26c820..1aae7cd 100644
--- a/ambari-web/app/controllers/main/service/item.js
+++ b/ambari-web/app/controllers/main/service/item.js
@@ -966,31 +966,76 @@ App.MainServiceItemController = Em.Controller.extend(App.SupportClientConfigsDow
   },
 
   /**
+   * Returns interdependent services
+   *
+   * @param serviceName
+   * @returns {string[]}
+   */
+  interDependentServices: function(serviceName) {
+    var interDependentServices = [];
+    App.StackService.find(serviceName).get('requiredServices').forEach(function(requiredService) {
+      if (App.StackService.find(requiredService).get('requiredServices').contains(serviceName)) {
+        interDependentServices.push(requiredService);
+      }
+    });
+    return interDependentServices;
+  },
+
+  /**
    * find dependent services
-   * @param {string} serviceName
+   * @param {string[]} serviceNamesToDelete
    * @returns {Array}
    */
-  findDependentServices: function(serviceName) {
+  findDependentServices: function (serviceNamesToDelete) {
     var dependentServices = [];
 
-    App.StackService.find().forEach(function(stackService) {
-      if (App.Service.find(stackService.get('serviceName')).get('isLoaded')
-          && stackService.get('requiredServices').contains(serviceName)) {
-        dependentServices.push(stackService.get('serviceName'));
+    App.Service.find().forEach(function (service) {
+      if (!serviceNamesToDelete.contains(service.get('serviceName'))) {
+        var requiredServices = App.StackService.find(service.get('serviceName')).get('requiredServices');
+        serviceNamesToDelete.forEach(function (dependsOnService) {
+          if (requiredServices.contains(dependsOnService)) {
+            dependentServices.push(service.get('serviceName'));
+          }
+        });
       }
     }, this);
     return dependentServices;
   },
 
   /**
+   * @param serviceNames
+   * @returns {string}
+   */
+  servicesDisplayNames: function(serviceNames) {
+    return serviceNames.map(function(serviceName) {
+      return App.format.role(serviceName);
+    }).join(',');
+  },
+
+  /**
+   * Is services can be removed based on work status
+   * @param serviceNames
+   */
+  allowUninstallServices: function(serviceNames) {
+    return !App.Service.find().filter(function (service) {
+      return serviceNames.contains(service.get('serviceName'));
+    }).mapProperty('workStatus').some(function (workStatus) {
+      return !App.Service.allowUninstallStates.contains(workStatus);
+    });
+  },
+
+  /**
    * delete service action
    * @param {string} serviceName
    */
   deleteService: function(serviceName) {
-    var dependentServices = this.findDependentServices(serviceName),
-        self = this,
-        displayName = App.format.role(serviceName),
-        popupHeader = Em.I18n.t('services.service.delete.popup.header');
+    var self = this,
+      interDependentServices = this.interDependentServices(serviceName),
+      serviceNamesToDelete = interDependentServices.concat(serviceName),
+      dependentServices = this.findDependentServices(serviceNamesToDelete),
+      displayName = App.format.role(serviceName),
+      popupHeader = Em.I18n.t('services.service.delete.popup.header'),
+      dependentServicesToDeleteFmt = this.servicesDisplayNames(interDependentServices);
 
     if (App.Service.find().get('length') === 1) {
       //at least one service should be installed
@@ -1002,21 +1047,26 @@ App.MainServiceItemController = Em.Controller.extend(App.SupportClientConfigsDow
       });
     } else if (dependentServices.length > 0) {
       this.dependentServicesWarning(serviceName, dependentServices);
-    } else if (App.Service.allowUninstallStates.contains(App.Service.find(serviceName).get('workStatus'))) {
+    } else if (this.allowUninstallServices(serviceNamesToDelete)) {
       App.showConfirmationPopup(
-        function() {self.confirmDeleteService(serviceName)},
-        Em.I18n.t('services.service.delete.popup.warning').format(displayName),
+        function() {self.confirmDeleteService(serviceName, interDependentServices, dependentServicesToDeleteFmt)},
+        Em.I18n.t('services.service.delete.popup.warning').format(displayName) +
+        (interDependentServices.length ? Em.I18n.t('services.service.delete.popup.warning.dependent').format(dependentServicesToDeleteFmt) : ''),
         null,
         popupHeader,
         Em.I18n.t('common.delete'),
         true
       );
     } else {
+      var body = Em.I18n.t('services.service.delete.popup.mustBeStopped').format(displayName);
+      if (interDependentServices.length) {
+        body += Em.I18n.t('services.service.delete.popup.mustBeStopped.dependent').format(dependentServicesToDeleteFmt)
+      }
       App.ModalPopup.show({
         secondary: null,
         header: popupHeader,
         encodeBody: false,
-        body: Em.I18n.t('services.service.delete.popup.mustBeStopped').format(displayName)
+        body: body
       });
     }
   },
@@ -1048,9 +1098,13 @@ App.MainServiceItemController = Em.Controller.extend(App.SupportClientConfigsDow
   /**
    * Confirmation popup of service deletion
    * @param {string} serviceName
+   * @param {string[]} [dependentServiceNames]
+   * @param {string} [servicesToDeleteFmt]
    */
-  confirmDeleteService: function (serviceName) {
-    var message = Em.I18n.t('services.service.confirmDelete.popup.body').format(App.format.role(serviceName)),
+  confirmDeleteService: function (serviceName, dependentServiceNames, servicesToDeleteFmt) {
+    var message = dependentServiceNames && dependentServiceNames.length
+        ? Em.I18n.t('services.service.confirmDelete.popup.body.dependent').format(App.format.role(serviceName), servicesToDeleteFmt)
+        : Em.I18n.t('services.service.confirmDelete.popup.body').format(App.format.role(serviceName)),
         confirmKey = 'yes',
         self = this;
 
@@ -1060,7 +1114,7 @@ App.MainServiceItemController = Em.Controller.extend(App.SupportClientConfigsDow
        * @function onPrimary
        */
       onPrimary: function() {
-        self.deleteServiceCall(serviceName);
+        self.deleteServiceCall([serviceName].concat(dependentServiceNames));
         this._super();
       },
 
@@ -1105,22 +1159,31 @@ App.MainServiceItemController = Em.Controller.extend(App.SupportClientConfigsDow
 
   /**
    * Ajax call to delete service
-   * @param {string} serviceName
+   * @param {string[]} serviceNames
    * @returns {$.ajax}
    */
-  deleteServiceCall: function(serviceName) {
+  deleteServiceCall: function(serviceNames) {
+    var serviceToDeleteNow = serviceNames[0];
+    if (serviceNames.length > 1) {
+      var servicesToDeleteNext = serviceNames.slice(1);
+    }
     return App.ajax.send({
-      name : 'service.item.delete',
+      name : 'common.delete.service',
       sender: this,
       data : {
-        serviceName : serviceName
+        serviceName : serviceToDeleteNow,
+        servicesToDeleteNext: servicesToDeleteNext
       },
       success : 'deleteServiceCallSuccessCallback'
     });
   },
 
   deleteServiceCallSuccessCallback: function(data, ajaxOptions, params) {
-    window.location.reload();
+    if (params.servicesToDeleteNext) {
+      this.deleteServiceCall(params.servicesToDeleteNext);
+    } else {
+      window.location.reload();
+    }
   }
 
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/315f502b/ambari-web/app/messages.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index e341de4..f24876b 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -1693,12 +1693,16 @@ Em.I18n.translations = {
   'services.service.delete.lastService.popup.body': 'The <b>{0}</b> service can\'t be deleted, at least one service must be installed.',
   'services.service.delete.popup.dependentServices': 'Prior to deleting <b>{0}</b>, you must delete the following dependent services:',
   'services.service.delete.popup.mustBeStopped': 'Prior to deleting <b>{0}</b>, you must stop the service.',
+  'services.service.delete.popup.mustBeStopped.dependent': ' Along with dependent service <b>{0}</b>.',
   'services.service.delete.popup.warning': 'The <b>{0} service will be removed from Ambari and all configurations' +
   ' and configuration history will be lost.</b>',
+  'services.service.delete.popup.warning.dependent': '<b>Note! {0} will be deleted too.</b>',
   'services.service.confirmDelete.popup.header': 'Confirm Delete',
   'services.service.confirmDelete.popup.body': 'You must confirm delete of <b>{0}</b> by typing "yes"' +
   ' in the confirmation box. <b>This operation is not reversible and all configuration history will be lost.</b>',
 
+  'services.service.confirmDelete.popup.body.dependent': 'You must confirm delete of <b>{0}</b> and <b>{1}</b> by typing "yes"' +
+  ' in the confirmation box. <b>This operation is not reversible and all configuration history will be lost.</b>',
   'services.service.summary.unknown':'unknown',
   'services.service.summary.notRunning':'Not Running',
   'services.service.summary.notAvailable':'n/a',

http://git-wip-us.apache.org/repos/asf/ambari/blob/315f502b/ambari-web/app/models/stack_service.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/stack_service.js b/ambari-web/app/models/stack_service.js
index a7f5f4e..5814386 100644
--- a/ambari-web/app/models/stack_service.js
+++ b/ambari-web/app/models/stack_service.js
@@ -42,7 +42,7 @@ App.StackService = DS.Model.extend({
   stack: DS.belongsTo('App.Stack'),
   serviceComponents: DS.hasMany('App.StackServiceComponent'),
   configs: DS.attr('array'),
-  requiredServices: DS.attr('array'),
+  requiredServices: DS.attr('array', {defaultValue: []}),
 
   /**
    * @type {String[]}

http://git-wip-us.apache.org/repos/asf/ambari/blob/315f502b/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 423941f..8b89365 100644
--- a/ambari-web/app/utils/ajax/ajax.js
+++ b/ambari-web/app/utils/ajax/ajax.js
@@ -498,11 +498,6 @@ var urls = {
       };
     }
   },
-  'service.item.delete': {
-    'real': '/clusters/{clusterName}/services/{serviceName}',
-    'mock': '',
-    'type': 'DELETE'
-  },
   'service.item.smoke': {
     'real': '/clusters/{clusterName}/requests',
     'mock': '/data/wizard/deploy/poll_1.json',

http://git-wip-us.apache.org/repos/asf/ambari/blob/315f502b/ambari-web/test/controllers/main/service/item_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/service/item_test.js b/ambari-web/test/controllers/main/service/item_test.js
index 90a1d90..8fb772a 100644
--- a/ambari-web/test/controllers/main/service/item_test.js
+++ b/ambari-web/test/controllers/main/service/item_test.js
@@ -28,6 +28,28 @@ require('controllers/main/service/reassign_controller');
 require('controllers/main/service/item');
 var batchUtils = require('utils/batch_scheduled_requests');
 var testHelpers = require('test/helpers');
+var stackSerivceModel = {
+  'HDFS': Em.Object.create({
+    serviceName: 'HDFS',
+    requiredServices: ['ZOOKEEPER']
+  }),
+  'YARN': Em.Object.create({
+    serviceName: 'YARN',
+    requiredServices: ['MAPREDUCE2', 'HDFS']
+  }),
+  'MAPREDUCE2': Em.Object.create({
+    serviceName: 'MAPREDUCE2',
+    requiredServices: ['YARN']
+  }),
+  'TEZ': Em.Object.create({
+    serviceName: 'TEZ',
+    requiredServices: ['YARN']
+  }),
+  'HIVE': Em.Object.create({
+    serviceName: 'HIVE',
+    requiredServices: ['YARN', 'TEZ']
+  })
+};
 
 describe('App.MainServiceItemController', function () {
 
@@ -1182,35 +1204,50 @@ describe('App.MainServiceItemController', function () {
 
     beforeEach(function() {
       mainServiceItemController = App.MainServiceItemController.create({});
-      this.mockStackService = sinon.stub(App.StackService, 'find');
+      sinon.stub(App.StackService, 'find', function (serviceName) {
+        return stackSerivceModel[serviceName];
+      });
       this.mockService = sinon.stub(App.Service, 'find');
     });
     afterEach(function() {
-      this.mockStackService.restore();
+      App.StackService.find.restore();
       this.mockService.restore();
     });
 
-    it("no stack services", function() {
-      this.mockStackService.returns([]);
-      expect(mainServiceItemController.findDependentServices('S1')).to.be.empty;
+    it("no services", function() {
+      this.mockService.returns([]);
+      expect(mainServiceItemController.findDependentServices(['S1'])).to.be.empty;
     });
 
-    it("dependent service not installed", function() {
-      this.mockStackService.returns([Em.Object.create({serviceName: 'S2'})]);
-      this.mockService.withArgs('S2').returns(Em.Object.create({isLoaded: false}));
-      expect(mainServiceItemController.findDependentServices('S1')).to.be.empty;
+    it("service has dependencies", function() {
+      this.mockService.returns([
+        Em.Object.create({ serviceName: 'HDFS' }),
+        Em.Object.create({ serviceName: 'YARN' }),
+        Em.Object.create({ serviceName: 'MAPREDUCE2' }),
+        Em.Object.create({ serviceName: 'TEZ' }),
+        Em.Object.create({ serviceName: 'HIVE' })
+      ]);
+      expect(mainServiceItemController.findDependentServices(['YARN', 'MAPREDUCE2'])).to.eql(['TEZ', 'HIVE']);
     });
 
     it("service has no dependencies", function() {
-      this.mockStackService.returns([Em.Object.create({serviceName: 'S2', requiredServices: []})]);
-      this.mockService.withArgs('S2').returns(Em.Object.create({isLoaded: true}));
-      expect(mainServiceItemController.findDependentServices('S1')).to.be.empty;
+       this.mockService.returns([
+         Em.Object.create({ serviceName: 'HDFS' }),
+         Em.Object.create({ serviceName: 'YARN' }),
+         Em.Object.create({ serviceName: 'MAPREDUCE2' }),
+         Em.Object.create({ serviceName: 'TEZ' }),
+         Em.Object.create({ serviceName: 'HIVE' })
+      ]);
+      expect(mainServiceItemController.findDependentServices(['HIVE'])).to.be.empty;
     });
 
-    it("service has dependencies", function() {
-      this.mockStackService.returns([Em.Object.create({serviceName: 'S2', requiredServices: ['S1']})]);
-      this.mockService.withArgs('S2').returns(Em.Object.create({isLoaded: true}));
-      expect(mainServiceItemController.findDependentServices('S1')).to.eql(['S2']);
+    it("service has no dependencies (except interdependent)", function() {
+      this.mockService.returns([
+        Em.Object.create({ serviceName: 'HDFS' }),
+        Em.Object.create({ serviceName: 'YARN' }),
+        Em.Object.create({ serviceName: 'MAPREDUCE2' })
+      ]);
+      expect(mainServiceItemController.findDependentServices(['YARN', 'MAPREDUCE2'])).to.be.empty;
     });
 
   });
@@ -1222,12 +1259,20 @@ describe('App.MainServiceItemController', function () {
       mainServiceItemController = App.MainServiceItemController.create({});
       this.mockDependentServices = sinon.stub(mainServiceItemController, 'findDependentServices');
       sinon.stub(mainServiceItemController, 'dependentServicesWarning');
+      sinon.stub(mainServiceItemController, 'servicesDisplayNames', function(servicesDisplayNames) {
+        return servicesDisplayNames;
+      });
+      sinon.stub(mainServiceItemController, 'interDependentServices').returns([]);
+      this.allowUninstallServices = sinon.stub(mainServiceItemController, 'allowUninstallServices');
       this.mockService = sinon.stub(App.Service, 'find');
       sinon.stub(App, 'showConfirmationPopup');
       sinon.stub(App.ModalPopup, 'show');
       sinon.stub(App.format, 'role', function(name) {return name});
     });
     afterEach(function() {
+      mainServiceItemController.allowUninstallServices.restore();
+      mainServiceItemController.interDependentServices.restore();
+      mainServiceItemController.servicesDisplayNames.restore();
       this.mockDependentServices.restore();
       this.mockService.restore();
       mainServiceItemController.dependentServicesWarning.restore();
@@ -1250,21 +1295,23 @@ describe('App.MainServiceItemController', function () {
 
     it("service has installed dependent services", function() {
       this.mockDependentServices.returns(['S2']);
-      this.mockService.returns(Em.Object.create({workStatus: App.Service.statesMap.stopped}));
+      this.mockService.returns([Em.Object.create({workStatus: App.Service.statesMap.stopped}), Em.Object.create({workStatus: App.Service.statesMap.stopped})]);
       mainServiceItemController.deleteService('S1');
       expect(mainServiceItemController.dependentServicesWarning.calledWith('S1', ['S2'])).to.be.true;
     });
 
     it("service has not dependent services, and stopped", function() {
       this.mockDependentServices.returns([]);
-      this.mockService.returns(Em.Object.create({workStatus: App.Service.statesMap.stopped}));
+      this.allowUninstallServices.returns(true);
+      this.mockService.returns([Em.Object.create({workStatus: App.Service.statesMap.stopped}), Em.Object.create({workStatus: App.Service.statesMap.stopped})]);
       mainServiceItemController.deleteService('S1');
       expect(App.showConfirmationPopup.calledOnce).to.be.true;
     });
 
     it("service has not dependent services, and install failed", function() {
       this.mockDependentServices.returns([]);
-      this.mockService.returns(Em.Object.create({workStatus: App.Service.statesMap.install_failed}));
+      this.allowUninstallServices.returns(true);
+      this.mockService.returns([Em.Object.create({workStatus: App.Service.statesMap.install_failed}), Em.Object.create({workStatus: App.Service.statesMap.install_failed})]);
       mainServiceItemController.deleteService('S1');
       expect(App.showConfirmationPopup.calledOnce).to.be.true;
     });
@@ -1318,6 +1365,26 @@ describe('App.MainServiceItemController', function () {
     });
   });
 
+  describe('#interDependentServices', function() {
+    var mainServiceItemController;
+
+    beforeEach(function() {
+      sinon.stub(App.StackService, 'find', function (serviceName) {
+        return stackSerivceModel[serviceName];
+      });
+      mainServiceItemController = App.MainServiceItemController.create({});
+    });
+
+    afterEach(function() {
+      App.StackService.find.restore();
+    });
+
+    it('get interdependent services', function() {
+      expect(mainServiceItemController.interDependentServices('YARN')).to.eql(['MAPREDUCE2']);
+      expect(mainServiceItemController.interDependentServices('MAPREDUCE2')).to.eql(['YARN']);
+    });
+  });
+
   describe("#deleteServiceCall()", function() {
     var mainServiceItemController;
 
@@ -1326,12 +1393,13 @@ describe('App.MainServiceItemController', function () {
     });
 
     it("App.ajax.send should be called", function() {
-      mainServiceItemController.deleteServiceCall('S1');
-      var args = testHelpers.findAjaxRequest('name', 'service.item.delete');
+      mainServiceItemController.deleteServiceCall(['S1', 'S2']);
+      var args = testHelpers.findAjaxRequest('name', 'common.delete.service');
       expect(args[0]).exists;
       expect(args[0].sender).to.be.eql(mainServiceItemController);
       expect(args[0].data).to.be.eql({
-        serviceName : 'S1'
+        serviceName : 'S1',
+        servicesToDeleteNext: ['S2']
       });
     });
   });
@@ -1342,15 +1410,23 @@ describe('App.MainServiceItemController', function () {
     beforeEach(function() {
       mainServiceItemController = App.MainServiceItemController.create({});
       sinon.stub(window.location, 'reload');
+      sinon.spy(mainServiceItemController, 'deleteServiceCall');
     });
     afterEach(function() {
       window.location.reload.restore();
     });
 
     it("window.location.reload should be called", function() {
-      mainServiceItemController.deleteServiceCallSuccessCallback();
+      mainServiceItemController.deleteServiceCallSuccessCallback([], null, {});
+      expect(mainServiceItemController.deleteServiceCall.called).to.be.false;
       expect(window.location.reload.calledOnce).to.be.true;
     });
+
+    it("deleteServiceCall should be called", function() {
+      mainServiceItemController.deleteServiceCallSuccessCallback([], null, {servicesToDeleteNext: true});
+      expect(mainServiceItemController.deleteServiceCall.calledOnce).to.be.true;
+      expect(window.location.reload.called).to.be.false;
+    });
   });
 
 });