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

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

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/branch-dev-patch-upgrade
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;
+    });
   });
 
 });