Browse Source

AMBARI-10693. Install clients on host: dependent clients aren't installed, dependencies info is displayed behind BG operations popup (alexantonenko)

Alex Antonenko 10 years ago
parent
commit
14436286b4

+ 67 - 36
ambari-web/app/controllers/main/host/details.js

@@ -432,20 +432,18 @@ App.MainHostDetailsController = Em.Controller.extend({
    * add component as <code>addComponent<code> method but perform
    * kdc sessionstate if cluster is secure;
    * @param event
-   * @param skipConfirmation
    */
-  addComponentWithCheck: function (event, skipConfirmation) {
+  addComponentWithCheck: function (event) {
     var componentName = event.context ? event.context.get('componentName') : "";
     event.hiveMetastoreHost = (componentName == "HIVE_METASTORE" && !!this.get('content.hostName')) ? this.get('content.hostName') : null;
-    App.get('router.mainAdminKerberosController').getKDCSessionState(this.addComponent.bind(this, event, skipConfirmation));
+    App.get('router.mainAdminKerberosController').getKDCSessionState(this.addComponent.bind(this, event));
   },
   /**
    * Send command to server to install selected host component
    * @param {object} event
-   * @param {boolean} skipConfirmation
    * @method addComponent
    */
-  addComponent: function (event, skipConfirmation) {
+  addComponent: function (event) {
     var
       returnFunc,
       self = this,
@@ -477,41 +475,40 @@ App.MainHostDetailsController = Em.Controller.extend({
         }, Em.I18n.t('hosts.host.addComponent.' + componentName ));
         break;
       default:
-        returnFunc = this.addClientComponent(component, skipConfirmation);
+        returnFunc = this.addClientComponent(component);
       }
     return returnFunc;
   },
   /**
    * Send command to server to install client on selected host
    * @param component
-   * @param skipConfirmation
    */
-  addClientComponent: function (component, skipConfirmation) {
+  addClientComponent: function (component) {
     var self = this;
     var message = this.formatClientsMessage(component);
-    var returnFunction;
-    if (skipConfirmation) {
-      returnFunction = this.primary(component);
-    } else {
-      returnFunction = App.ModalPopup.show({
-        primary: Em.I18n.t('hosts.host.addComponent.popup.confirm'),
-        header: Em.I18n.t('popup.confirmation.commonHeader'),
+    return this.showAddComponentPopup(message, function () {
+      self.primary(component);
+    });
+  },
 
-        addComponentMsg: function () {
-          return Em.I18n.t('hosts.host.addComponent.msg').format(message);
-        }.property(),
+  showAddComponentPopup: function (message, primary) {
+    return App.ModalPopup.show({
+      primary: Em.I18n.t('hosts.host.addComponent.popup.confirm'),
+      header: Em.I18n.t('popup.confirmation.commonHeader'),
 
-        bodyClass: Em.View.extend({
-          templateName: require('templates/main/host/details/addComponentPopup')
-        }),
+      addComponentMsg: function () {
+        return Em.I18n.t('hosts.host.addComponent.msg').format(message);
+      }.property(),
 
-        onPrimary: function () {
-          this.hide();
-          self.primary(component);
-        }
-      });
-    }
-    return returnFunction;
+      bodyClass: Em.View.extend({
+        templateName: require('templates/main/host/details/addComponentPopup')
+      }),
+
+      onPrimary: function () {
+        this.hide();
+        primary();
+      }
+    });
   },
 
   /**
@@ -1933,7 +1930,10 @@ App.MainHostDetailsController = Em.Controller.extend({
 
   installClients: function(event) {
     var clientsToInstall = [],
-      clientsToAdd = [];
+      clientsToAdd = [],
+      missedComponents = [],
+      dependentComponents = [],
+      self = this;
     event.context.forEach(function (component) {
       if (['INIT', 'INSTALL_FAILED'].contains(component.get('workStatus'))) {
         clientsToInstall.push(component);
@@ -1941,14 +1941,45 @@ App.MainHostDetailsController = Em.Controller.extend({
         clientsToAdd.push(component);
       }
     });
-    if (clientsToInstall.length) {
-      this.sendComponentCommand(clientsToInstall, Em.I18n.t('host.host.details.installClients'), 'INSTALLED');
-    }
-    clientsToAdd.forEach(function (component) {
-      this.addComponentWithCheck({
-        context: component
-      }, true);
+    clientsToAdd.forEach(function (component, index, array) {
+      var dependencies = componentsUtils.checkComponentDependencies(component.get('componentName'), {
+        scope: 'host',
+        installedComponents: this.get('content.hostComponents').mapProperty('componentName')
+      }).reject(function (componentName) {
+          return array.mapProperty('componentName').contains(componentName);
+        });
+      if (dependencies.length) {
+        missedComponents.pushObjects(dependencies);
+        dependentComponents.push(component.get('displayName'));
+      }
     }, this);
+    missedComponents = missedComponents.uniq();
+    if (missedComponents.length) {
+      var popupMessage = Em.I18n.t('host.host.addComponent.popup.clients.dependedComponents.body').format(stringUtils.getFormattedStringFromArray(dependentComponents),
+        stringUtils.getFormattedStringFromArray(missedComponents.map(function(componentName) {
+          return App.StackServiceComponent.find(componentName).get('displayName');
+        })));
+      App.showAlertPopup(Em.I18n.t('host.host.addComponent.popup.dependedComponents.header'), popupMessage);
+    } else {
+      App.get('router.mainAdminKerberosController').getKDCSessionState(function () {
+        var sendInstallCommand = function () {
+          if (clientsToInstall.length) {
+            self.sendComponentCommand(clientsToInstall, Em.I18n.t('host.host.details.installClients'), 'INSTALLED');
+          }
+        };
+        if (clientsToAdd.length) {
+          var message = stringUtils.getFormattedStringFromArray(clientsToAdd.mapProperty('displayName'));
+          self.showAddComponentPopup(message, function () {
+            sendInstallCommand();
+            clientsToAdd.forEach(function (component) {
+              this.primary(component);
+            }, self);
+          });
+        } else {
+          sendInstallCommand();
+        }
+      });
+    }
   },
 
   /**

+ 1 - 0
ambari-web/app/messages.js

@@ -2142,6 +2142,7 @@ Em.I18n.translations = {
   'hosts.host.addComponent.deleteHostWithZooKeeper':'Deleting host with ZooKeeper Server may reconfigure such properties:<ul><li>ha.zookeeper.quorum</li><li>hbase.zookeeper.quorum</li><li>templeton.zookeeper.hosts</li><li>yarn.resourcemanager.zk-address</li><li>hive.zookeeper.quorum</li><li>hive.cluster.delegation.token.store.zookeeper.connectString</li></ul>',
   'host.host.addComponent.popup.dependedComponents.body': '{0} requires {1} to be installed along with it on the same host. Please add them first and then try adding {0}',
   'host.host.addComponent.popup.dependedComponents.header': 'Component dependencies',
+  'host.host.addComponent.popup.clients.dependedComponents.body': '{0} require {1} to be installed along with them on the same host. Please add them first',
   'hosts.host.zooKeeper.configs.save.note': 'This configuration is created by ambari while installing/deleting zookeeper component on a host',
   'hosts.host.addComponent.securityNote':'You are running your cluster in secure mode. You must set up the keytab for {0} on {1} before you proceed. Otherwise, the component will not be able to start properly.',
   'hosts.host.addComponent.popup.confirm':'Confirm Add',

+ 138 - 47
ambari-web/test/controllers/main/host/details_test.js

@@ -499,24 +499,44 @@ describe('App.MainHostDetailsController', function () {
     });
 
     beforeEach(function () {
-      sinon.spy(App.ModalPopup, "show");
-      sinon.stub(controller, "primary", Em.K);
+      sinon.stub(controller, 'showAddComponentPopup', Em.K);
     });
 
     afterEach(function () {
-      App.ModalPopup.show.restore();
-      controller.primary.restore();
+      controller.showAddComponentPopup.restore();
     });
 
     it('any CLIENT component', function () {
       var popup = controller.addClientComponent(component);
-      expect(App.ModalPopup.show.calledOnce).to.be.true;
-      popup.onPrimary();
-      expect(controller.primary.calledWith(component)).to.be.true;
+      expect(controller.showAddComponentPopup.calledOnce).to.be.true;
+    });
+
+  });
+
+  describe('#showAddComponentPopup()', function () {
+
+    var message = 'Comp1',
+      component = Em.Object.create({
+        componentName: ' Comp1'
+      });
+
+    beforeEach(function () {
+      sinon.spy(App.ModalPopup, 'show');
+      sinon.stub(controller, 'primary', Em.K);
+    });
+
+    afterEach(function () {
+      App.ModalPopup.show.restore();
+      controller.primary.restore();
     });
 
-    it('should launch primary method without confirmation', function () {
-      controller.addClientComponent(component, true);
+    it('should display add component confirmation', function () {
+      var popup = controller.showAddComponentPopup(message, function () {
+        controller.primary(component);
+      });
+      expect(App.ModalPopup.show.calledOnce).to.be.true;
+      expect(popup.get('addComponentMsg')).to.eql(Em.I18n.t('hosts.host.addComponent.msg').format(message));
+      popup.onPrimary();
       expect(controller.primary.calledWith(component)).to.be.true;
     });
   });
@@ -2148,50 +2168,121 @@ describe('App.MainHostDetailsController', function () {
   });
 
   describe('#installClients()', function () {
+
+    var cases = [
+        {
+          context: [
+            Em.Object.create({
+              componentName: 'c0',
+              workStatus: 'INSTALLED'
+            }),
+            Em.Object.create({
+              componentName: 'c1',
+              workStatus: 'INIT'
+            }),
+            Em.Object.create({
+              componentName: 'c2',
+              workStatus: 'INSTALL_FAILED'
+            })
+          ],
+          dependencies: {
+            c0: [],
+            c1: [],
+            c2: []
+          },
+          getKDCSessionStateCalled: true,
+          sendComponentCommandCalled: true,
+          showAlertPopupCalled: false,
+          title: 'No clients to add, some clients to install'
+        },
+        {
+          context: [
+            Em.Object.create({
+              componentName: 'c3',
+              displayName: 'c3'
+            })
+          ],
+          dependencies: {
+            c3: []
+          },
+          getKDCSessionStateCalled: true,
+          sendComponentCommandCalled: false,
+          showAlertPopupCalled: false,
+          title: 'No clients to install, some clients to add'
+        },
+        {
+          context: [
+            Em.Object.create({
+              componentName: 'c4',
+              displayName: 'c4'
+            })
+          ],
+          dependencies: {
+            c4: ['c5']
+          },
+          getKDCSessionStateCalled: false,
+          sendComponentCommandCalled: false,
+          showAlertPopupCalled: true,
+          title: 'Clients to add have unresolved dependencies'
+        },
+        {
+          context: [
+            Em.Object.create({
+              componentName: 'c5',
+              displayName: 'c5'
+            }),
+            Em.Object.create({
+              componentName: 'c6',
+              displayName: 'c6'
+            })
+          ],
+          dependencies: {
+            c5: ['c6'],
+            c6: ['c5']
+          },
+          getKDCSessionStateCalled: true,
+          sendComponentCommandCalled: false,
+          showAlertPopupCalled: false,
+          title: 'Clients to add have mutual dependencies'
+        }
+      ],
+      componentsUtils = require('utils/components');
+
     beforeEach(function () {
       sinon.stub(controller, 'sendComponentCommand', Em.K);
-      sinon.stub(controller, 'addComponentWithCheck', Em.K);
+      sinon.stub(controller, 'showAddComponentPopup', Em.K);
+      sinon.stub(App.get('router.mainAdminKerberosController'), 'getKDCSessionState', function (arg) {
+        return arg();
+      });
+      sinon.stub(App, 'showAlertPopup', Em.K);
+      sinon.stub(App.StackServiceComponent, 'find', function (componentName) {
+        return Em.Object.create({
+          displayName: componentName
+        });
+      });
+      controller.set('content.hostComponents', []);
     });
     afterEach(function () {
       controller.sendComponentCommand.restore();
-      controller.addComponentWithCheck.restore();
-    });
-    it('No clients to install, some clients to add', function () {
-      var event = {
-        context: [
-          Em.Object.create()
-        ]
-      };
-      controller.installClients(event);
-      expect(controller.sendComponentCommand.called).to.be.false;
-      expect(controller.addComponentWithCheck.calledWith({
-        context: Em.Object.create()
-      }, true)).to.be.true;
+      controller.showAddComponentPopup.restore();
+      App.get('router.mainAdminKerberosController').getKDCSessionState.restore();
+      App.showAlertPopup.restore();
+      App.StackServiceComponent.find.restore();
+      componentsUtils.checkComponentDependencies.restore();
     });
-    it('Some clients to install, no clients to add', function () {
-      var event = {
-        context: [
-          Em.Object.create({
-            workStatus: 'INSTALLED'
-          }),
-          Em.Object.create({
-            workStatus: 'INIT'
-          }),
-          Em.Object.create({
-            workStatus: 'INSTALL_FAILED'
-          })
-        ]
-      };
-      controller.installClients(event);
-      expect(controller.sendComponentCommand.calledWith([
-        Em.Object.create({
-          workStatus: 'INIT'
-        }),
-        Em.Object.create({
-          workStatus: 'INSTALL_FAILED'
-        })
-      ], Em.I18n.t('host.host.details.installClients'), 'INSTALLED')).to.be.true;
-      expect(controller.addComponentWithCheck.called).to.be.false;
+
+    cases.forEach(function (item) {
+      it(item.title, function () {
+        sinon.stub(componentsUtils, 'checkComponentDependencies', function (componentName, params) {
+          return item.dependencies[componentName];
+        });
+        controller.installClients({
+          context: item.context
+        });
+        expect(App.get('router.mainAdminKerberosController').getKDCSessionState.calledOnce).to.equal(item.getKDCSessionStateCalled);
+        expect(controller.sendComponentCommand.calledOnce).to.equal(item.sendComponentCommandCalled);
+        expect(App.showAlertPopup.calledOnce).to.equal(item.showAlertPopupCalled);
+      });
     });
   });