فهرست منبع

AMBARI-5493 UI unit tests for disable, security progress, high availability controllers. (atkach)

atkach 11 سال پیش
والد
کامیت
fa78c73d59

+ 3 - 0
ambari-web/app/assets/test/tests.js

@@ -28,7 +28,10 @@ require('test/controllers/global/configuration_controller_test');
 require('test/controllers/main/app_contoller_test');
 require('test/controllers/main/admin/cluster_test');
 require('test/controllers/main/admin/misc_controller_test');
+require('test/controllers/main/admin/highAvailability_controller_test');
 require('test/controllers/main/admin/security_test');
+require('test/controllers/main/admin/security/disable_test');
+require('test/controllers/main/admin/security/security_progress_controller_test');
 require('test/controllers/main/admin/security/add/addSecurity_controller_test');
 require('test/controllers/main/admin/security/add/step2_test');
 require('test/controllers/main/admin/security/add/step3_test');

+ 29 - 15
ambari-web/app/controllers/main/admin/highAvailability_controller.js

@@ -27,17 +27,22 @@ App.MainAdminHighAvailabilityController = Em.Controller.extend({
 
   dataIsLoaded: false,
 
+  /**
+   * enable High Availability
+   * @return {Boolean}
+   */
   enableHighAvailability: function () {
     var message = [];
+    var hostComponents = App.HostComponent.find();
     //Prerequisite Checks
     if (this.get('securityEnabled')) {
       this.showErrorPopup(Em.I18n.t('admin.highAvailability.error.security'));
-      return;
+      return false;
     } else {
-      if (App.HostComponent.find().findProperty('componentName', 'NAMENODE').get('workStatus') !== 'STARTED') {
+      if (hostComponents.findProperty('componentName', 'NAMENODE').get('workStatus') !== 'STARTED') {
         message.push(Em.I18n.t('admin.highAvailability.error.namenodeStarted'));
       }
-      if (App.HostComponent.find().filterProperty('componentName', 'ZOOKEEPER_SERVER').length < 3) {
+      if (hostComponents.filterProperty('componentName', 'ZOOKEEPER_SERVER').length < 3) {
         message.push(Em.I18n.t('admin.highAvailability.error.zooKeeperNum'));
       }
 
@@ -46,10 +51,11 @@ App.MainAdminHighAvailabilityController = Em.Controller.extend({
       }
       if (message.length > 0) {
         this.showErrorPopup(message);
-        return;
+        return false;
       }
     }
     App.router.transitionTo('enableHighAvailability');
+    return true;
   },
 
   disableHighAvailability: function () {
@@ -80,12 +86,15 @@ App.MainAdminHighAvailabilityController = Em.Controller.extend({
     if ('global' in configs) {
       this.set('tag', configs['global'].tag);
       this.getServiceConfigsFromServer();
-    }
-    else {
+    } else {
       this.showErrorPopup(Em.I18n.t('admin.security.status.error'));
     }
   },
 
+  /**
+   * get service configs from server and
+   * indicate whether security is enabled
+   */
   getServiceConfigsFromServer: function () {
     var tags = [
       {
@@ -95,20 +104,25 @@ App.MainAdminHighAvailabilityController = Em.Controller.extend({
     ];
     var data = App.router.get('configurationController').getConfigsByTags(tags);
     var configs = data.findProperty('tag', this.get('tag')).properties;
-    if (configs && (configs['security_enabled'] === 'true' || configs['security_enabled'] === true)) {
-      this.set('securityEnabled', true);
+    var securityEnabled = !!(configs && (configs['security_enabled'] === 'true' || configs['security_enabled'] === true));
+    this.set('securityEnabled', securityEnabled);
+    this.set('dataIsLoaded', true);
+  },
+  /**
+   * join or wrap message depending on whether it is array or string
+   * @param message
+   * @return {*}
+   */
+  joinMessage: function (message) {
+    if (Array.isArray(message)) {
+      return message.join('<br/>');
     } else {
-      this.set('securityEnabled', false);
+      return '<p>' + message + '</p>';
     }
-    this.set('dataIsLoaded', true);
   },
 
   showErrorPopup: function (message) {
-    if(Array.isArray(message)){
-      message = message.join('<br/>');
-    } else {
-      message = '<p>' + message + '</p>';
-    }
+    message = this.joinMessage(message);
     App.ModalPopup.show({
       header: Em.I18n.t('common.error'),
       bodyClass: Ember.View.extend({

+ 0 - 13
ambari-web/app/controllers/main/admin/security/add/step4.js

@@ -382,19 +382,6 @@ App.MainAdminSecurityAddStep4Controller = App.MainAdminSecurityProgressControlle
     return true;
   },
 
-  deleteComponents: function(componentName, hostName) {
-    App.ajax.send({
-      name: 'admin.delete_component',
-      sender: this,
-      data: {
-        componentName: componentName,
-        hostName: hostName
-      },
-      success: 'onDeleteComplete',
-      error: 'onDeleteError'
-    });
-  },
-
   onDeleteComplete: function () {
     var deleteAtsCommand = this.get('commands').findProperty('name', 'DELETE_ATS');
     console.warn('APP_TIMELINE_SERVER doesn\'t support security mode. It has been removed from YARN service ');

+ 110 - 68
ambari-web/app/controllers/main/admin/security/disable.js

@@ -17,17 +17,42 @@
  */
 
 var App = require('app');
-App.MainAdminSecurityDisableController = App.MainAdminSecurityProgressController.extend({
+require('controllers/main/admin/security/security_progress_controller');
 
+App.MainAdminSecurityDisableController = App.MainAdminSecurityProgressController.extend({
   name: 'mainAdminSecurityDisableController',
   secureServices: [],
+  /**
+   * values of site configs when security disabled
+   */
+  secureConfigValuesMap: {
+    'dfs.datanode.address': '0.0.0.0:50010',
+    'dfs.datanode.http.address': '0.0.0.0:50075',
+    'mapred.task.tracker.task-controller': 'org.apache.hadoop.mapred.DefaultTaskController',
+    'yarn.nodemanager.container-executor.class': 'org.apache.hadoop.yarn.server.nodemanager.DefaultContainerExecutor',
+    'hbase.security.authentication': 'simple',
+    'hbase.rpc.engine': 'org.apache.hadoop.hbase.ipc.WritableRpcEngine',
+    'hbase.security.authorization': 'false',
+    'zookeeper.znode.parent': '/hbase-unsecure',
+    'hive.security.authorization.enabled': 'false'
+  },
 
+  isSubmitDisabled: function () {
+    return !(this.get('commands').someProperty('isError') || this.get('commands').everyProperty('isSuccess'));
+  }.property('commands.@each.isCompleted'),
+
+  /**
+   * clear step info
+   */
   clearStep: function () {
     this.get('commands').clear();
     this.get('secureServices').clear();
     this.get('serviceConfigTags').clear();
   },
 
+  /**
+   * load info required by current step
+   */
   loadStep: function () {
     this.clearStep();
     var commands = App.db.getSecurityDeployCommands();
@@ -50,30 +75,48 @@ App.MainAdminSecurityDisableController = App.MainAdminSecurityProgressController
     } else {
       this.loadCommands();
       this.addInfoToCommands();
-      var runningOperations = App.router.get('backgroundOperationsController.services').filterProperty('isRunning');
-      var stopAllOperation = runningOperations.findProperty('name', 'Stop All Services');
-      var stopCommand = this.get('commands').findProperty('name', 'STOP_SERVICES');
-      if (stopCommand.get('name') === 'STOP_SERVICES' && stopAllOperation) {
-        stopCommand.set('requestId', stopAllOperation.get('id'));
-      }
+      this.syncStopServicesCommand();
     }
     this.loadSecureServices();
     this.addObserverToCommands();
     this.moveToNextCommand();
   },
 
+  /**
+   * resume info about commands from local storage
+   * @return {Boolean}
+   */
+  resumeCommands: function () {
+    var commands = App.db.getSecurityDeployCommands();
+    if (!commands || commands.length === 0) return false;
 
-  enableSubmit: function () {
-    if (this.get('commands').someProperty('isError', true) || this.get('commands').everyProperty('isSuccess', true)) {
-      this.set('isSubmitDisabled', false);
-    } else {
-      this.set('isSubmitDisabled', true);
+    commands.forEach(function (_command) {
+      this.get('commands').pushObject(App.Poll.create(_command));
+    }, this);
+    var runningCommand = this.get('commands').filterProperty('isStarted').findProperty('isCompleted', false);
+    if (runningCommand) {
+      runningCommand.set('isStarted', false);
     }
-  }.observes('commands.@each.isCompleted'),
+    return true;
+  },
 
+  /**
+   * synchronize existing background operation "Stop All Services" with command in Security wizard
+   */
+  syncStopServicesCommand: function () {
+    var runningOperations = App.router.get('backgroundOperationsController.services').filterProperty('isRunning');
+    var stopAllOperation = runningOperations.findProperty('name', 'Stop All Services');
+    var stopCommand = this.get('commands').findProperty('name', 'STOP_SERVICES');
+    if (stopCommand && stopAllOperation) {
+      stopCommand.set('requestId', stopAllOperation.get('id'));
+    }
+  },
 
+  /**
+   * load secure configs of installed services
+   */
   loadSecureServices: function () {
-    var secureServices = App.get('isHadoop2Stack')?require('data/HDP2/secure_configs'):require('data/secure_configs');
+    var secureServices = App.get('isHadoop2Stack') ? require('data/HDP2/secure_configs') : require('data/secure_configs');
     var installedServices = App.Service.find().mapProperty('serviceName');
     this.get('secureServices').pushObject(secureServices.findProperty('serviceName', 'GENERAL'));
     //General (only non service tab) tab is always displayed
@@ -85,68 +128,67 @@ App.MainAdminSecurityDisableController = App.MainAdminSecurityProgressController
     }, this);
   },
 
-
+  /**
+   * manage configurations from serviceConfigTags
+   * @return {Boolean}
+   */
   manageSecureConfigs: function () {
-    try {
-      this.get('serviceConfigTags').forEach(function (_serviceConfigTags, index) {
+    var serviceConfigTags = this.get('serviceConfigTags');
+    var secureProperties = this.get('secureProperties');
+    var secureMapping = this.get('secureMapping');
+    if (!serviceConfigTags || !secureProperties || !secureMapping) {
+      var command = this.get('commands').findProperty('name', 'APPLY_CONFIGURATIONS');
+      command.set('isSuccess', false);
+      command.set('isError', true);
+      return false;
+    } else {
+      serviceConfigTags.forEach(function (_serviceConfigTags) {
         _serviceConfigTags.newTagName = 'version' + (new Date).getTime();
         if (_serviceConfigTags.siteName === 'global') {
-          this.get('secureProperties').forEach(function (_config) {
-            if (_config.name in _serviceConfigTags.configs) {
-              delete _serviceConfigTags.configs[_config.name];
-            }
-          }, this);
+          this.deleteDisabledGlobalConfigs(secureProperties, _serviceConfigTags);
           _serviceConfigTags.configs.security_enabled = 'false';
         } else {
-          this.get('secureMapping').filterProperty('filename', _serviceConfigTags.siteName + '.xml').forEach(function (_config) {
-            var configName = _config.name;
-            if (configName in _serviceConfigTags.configs) {
-              switch (configName) {
-                case 'dfs.datanode.address':
-                  _serviceConfigTags.configs[configName] = '0.0.0.0:50010';
-                  break;
-                case 'dfs.datanode.http.address':
-                  _serviceConfigTags.configs[configName] = '0.0.0.0:50075';
-                  break;
-                case 'mapred.task.tracker.task-controller':
-                  _serviceConfigTags.configs[configName] = 'org.apache.hadoop.mapred.DefaultTaskController';
-                  break;
-                case 'yarn.nodemanager.container-executor.class':
-                  _serviceConfigTags.configs[configName] = 'org.apache.hadoop.yarn.server.nodemanager.DefaultContainerExecutor';
-                  break;
-                case 'hbase.security.authentication':
-                  _serviceConfigTags.configs[configName] = 'simple';
-                  break;
-                case 'hbase.rpc.engine':
-                  _serviceConfigTags.configs[configName] = 'org.apache.hadoop.hbase.ipc.WritableRpcEngine';
-                  break;
-                case 'hbase.security.authorization':
-                  _serviceConfigTags.configs[configName] = 'false';
-                  break;
-                case 'zookeeper.znode.parent':
-                  _serviceConfigTags.configs[configName] = '/hbase-unsecure';
-                  break;
-                case 'hive.security.authorization.enabled':
-                  _serviceConfigTags.configs[configName] = 'false';
-                  break;
-                default:
-                  delete _serviceConfigTags.configs[configName];
-              }
-            }
-            console.log("Not Deleted" + _config.name);
-          }, this);
+          this.modifySiteConfigs(secureMapping, _serviceConfigTags);
         }
       }, this);
-    } catch (err) {
-      var command = this.get('commands').findProperty('name', 'APPLY_CONFIGURATIONS');
-      command.set('isSuccess', false);
-      command.set('isError', true);
-      if (err) {
-        console.log("Error: Error occurred while applying secure configs to the server. Error message: " + err);
-      }
-      return false;
+      return true;
     }
+  },
+  /**
+   * delete global configs, which aren't required when security disabled
+   * @param secureProperties
+   * @param _serviceConfigTags
+   * @return {Boolean}
+   */
+  deleteDisabledGlobalConfigs: function (secureProperties, _serviceConfigTags) {
+    if (!secureProperties || !_serviceConfigTags) return false;
+    secureProperties.forEach(function (_config) {
+      if (_config.name in _serviceConfigTags.configs) {
+        delete _serviceConfigTags.configs[_config.name];
+      }
+    }, this);
+    return true;
+  },
+  /**
+   * delete unnecessary site configs and
+   * change config values
+   * @param secureMapping
+   * @param _serviceConfigTags
+   * @return {Boolean}
+   */
+  modifySiteConfigs: function (secureMapping, _serviceConfigTags) {
+    var secureConfigValuesMap = this.get('secureConfigValuesMap');
+    if (!secureMapping || !_serviceConfigTags) return false;
+    secureMapping.filterProperty('filename', _serviceConfigTags.siteName + '.xml').forEach(function (_config) {
+      var configName = _config.name;
+      if (configName in _serviceConfigTags.configs) {
+        if (secureConfigValuesMap[configName]) {
+          _serviceConfigTags.configs[configName] = secureConfigValuesMap[configName]
+        } else {
+          delete _serviceConfigTags.configs[configName]
+        }
+      }
+    }, this);
     return true;
   }
-
 });

+ 123 - 94
ambari-web/app/controllers/main/admin/security/security_progress_controller.js

@@ -17,38 +17,50 @@
  */
 
 var App = require('app');
-App.MainAdminSecurityProgressController = Em.Controller.extend({
 
+App.MainAdminSecurityProgressController = Em.Controller.extend({
   name: 'mainAdminSecurityProgressController',
-  secureMapping: function () {
-    if (App.get('isHadoop2Stack')) {
-      return require('data/HDP2/secure_mapping');
-    } else {
-      return require('data/secure_mapping');
-    }
-  }.property(App.isHadoop2Stack),
-  secureProperties: function () {
-    if (App.get('isHadoop2Stack')) {
-      return require('data/HDP2/secure_properties').configProperties;
-    } else {
-      return require('data/secure_properties').configProperties;
-    }
-  }.property(App.isHadoop2Stack),
+
   commands: [],
   configs: [],
   serviceConfigTags: [],
   globalProperties: [],
   totalSteps: 3,
   isSubmitDisabled: true,
-
-
   hasHostPopup: true,
   services: [],
   serviceTimestamp: null,
+  operationsInfo: [
+    {
+      name: 'STOP_SERVICES',
+      realUrl: '/services',
+      testUrl: '/data/wizard/deploy/2_hosts/poll_1.json',
+      data: '{"RequestInfo": {"context" :"' + Em.I18n.t('requestInfo.stopAllServices') + '"}, "Body": {"ServiceInfo": {"state": "INSTALLED"}}}'
+    },
+    {
+      name: 'START_SERVICES',
+      realUrl: '/services?params/run_smoke_test=true',
+      testUrl: '/data/wizard/deploy/2_hosts/poll_1.json',
+      data: '{"RequestInfo": {"context": "' + Em.I18n.t('requestInfo.startAllServices') + '"}, "Body": {"ServiceInfo": {"state": "STARTED"}}}'
+    }
+  ],
 
+  secureMapping: function () {
+    return (App.get('isHadoop2Stack')) ? require('data/HDP2/secure_mapping') : require('data/secure_mapping');
+  }.property('App.isHadoop2Stack'),
+  secureProperties: function () {
+    if (App.get('isHadoop2Stack')) {
+      return require('data/HDP2/secure_properties').configProperties;
+    } else {
+      return require('data/secure_properties').configProperties;
+    }
+  }.property('App.isHadoop2Stack'),
 
+  /**
+   * prepare and restart failed command
+   */
   retry: function () {
-    var failedCommand = this.get('commands').findProperty('isError', true);
+    var failedCommand = this.get('commands').findProperty('isError');
     if (failedCommand) {
       failedCommand.set('isStarted', false);
       failedCommand.set('isError', false);
@@ -56,21 +68,25 @@ App.MainAdminSecurityProgressController = Em.Controller.extend({
     }
   },
 
+  /**
+   * update info about progress of operation of commands
+   */
   updateServices: function () {
     this.services.clear();
     var services = this.get("services");
     this.get("commands").forEach(function (command) {
+      var polledData = command.get('polledData');
       var newService = Ember.Object.create({
-        name: command.label,
+        name: command.get('label'),
         hosts: []
       });
-      if (command && command.get("polledData")) {
-        var hostNames = command.get("polledData").mapProperty('Tasks.host_name').uniq();
+      if (polledData) {
+        var hostNames = polledData.mapProperty('Tasks.host_name').uniq();
         hostNames.forEach(function (name) {
           newService.hosts.push({
             name: name,
             publicName: name,
-            logTasks: command.polledData.filterProperty("Tasks.host_name", name)
+            logTasks: polledData.filterProperty("Tasks.host_name", name)
           });
         });
         services.push(newService);
@@ -78,7 +94,9 @@ App.MainAdminSecurityProgressController = Em.Controller.extend({
     });
     this.set('serviceTimestamp', App.dateTime());
   }.observes('commands.@each.polledData'),
-
+  /**
+   * initialize default commands
+   */
   loadCommands: function () {
     this.get('commands').pushObjects([
       App.Poll.create({name: 'STOP_SERVICES', label: Em.I18n.translations['admin.addSecurity.apply.stop.services'], isPolling: true }),
@@ -87,49 +105,58 @@ App.MainAdminSecurityProgressController = Em.Controller.extend({
     ]);
   },
 
-  addObserverToCommands: function() {
+  addObserverToCommands: function () {
     this.setIndex(this.get('commands'));
     this.addObserver('commands.@each.isSuccess', this, 'onCompleteCommand');
   },
-
-  setIndex: function(commandArray) {
-    commandArray.forEach(function(command,index){
-      command.set('index',index+1);
-    },this);
+  /**
+   * set index to each command
+   * @param commandArray
+   */
+  setIndex: function (commandArray) {
+    commandArray.forEach(function (command, index) {
+      command.set('index', index + 1);
+    }, this);
     this.set('totalSteps', commandArray.length);
   },
 
-  startCommand: function (commnad) {
+  startCommand: function (command) {
     if (this.get('commands').length === this.get('totalSteps')) {
-      if (!commnad) {
+      if (!command) {
         var startedCommand = this.get('commands').filterProperty('isStarted', true);
-        commnad = startedCommand.findProperty('isCompleted', false);
+        command = startedCommand.findProperty('isCompleted', false);
       }
-      if (commnad && commnad.get('isPolling') === true) {
-        commnad.set('isStarted', true);
-        commnad.start();
-      } else if (commnad && commnad.get('name') === 'APPLY_CONFIGURATIONS') {
-        commnad.set('isStarted', true);
-        if (App.testMode) {
-          commnad.set('isError', false);
-          commnad.set('isSuccess', true);
-        } else {
-          this.loadClusterConfigs();
-        }
-      } else if (commnad && commnad.get('name') === 'DELETE_ATS') {
-        commnad.set('isStarted', true);
-        if (App.testMode) {
-          commnad.set('isError', false);
-          commnad.set('isSuccess', true);
-        } else {
-          var timeLineServer = App.Service.find('YARN').get('hostComponents').findProperty('componentName', 'APP_TIMELINE_SERVER');
-          this.deleteComponents('APP_TIMELINE_SERVER', timeLineServer.get('host.hostName'));
+      if (command) {
+        if (command.get('isPolling')) {
+          command.set('isStarted', true);
+          command.start();
+        } else if (command.get('name') === 'APPLY_CONFIGURATIONS') {
+          command.set('isStarted', true);
+          if (App.testMode) {
+            command.set('isError', false);
+            command.set('isSuccess', true);
+          } else {
+            this.loadClusterConfigs();
+          }
+        } else if (command.get('name') === 'DELETE_ATS') {
+          command.set('isStarted', true);
+          if (App.testMode) {
+            command.set('isError', false);
+            command.set('isSuccess', true);
+          } else {
+            var timeLineServer = App.HostComponent.find().findProperty('componentName', 'APP_TIMELINE_SERVER');
+            this.deleteComponents('APP_TIMELINE_SERVER', timeLineServer.get('host.hostName'));
+          }
         }
+        return true;
       }
     }
+    return false;
   },
-
-
+  /**
+   * on command completion move to next command
+   * @return {Boolean}
+   */
   onCompleteCommand: function () {
     if (this.get('commands').length === this.get('totalSteps')) {
       var index = this.get('commands').filterProperty('isSuccess', true).length;
@@ -138,40 +165,37 @@ App.MainAdminSecurityProgressController = Em.Controller.extend({
         if (lastCompletedCommandResult) {
           var nextCommand = this.get('commands').objectAt(index);
           this.moveToNextCommand(nextCommand);
+          return true;
         }
       }
     }
+    return false;
   },
-
+  /**
+   * move to next command
+   * @param nextCommand
+   */
   moveToNextCommand: function (nextCommand) {
-    if (!nextCommand) {
-      nextCommand = this.get('commands').findProperty('isStarted', false);
-    }
+    nextCommand = nextCommand || this.get('commands').findProperty('isStarted', false);
     if (nextCommand) {
       this.startCommand(nextCommand);
+      return true;
     }
+    return false;
   },
 
+  /**
+   * add query information(url, data) to commands
+   */
   addInfoToCommands: function () {
-    this.addInfoToStopService();
-    this.addInfoToStartServices();
-  },
-
-
-  addInfoToStopService: function () {
-    var command = this.get('commands').findProperty('name', 'STOP_SERVICES');
-    var url = (App.testMode) ? '/data/wizard/deploy/2_hosts/poll_1.json' : App.apiPrefix + '/clusters/' + App.router.getClusterName() + '/services';
-    var data = '{"RequestInfo": {"context" :"' + Em.I18n.t('requestInfo.stopAllServices') + '"}, "Body": {"ServiceInfo": {"state": "INSTALLED"}}}';
-    command.set('url', url);
-    command.set('data', data);
-  },
-
-  addInfoToStartServices: function () {
-    var command = this.get('commands').findProperty('name', 'START_SERVICES');
-    var url = (App.testMode) ? '/data/wizard/deploy/2_hosts/poll_1.json' : App.apiPrefix + '/clusters/' + App.router.getClusterName() + '/services?params/run_smoke_test=true';
-    var data = '{"RequestInfo": {"context": "' + Em.I18n.t('requestInfo.startAllServices') + '"}, "Body": {"ServiceInfo": {"state": "STARTED"}}}';
-    command.set('url', url);
-    command.set('data', data);
+    var operationsInfo = this.get('operationsInfo');
+    var urlPrefix = App.apiPrefix + '/clusters/' + App.get('clusterName');
+    operationsInfo.forEach(function (operation) {
+      var command = this.get('commands').findProperty('name', operation.name);
+      var url = (App.testMode) ? operation.testUrl : urlPrefix + operation.realUrl;
+      command.set('url', url);
+      command.set('data', operation.data);
+    }, this);
   },
 
   loadClusterConfigs: function () {
@@ -184,23 +208,25 @@ App.MainAdminSecurityProgressController = Em.Controller.extend({
   },
 
   loadClusterConfigsSuccessCallback: function (data) {
-    var self = this;
     //prepare tags to fetch all configuration for a service
     this.get('secureServices').forEach(function (_secureService) {
-      self.setServiceTagNames(_secureService, data.Clusters.desired_configs);
-    },this);
+      this.setServiceTagNames(_secureService, data.Clusters.desired_configs);
+    }, this);
     this.getAllConfigurations();
   },
 
   loadClusterConfigsErrorCallback: function (request, ajaxOptions, error) {
     var command = this.get('commands').findProperty('name', 'APPLY_CONFIGURATIONS');
-    command .set('isSuccess', false);
-    command .set('isError', true);
+    command.set('isSuccess', false);
+    command.set('isError', true);
     console.log("TRACE: error code status is: " + request.status);
   },
 
   /**
-   * set tagnames for configuration of the *-site.xml
+   * set tag names according to installed services and desired configs
+   * @param secureService
+   * @param configs
+   * @return {Object}
    */
   setServiceTagNames: function (secureService, configs) {
     //var serviceConfigTags = this.get('serviceConfigTags');
@@ -218,6 +244,9 @@ App.MainAdminSecurityProgressController = Em.Controller.extend({
     return serviceConfigObj;
   },
 
+  /**
+   * form query data and apply security configurations to server
+   */
   applyConfigurationsToCluster: function () {
     var configData = [];
     this.get('serviceConfigTags').forEach(function (_serviceConfig) {
@@ -316,7 +345,13 @@ App.MainAdminSecurityProgressController = Em.Controller.extend({
     }, this);
   },
 
-  setServerConfigValue: function(configName, value) {
+  /**
+   * set specific server values to config
+   * @param configName
+   * @param value
+   * @return {*}
+   */
+  setServerConfigValue: function (configName, value) {
     switch (configName) {
       case 'storm.zookeeper.servers':
         return value;
@@ -324,15 +359,9 @@ App.MainAdminSecurityProgressController = Em.Controller.extend({
         return App.config.escapeXMLCharacters(value);
     }
   },
-
-  saveCommandsOnRequestId: function () {
-    this.saveCommands();
-  }.observes('commands.@each.requestId'),
-
-  saveCommandsOnCompleted: function () {
-    this.saveCommands();
-  }.observes('commands.@each.isCompleted'),
-
+  /**
+   * save commands to server and local storage
+   */
   saveCommands: function () {
     var commands = [];
     if (this.get('commands').length === this.get('totalSteps')) {
@@ -341,7 +370,7 @@ App.MainAdminSecurityProgressController = Em.Controller.extend({
           name: _command.get('name'),
           label: _command.get('label'),
           isPolling: _command.get('isPolling'),
-          isVisible:  _command.get('isVisible'),
+          isVisible: _command.get('isVisible'),
           isStarted: _command.get('isStarted'),
           requestId: _command.get('requestId'),
           isSuccess: _command.get('isSuccess'),
@@ -362,5 +391,5 @@ App.MainAdminSecurityProgressController = Em.Controller.extend({
         });
       }
     }
-  }
+  }.observes('commands.@each.isCompleted', 'commands.@each.requestId')
 });

+ 3 - 3
ambari-web/app/templates/main/admin/security/disable.hbs

@@ -17,10 +17,10 @@
 }}
 
 {{#if view.message}}
-  <p {{bindAttr class="view.msgColor :alert"}}>{{view.message}}</p>
+    <p {{bindAttr class="view.msgColor :alert"}}>{{view.message}}</p>
 {{/if}}
 {{view App.MainServiceReconfigureView}}
 <div class="btn-area">
-  <a class="btn btn-success pull-right" {{bindAttr disabled="isSubmitDisabled"}}
-    {{action done}}>{{t common.done}} </a>
+    <button class="btn btn-success pull-right" {{bindAttr disabled="isSubmitDisabled"}}
+      {{action done}}>{{t common.done}} </button>
 </div>

+ 306 - 0
ambari-web/test/controllers/main/admin/highAvailability_controller_test.js

@@ -0,0 +1,306 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+var App = require('app');
+require('controllers/main/admin/highAvailability_controller');
+require('models/host_component');
+require('models/host');
+require('utils/ajax/ajax');
+
+describe('App.MainAdminHighAvailabilityController', function () {
+
+  var controller = App.MainAdminHighAvailabilityController.create();
+
+  describe('#enableHighAvailability()', function () {
+
+    beforeEach(function () {
+      sinon.spy(controller, "showErrorPopup");
+    });
+    afterEach(function () {
+      controller.showErrorPopup.restore();
+    });
+
+    it('Security enabled', function () {
+      controller.set('securityEnabled', true);
+      expect(controller.enableHighAvailability()).to.be.false;
+      expect(controller.showErrorPopup.calledOnce).to.be.true;
+    });
+    it('less than 3 ZooKeeper Servers', function () {
+      controller.set('securityEnabled', false);
+      App.store.load(App.HostComponent, {
+        id: "NAMENODE_host1",
+        component_name: 'NAMENODE',
+        work_status: 'STARTED'
+      });
+      expect(controller.enableHighAvailability()).to.be.false;
+      expect(controller.showErrorPopup.calledOnce).to.be.true;
+      App.store.loadMany(App.HostComponent, [
+        {
+          id: "ZOOKEEPER_SERVER_host1",
+          component_name: 'ZOOKEEPER_SERVER'
+        },
+        {
+          id: "ZOOKEEPER_SERVER_host2",
+          component_name: 'ZOOKEEPER_SERVER'
+        },
+        {
+          id: "ZOOKEEPER_SERVER_host3",
+          component_name: 'ZOOKEEPER_SERVER'
+        }
+      ]);
+    });
+    it('Security disabled and all checks passed', function () {
+      App.router.set('transitionTo', function () {
+      });
+      expect(controller.enableHighAvailability()).to.be.true;
+      expect(controller.showErrorPopup.called).to.be.false;
+    });
+    it('NameNode is started', function () {
+      App.HostComponent.find('NAMENODE_host1').set('workStatus', 'INSTALLED');
+      expect(controller.enableHighAvailability()).to.be.false;
+      expect(controller.showErrorPopup.calledOnce).to.be.true;
+      App.HostComponent.find('NAMENODE_host1').set('workStatus', 'STARTED');
+    });
+  });
+
+  describe('#setSecurityStatus()', function () {
+
+    beforeEach(function () {
+      sinon.stub(App.ajax, "send", Em.K);
+    });
+    afterEach(function () {
+      App.ajax.send.restore();
+    });
+
+    it('testMode = true', function () {
+      App.testEnableSecurity = false;
+      App.testMode = true;
+      controller.set('securityEnabled', false);
+      controller.set('dataIsLoaded', false);
+      controller.setSecurityStatus();
+      expect(controller.get('securityEnabled')).to.be.true;
+      expect(controller.get('dataIsLoaded')).to.be.true;
+      expect(App.ajax.send.called).to.be.false;
+
+    });
+    it('testMode = false', function () {
+      App.testMode = false;
+      controller.set('securityEnabled', false);
+      controller.set('dataIsLoaded', false);
+      controller.setSecurityStatus();
+      expect(controller.get('securityEnabled')).to.be.false;
+      expect(controller.get('dataIsLoaded')).to.be.false;
+      expect(App.ajax.send.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#getSecurityStatusFromServerSuccessCallback()', function () {
+
+    beforeEach(function () {
+      sinon.stub(controller, "getServiceConfigsFromServer", Em.K);
+      sinon.stub(controller, "showErrorPopup", Em.K);
+    });
+    afterEach(function () {
+      controller.getServiceConfigsFromServer.restore();
+      controller.showErrorPopup.restore();
+    });
+
+    it('desired_configs is empty', function () {
+      var data = {
+        Clusters: {
+          desired_configs: {}
+        }
+      };
+      controller.getSecurityStatusFromServerSuccessCallback(data);
+      expect(controller.showErrorPopup.calledOnce).to.be.true;
+    });
+    it('desired_configs does not have "global"', function () {
+      var data = {
+        Clusters: {
+          desired_configs: {
+            'hdfs-site': {}
+          }
+        }
+      };
+      controller.getSecurityStatusFromServerSuccessCallback(data);
+      expect(controller.showErrorPopup.calledOnce).to.be.true;
+    });
+    it('desired_configs does not have "global"', function () {
+      var data = {
+        Clusters: {
+          desired_configs: {
+            'global': {
+              tag: 1
+            }
+          }
+        }
+      };
+      controller.getSecurityStatusFromServerSuccessCallback(data);
+      expect(controller.get('tag')).to.equal(1);
+      expect(controller.getServiceConfigsFromServer.calledOnce).to.be.true;
+      expect(controller.showErrorPopup.called).to.be.false;
+    });
+  });
+
+  describe('#getSecurityStatusFromServerSuccessCallback()', function () {
+
+    beforeEach(function () {
+      sinon.stub(controller, "getServiceConfigsFromServer", Em.K);
+      sinon.stub(controller, "showErrorPopup", Em.K);
+    });
+    afterEach(function () {
+      controller.getServiceConfigsFromServer.restore();
+      controller.showErrorPopup.restore();
+    });
+
+    it('desired_configs is empty', function () {
+      var data = {
+        Clusters: {
+          desired_configs: {}
+        }
+      };
+      controller.getSecurityStatusFromServerSuccessCallback(data);
+      expect(controller.showErrorPopup.calledOnce).to.be.true;
+    });
+    it('desired_configs does not have "global"', function () {
+      var data = {
+        Clusters: {
+          desired_configs: {
+            'hdfs-site': {}
+          }
+        }
+      };
+      controller.getSecurityStatusFromServerSuccessCallback(data);
+      expect(controller.showErrorPopup.calledOnce).to.be.true;
+    });
+    it('desired_configs does not have "global"', function () {
+      var data = {
+        Clusters: {
+          desired_configs: {
+            'global': {
+              tag: 1
+            }
+          }
+        }
+      };
+      controller.getSecurityStatusFromServerSuccessCallback(data);
+      expect(controller.get('tag')).to.equal(1);
+      expect(controller.getServiceConfigsFromServer.calledOnce).to.be.true;
+      expect(controller.showErrorPopup.called).to.be.false;
+    });
+  });
+
+  describe('#joinMessage()', function () {
+    it('message is empty', function () {
+      var message = [];
+      expect(controller.joinMessage(message)).to.be.empty;
+    });
+    it('message is array from two strings', function () {
+      var message = ['yes', 'no'];
+      expect(controller.joinMessage(message)).to.equal('yes<br/>no');
+    });
+    it('message is string', function () {
+      var message = 'hello';
+      expect(controller.joinMessage(message)).to.equal('<p>hello</p>');
+    });
+  });
+
+  describe('#getServiceConfigsFromServer()', function () {
+
+    it('properties is null', function () {
+      App.router.set('configurationController', Em.Object.create({
+        getConfigsByTags: function () {
+          return this.get('data');
+        },
+        data: [
+          {
+            tag: 1,
+            properties: null
+          }
+        ]
+      }));
+      controller.getServiceConfigsFromServer();
+      expect(controller.get('dataIsLoaded')).to.be.true;
+      expect(controller.get('securityEnabled')).to.be.false;
+    });
+    it('"security_enabled" config is absent', function () {
+      App.router.set('configurationController.data', [
+        {
+          tag: 1,
+          properties: {}
+        }
+      ]);
+      controller.getServiceConfigsFromServer();
+      expect(controller.get('dataIsLoaded')).to.be.true;
+      expect(controller.get('securityEnabled')).to.be.false;
+    });
+    it('"security_enabled" is false', function () {
+      App.router.set('configurationController.data', [
+        {
+          tag: 1,
+          properties: {
+            'security_enabled': false
+          }
+        }
+      ]);
+      controller.getServiceConfigsFromServer();
+      expect(controller.get('dataIsLoaded')).to.be.true;
+      expect(controller.get('securityEnabled')).to.be.false;
+    });
+    it('"security_enabled" is "false"', function () {
+      App.router.set('configurationController.data', [
+        {
+          tag: 1,
+          properties: {
+            'security_enabled': "false"
+          }
+        }
+      ]);
+      controller.getServiceConfigsFromServer();
+      expect(controller.get('dataIsLoaded')).to.be.true;
+      expect(controller.get('securityEnabled')).to.be.false;
+    });
+    it('"security_enabled" is "true"', function () {
+      App.router.set('configurationController.data', [
+        {
+          tag: 1,
+          properties: {
+            'security_enabled': "true"
+          }
+        }
+      ]);
+      controller.getServiceConfigsFromServer();
+      expect(controller.get('dataIsLoaded')).to.be.true;
+      expect(controller.get('securityEnabled')).to.be.true;
+    });
+    it('"security_enabled" is true', function () {
+      App.router.set('configurationController.data', [
+        {
+          tag: 1,
+          properties: {
+            'security_enabled': true
+          }
+        }
+      ]);
+      controller.getServiceConfigsFromServer();
+      expect(controller.get('dataIsLoaded')).to.be.true;
+      expect(controller.get('securityEnabled')).to.be.true;
+    });
+  });
+});

+ 449 - 0
ambari-web/test/controllers/main/admin/security/disable_test.js

@@ -0,0 +1,449 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+var App = require('app');
+require('controllers/main/admin/security/disable');
+
+
+describe('App.MainAdminSecurityDisableController', function () {
+
+  var controller = App.MainAdminSecurityDisableController.create({
+    serviceConfigTags: null,
+    secureProperties: null,
+    secureMapping: null
+  });
+
+
+  describe('#resumeCommands()', function () {
+    var context = {
+      getSecurityDeployCommands: function () {
+        return this.testData;
+      }
+    };
+
+    beforeEach(function () {
+      sinon.stub(App.db, "getSecurityDeployCommands", context.getSecurityDeployCommands);
+    });
+    afterEach(function () {
+      App.db.getSecurityDeployCommands.restore();
+    });
+
+    it('commands are absent in local storage', function () {
+      App.db.testData = null;
+      expect(controller.resumeCommands()).to.be.false;
+    });
+    it('zero commands in local storage', function () {
+      App.db.testData = [];
+      expect(controller.resumeCommands()).to.be.false;
+    });
+    it('one command is present', function () {
+      App.db.testData = [
+        {
+          name: 'command1'
+        }
+      ];
+      controller.get('commands').clear();
+      expect(controller.resumeCommands()).to.be.true;
+      expect(controller.get('commands').mapProperty('name')).to.eql(['command1']);
+    });
+    it('command is started and completed', function () {
+      App.db.testData = [
+        {
+          name: 'command1',
+          isStarted: true,
+          isCompleted: true
+        }
+      ];
+      controller.get('commands').clear();
+      expect(controller.resumeCommands()).to.be.true;
+      expect(controller.get('commands').mapProperty('name')).to.eql(['command1']);
+      expect(controller.get('commands').findProperty('name', 'command1').get('isStarted')).to.be.true;
+    });
+    it('command is started but not completed', function () {
+      App.db.testData = [
+        {
+          name: 'command1',
+          isStarted: true,
+          isCompleted: false
+        }
+      ];
+      controller.get('commands').clear();
+      expect(controller.resumeCommands()).to.be.true;
+      expect(controller.get('commands').mapProperty('name')).to.eql(['command1']);
+      expect(controller.get('commands').findProperty('name', 'command1').get('isStarted')).to.be.false;
+    });
+  });
+
+  describe('#isSubmitDisabled', function () {
+    var testCases = [
+      {
+        title: 'commands is empty',
+        commands: [],
+        result: false
+      },
+      {
+        title: 'one started command',
+        commands: [Em.Object.create({
+          isStarted: true
+        })],
+        result: true
+      },
+      {
+        title: 'one failed command',
+        commands: [Em.Object.create({
+          isError: true
+        })],
+        result: false
+      },
+      {
+        title: 'one success command',
+        commands: [Em.Object.create({
+          isSuccess: true
+        })],
+        result: false
+      },
+      {
+        title: 'not all commands are success',
+        commands: [
+          Em.Object.create({
+            isSuccess: true
+          }),
+          Em.Object.create({
+            isSuccess: false
+          })
+        ],
+        result: true
+      }
+    ];
+
+    testCases.forEach(function (test) {
+      it(test.title, function () {
+        controller.set('commands', test.commands);
+        expect(controller.get('isSubmitDisabled')).to.equal(test.result);
+      });
+    });
+  });
+
+  describe('#syncStopServicesCommand()', function () {
+    App.router = Em.Object.create({
+      backgroundOperationsController: Em.Object.create({
+        services: []
+      })
+    });
+
+    it('No background operations', function () {
+      controller.set('commands', [Em.Object.create({
+        name: 'STOP_SERVICES',
+        requestId: 1
+      })]);
+      controller.syncStopServicesCommand.apply(controller);
+      expect(controller.get('commands').findProperty('name', 'STOP_SERVICES').get('requestId')).to.equal(1);
+    });
+    it('background operation is not running', function () {
+      App.router.set('backgroundOperationsController.services', [
+        Em.Object.create({
+          isRunning: false
+        })
+      ]);
+      controller.syncStopServicesCommand.apply(controller);
+      expect(controller.get('commands').findProperty('name', 'STOP_SERVICES').get('requestId')).to.equal(1);
+    });
+    it('background operation is running but not "Stop All Services"', function () {
+      App.router.set('backgroundOperationsController.services', [
+        Em.Object.create({
+          isRunning: true
+        })
+      ]);
+      controller.syncStopServicesCommand.apply(controller);
+      expect(controller.get('commands').findProperty('name', 'STOP_SERVICES').get('requestId')).to.equal(1);
+    });
+    it('"Stop All Services" operation is running', function () {
+      App.router.set('backgroundOperationsController.services', [
+        Em.Object.create({
+          name: 'Stop All Services',
+          isRunning: true,
+          id: 2
+        })
+      ]);
+      controller.syncStopServicesCommand.apply(controller);
+      expect(controller.get('commands').findProperty('name', 'STOP_SERVICES').get('requestId')).to.equal(2);
+    });
+  });
+
+  describe('#manageSecureConfigs()', function () {
+
+    beforeEach(function () {
+      sinon.spy(controller, "deleteDisabledGlobalConfigs");
+      sinon.spy(controller, "modifySiteConfigs");
+    });
+    afterEach(function () {
+      controller.deleteDisabledGlobalConfigs.restore();
+      controller.modifySiteConfigs.restore();
+    });
+
+    var testCases = [
+      {
+        title: 'serviceConfigTags, secureProperties, secureMapping are null',
+        content: {
+          serviceConfigTags: null,
+          secureProperties: null,
+          secureMapping: null
+        }
+      },
+      {
+        title: 'serviceConfigTags is null',
+        content: {
+          serviceConfigTags: null,
+          secureProperties: [],
+          secureMapping: []
+        }
+      },
+      {
+        title: 'secureProperties is null',
+        content: {
+          serviceConfigTags: [],
+          secureProperties: null,
+          secureMapping: []
+        }
+      },
+      {
+        title: 'secureMapping is null',
+        content: {
+          serviceConfigTags: [],
+          secureProperties: [],
+          secureMapping: null
+        }
+      }
+    ];
+
+    testCases.forEach(function (test) {
+      it(test.title, function () {
+        controller.set('commands', [Em.Object.create({
+          name: 'APPLY_CONFIGURATIONS'
+        })]);
+        controller.set('serviceConfigTags', test.content.serviceConfigTags);
+        controller.set('secureProperties', test.content.secureProperties);
+        controller.set('secureMapping', test.content.secureMapping);
+
+        expect(controller.manageSecureConfigs()).to.be.false;
+        expect(controller.get('commands').findProperty('name', 'APPLY_CONFIGURATIONS').get('isSuccess')).to.be.false;
+        expect(controller.get('commands').findProperty('name', 'APPLY_CONFIGURATIONS').get('isError')).to.be.true;
+      });
+    });
+    it('serviceConfigTags is empty', function () {
+      controller.set('serviceConfigTags', []);
+      controller.set('secureProperties', []);
+      controller.set('secureMapping', []);
+
+      expect(controller.manageSecureConfigs()).to.be.true;
+    });
+    it('serviceConfigTags has global site', function () {
+      controller.set('serviceConfigTags', [
+        {
+          siteName: 'global',
+          configs: {}
+        }
+      ]);
+
+      expect(controller.manageSecureConfigs()).to.be.true;
+      expect(controller.deleteDisabledGlobalConfigs.calledOnce).to.be.true;
+      expect(controller.get('serviceConfigTags').findProperty('siteName', 'global').configs.security_enabled).to.equal('false');
+    });
+    it('serviceConfigTags has site.xml', function () {
+      controller.set('serviceConfigTags', [
+        {
+          siteName: 'site'
+        }
+      ]);
+      expect(controller.manageSecureConfigs()).to.be.true;
+      expect(controller.modifySiteConfigs.calledOnce).to.be.true;
+    });
+  });
+
+  describe('#deleteDisabledGlobalConfigs()', function () {
+    var testCases = [
+      {
+        title: '_serviceConfigTags and secureProperties are null',
+        content: {
+          secureProperties: null,
+          _serviceConfigTags: null
+        },
+        result: false
+      },
+      {
+        title: '_serviceConfigTags is null',
+        content: {
+          secureProperties: [],
+          _serviceConfigTags: null
+        },
+        result: false
+      },
+      {
+        title: 'secureProperties is null',
+        content: {
+          secureProperties: null,
+          _serviceConfigTags: {}
+        },
+        result: false
+      },
+      {
+        title: 'secureProperties and _serviceConfigTags are empty',
+        content: {
+          secureProperties: [],
+          _serviceConfigTags: {}
+        },
+        result: true
+      }
+    ];
+
+    testCases.forEach(function (test) {
+      it(test.title, function () {
+        expect(controller.deleteDisabledGlobalConfigs(test.content.secureProperties, test.content._serviceConfigTags)).to.equal(test.result);
+      });
+    });
+    it('_serviceConfigTags doesn\'t contain secureProperties', function () {
+      var secureProperties = [
+        {name: 'config1'}
+      ];
+      var _serviceConfigTags = {
+        configs: {
+          'config2': true
+        }
+      };
+      expect(controller.deleteDisabledGlobalConfigs(secureProperties, _serviceConfigTags)).to.be.true;
+      expect(_serviceConfigTags.configs.config2).to.be.true;
+    });
+    it('_serviceConfigTags contains secureProperties', function () {
+      var secureProperties = [
+        {name: 'config1'}
+      ];
+      var _serviceConfigTags = {
+        configs: {
+          'config1': true
+        }
+      };
+      expect(controller.deleteDisabledGlobalConfigs(secureProperties, _serviceConfigTags)).to.be.true;
+      expect(_serviceConfigTags.configs.config1).to.be.undefined;
+    });
+  });
+
+  describe('#modifySiteConfigs()', function () {
+    var testCases = [
+      {
+        title: '_serviceConfigTags and secureMapping are null',
+        content: {
+          secureMapping: null,
+          _serviceConfigTags: null
+        },
+        result: false
+      },
+      {
+        title: '_serviceConfigTags is null',
+        content: {
+          secureMapping: [],
+          _serviceConfigTags: null
+        },
+        result: false
+      },
+      {
+        title: 'secureMapping is null',
+        content: {
+          secureMapping: null,
+          _serviceConfigTags: {}
+        },
+        result: false
+      },
+      {
+        title: 'secureMapping and _serviceConfigTags are empty',
+        content: {
+          secureMapping: [],
+          _serviceConfigTags: {}
+        },
+        result: true
+      }
+    ];
+
+    testCases.forEach(function (test) {
+      it(test.title, function () {
+        expect(controller.modifySiteConfigs(test.content.secureMapping, test.content._serviceConfigTags)).to.equal(test.result);
+      });
+    });
+    it('secureMapping doesn\'t contain passed siteName', function () {
+      var secureMapping = [];
+      var _serviceConfigTags = {
+        configs: {
+          'config2': true
+        },
+        siteName: 'site1'
+      };
+      expect(controller.modifySiteConfigs(secureMapping, _serviceConfigTags)).to.be.true;
+      expect(_serviceConfigTags.configs.config2).to.be.true;
+    });
+    it('secureMapping contain passed siteName but doesn\'t match config name', function () {
+      var secureMapping = [
+        {
+          filename: 'site1.xml'
+        }
+      ];
+      var _serviceConfigTags = {
+        configs: {
+          'config2': true
+        },
+        siteName: 'site1'
+      };
+      expect(controller.modifySiteConfigs(secureMapping, _serviceConfigTags)).to.be.true;
+      expect(_serviceConfigTags.configs.config2).to.be.true;
+    });
+    it('secureMapping contain passed siteName and match config name', function () {
+      var secureMapping = [
+        {
+          filename: 'site1.xml',
+          name: 'config2'
+        }
+      ];
+      var _serviceConfigTags = {
+        configs: {
+          'config2': true
+        },
+        siteName: 'site1'
+      };
+      expect(controller.modifySiteConfigs(secureMapping, _serviceConfigTags)).to.be.true;
+      expect(_serviceConfigTags.configs.config2).to.be.undefined;
+    });
+    it('secureMapping contain passed siteName and included in secureConfigValuesMap', function () {
+      var secureMapping = [
+        {
+          filename: 'site1.xml',
+          name: 'config2'
+        }
+      ];
+      var _serviceConfigTags = {
+        configs: {
+          'config2': true
+        },
+        siteName: 'site1'
+      };
+      controller.set('secureConfigValuesMap', {
+        'config2': 'value'
+      });
+      expect(controller.modifySiteConfigs(secureMapping, _serviceConfigTags)).to.be.true;
+      expect(_serviceConfigTags.configs.config2).to.equal('value');
+    });
+  });
+});

+ 487 - 0
ambari-web/test/controllers/main/admin/security/security_progress_controller_test.js

@@ -0,0 +1,487 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+var App = require('app');
+require('controllers/main/admin/security/security_progress_controller');
+require('models/host_component');
+require('models/host');
+
+describe('App.MainAdminSecurityProgressController', function () {
+
+  var controller = App.MainAdminSecurityProgressController.create({
+    loadClusterConfigs: function () {},
+    deleteComponents: function () {}
+  });
+
+  describe('#retry()', function () {
+
+    beforeEach(function () {
+      sinon.spy(controller, "startCommand");
+    });
+    afterEach(function () {
+      controller.startCommand.restore();
+    });
+
+    it('commands are empty', function () {
+      controller.set('commands', []);
+      controller.retry();
+      expect(controller.startCommand.called).to.be.false;
+    });
+
+    it('command is successful', function () {
+      controller.set('commands', [
+        Em.Object.create({
+          name: 'test',
+          isSuccess: true,
+          isError: false,
+          isStarted: true
+        })
+      ]);
+      controller.retry();
+      expect(controller.startCommand.calledOnce).to.be.false;
+      expect(controller.get('commands').findProperty('name', 'test').get('isError')).to.be.false;
+      expect(controller.get('commands').findProperty('name', 'test').get('isStarted')).to.be.true;
+    });
+
+    it('command is failed', function () {
+      controller.set('commands', [
+        Em.Object.create({
+          name: 'test',
+          isSuccess: true,
+          isError: true,
+          isStarted: true
+        })
+      ]);
+      controller.retry();
+      expect(controller.startCommand.calledOnce).to.be.true;
+      expect(controller.get('commands').findProperty('name', 'test').get('isError')).to.be.false;
+      expect(controller.get('commands').findProperty('name', 'test').get('isStarted')).to.be.false;
+    });
+  });
+
+  describe('#updateServices()', function () {
+
+    it('commands are empty', function () {
+      controller.set('services', [
+        {}
+      ]);
+      controller.set('commands', []);
+      controller.updateServices();
+      expect(controller.get('services')).to.be.empty;
+    });
+
+    it('command doesn\'t have polledData', function () {
+      controller.set('services', [
+        {}
+      ]);
+      controller.set('commands', [Em.Object.create({
+        label: 'label'
+      })]);
+      controller.updateServices();
+      expect(controller.get('services')).to.be.empty;
+    });
+
+    it('command has polledData', function () {
+      controller.set('services', [
+        {}
+      ]);
+      controller.set('commands', [Em.Object.create({
+        label: 'service1',
+        polledData: [
+          {
+            Tasks: {
+              host_name: 'host1'
+            }
+          }
+        ]
+      })]);
+      controller.updateServices();
+      expect(controller.get('services').findProperty('name', 'service1').get('hosts')).to.eql([
+        {
+          name: 'host1',
+          publicName: 'host1',
+          logTasks: [
+            {
+              Tasks: {host_name: 'host1'}
+            }
+          ]
+        }
+      ]);
+    });
+  });
+
+  describe('#setIndex()', function () {
+    it('commandArray is empty', function () {
+      var commandArray = [];
+      controller.setIndex(commandArray);
+      expect(commandArray).to.be.empty;
+      expect(controller.get('totalSteps')).to.equal(0);
+    });
+    it('one command in commandArray', function () {
+      var commandArray = [
+        Em.Object.create({name: 'command1'})
+      ];
+      controller.setIndex(commandArray);
+      expect(commandArray[0].get('index')).to.equal(1);
+      expect(controller.get('totalSteps')).to.equal(1);
+    });
+    it('commands with random indexes', function () {
+      var commandArray = [];
+      commandArray[3] = Em.Object.create({name: 'command3'});
+      commandArray[11] = Em.Object.create({name: 'command11'});
+      controller.setIndex(commandArray);
+      expect(commandArray[3].get('index')).to.equal(4);
+      expect(commandArray[11].get('index')).to.equal(12);
+      expect(controller.get('totalSteps')).to.equal(12);
+    });
+  });
+
+  describe('#startCommand()', function () {
+
+    var command = Em.Object.create({
+      start: function () {
+      }
+    });
+    beforeEach(function () {
+      sinon.spy(command, "start");
+      sinon.spy(controller, "loadClusterConfigs");
+      sinon.spy(controller, "deleteComponents");
+    });
+    afterEach(function () {
+      command.start.restore();
+      controller.loadClusterConfigs.restore();
+      controller.deleteComponents.restore();
+    });
+
+    it('number of commands doesn\'t match totalSteps', function () {
+      controller.set('commands', []);
+      controller.set('totalSteps', 1);
+      expect(controller.startCommand()).to.be.false;
+    });
+    it('commands is empty', function () {
+      controller.set('commands', []);
+      controller.set('totalSteps', 0);
+      expect(controller.startCommand()).to.be.false;
+    });
+    it('command is started and completed', function () {
+      controller.set('commands', [Em.Object.create({
+        isStarted: true,
+        isCompleted: true
+      })]);
+      controller.set('totalSteps', 1);
+      expect(controller.startCommand()).to.be.false;
+    });
+    it('command is started and incompleted', function () {
+      controller.set('commands', [Em.Object.create({
+        isStarted: true,
+        isCompleted: false
+      })]);
+      controller.set('totalSteps', 1);
+      expect(controller.startCommand()).to.be.true;
+    });
+    it('command parameter passed, isPolling is true', function () {
+      controller.set('commands', []);
+      controller.set('totalSteps', 0);
+      command.set('isPolling', true);
+      expect(controller.startCommand(command)).to.be.true;
+      expect(command.get('isStarted')).to.be.true;
+      expect(command.start.calledOnce).to.be.true;
+      command.set('isPolling', false);
+    });
+    it('command parameter passed, name is "APPLY_CONFIGURATIONS"', function () {
+      command.set('name', 'APPLY_CONFIGURATIONS');
+      expect(controller.startCommand(command)).to.be.true;
+      expect(command.get('isStarted')).to.be.true;
+      expect(controller.loadClusterConfigs.calledOnce).to.be.true;
+    });
+    it('command parameter passed, name is "APPLY_CONFIGURATIONS", testMode = true', function () {
+      App.testMode = true;
+      expect(controller.startCommand(command)).to.be.true;
+      expect(command.get('isStarted')).to.be.true;
+      expect(command.get('isError')).to.be.false;
+      expect(command.get('isSuccess')).to.be.true;
+      expect(controller.loadClusterConfigs.called).to.be.false;
+      App.testMode = false;
+    });
+    it('command parameter passed, name is "DELETE_ATS"', function () {
+      command.set('name', 'DELETE_ATS');
+      App.store.load(App.HostComponent, {
+        id: 'APP_TIMELINE_SERVER_ats_host',
+        component_name: 'APP_TIMELINE_SERVER',
+        host_id: 'ats_host'
+      });
+      App.store.load(App.Host, {
+        id: 'ats_host',
+        host_name: 'ats_host',
+        host_components: ['APP_TIMELINE_SERVER_ats_host']
+      });
+      expect(controller.startCommand(command)).to.be.true;
+      expect(command.get('isStarted')).to.be.true;
+      expect(controller.deleteComponents.calledWith('APP_TIMELINE_SERVER', 'ats_host')).to.be.true;
+    });
+    it('command parameter passed, name is "DELETE_ATS", testMode = true', function () {
+      App.testMode = true;
+      expect(controller.startCommand(command)).to.be.true;
+      expect(command.get('isStarted')).to.be.true;
+      expect(command.get('isError')).to.be.false;
+      expect(command.get('isSuccess')).to.be.true;
+      expect(controller.deleteComponents.called).to.be.false;
+      App.testMode = false;
+    });
+  });
+
+  describe('#onCompleteCommand()', function () {
+
+    beforeEach(function () {
+      sinon.spy(controller, "moveToNextCommand");
+    });
+    afterEach(function () {
+      controller.moveToNextCommand.restore();
+    });
+
+    it('number of commands doesn\'t match totalSteps', function () {
+      controller.set('commands', []);
+      controller.set('totalSteps', 1);
+      expect(controller.onCompleteCommand()).to.be.false;
+    });
+    it('No successful commands', function () {
+      controller.set('commands', [Em.Object.create({
+        isSuccess: false
+      })]);
+      controller.set('totalSteps', 1);
+      expect(controller.onCompleteCommand()).to.be.false;
+    });
+    it('No successful commands', function () {
+      controller.set('commands', [Em.Object.create({
+        isSuccess: false
+      })]);
+      controller.set('totalSteps', 1);
+      expect(controller.onCompleteCommand()).to.be.false;
+    });
+    it('Last command is successful', function () {
+      controller.set('commands', [
+        Em.Object.create({
+          isSuccess: false
+        }),
+        Em.Object.create({
+          isSuccess: true
+        })
+      ]);
+      controller.set('totalSteps', 2);
+      expect(controller.onCompleteCommand()).to.be.false;
+    });
+    it('all commands are successful', function () {
+      controller.set('commands', [
+        Em.Object.create({
+          isSuccess: true,
+          name: 'command1'
+        }),
+        Em.Object.create({
+          isSuccess: false,
+          name: 'command2'
+        })
+      ]);
+      controller.set('totalSteps', 2);
+      expect(controller.onCompleteCommand()).to.be.true;
+      expect(controller.moveToNextCommand.calledWith(Em.Object.create({
+        isSuccess: false,
+        name: 'command2'
+      }))).to.be.true;
+    });
+  });
+
+  describe('#moveToNextCommand()', function () {
+
+    beforeEach(function () {
+      sinon.spy(controller, "startCommand");
+    });
+    afterEach(function () {
+      controller.startCommand.restore();
+    });
+
+    it('No commands present', function () {
+      controller.set('commands', []);
+      expect(controller.moveToNextCommand()).to.be.false;
+    });
+    it('Only started command present', function () {
+      controller.set('commands', [
+        Em.Object.create({
+          isStarted: true
+        })
+      ]);
+      expect(controller.moveToNextCommand()).to.be.false;
+    });
+    it('Command is not started', function () {
+      controller.set('commands', [
+        Em.Object.create({
+          isStarted: false,
+          name: 'command1'
+        })
+      ]);
+      expect(controller.moveToNextCommand()).to.be.true;
+      expect(controller.startCommand.calledWith(Em.Object.create({
+        isStarted: false,
+        name: 'command1'
+      }))).to.be.true;
+    });
+    it('Next command provide as argument', function () {
+      var nextCommand = Em.Object.create({
+        isStarted: false,
+        name: 'command2'
+      });
+      expect(controller.moveToNextCommand(nextCommand)).to.be.true;
+      expect(controller.startCommand.calledWith(Em.Object.create({
+        isStarted: false,
+        name: 'command2'
+      }))).to.be.true;
+    });
+  });
+
+  describe('#setServiceTagNames()', function () {
+    var testCases = [
+      {
+        title: 'configs is empty object',
+        content: {
+          secureService: {},
+          configs: {}
+        },
+        result: undefined
+      },
+      {
+        title: 'secureService.sites is null',
+        content: {
+          secureService: {
+            sites: null
+          },
+          configs: {
+            site1: {}
+          }
+        },
+        result: undefined
+      },
+      {
+        title: 'secureService.sites doesn\'t contain required config tag',
+        content: {
+          secureService: {
+            sites: []
+          },
+          configs: {
+            site1: {}
+          }
+        },
+        result: undefined
+      },
+      {
+        title: 'secureService.sites contains required config tag',
+        content: {
+          secureService: {
+            sites: ['site1']
+          },
+          configs: {
+            site1: {
+              tag: 'tag1'
+            }
+          }
+        },
+        result: {
+          siteName: 'site1',
+          tagName: 'tag1',
+          newTagName: null,
+          configs: {}
+        }
+      }
+    ];
+    testCases.forEach(function (test) {
+      it(test.title, function () {
+        expect(controller.setServiceTagNames(test.content.secureService, test.content.configs)).to.eql(test.result);
+      });
+    });
+  });
+
+  describe('#setServerConfigValue()', function () {
+
+    beforeEach(function () {
+      sinon.spy(App.config, "escapeXMLCharacters");
+    });
+    afterEach(function () {
+      App.config.escapeXMLCharacters.restore();
+    });
+
+    it('Empty config', function () {
+      expect(controller.setServerConfigValue('', '')).to.equal('');
+      expect(App.config.escapeXMLCharacters.calledWith('')).to.be.true;
+    });
+    it('Config1, value = "value1"', function () {
+      expect(controller.setServerConfigValue('config1', 'value1')).to.equal('value1');
+      expect(App.config.escapeXMLCharacters.calledWith('value1')).to.be.true;
+    });
+    it('config = "storm.zookeeper.servers", value = "value1"', function () {
+      expect(controller.setServerConfigValue('storm.zookeeper.servers', 'value1')).to.equal('value1');
+      expect(App.config.escapeXMLCharacters.called).to.be.false;
+    });
+  });
+
+  describe('#escapeXMLCharacters()', function () {
+
+    beforeEach(function () {
+      sinon.spy(controller, "setServerConfigValue");
+    });
+    afterEach(function () {
+      controller.setServerConfigValue.restore();
+    });
+
+    it('serviceConfigTags is empty', function () {
+      var serviceConfigTags = [];
+      controller.escapeXMLCharacters(serviceConfigTags);
+      expect(controller.setServerConfigValue.called).to.be.false;
+    });
+    it('configs is empty', function () {
+      var serviceConfigTags = [
+        {
+          configs: {}
+        }
+      ];
+      controller.escapeXMLCharacters(serviceConfigTags);
+      expect(controller.setServerConfigValue.called).to.be.false;
+    });
+    it('serviceConfigTags has property', function () {
+      var serviceConfigTags = [
+        {
+          configs: {
+            'p1': 'value1'
+          }
+        }
+      ];
+      controller.escapeXMLCharacters(serviceConfigTags);
+      expect(controller.setServerConfigValue.withArgs('p1', 'value1').calledOnce).to.be.true;
+    });
+    it('serviceConfigTags has multiple properties', function () {
+      var serviceConfigTags = [
+        {
+          configs: {
+            'p1': 'value1',
+            'p2': 'value2'
+          }
+        }
+      ];
+      controller.escapeXMLCharacters(serviceConfigTags);
+      expect(controller.setServerConfigValue.callCount).to.equal(2);
+    });
+  });
+});