Repository: ambari Updated Branches: refs/heads/trunk af5a151ab -> e1e479057
AMBARI-10300. Unable to install all missing clients to host at once after skipping them on Add Service (alexantonenko) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/e1e47905 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/e1e47905 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/e1e47905 Branch: refs/heads/trunk Commit: e1e4790575b7d292e1e267af2a65dce81a800ed1 Parents: 1beb7d1 Author: Alex Antonenko <[email protected]> Authored: Tue Mar 31 21:09:06 2015 +0300 Committer: Alex Antonenko <[email protected]> Committed: Tue Mar 31 21:46:14 2015 +0300 ---------------------------------------------------------------------- ambari-web/app/controllers/main/host/details.js | 71 ++++++++----- ambari-web/app/templates/main/host/summary.hbs | 2 +- ambari-web/app/views/main/host/summary.js | 22 ++-- .../test/controllers/main/host/details_test.js | 72 ++++++++----- ambari-web/test/views/main/host/summary_test.js | 100 ++++++++++++++++++- 5 files changed, 205 insertions(+), 62 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/e1e47905/ambari-web/app/controllers/main/host/details.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/controllers/main/host/details.js b/ambari-web/app/controllers/main/host/details.js index 70d0ad4..40254d6 100644 --- a/ambari-web/app/controllers/main/host/details.js +++ b/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); }, /** http://git-wip-us.apache.org/repos/asf/ambari/blob/e1e47905/ambari-web/app/templates/main/host/summary.hbs ---------------------------------------------------------------------- diff --git a/ambari-web/app/templates/main/host/summary.hbs b/ambari-web/app/templates/main/host/summary.hbs index e13af3a..58d4cc6 100644 --- a/ambari-web/app/templates/main/host/summary.hbs +++ b/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> http://git-wip-us.apache.org/repos/asf/ambari/blob/e1e47905/ambari-web/app/views/main/host/summary.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views/main/host/summary.js b/ambari-web/app/views/main/host/summary.js index 92108b8..085ec9f 100644 --- a/ambari-web/app/views/main/host/summary.js +++ b/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('[email protected]'), + areClientsNotInstalled: function() { + return this.get('clients').someProperty('isInstallFailed', true) || !!this.get('installableClientComponents.length'); + }.property('[email protected]', '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'), http://git-wip-us.apache.org/repos/asf/ambari/blob/e1e47905/ambari-web/test/controllers/main/host/details_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/controllers/main/host/details_test.js b/ambari-web/test/controllers/main/host/details_test.js index 76d8de2..5a383a3 100644 --- a/ambari-web/test/controllers/main/host/details_test.js +++ b/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; }); }); http://git-wip-us.apache.org/repos/asf/ambari/blob/e1e47905/ambari-web/test/views/main/host/summary_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/views/main/host/summary_test.js b/ambari-web/test/views/main/host/summary_test.js index 538235a..40c4a69 100644 --- a/ambari-web/test/views/main/host/summary_test.js +++ b/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' + }) + ]); + }); + + }); });
