Преглед на файлове

AMBARI-10300. Unable to install all missing clients to host at once after skipping them on Add Service (alexantonenko)

Alex Antonenko преди 10 години
родител
ревизия
e1e4790575

+ 46 - 25
ambari-web/app/controllers/main/host/details.js

@@ -432,18 +432,20 @@ 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) {
+  addComponentWithCheck: function (event, skipConfirmation) {
     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));
+    App.get('router.mainAdminKerberosController').getKDCSessionState(this.addComponent.bind(this, event, skipConfirmation));
   },
   /**
    * Send command to server to install selected host component
    * @param {object} event
+   * @param {boolean} skipConfirmation
    * @method addComponent
    */
-  addComponent: function (event) {
+  addComponent: function (event, skipConfirmation) {
     var
       returnFunc,
       self = this,
@@ -475,34 +477,41 @@ App.MainHostDetailsController = Em.Controller.extend({
         }, Em.I18n.t('hosts.host.addComponent.' + componentName ));
         break;
       default:
-        returnFunc = this.addClientComponent(component);
+        returnFunc = this.addClientComponent(component, skipConfirmation);
       }
     return returnFunc;
   },
   /**
    * Send command to server to install client on selected host
    * @param component
+   * @param skipConfirmation
    */
-  addClientComponent: function (component) {
+  addClientComponent: function (component, skipConfirmation) {
     var self = this;
     var message = this.formatClientsMessage(component);
-    return App.ModalPopup.show({
-      primary: Em.I18n.t('hosts.host.addComponent.popup.confirm'),
-      header: Em.I18n.t('popup.confirmation.commonHeader'),
+    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'),
 
-      addComponentMsg: function () {
-        return Em.I18n.t('hosts.host.addComponent.msg').format(message);
-      }.property(),
+        addComponentMsg: function () {
+          return Em.I18n.t('hosts.host.addComponent.msg').format(message);
+        }.property(),
 
-      bodyClass: Em.View.extend({
-        templateName: require('templates/main/host/details/addComponentPopup')
-      }),
+        bodyClass: Em.View.extend({
+          templateName: require('templates/main/host/details/addComponentPopup')
+        }),
 
-      onPrimary: function () {
-        this.hide();
-        self.primary(component);
-      }
-    });
+        onPrimary: function () {
+          this.hide();
+          self.primary(component);
+        }
+      });
+    }
+    return returnFunction;
   },
 
   /**
@@ -540,7 +549,7 @@ App.MainHostDetailsController = Em.Controller.extend({
    * @method installNewComponentSuccessCallbƒack
    */
   installNewComponentSuccessCallback: function (data, opt, params) {
-    if (!data.Requests || !data.Requests.id) {
+    if (!data || !data.Requests || !data.Requests.id) {
       return false;
     }
     var self = this;
@@ -1913,12 +1922,24 @@ App.MainHostDetailsController = Em.Controller.extend({
     });
   },
 
-  reinstallClients: function(event) {
-    var clientsToInstall = event.context.filter(function(component) {
-      return ['INIT', 'INSTALL_FAILED'].contains(component.get('workStatus'));
+  installClients: function(event) {
+    var clientsToInstall = [],
+      clientsToAdd = [];
+    event.context.forEach(function (component) {
+      if (['INIT', 'INSTALL_FAILED'].contains(component.get('workStatus'))) {
+        clientsToInstall.push(component);
+      } else if (typeof component.get('workStatus') == 'undefined') {
+        clientsToAdd.push(component);
+      }
     });
-    if (!clientsToInstall.length) return;
-    this.sendComponentCommand(clientsToInstall, Em.I18n.t('host.host.details.installClients'), 'INSTALLED');
+    if (clientsToInstall.length) {
+      this.sendComponentCommand(clientsToInstall, Em.I18n.t('host.host.details.installClients'), 'INSTALLED');
+    }
+    clientsToAdd.forEach(function (component) {
+      this.addComponentWithCheck({
+        context: component
+      }, true);
+    }, this);
   },
 
   /**

+ 1 - 1
ambari-web/app/templates/main/host/summary.hbs

@@ -91,7 +91,7 @@
                         </a>
                       </li>
                       <li>
-                        <a href="javascript:void(null)" {{bindAttr class="view.areClientsInstallFailed::disabled" }} data-toggle="modal" {{action reinstallClients view.clients target="controller"}}>
+                        <a href="javascript:void(null)" {{bindAttr class="view.areClientsNotInstalled::disabled" }} data-toggle="modal" {{action installClients view.notInstalledClientComponents target="controller"}}>
                           {{t host.host.details.installClients}}
                         </a>
                       </li>

+ 12 - 10
ambari-web/app/views/main/host/summary.js

@@ -203,9 +203,9 @@ App.MainHostSummaryView = Em.View.extend({
    *
    * @type {bool}
    **/
-  areClientsInstallFailed: function() {
-    return this.get('clients').someProperty('isInstallFailed', true);
-  }.property('clients.@each.workStatus'),
+  areClientsNotInstalled: function() {
+    return this.get('clients').someProperty('isInstallFailed', true) || !!this.get('installableClientComponents.length');
+  }.property('clients.@each.workStatus', 'installableClientComponents.length'),
 
   /**
    * Check if some clients have stale configs
@@ -256,13 +256,19 @@ App.MainHostSummaryView = Em.View.extend({
     var clientComponents = App.StackServiceComponent.find().filterProperty('isClient');
     var installedServices = this.get('installedServices');
     var installedClients = this.get('clients').mapProperty('componentName');
-    var installableClients = clientComponents.filter(function(componentName) {
+    var installableClients = clientComponents.filter(function(component) {
       // service for current client is installed but client isn't installed on current host
-      return installedServices.contains(clientComponents.get('serviceName')) && !installedClients.contains(clientComponents.get('componentName'));
+      return installedServices.contains(component.get('serviceName')) && !installedClients.contains(component.get('componentName'));
     });
-    return installableClients.mapProperty('componentName');
+    return installableClients;
   }.property('content.hostComponents.length', 'installedServices.length'),
 
+  notInstalledClientComponents: function () {
+    return this.get('clients').filter(function(component) {
+      return ['INIT', 'INSTALL_FAILED'].contains(component.get('workStatus'));
+    }).concat(this.get('installableClientComponents'));
+  }.property('installableClientComponents.length', 'clients.length'),
+
   /**
    * List of components that may be added to the current host
    * @type {Em.Object[]}
@@ -270,7 +276,6 @@ App.MainHostSummaryView = Em.View.extend({
   addableComponents: function () {
     var components = [];
     var self = this;
-    var installableClients = this.get('installableClientComponents');
     var installedComponents = this.get('content.hostComponents').mapProperty('componentName');
     var addableToHostComponents = App.StackServiceComponent.find().filterProperty('isAddableToHost');
     var installedServices = this.get('installedServices');
@@ -283,9 +288,6 @@ App.MainHostSummaryView = Em.View.extend({
         components.pushObject(self.addableComponentObject.create({'componentName': addableComponent.get('componentName'), 'serviceName': addableComponent.get('serviceName')}));
       }
     });
-    if (installableClients.length > 0) {
-      components.pushObject(this.addableComponentObject.create({ 'componentName': 'CLIENTS', subComponentNames: installableClients }));
-    }
 
     return components;
   }.property('content.hostComponents.length', 'installableClientComponents', 'App.components.addableToHost.@each'),

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

@@ -493,6 +493,10 @@ describe('App.MainHostDetailsController', function () {
 
   describe('#addClientComponent()', function () {
 
+    var component = Em.Object.create({
+      componentName: ' Comp1'
+    });
+
     beforeEach(function () {
       sinon.spy(App.ModalPopup, "show");
       sinon.stub(controller, "primary", Em.K);
@@ -504,11 +508,15 @@ describe('App.MainHostDetailsController', function () {
     });
 
     it('any CLIENT component', function () {
-      var component = Em.Object.create({'componentName': 'Comp1'});
       var popup = controller.addClientComponent(component);
       expect(App.ModalPopup.show.calledOnce).to.be.true;
       popup.onPrimary();
-      expect(controller.primary.calledWith(Em.Object.create({'componentName': 'Comp1'}))).to.be.true;
+      expect(controller.primary.calledWith(component)).to.be.true;
+    });
+
+    it('should launch primary method without confirmation', function () {
+      controller.addClientComponent(component, true);
+      expect(controller.primary.calledWith(component)).to.be.true;
     });
   });
 
@@ -532,6 +540,11 @@ describe('App.MainHostDetailsController', function () {
       controller.showBackgroundOperationsPopup.restore();
     });
 
+    it('data is null', function () {
+      var data = {Requests: null};
+      expect(controller.installNewComponentSuccessCallback(null, {}, {})).to.be.false;
+      expect(controller.showBackgroundOperationsPopup.called).to.be.false;
+    });
     it('data.Requests is null', function () {
       var data = {Requests: null};
       expect(controller.installNewComponentSuccessCallback(data, {}, {})).to.be.false;
@@ -2124,42 +2137,51 @@ describe('App.MainHostDetailsController', function () {
     });
   });
 
-  describe('#reinstallClients()', function () {
+  describe('#installClients()', function () {
     beforeEach(function () {
-      sinon.stub(controller, 'sendComponentCommand');
+      sinon.stub(controller, 'sendComponentCommand', Em.K);
+      sinon.stub(controller, 'addComponentWithCheck', Em.K);
     });
     afterEach(function () {
       controller.sendComponentCommand.restore();
+      controller.addComponentWithCheck.restore();
     });
-    it('No clients to install', function () {
-      var event = {context: [
-        Em.Object.create({
-          workStatus: 'INSTALLED'
-        })
-      ]};
-      controller.reinstallClients(event);
+    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;
     });
-    it('No clients to install', function () {
-      var event = {context: [
-        Em.Object.create({
-          workStatus: 'INSTALLED'
-        }),
-        Em.Object.create({
-          workStatus: 'INIT'
-        }),
-        Em.Object.create({
-          workStatus: 'INSTALL_FAILED'
-        })
-      ]};
-      controller.reinstallClients(event);
+    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;
+        })
+      ], Em.I18n.t('host.host.details.installClients'), 'INSTALLED')).to.be.true;
+      expect(controller.addComponentWithCheck.called).to.be.false;
     });
   });
 

+ 99 - 1
ambari-web/test/views/main/host/summary_test.js

@@ -288,7 +288,7 @@ describe('App.MainHostSummaryView', function() {
           ])
         }),
         services: ['HDFS', 'YARN', 'MAPREDUCE2'],
-        e: ['MAPREDUCE2_CLIENT', 'NODEMANAGER', 'YARN_CLIENT', 'CLIENTS'],
+        e: ['MAPREDUCE2_CLIENT', 'NODEMANAGER', 'YARN_CLIENT'],
         m: 'some components are already installed'
       },
       {
@@ -377,4 +377,102 @@ describe('App.MainHostSummaryView', function() {
       expect(mainHostSummaryView.get('clientsWithCustomCommands')).to.have.deep.property('[0].commands[0].command', 'CUSTOMCOMMAND');
     });
   });
+
+  describe('#areClientsNotInstalled', function () {
+
+    var cases = [
+      {
+        clients: [
+          {
+            isInstallFailed: true
+          }
+        ],
+        installableClientComponents: [],
+        areClientsNotInstalled: true,
+        title: 'some clients failed to install, no clients to add'
+      },
+      {
+        clients: [
+          {
+            isInstallFailed: false
+          }
+        ],
+        installableClientComponents: [{}],
+        areClientsNotInstalled: true,
+        title: 'no clients failed to install, some clients to add'
+      },
+      {
+        clients: [
+          {
+            isInstallFailed: true
+          }
+        ],
+        installableClientComponents: [{}],
+        areClientsNotInstalled: true,
+        title: 'some clients failed to install, some clients to add'
+      },
+      {
+        clients: [
+          {
+            isInstallFailed: false
+          }
+        ],
+        installableClientComponents: [],
+        areClientsNotInstalled: false,
+        title: 'no clients failed to install, no clients to add'
+      }
+    ];
+
+    cases.forEach(function (item) {
+      it(item.title, function () {
+        mainHostSummaryView.reopen({
+          clients: item.clients,
+          installableClientComponents: item.installableClientComponents
+        });
+        expect(mainHostSummaryView.get('areClientsNotInstalled')).to.equal(item.areClientsNotInstalled);
+      });
+    });
+
+  });
+
+  describe('#notInstalledClientComponents', function () {
+
+    it('should concat not added clients and the ones that failed to install', function () {
+      mainHostSummaryView.reopen({
+        clients: [
+          Em.Object.create({
+            componentName: 'c0',
+            workStatus: 'INIT'
+          }),
+          Em.Object.create({
+            componentName: 'c1',
+            workStatus: 'INSTALL_FAILED'
+          }),
+          Em.Object.create({
+            componentName: 'c2',
+            workStatus: 'INSTALLED'
+          })
+        ],
+        installableClientComponents: [
+          Em.Object.create({
+            componentName: 'c3'
+          })
+        ]
+      });
+      expect(mainHostSummaryView.get('notInstalledClientComponents')).to.eql([
+        Em.Object.create({
+          componentName: 'c0',
+          workStatus: 'INIT'
+        }),
+        Em.Object.create({
+          componentName: 'c1',
+          workStatus: 'INSTALL_FAILED'
+        }),
+        Em.Object.create({
+          componentName: 'c3'
+        })
+      ]);
+    });
+
+  });
 });