You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by ab...@apache.org on 2014/12/23 15:33:12 UTC

ambari git commit: AMBARI-8886 Add Service wizard: Show identities and secure configuration for new services. (ababiichuk)

Repository: ambari
Updated Branches:
  refs/heads/trunk 3029048b5 -> 5704491c2


AMBARI-8886 Add Service wizard: Show identities and secure configuration for new services. (ababiichuk)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/5704491c
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/5704491c
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/5704491c

Branch: refs/heads/trunk
Commit: 5704491c269f61cbe29ac329425adde51dfdeb67
Parents: 3029048
Author: aBabiichuk <ab...@cybervisiontech.com>
Authored: Tue Dec 23 16:03:04 2014 +0200
Committer: aBabiichuk <ab...@cybervisiontech.com>
Committed: Tue Dec 23 16:32:16 2014 +0200

----------------------------------------------------------------------
 .../main/admin/kerberos/step4_controller.js     |  47 +++++++--
 .../controllers/main/service/add_controller.js  |  20 +++-
 .../app/controllers/wizard/step7_controller.js  |   2 +
 ambari-web/app/routes/add_service_routes.js     | 102 ++++++++++++++-----
 ambari-web/app/templates/main/service/add.hbs   |  14 ++-
 .../admin/kerberos/step4_controller_test.js     |  82 ++++++++++++++-
 .../main/service/add_controller_test.js         |  62 ++++++++++-
 7 files changed, 283 insertions(+), 46 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/5704491c/ambari-web/app/controllers/main/admin/kerberos/step4_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/admin/kerberos/step4_controller.js b/ambari-web/app/controllers/main/admin/kerberos/step4_controller.js
index 279ef58..f6dba20 100644
--- a/ambari-web/app/controllers/main/admin/kerberos/step4_controller.js
+++ b/ambari-web/app/controllers/main/admin/kerberos/step4_controller.js
@@ -18,6 +18,8 @@
 
 App.KerberosWizardStep4Controller = App.WizardStep7Controller.extend(App.AddSecurityConfigs, {
   name: 'kerberosWizardStep4Controller',
+
+  adminPropertyNames: ['admin_principal', 'admin_password'],
   
   clearStep: function() {
     this.set('isRecommendedLoaded', false);
@@ -61,12 +63,24 @@ App.KerberosWizardStep4Controller = App.WizardStep7Controller.extend(App.AddSecu
   setStepConfigs: function(configs) {
     var selectedService = App.StackService.find().findProperty('serviceName', 'KERBEROS');
     var configCategories = selectedService.get('configCategories');
-    this.get('stepConfigs').pushObject(this.createServiceConfig(configCategories, this.prepareConfigProperties(configs)));
+    var configProperties = this.prepareConfigProperties(configs);
+    if (this.get('wizardController.name') == 'addServiceController') {
+      // config properties for installed services should be disabled on Add Service Wizard
+      configProperties.forEach(function(item) {
+        if (this.get('adminPropertyNames').contains(item.get('name'))) return;
+        if (this.get('installedServiceNames').contains(item.get('serviceName')) || item.get('serviceName') == 'Cluster') {
+          item.set('isEditable', false);
+        }
+      }, this);
+    }
+    this.get('stepConfigs').pushObject(this.createServiceConfig(configCategories, configProperties));
     this.set('selectedService', this.get('stepConfigs')[0]);
   },
 
   /**
-   * Filter configs by installed services. Set property value observer.
+   * Filter configs by installed services for Kerberos Wizard or by installed + selected services
+   * for Add Service Wizard.
+   * Set property value observer.
    * Set realm property with value from previous configuration step.
    * Set appropriate category for all configs.
    *
@@ -75,16 +89,31 @@ App.KerberosWizardStep4Controller = App.WizardStep7Controller.extend(App.AddSecu
    */
   prepareConfigProperties: function(configs) {
     var self = this;
-    var realmValue = this.get('wizardController').getDBProperty('serviceConfigProperties').findProperty('name', 'realm').value;
+    var storedServiceConfigs = this.get('wizardController').getDBProperty('serviceConfigProperties');
+    var realmValue = storedServiceConfigs.findProperty('name', 'realm').value;
     var installedServiceNames = ['Cluster'].concat(App.Service.find().mapProperty('serviceName'));
-    configs = configs.filter(function(item) {
+    var adminProps = [];
+    var configProperties = configs.slice(0);
+    if (this.get('wizardController.name') == 'addServiceController') {
+      installedServiceNames = installedServiceNames.concat(this.get('selectedServiceNames'));
+      this.get('adminPropertyNames').forEach(function(item) {
+        var property = storedServiceConfigs.filterProperty('filename', 'krb5-conf.xml').findProperty('name', item);
+        if (!!property) {
+          var _prop = App.ServiceConfigProperty.create($.extend({}, property, { value: '', defaultValue: '', serviceName: 'Cluster', displayName: item }));
+          _prop.validate();
+          adminProps.push(_prop);
+        }
+      });
+      configProperties = adminProps.concat(configProperties);
+    }
+    configProperties = configProperties.filter(function(item) {
       return installedServiceNames.contains(item.get('serviceName'));
     });
-    configs.findProperty('name', 'realm').set('value', realmValue);
-    configs.findProperty('name', 'realm').set('defaultValue', realmValue);
+    configProperties.findProperty('name', 'realm').set('value', realmValue);
+    configProperties.findProperty('name', 'realm').set('defaultValue', realmValue);
     
-    configs.setEach('isSecureConfig', false);
-    configs.forEach(function(property, item, allConfigs) {
+    configProperties.setEach('isSecureConfig', false);
+    configProperties.forEach(function(property, item, allConfigs) {
       if (['spnego_keytab', 'spnego_principal'].contains(property.get('name'))) {
         property.addObserver('value', self, 'spnegoPropertiesObserver');
       }
@@ -97,7 +126,7 @@ App.KerberosWizardStep4Controller = App.WizardStep7Controller.extend(App.AddSecu
       else property.set('category', 'Advanced');
     });
 
-    return configs;
+    return configProperties;
   },
 
   /**

http://git-wip-us.apache.org/repos/asf/ambari/blob/5704491c/ambari-web/app/controllers/main/service/add_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/service/add_controller.js b/ambari-web/app/controllers/main/service/add_controller.js
index 190940e..dc7bbdd 100644
--- a/ambari-web/app/controllers/main/service/add_controller.js
+++ b/ambari-web/app/controllers/main/service/add_controller.js
@@ -21,8 +21,8 @@ var App = require('app');
 App.AddServiceController = App.WizardController.extend({
 
   name: 'addServiceController',
-
-  totalSteps: 7,
+  // @TODO: remove after Kerberos Automation supports
+  totalSteps: App.supports.automatedKerberos ? 8 : 7,
 
   /**
    * Used for hiding back button in wizard
@@ -120,6 +120,11 @@ App.AddServiceController = App.WizardController.extend({
     var selectedServices = stepController.get('content').filterProperty('isSelected', true).filterProperty('isInstalled', false).mapProperty('serviceName');
     services.selectedServices.pushObjects(selectedServices);
     services.installedServices.pushObjects(stepController.get('content').filterProperty('isInstalled', true).mapProperty('serviceName'));
+    // save services that already installed but ignored on choose services page
+    // these services marked by `isInstallable` flag with value `false`, for example `Kerberos` service
+    services.installedServices.pushObjects(App.Service.find().mapProperty('serviceName').filter(function(serviceName) {
+      return !services.installedServices.contains(serviceName);
+    }));
     this.setDBProperty('services', services);
     console.log('AddServiceController.saveServices: saved data', stepController.get('content'));
 
@@ -347,9 +352,11 @@ App.AddServiceController = App.WizardController.extend({
   loadAllPriorSteps: function () {
     var step = this.get('currentStep');
     switch (step) {
+      case '8':
       case '7':
       case '6':
       case '5':
+        this.checkSecurityStatus();
         this.load('cluster');
         this.set('content.additionalClients', []);
       case '4':
@@ -445,5 +452,14 @@ App.AddServiceController = App.WizardController.extend({
         });
       }
     }, this);
+  },
+
+  checkSecurityStatus: function() {
+    if (App.supports.automatedKerberos) {
+      if (!App.router.get('mainAdminSecurityController.securityEnabled')) {
+        this.get('isStepDisabled').findProperty('step', 5).set('value', true);
+      }
+    }
   }
+
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/5704491c/ambari-web/app/controllers/wizard/step7_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/wizard/step7_controller.js b/ambari-web/app/controllers/wizard/step7_controller.js
index 7953fab..ea17226 100644
--- a/ambari-web/app/controllers/wizard/step7_controller.js
+++ b/ambari-web/app/controllers/wizard/step7_controller.js
@@ -707,6 +707,8 @@ App.WizardStep7Controller = Em.Controller.extend(App.ServerValidatorMixin, {
       }, this);
       this.get('installedServiceNames').forEach(function (serviceName) {
         var serviceConfigObj = serviceConfigs.findProperty('serviceName', serviceName);
+        var isInstallableService = App.StackService.find(serviceName).get('isInstallable');
+        if (!isInstallableService) serviceConfigObj.set('showConfig', false);
         if (this.get('securityEnabled')) {
           this.setSecureConfigs(serviceConfigObj, serviceName);
         }

http://git-wip-us.apache.org/repos/asf/ambari/blob/5704491c/ambari-web/app/routes/add_service_routes.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/routes/add_service_routes.js b/ambari-web/app/routes/add_service_routes.js
index 27ef5d4..71d5e3e 100644
--- a/ambari-web/app/routes/add_service_routes.js
+++ b/ambari-web/app/routes/add_service_routes.js
@@ -17,6 +17,7 @@
  */
 
 var App = require('app');
+// @todo: remove App.supports.kerberosAutomated after Kerberos Automation Wizard support
 
 module.exports = App.WizardRoute.extend({
   route: '/service/add',
@@ -104,7 +105,7 @@ module.exports = App.WizardRoute.extend({
       controller.dataLoading().done(function () {
         controller.loadAllPriorSteps();
         controller.connectOutlet('wizardStep4', controller.get('content.services').filterProperty('isInstallable', true));
-      })
+      });
     },
     next: function (router) {
       var addServiceController = router.get('addServiceController');
@@ -129,7 +130,7 @@ module.exports = App.WizardRoute.extend({
       controller.dataLoading().done(function () {
         controller.loadAllPriorSteps();
         controller.connectOutlet('wizardStep5', controller.get('content'));
-      })
+      });
 
     },
     back: Em.Router.transitionTo('step1'),
@@ -155,7 +156,7 @@ module.exports = App.WizardRoute.extend({
         var wizardStep6Controller = router.get('wizardStep6Controller');
         wizardStep6Controller.set('wizardController', controller);
         controller.connectOutlet('wizardStep6', controller.get('content'));
-      })
+      });
     },
     back: function(router){
       var controller = router.get('addServiceController');
@@ -188,15 +189,15 @@ module.exports = App.WizardRoute.extend({
       var controller = router.get('addServiceController');
       controller.setCurrentStep('4');
       controller.dataLoading().done(function () {
-      var wizardStep7Controller = router.get('wizardStep7Controller');
-      controller.loadAllPriorSteps();
-      controller.loadAdvancedConfigs(wizardStep7Controller);
-      wizardStep7Controller.getConfigTags();
-      wizardStep7Controller.set('wizardController', controller);
-      controller.usersLoading().done(function () {
+        var wizardStep7Controller = router.get('wizardStep7Controller');
+        controller.loadAllPriorSteps();
+        controller.loadAdvancedConfigs(wizardStep7Controller);
+        wizardStep7Controller.getConfigTags();
+        wizardStep7Controller.set('wizardController', controller);
+        controller.usersLoading().done(function () {
           controller.connectOutlet('wizardStep7', controller.get('content'));
+        });
       });
-      })
     },
     back: function(router){
       var controller = router.get('addServiceController');
@@ -213,25 +214,71 @@ module.exports = App.WizardRoute.extend({
       var wizardStep7Controller = router.get('wizardStep7Controller');
       addServiceController.saveServiceConfigProperties(wizardStep7Controller);
       addServiceController.saveServiceConfigGroups(wizardStep7Controller, true);
-      router.transitionTo('step5');
+      if (App.supports.automatedKerberos) {
+        if (router.get('mainAdminSecurityController.securityEnabled')) {
+          router.transitionTo('step5');
+          return;
+        }
+      }
+      router.transitionTo('step6');
     }
   }),
 
   step5: Em.Route.extend({
     route: '/step5',
+    connectOutlets: function (router) {
+      var controller = router.get('addServiceController');
+      controller.setCurrentStep('5');
+      controller.dataLoading().done(function () {
+        var kerberosStep4Controller = router.get('kerberosWizardStep4Controller');
+        controller.loadAllPriorSteps();
+        kerberosStep4Controller.set('wizardController', controller);
+        controller.connectOutlet('kerberosWizardStep4', controller.get('content'));
+      });
+    },
+    back: function(router){
+      var controller = router.get('addServiceController');
+      if(!controller.get('content.skipConfigStep')) {
+        router.transitionTo('step4');
+      }
+      else {
+        if(!controller.get('content.skipSlavesStep')) {
+          router.transitionTo('step3');
+        }
+        else {
+          if(!controller.get('content.skipMasterStep')) {
+            router.transitionTo('step2');
+          }
+          else {
+            router.transitionTo('step1');
+          }
+        }
+      }
+    },
+    next: function (router) {
+      router.transitionTo('step6');
+    }
+  }),
+
+  step6: Em.Route.extend({
+    route: App.supports.automatedKerberos ? '/step6' : '/step5',
     connectOutlets: function (router, context) {
       console.log('in addService.step5:connectOutlets');
       var controller = router.get('addServiceController');
-      controller.setCurrentStep('5');
+      controller.setCurrentStep(App.supports.automatedKerberos ? '6' : '5');
       controller.dataLoading().done(function () {
         controller.loadAllPriorSteps();
         var wizardStep8Controller = router.get('wizardStep8Controller');
         wizardStep8Controller.set('wizardController', controller);
         controller.connectOutlet('wizardStep8', controller.get('content'));
-      })
+      });
     },
     back: function(router){
       var controller = router.get('addServiceController');
+      if (App.supports.automatedKerberos && router.get('mainAdminSecurityController.securityEnabled')) {
+        router.transitionTo('step5');
+        return;
+      }
       if(!controller.get('content.skipConfigStep')) {
         router.transitionTo('step4');
       }
@@ -257,28 +304,28 @@ module.exports = App.WizardRoute.extend({
 
         addServiceController.saveClusterState('ADD_SERVICES_INSTALLING_3');
         wizardStep8Controller.set('servicesInstalled', true);
-        router.transitionTo('step6');
+        router.transitionTo('step7');
       });
     }
   }),
 
-  step6: Em.Route.extend({
-    route: '/step6',
+  step7: Em.Route.extend({
+    route: App.supports.automatedKerberos ? '/step7' : '/step6',
     connectOutlets: function (router, context) {
       console.log('in addService.step6:connectOutlets');
       var controller = router.get('addServiceController');
-      controller.setCurrentStep('6');
+      controller.setCurrentStep(App.supports.automatedKerberos ? '7' : '6');
       controller.dataLoading().done(function () {
         controller.loadAllPriorSteps();
         var wizardStep9Controller = router.get('wizardStep9Controller');
         wizardStep9Controller.set('wizardController', controller);
         if (!App.get('testMode')) {              //if test mode is ON don't disable prior steps link.
-          controller.setLowerStepsDisable(6);
+          controller.setLowerStepsDisable(App.supports.automatedKerberos ? 7 : 6);
         }
         controller.connectOutlet('wizardStep9', controller.get('content'));
-      })
+      });
     },
-    back: Em.Router.transitionTo('step5'),
+    back: Em.Router.transitionTo('step6'),
     retry: function(router,context) {
       var addServiceController = router.get('addServiceController');
       var wizardStep9Controller = router.get('wizardStep9Controller');
@@ -306,24 +353,23 @@ module.exports = App.WizardRoute.extend({
 
       // We need to do recovery based on whether we are in Add Host or Installer wizard
       addServiceController.saveClusterState('ADD_SERVICES_INSTALLED_4');
-
-      router.transitionTo('step7');
+      router.transitionTo('step8');
     }
   }),
 
-  step7: Em.Route.extend({
-    route: '/step7',
+  step8: Em.Route.extend({
+    route: App.supports.automatedKerberos ? '/step8' : '/step7',
     connectOutlets: function (router, context) {
       console.log('in addService.step7:connectOutlets');
       var controller = router.get('addServiceController');
-      controller.setCurrentStep('7');
+      controller.setCurrentStep(App.supports.automatedKerberos ? '8' : '7');
       controller.dataLoading().done(function () {
         controller.loadAllPriorSteps();
-        controller.setLowerStepsDisable(7);
+        controller.setLowerStepsDisable(App.supports.automatedKerberos ? 8 : 7);
         controller.connectOutlet('wizardStep10', controller.get('content'));
-      })
+      });
     },
-    back: Em.Router.transitionTo('step6'),
+    back: Em.Router.transitionTo('step7'),
     complete: function (router, context) {
       var addServiceController = router.get('addServiceController');
       addServiceController.get('popup').onClose();

http://git-wip-us.apache.org/repos/asf/ambari/blob/5704491c/ambari-web/app/templates/main/service/add.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/service/add.hbs b/ambari-web/app/templates/main/service/add.hbs
index faecabd..a42d1fe 100644
--- a/ambari-web/app/templates/main/service/add.hbs
+++ b/ambari-web/app/templates/main/service/add.hbs
@@ -32,9 +32,17 @@
               <li {{bindAttr class="isStep2:active view.isStep2Disabled:disabled"}}><a href="javascript:void(null);"  {{action gotoStep2 target="controller"}}>{{t installer.step5.header}}</a></li>
               <li {{bindAttr class="isStep3:active view.isStep3Disabled:disabled"}}><a href="javascript:void(null);"  {{action gotoStep3 target="controller"}}>{{t installer.step6.header}}</a></li>
               <li {{bindAttr class="isStep4:active view.isStep4Disabled:disabled"}}><a href="javascript:void(null);"  {{action gotoStep4 target="controller"}}>{{t installer.step7.header}}</a></li>
-              <li {{bindAttr class="isStep5:active view.isStep5Disabled:disabled"}}><a href="javascript:void(null);"  {{action gotoStep5 target="controller"}}>{{t installer.step8.header}}</a></li>
-              <li {{bindAttr class="isStep6:active view.isStep6Disabled:disabled"}}><a href="javascript:void(null);"  {{action gotoStep6 target="controller"}}>{{t installer.step9.header}}</a></li>
-              <li {{bindAttr class="isStep7:active view.isStep7Disabled:disabled"}}><a href="javascript:void(null);"  {{action gotoStep7 target="controller"}}>{{t installer.step10.header}}</a></li>
+              {{! @TODO: replace/remove after Kerberos Automation supports }}
+              {{#if App.supports.automatedKerberos}}
+                <li {{bindAttr class="isStep5:active view.isStep5Disabled:disabled"}}><a href="javascript:void(null);"  {{action gotoStep5 target="controller"}}>{{t admin.kerberos.wizard.step4.header}}</a></li>
+                <li {{bindAttr class="isStep6:active view.isStep6Disabled:disabled"}}><a href="javascript:void(null);"  {{action gotoStep5 target="controller"}}>{{t installer.step8.header}}</a></li>
+                <li {{bindAttr class="isStep7:active view.isStep7Disabled:disabled"}}><a href="javascript:void(null);"  {{action gotoStep6 target="controller"}}>{{t installer.step9.header}}</a></li>
+                <li {{bindAttr class="isStep8:active view.isStep8Disabled:disabled"}}><a href="javascript:void(null);"  {{action gotoStep7 target="controller"}}>{{t installer.step10.header}}</a></li>
+              {{else}}
+                <li {{bindAttr class="isStep5:active view.isStep5Disabled:disabled"}}><a href="javascript:void(null);"  {{action gotoStep5 target="controller"}}>{{t installer.step8.header}}</a></li>
+                <li {{bindAttr class="isStep6:active view.isStep6Disabled:disabled"}}><a href="javascript:void(null);"  {{action gotoStep6 target="controller"}}>{{t installer.step9.header}}</a></li>
+                <li {{bindAttr class="isStep7:active view.isStep7Disabled:disabled"}}><a href="javascript:void(null);"  {{action gotoStep7 target="controller"}}>{{t installer.step10.header}}</a></li>
+              {{/if}}
             </ul>
           </div>
         </div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/5704491c/ambari-web/test/controllers/main/admin/kerberos/step4_controller_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/admin/kerberos/step4_controller_test.js b/ambari-web/test/controllers/main/admin/kerberos/step4_controller_test.js
index eaf663b..361175b 100644
--- a/ambari-web/test/controllers/main/admin/kerberos/step4_controller_test.js
+++ b/ambari-web/test/controllers/main/admin/kerberos/step4_controller_test.js
@@ -131,7 +131,85 @@ describe('App.KerberosWizardStep4Controller', function() {
         }, this);
       }, this);
     });
-    
   });
-});
+  
+  describe('#setStepConfigs', function() {
+
+    describe('Add Service Wizard', function() {
+      before(function() {
+        sinon.stub(App.StackService, 'find').returns([
+          Em.Object.create({
+            serviceName: 'KERBEROS',
+            configCategories: []
+          }),
+          Em.Object.create({
+            serviceName: 'HDFS',
+            configCategories: [],
+          }),
+          Em.Object.create({
+            serviceName: 'MAPREDUCE2'
+          })
+        ]);
+        sinon.stub(App.Service, 'find').returns([
+          Em.Object.create({
+            serviceName: 'HDFS'
+          }),
+          Em.Object.create({
+            serviceName: 'KERBEROS'
+          })
+        ]);
+        var controller = App.KerberosWizardStep4Controller.create({
+          selectedServiceNames: ['FALCON', 'MAPREDUCE2'],
+          installedServiceNames: ['HDFS', 'KERBEROS'],
+          wizardController: Em.Object.create({
+            name: 'addServiceController',
+            getDBProperty: function() {
+              return Em.A([
+                Em.Object.create({ name: 'realm', value: 'realm_value' }),
+                Em.Object.create({ name: 'admin_principal', value: 'some_val1', defaultValue: 'some_val1', filename: 'krb5-conf.xml' }),
+                Em.Object.create({ name: 'admin_password', value: 'some_password', defaultValue: 'some_password', filename: 'krb5-conf.xml' }),
+              ]);
+            }
+          })
+        });
+        controller.setStepConfigs(properties);
+        this.result = controller.get('stepConfigs')[0].get('configs');
+      });
+
+      after(function() {
+        App.StackService.find.restore();
+        App.Service.find.restore();
+      });
+
+      var properties = Em.A([
+        Em.Object.create({ name: 'realm', value: '', serviceName: 'Cluster' }),
+        Em.Object.create({ name: 'spnego_keytab', value: 'spnego_keytab_value', serviceName: 'Cluster', isEditable: true }),
+        Em.Object.create({ name: 'hdfs_keytab', value: '', serviceName: 'HDFS', observesValueFrom: 'spnego_keytab', isEditable: true }),
+        Em.Object.create({ name: 'falcon_keytab', value: 'falcon_keytab_value', serviceName: 'FALCON', isEditable: true }),
+        Em.Object.create({ name: 'mapreduce_keytab', value: 'mapreduce_keytab_value', serviceName: 'MAPREDUCE2', isEditable: true })
+      ]);
 
+      var propertiesEditableTests = [
+        { name: 'spnego_keytab', e: false },
+        { name: 'falcon_keytab', e: true },
+        { name: 'hdfs_keytab', e: false },
+        { name: 'mapreduce_keytab', e: true },
+        { name: 'admin_principal', e: true },
+        { name: 'admin_password', e: true }
+      ];
+      
+      propertiesEditableTests.forEach(function(test) {
+        it('Add Service: property `{0}` should be {1}editable'.format(test.name, !!test.e ? '' : 'not '), function() {
+          expect(this.result.findProperty('name', test.name).get('isEditable')).to.eql(test.e);
+        });
+      });
+
+      ['admin_principal', 'admin_password'].forEach(function(item) {
+        it('property `{0}` should have empty value'.format(item), function() {
+          expect(this.result.findProperty('name', item).get('value')).to.be.empty;
+        });
+      });
+    });
+  });
+
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/5704491c/ambari-web/test/controllers/main/service/add_controller_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/service/add_controller_test.js b/ambari-web/test/controllers/main/service/add_controller_test.js
index 03da51d..95e4b2c 100644
--- a/ambari-web/test/controllers/main/service/add_controller_test.js
+++ b/ambari-web/test/controllers/main/service/add_controller_test.js
@@ -94,14 +94,72 @@ describe('App.AddServiceController', function() {
         "ServiceInfo": {"state": "INSTALLED"},
         "urlParams": "ServiceInfo/service_name.in(OOZIE,HDFS,YARN,MAPREDUCE,MAPREDUCE2)"
       }
-    }
-    ]
+    }];
     tests.forEach(function(t){
       it('should generate data with ' + t.selected.join(","), function () {
         expect(addServiceController.generateDataForInstallServices(t.selected)).to.be.eql(t.res);
       });
     });
+  });
+
+  describe('#saveServices', function() {
+    beforeEach(function() {
+      sinon.stub(addServiceController, 'setDBProperty', Em.K);
+    });
 
+    afterEach(function() {
+      addServiceController.setDBProperty.restore();
+    });
 
+    var tests = [
+      {
+        appService: [
+          Em.Object.create({ serviceName: 'HDFS' }),
+          Em.Object.create({ serviceName: 'KERBEROS' })
+        ],
+        stepCtrlContent: Em.Object.create({
+          content: Em.A([
+            Em.Object.create({ serviceName: 'HDFS', isInstalled: true, isSelected: true }),
+            Em.Object.create({ serviceName: 'YARN', isInstalled: false, isSelected: true })
+          ])
+        }),
+        e: {
+          selected: ['YARN'],
+          installed: ['HDFS', 'KERBEROS']
+        }
+      },
+      {
+        appService: [
+          Em.Object.create({ serviceName: 'HDFS' }),
+          Em.Object.create({ serviceName: 'STORM' })
+        ],
+        stepCtrlContent: Em.Object.create({
+          content: Em.A([
+            Em.Object.create({ serviceName: 'HDFS', isInstalled: true, isSelected: true }),
+            Em.Object.create({ serviceName: 'YARN', isInstalled: false, isSelected: true }),
+            Em.Object.create({ serviceName: 'MAPREDUCE2', isInstalled: false, isSelected: true })
+          ])
+        }),
+        e: {
+          selected: ['YARN', 'MAPREDUCE2'],
+          installed: ['HDFS', 'STORM']
+        }
+      }
+    ];
+
+    var message = '{0} installed, {1} selected. Installed list should be {2} and selected - {3}';
+    tests.forEach(function(test) {
+      var installed = test.appService.mapProperty('serviceName');
+      var selected = test.stepCtrlContent.get('content').filterProperty('isSelected', true)
+        .filterProperty('isInstalled', false).mapProperty('serviceName');
+      it(message.format(installed, selected, test.e.installed, test.e.selected), function() {
+        sinon.stub(App.Service, 'find').returns(test.appService);
+        addServiceController.saveServices(test.stepCtrlContent);
+        App.Service.find.restore();
+        var savedServices = addServiceController.setDBProperty.withArgs('services').args[0][1];
+        expect(savedServices.selectedServices).to.have.members(test.e.selected);
+        expect(savedServices.installedServices).to.have.members(test.e.installed);
+      });
+    });
   });
 });