AMBARI-15780. Kerberos wizard: Integrate Configure Identities page with stack advisor recommendations (alexantonenko)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/6de04a33 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/6de04a33 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/6de04a33 Branch: refs/heads/trunk Commit: 6de04a33c63ed9f6523839048e0d5a588d2037a3 Parents: e44c4f7 Author: Alex Antonenko <[email protected]> Authored: Fri Apr 8 19:34:38 2016 +0300 Committer: Alex Antonenko <[email protected]> Committed: Sat Apr 9 21:14:47 2016 +0300 ---------------------------------------------------------------------- ambari-web/app/config.js | 3 +- .../app/controllers/main/admin/kerberos.js | 10 + .../main/admin/kerberos/step4_controller.js | 350 +++++++++++++++++-- .../app/mixins/wizard/addSecurityConfigs.js | 19 +- .../admin/kerberos/step4_controller_test.js | 202 ++++++++++- 5 files changed, 553 insertions(+), 31 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/6de04a33/ambari-web/app/config.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/config.js b/ambari-web/app/config.js index b1f6bd5..06afa49 100644 --- a/ambari-web/app/config.js +++ b/ambari-web/app/config.js @@ -81,7 +81,8 @@ App.supports = { logSearch: false, redhatSatellite: false, enableIpa: false, - addingNewRepository: false + addingNewRepository: false, + kerberosStackAdvisor: true }; if (App.enableExperimental) { http://git-wip-us.apache.org/repos/asf/ambari/blob/6de04a33/ambari-web/app/controllers/main/admin/kerberos.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/controllers/main/admin/kerberos.js b/ambari-web/app/controllers/main/admin/kerberos.js index 5472413..2db825a 100644 --- a/ambari-web/app/controllers/main/admin/kerberos.js +++ b/ambari-web/app/controllers/main/admin/kerberos.js @@ -595,6 +595,16 @@ App.MainAdminKerberosController = App.KerberosWizardStep4Controller.extend({ showManageKDCCredentialsPopup: function() { return App.showManageCredentialsPopup(); + }, + + loadStep: function() { + var self = this; + this.clearStep(); + this.getDescriptor().then(function (properties) { + self.setStepConfigs(self.createServicesStackDescriptorConfigs(properties)); + }).always(function() { + self.set('isRecommendedLoaded', true); + }); } }); http://git-wip-us.apache.org/repos/asf/ambari/blob/6de04a33/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 ac8c212..b3acb8b 100644 --- a/ambari-web/app/controllers/main/admin/kerberos/step4_controller.js +++ b/ambari-web/app/controllers/main/admin/kerberos/step4_controller.js @@ -23,6 +23,9 @@ App.KerberosWizardStep4Controller = App.WizardStep7Controller.extend(App.AddSecu name: 'kerberosWizardStep4Controller', isWithinAddService: Em.computed.equal('wizardController.name', 'addServiceController'), + // stores configurations loaded by ConfigurationsController.getConfigsByTags + servicesConfigurations: null, + clearStep: function() { this.set('isRecommendedLoaded', false); this.set('selectedService', null); @@ -30,15 +33,28 @@ App.KerberosWizardStep4Controller = App.WizardStep7Controller.extend(App.AddSecu }, loadStep: function() { + var self, stored; if (this.get('wizardController.skipConfigureIdentitiesStep')) { App.router.send('next'); return; } - var self = this; + self = this; this.clearStep(); - this.getDescriptor().then(function (properties) { - self.setStepConfigs(self.createServicesStackDescriptorConfigs(properties)); - }).always(function() { + stored = this.get('wizardController').loadCachedStepConfigValues(this) || []; + this.getDescriptor().then(function (kerberosDescriptor) { + var stepConfigs = self.setStepConfigs(self.createServicesStackDescriptorConfigs(kerberosDescriptor)); + self.set('stepConfigs', stepConfigs); + // when configurations were stored no need to apply recommendations again + if (App.get('supports.kerberosStackAdvisor') && !stored.length) { + self.bootstrapRecommendationPayload(kerberosDescriptor).then(function(recommendations) { + self.loadServerSideConfigsRecommendations(recommendations).done(function() { + self.applyServiceConfigs(stepConfigs); + }); + }); + } else { + self.applyServiceConfigs(stepConfigs); + } + }, function() { self.set('isRecommendedLoaded', true); }); }, @@ -100,6 +116,7 @@ App.KerberosWizardStep4Controller = App.WizardStep7Controller.extend(App.AddSecu serviceName: 'KERBEROS_GENERAL', configCategories: categoryForGeneralConfigs, configs: generalConfigs, + configGroups: [], showConfig: true }), App.ServiceConfig.create({ @@ -108,6 +125,7 @@ App.KerberosWizardStep4Controller = App.WizardStep7Controller.extend(App.AddSecu serviceName: 'KERBEROS_ADVANCED', configCategories: categoryForAdvancedConfigs, configs: advancedConfigs, + configGroups: [], showConfig: true }) ]; @@ -119,7 +137,7 @@ App.KerberosWizardStep4Controller = App.WizardStep7Controller.extend(App.AddSecu */ createCategoryForServices: function() { var services = []; - if (this.get('wizardController.name') == 'addServiceController') { + if (this.get('wizardController.name') === 'addServiceController') { services = App.StackService.find().filter(function(item) { return item.get('isInstalled') || item.get('isSelected'); }); @@ -135,13 +153,13 @@ App.KerberosWizardStep4Controller = App.WizardStep7Controller.extend(App.AddSecu * Prepare step configs using stack descriptor properties. * * @param {App.ServiceConfigProperty[]} configs - * @param {App.ServiceConfigProperty[]} stackConfigs + * @param {App.ServiceConfigProperty[]} stackConfigs converted kerberos descriptor */ setStepConfigs: function(configs, stackConfigs) { var configProperties = this.prepareConfigProperties(configs), stackConfigProperties = stackConfigs ? this.prepareConfigProperties(stackConfigs) : [], alterProperties = ['value','initialValue', 'defaultValue']; - if (this.get('wizardController.name') == 'addServiceController') { + 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('installedServiceNames').contains(item.get('serviceName')) || item.get('serviceName') == 'Cluster') { @@ -159,7 +177,7 @@ App.KerberosWizardStep4Controller = App.WizardStep7Controller.extend(App.AddSecu stackConfigProperties.forEach(function(_stackConfigProperty){ var isPropertyInClusterDescriptor = configProperties.filterProperty('filename', _stackConfigProperty.get('filename')).someProperty('name', _stackConfigProperty.get('name')); if (!isPropertyInClusterDescriptor) { - if (this.get('installedServiceNames').contains(_stackConfigProperty.get('serviceName')) || _stackConfigProperty.get('serviceName') == 'Cluster') { + if (this.get('installedServiceNames').contains(_stackConfigProperty.get('serviceName')) || _stackConfigProperty.get('serviceName') === 'Cluster') { _stackConfigProperty.set('isEditable', false); } configProperties.pushObject(_stackConfigProperty); @@ -167,8 +185,9 @@ App.KerberosWizardStep4Controller = App.WizardStep7Controller.extend(App.AddSecu }, this); } configProperties = App.config.sortConfigs(configProperties); - this.get('stepConfigs').pushObjects(this.createServiceConfig(configProperties)); - this.set('selectedService', this.get('stepConfigs')[0]); + var stepConfigs = this.createServiceConfig(configProperties); + this.set('selectedService', stepConfigs[0]); + return stepConfigs; }, /** @@ -189,6 +208,7 @@ App.KerberosWizardStep4Controller = App.WizardStep7Controller.extend(App.AddSecu var installedServiceNames = ['Cluster'].concat(App.Service.find().mapProperty('serviceName')); var configProperties = configs.slice(0); var siteProperties = App.configsCollection.getAll(); + var realmValue; // override stored values App.config.mergeStoredValue(configProperties, this.get('wizardController').loadCachedStepConfigValues(this)); @@ -199,8 +219,8 @@ App.KerberosWizardStep4Controller = App.WizardStep7Controller.extend(App.AddSecu configProperties = configProperties.filter(function(item) { return installedServiceNames.contains(item.get('serviceName')); }); - if (this.get('wizardController.name') != 'addServiceController') { - var realmValue = storedServiceConfigs.findProperty('name', 'realm').value; + if (this.get('wizardController.name') !== 'addServiceController') { + realmValue = storedServiceConfigs.findProperty('name', 'realm').value; configProperties.findProperty('name', 'realm').set('value', realmValue); configProperties.findProperty('name', 'realm').set('savedValue', realmValue); configProperties.findProperty('name', 'realm').set('recommendedValue', realmValue); @@ -216,7 +236,7 @@ App.KerberosWizardStep4Controller = App.WizardStep7Controller.extend(App.AddSecu property.set('value', observedValue); property.set('recommendedValue', observedValue); } - if (property.get('serviceName') == 'Cluster') { + if (property.get('serviceName') === 'Cluster') { property.set('category', 'Global'); } else { @@ -246,17 +266,21 @@ App.KerberosWizardStep4Controller = App.WizardStep7Controller.extend(App.AddSecu * Function to override kerberos descriptor's property values */ tweakConfigProperty: function(config) { + var defaultHiveMsPort = "9083", + hiveMSHosts, + port, + hiveMSHostNames, + configValue; if (config.name === 'templeton.hive.properties') { - var defaultHiveMsPort = "9083"; - var hiveMSHosts = App.HostComponent.find().filterProperty('componentName', 'HIVE_METASTORE'); + hiveMSHosts = App.HostComponent.find().filterProperty('componentName', 'HIVE_METASTORE'); if (hiveMSHosts.length > 1) { - var hiveMSHostNames = hiveMSHosts.mapProperty('hostName'); - var port = config.value.match(/:[0-9]{2,4}/); + hiveMSHostNames = hiveMSHosts.mapProperty('hostName'); + port = config.value.match(/:[0-9]{2,4}/); port = port ? port[0].slice(1) : defaultHiveMsPort; for (var i = 0; i < hiveMSHostNames.length; i++) { hiveMSHostNames[i] = "thrift://" + hiveMSHostNames[i] + ":" + port; } - var configValue = config.value.replace(/thrift.+[0-9]{2,},/i, hiveMSHostNames.join('\\,') + ","); + configValue = config.value.replace(/thrift.+[0-9]{2,},/i, hiveMSHostNames.join('\\,') + ","); config.set('value', configValue); config.set('recommendedValue', configValue); } @@ -265,14 +289,14 @@ App.KerberosWizardStep4Controller = App.WizardStep7Controller.extend(App.AddSecu /** * Sync up values between inherited property and its reference. - * + * * @param {App.ServiceConfigProperty} configProperty */ spnegoPropertiesObserver: function(configProperty) { var self = this; - var stepConfig = this.get('stepConfigs').findProperty('name', 'ADVANCED'); + var stepConfig = this.get('stepConfigs').findProperty('name', 'ADVANCED'); stepConfig.get('configs').forEach(function(config) { - if (config.get('observesValueFrom') == configProperty.get('name')) { + if (config.get('observesValueFrom') === configProperty.get('name')) { Em.run.once(self, function() { config.set('value', configProperty.get('value')); config.set('recommendedValue', configProperty.get('value')); @@ -294,5 +318,289 @@ App.KerberosWizardStep4Controller = App.WizardStep7Controller.extend(App.AddSecu }); this.updateKerberosDescriptor(kerberosDescriptor, configs); App.get('router.kerberosWizardController').saveKerberosDescriptorConfigs(kerberosDescriptor); + }, + + /** + * Add/update property in `properties` object for each config type with + * associated kerberos descriptor config value. + * + * @private + * @param {object[]} configurations + * @param {App.ServiceConfigProperty[]} kerberosDescriptor + * @returns {object[]} + */ + mergeDescriptorToConfigurations: function(configurations, kerberosDescriptor) { + return configurations.map(function(configType) { + var properties = $.extend({}, configType.properties); + var filteredDescriptor = kerberosDescriptor.filterProperty('filename', configType.type); + if (filteredDescriptor.length) { + filteredDescriptor.forEach(function(descriptorConfig) { + var configName = Em.get(descriptorConfig, 'name'); + properties[configName] = Em.get(descriptorConfig, 'value'); + }); + } + return { + type: configType.type, + version: configType.version, + tag: configType.tag, + properties: properties + }; + }); + }, + + loadServerSideConfigsRecommendations: function(recommendations) { + return App.ajax.send({ + 'name': 'config.recommendations', + 'sender': this, + 'data': { + stackVersionUrl: App.get('stackVersionURL'), + dataToSend: { + recommend: 'configurations', + hosts: this.get('hostNames'), + services: this.get('serviceNames'), + recommendations: recommendations + } + }, + 'success': 'loadRecommendationsSuccess', + 'error': 'loadRecommendationsError' + }); + }, + + applyServiceConfigs: function(stepConfigs) { + this.set('isRecommendedLoaded', true); + this.set('selectedService', stepConfigs[0]); + }, + + /** + * Callback executed when all configs specified by tags are loaded. + * Here we handle configurations for instlled services and Kerberos. + * Gather needed info for recommendation request such as configurations object. + * + * @override + */ + getConfigTagsSuccess: function(data) { + // here we get all installed services including KERBEROS + var serviceNames = App.Service.find().mapProperty('serviceName').concat(['KERBEROS']).uniq(); + // collect all config types for selected services + var installedServiceSites = Array.prototype.concat.apply([], App.config.get('preDefinedServiceConfigs').filter(function(serviceConfig) { + return serviceNames.contains(Em.get(serviceConfig, 'serviceName')); + }).map(function (service) { + // when service have no configs return <code>null</code> instead return config types + if (!service.get('configTypes')) return null; + return Object.keys(service.get('configTypes')); + }, this).compact()).uniq(); // cleanup <code>null</code> + + // take all configs for selected services by config types recieved from API response + var serviceConfigTags = Em.keys(data.Clusters.desired_configs).reduce(function(tags, site) { + if (data.Clusters.desired_configs.hasOwnProperty(site)) { + // push cluster-env.xml also since it not associated with any service but need to further processing + if (installedServiceSites.contains(site) || site === 'cluster-env') { + tags.push({ + siteName: site, + tagName: data.Clusters.desired_configs[site].tag, + newTagName: null + }); + } + } + return tags; + }, []); + // store configurations + this.set('serviceConfigTags', serviceConfigTags); + this.set('isAppliedConfigLoaded', true); + }, + + /** + * Prepare all necessary data for recommendations payload. + * + * #mutates initialConfigValues + * @returns {$.Deferred.promise()} + */ + bootstrapRecommendationPayload: function(kerberosDescriptor) { + var dfd = $.Deferred(); + var self = this; + this.getServicesConfigurations().then(function(configurations) { + var recommendations = self.getBlueprintPayloadObject(configurations, kerberosDescriptor); + self.set('servicesConfigurations', configurations); + self.set('initialConfigValues', recommendations.blueprint.configurations); + dfd.resolve(recommendations); + }); + return dfd.promise(); + }, + + getServicesConfigurations: function() { + var dfd = $.Deferred(); + var self = this; + var configs, servicesConfigurations; + if (this.get('isWithinAddService')) { + configs = this.get('wizardController').getDBProperty('serviceConfigProperties'); + servicesConfigurations = configs.reduce(function(configTags, property) { + var fileName = App.config.getConfigTagFromFileName(property.filename), + configType; + if (!configTags.someProperty('type', fileName)) { + configTags.push({ + type: fileName, + properties: {} + }); + } + configType = configTags.findProperty('type', fileName); + configType.properties[property.name] = property.value; + return configTags; + }, []); + dfd.resolve(servicesConfigurations); + } else { + this.getConfigTags().then(function() { + App.router.get('configurationController').getConfigsByTags(self.get('serviceConfigTags')).done(function (configurations) { + dfd.resolve(configurations); + }); + }); + } + + return dfd.promise(); + }, + + /** + * Returns payload for recommendations request. + * Takes services' configurations and merge them with kerberos descriptor properties. + * + * @param {object[]} configurations services' configurations fetched from API + * @param {App.ServiceConfigProperty[]} kerberosDescriptor descriptor configs + * @returns {object} payload for recommendations request + */ + getBlueprintPayloadObject: function(configurations, kerberosDescriptor) { + var recommendations = this.get('hostGroups'); + var mergedConfigurations = this.mergeDescriptorToConfigurations(configurations, this.createServicesStackDescriptorConfigs(kerberosDescriptor)); + recommendations.blueprint.configurations = mergedConfigurations.reduce(function(p, c) { + p[c.type] = {}; + p[c.type].properties = c.properties; + return p; + }, {}); + + return recommendations; + }, + + /** + * @override + */ + _saveRecommendedValues: function(data) { + var recommendedConfigurations = Em.getWithDefault(data, 'resources.0.recommendations.blueprint.configurations', {}); + var allConfigs = Array.prototype.concat.apply([], this.get('stepConfigs').mapProperty('configs')); + var self = this; + // iterate by each config file name e.g. hdfs-site + var groupedProperties = this.groupRecommendationProperties(recommendedConfigurations, this.get('servicesConfigurations'), allConfigs); + var newProperties = []; + Em.keys(groupedProperties.add).forEach(function(fileName) { + var serviceName = self.getServiceByFilename(fileName); + Em.keys(groupedProperties.add[fileName]).forEach(function(propertyName) { + var property = self._createNewProperty(propertyName, fileName, serviceName, groupedProperties.add[fileName][propertyName]); + property.set('category', serviceName); + property.set('isOverridable', false); + property.set('supportsFinal', false); + property.set('isUserProperty', false); + property.set('filename', fileName); + newProperties.push(property); + }); + }); + Array.prototype.push.apply(self.getServicesConfigObject().get('configs'), newProperties); + Em.keys(groupedProperties.update).forEach(function(fileName) { + Em.keys(groupedProperties.update[fileName]).forEach(function(propertyName) { + var configProperty = allConfigs.findProperty('name', propertyName); + if (configProperty) { + self._updateConfigByRecommendation(configProperty, groupedProperties.update[fileName][propertyName], true, false); + } + }); + }); + Em.keys(groupedProperties.delete).forEach(function(fileName) { + Em.keys(groupedProperties.delete[fileName]).forEach(function(propertyName) { + var servicesConfigs = self.getServicesConfigObject().get('configs'); + servicesConfigs.removeObject(servicesConfigs.filterProperty('filename', fileName).findProperty('name', propertyName)); + }); + }); + }, + + /** + * Returns category where services' configuration located. + * + * @returns {App.ServiceConfig} + */ + getServicesConfigObject: function() { + return this.get('stepConfigs').findProperty('name', 'ADVANCED'); + }, + + /** + * Returns map with appropriate action and properties to process with. + * Key is an action e.g. `add`, `update`, `delete` and value is an object `fileName` -> `propertyName`: `propertyValue`. + * + * @param {object} recommendedConfigurations + * @param {object[]} servicesConfigurations services' configurations fetched from API + * @param {App.ServiceConfigProperty[]} allConfigs all current configurations stored in controller, basically kerberos descriptor + * @returns {object} + */ + groupRecommendationProperties: function(recommendedConfigurations, servicesConfigurations, allConfigs) { + var resultMap = { + update: {}, + add: {}, + delete: {} + }; + + /** + * Adds property to associated group `add`,`delete`,`update`. + * + * @param {object} propertyMap <code>resultMap</code> object + * @param {string} name property name + * @param {string} propertyValue property value + * @param {string} fileName property file name + * @return {object} <code>resultMap</code> + * @param {string} group, `add`,`delete`,`update` + */ + var addProperty = function(propertyMap, name, propertyValue, fileName, group) { + var ret = $.extend(true, {}, propertyMap); + if (ret.hasOwnProperty(group)) { + if (!ret[group].hasOwnProperty(fileName)) { + ret[group][fileName] = {}; + } + ret[group][fileName][name] = propertyValue; + } + return ret; + }; + + return Em.keys(recommendedConfigurations || {}).reduce(function(acc, fileName) { + var propertyMap = acc; + var recommendedProperties = Em.getWithDefault(recommendedConfigurations, fileName + '.properties', {}); + var recommendedAttributes = Em.getWithDefault(recommendedConfigurations, fileName + '.property_attributes', {}); + // check for properties that should be delted + Em.keys(recommendedAttributes).forEach(function(propertyName) { + var attribute = recommendedAttributes[propertyName]; + // delete properties which are present in kerberos descriptor + if (attribute.hasOwnProperty('delete') && allConfigs.filterProperty('filename', fileName).someProperty('name', propertyName)) { + propertyMap = addProperty(propertyMap, propertyName, '', fileName, 'delete'); + } + }); + + return Em.keys(recommendedProperties).reduce(function(a, propertyName) { + var propertyValue = recommendedProperties[propertyName]; + // check if property exist in saved configurations on server + var isExist = Em.getWithDefault(servicesConfigurations.findProperty('type', fileName) || {}, 'properties', {}).hasOwnProperty(propertyName); + if (!isExist) { + return addProperty(a, propertyName, propertyValue, fileName, 'add'); + } + // when property exist check that it present in current step configs (kerberos descriptor) + // and add it as property to `update` + if (allConfigs.filterProperty('filename', fileName).someProperty('name', propertyName)) { + return addProperty(a, propertyName, propertyValue, fileName, 'update'); + } + return a; + }, propertyMap); + }, resultMap); + }, + + getServiceByFilename: function(fileName) { + // core-site properties goes to HDFS + if (fileName === 'core-site' && App.Service.find().someProperty('serviceName', 'HDFS')) { + return 'HDFS'; + } + var associatedService = App.StackService.find().filter(function(service) { + return Em.keys(service.get('configTypes')).contains(fileName); + })[0]; + return associatedService ? associatedService.get('serviceName') : ''; } }); http://git-wip-us.apache.org/repos/asf/ambari/blob/6de04a33/ambari-web/app/mixins/wizard/addSecurityConfigs.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/mixins/wizard/addSecurityConfigs.js b/ambari-web/app/mixins/wizard/addSecurityConfigs.js index c1c301f..a829cb6 100644 --- a/ambari-web/app/mixins/wizard/addSecurityConfigs.js +++ b/ambari-web/app/mixins/wizard/addSecurityConfigs.js @@ -17,7 +17,6 @@ */ var App = require('app'); -var objectUtils = require('utils/object_utils'); /** * Mixin for loading and setting secure configs @@ -167,7 +166,12 @@ App.AddSecurityConfigs = Em.Mixin.create({ configObject.referenceProperty = name.substring(1) + ':' + item; configObject.isEditable = false; } - configObject.defaultValue = configObject.savedValue = configObject.value = itemValue; + Em.setProperties(configObject, { + recommendedValue: itemValue, + initialValue: itemValue, + defaultValue: itemValue, + value: itemValue + }); configObject.filename = prop.configuration ? prop.configuration.split('/')[0] : 'cluster-env'; configObject.name = prop.configuration ? prop.configuration.split('/')[1] : name + '_' + item; predefinedProperty = self.get('kerberosDescriptorProperties').findProperty('name', configObject.name); @@ -207,17 +211,20 @@ App.AddSecurityConfigs = Em.Mixin.create({ for (var propertyName in kerberosProperties) { var predefinedProperty = this.get('kerberosDescriptorProperties').findProperty('name', propertyName); + var value = kerberosProperties[propertyName]; + var isRequired = propertyName == 'additional_realms' ? false : value !== ""; var propertyObject = { name: propertyName, - value: kerberosProperties[propertyName], - defaultValue: kerberosProperties[propertyName], - savedValue: kerberosProperties[propertyName], + value: value, + defaultValue: value, + recommendedValue: value, + initialValue: value, serviceName: serviceName, filename: filename, displayName: serviceName == "Cluster" ? App.format.normalizeName(propertyName) : propertyName, isOverridable: false, isEditable: propertyName != 'realm', - isRequired: propertyName != 'additional_realms', + isRequired: isRequired, isSecureConfig: true, placeholderText: predefinedProperty && !Em.isNone(predefinedProperty.index) ? predefinedProperty.placeholderText : '', index: predefinedProperty && !Em.isNone(predefinedProperty.index) ? predefinedProperty.index : Infinity http://git-wip-us.apache.org/repos/asf/ambari/blob/6de04a33/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 01a3b00..75507f0 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 @@ -213,8 +213,8 @@ describe('App.KerberosWizardStep4Controller', function() { }) }); sinon.stub(App.router, 'get').withArgs('mainAdminKerberosController.isManualKerberos').returns(false); - controller.setStepConfigs(properties); - res = controller.get('stepConfigs')[0].get('configs').concat(controller.get('stepConfigs')[1].get('configs')); + var stepConfigs = controller.setStepConfigs(properties); + res = stepConfigs[0].get('configs').concat(stepConfigs[1].get('configs')); }); Em.A([ @@ -290,7 +290,7 @@ describe('App.KerberosWizardStep4Controller', function() { this.wizardController = App.AddServiceController.create({}); controller.set('wizardController', this.wizardController); sinon.stub(controller, 'clearStep').returns(true); - + sinon.stub(controller, 'getDescriptor').returns({ then: function() { return { always: function() {}}}}); sinon.stub(controller, 'setStepConfigs').returns(true); sinon.stub(App.router, 'send').withArgs('next'); }); @@ -341,4 +341,200 @@ describe('App.KerberosWizardStep4Controller', function() { }); }); }); + + describe('#mergeDescriptorToConfigurations', function() { + var genAppConfigProperty = function(name, fileName, value) { + return App.ServiceConfigProperty.create({ + name: name, + filename: fileName, + value: value + }); + }; + + var genPropertyCollection = function(configsList) { + return configsList.map(function(i) { + return genAppConfigProperty.apply(undefined, i); + }); + }; + + var genConfigType = function(fileName, properties) { + var configTypeObj = {}; + configTypeObj.type = fileName; + configTypeObj.properties = properties.reduce(function(p,c) { + p[c[0]] = c[1]; + return p; + }, {}); + return configTypeObj; + }; + + var genConfigTypeCollection = function(coll) { + return coll.map(function(i) { + return genConfigType(i[0], i[1]); + }); + }; + + var cases = [ + { + kerberosDescriptor: genPropertyCollection([]), + configurations: [], + e: [], + m: 'should return empty array' + }, + { + kerberosDescriptor: genPropertyCollection([ + ['hadoop.proxy.group', 'hadoop-env', 'val1'] + ]), + configurations: genConfigTypeCollection([ + ['hadoop-env', [ + ['hadoop.proxy.group', 'change_me'], + ['hadoop.proxy', 'val2'] + ]], + ['core-site', [ + ['hadoop.proxyuser.hcat.groups', '*'] + ]] + ]), + e: [ + { + type: 'hadoop-env', + properties: { + 'hadoop.proxy.group': 'val1', + 'hadoop.proxy': 'val2' + } + }, + { + type: 'core-site', + properties: { + 'hadoop.proxyuser.hcat.groups': '*' + } + } + ], + m: 'should change value of `hadoop.proxy.group`, rest object should not be changed.' + }, + { + kerberosDescriptor: genPropertyCollection([ + ['hadoop.proxy.group', 'hadoop-env', 'val1'], + ['new_site_prop', 'core-site', 'new_val'] + ]), + configurations: genConfigTypeCollection([ + ['hadoop-env', [ + ['hadoop.proxy.group', 'val1'], + ['hadoop.proxy', 'val2'] + ]], + ['core-site', [ + ['hadoop.proxyuser.hcat.groups', '*'] + ]] + ]), + e: [ + { + type: 'hadoop-env', + properties: { + 'hadoop.proxy.group': 'val1', + 'hadoop.proxy': 'val2' + } + }, + { + type: 'core-site', + properties: { + 'hadoop.proxyuser.hcat.groups': '*', + 'new_site_prop': 'new_val' + } + } + ], + m: 'should add property `new_site_prop` value to `core-site` file type, rest object should not be changed.' + } + ]; + + cases.forEach(function(test) { + it(test.m, function() { + var toObj = function(res) { + return JSON.parse(JSON.stringify(res)); + }; + expect(toObj(c.mergeDescriptorToConfigurations(test.configurations, test.kerberosDescriptor))).to.be.eql(test.e); + }); + }); + }); + + describe('#groupRecommendationProperties', function() { + var cases, controller; + beforeEach(function() { + controller = App.KerberosWizardStep4Controller.create({}); + }); + + afterEach(function() { + controller.destroy(); + controller = null; + }); + + cases = [ + { + recommendedConfigurations: {}, + servicesConfigurations: [], + allConfigs: [], + m: 'empty objects should not fail the code', + e: { + add: {}, + update: {}, + delete: {} + } + }, + { + recommendedConfigurations: { + 'some-site': { + properties: { + // property absent from servicesConfigurations and allConfigs + // should be added + 'new_prop1': 'val1', + // property present in servicesConfigurations but absent in allConfigs + // should be skipped + 'new_prop2': 'val2', + 'existing-prop': 'updated_val2' + }, + property_attributes: { + 'delete_prop1': { + 'delete': true + } + } + } + }, + servicesConfigurations: [ + { + type: 'some-site', + properties: { + 'existing-prop': 'val2', + 'new_prop2': 'val2' + } + } + ], + allConfigs: [ + Em.Object.create({ name: 'existing-prop', value: 'val3', filename: 'some-site'}), + Em.Object.create({ name: 'delete_prop1', value: 'val', filename: 'some-site'}) + ], + m: 'should add "new_prop1", remove "delete_prop1", skip adding "new_prop2" and update value for "existing-prop"', + e: { + update: { + 'some-site': { + 'existing-prop': 'updated_val2' + } + }, + add: { + 'some-site': { + 'new_prop1': 'val1' + } + }, + delete: { + 'some-site': { + 'delete_prop1': '' + } + } + } + } + ]; + + cases.forEach(function(test) { + it(test.m, function() { + expect(controller.groupRecommendationProperties(test.recommendedConfigurations, test.servicesConfigurations, test.allConfigs)) + .to.be.eql(test.e); + }); + }); + }); });
