Repository: ambari Updated Branches: refs/heads/trunk f1113a261 -> 403d59324
AMBARI-20488 Config types are validated before the cluster resources are persisted Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/403d5932 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/403d5932 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/403d5932 Branch: refs/heads/trunk Commit: 403d59324b8a537337f83274274610963241cf33 Parents: f1113a2 Author: lpuskas <lpus...@apache.org> Authored: Fri Mar 17 17:53:58 2017 +0100 Committer: lpuskas <lpus...@apache.org> Committed: Mon Mar 20 17:23:57 2017 +0100 ---------------------------------------------------------------------- .../internal/ProvisionClusterRequest.java | 47 +++--- .../topology/RequiredPasswordValidator.java | 156 ------------------ .../validators/ClusterConfigTypeValidator.java | 76 +++++++++ .../validators/RequiredPasswordValidator.java | 157 +++++++++++++++++++ .../internal/ProvisionClusterRequestTest.java | 12 +- .../topology/RequiredPasswordValidatorTest.java | 1 + .../ClusterConfigTypeValidatorTest.java | 110 +++++++++++++ 7 files changed, 375 insertions(+), 184 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/403d5932/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequest.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequest.java index 2c8d09a..a63013a 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequest.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequest.java @@ -18,7 +18,6 @@ package org.apache.ambari.server.controller.internal; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -36,15 +35,17 @@ import org.apache.ambari.server.topology.Credential; import org.apache.ambari.server.topology.HostGroupInfo; import org.apache.ambari.server.topology.InvalidTopologyTemplateException; import org.apache.ambari.server.topology.NoSuchBlueprintException; -import org.apache.ambari.server.topology.RequiredPasswordValidator; import org.apache.ambari.server.topology.SecurityConfiguration; import org.apache.ambari.server.topology.TopologyValidator; +import org.apache.ambari.server.topology.validators.ClusterConfigTypeValidator; +import org.apache.ambari.server.topology.validators.RequiredPasswordValidator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Enums; import com.google.common.base.Optional; import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; /** * Request for provisioning a cluster. @@ -118,7 +119,6 @@ public class ProvisionClusterRequest extends BaseClusterRequest { public static final String QUICKLINKS_PROFILE_SERVICES_PROPERTY = "quicklinks_profile/services"; - /** * configuration factory */ @@ -145,6 +145,8 @@ public class ProvisionClusterRequest extends BaseClusterRequest { private final String quickLinksProfileJson; + private final List<TopologyValidator> topologyValidators; + private final static Logger LOG = LoggerFactory.getLogger(ProvisionClusterRequest.class); /** @@ -177,7 +179,7 @@ public class ProvisionClusterRequest extends BaseClusterRequest { this.securityConfiguration = securityConfiguration; Configuration configuration = configurationFactory.getConfiguration( - (Collection<Map<String, String>>) properties.get(CONFIGURATIONS_PROPERTY)); + (Collection<Map<String, String>>) properties.get(CONFIGURATIONS_PROPERTY)); configuration.setParentConfiguration(blueprint.getConfiguration()); setConfiguration(configuration); @@ -191,10 +193,11 @@ public class ProvisionClusterRequest extends BaseClusterRequest { try { this.quickLinksProfileJson = processQuickLinksProfile(properties); - } - catch (QuickLinksProfileEvaluationException ex) { + } catch (QuickLinksProfileEvaluationException ex) { throw new InvalidTopologyTemplateException("Invalid quick links profile", ex); } + + topologyValidators = ImmutableList.of(new RequiredPasswordValidator(defaultPassword), new ClusterConfigTypeValidator()); } private String processQuickLinksProfile(Map<String, Object> properties) throws QuickLinksProfileEvaluationException { @@ -269,7 +272,7 @@ public class ProvisionClusterRequest extends BaseClusterRequest { @Override public List<TopologyValidator> getTopologyValidators() { - return Collections.<TopologyValidator>singletonList(new RequiredPasswordValidator(defaultPassword)); + return topologyValidators; } @Override @@ -304,7 +307,7 @@ public class ProvisionClusterRequest extends BaseClusterRequest { */ private void parseHostGroupInfo(Map<String, Object> properties) throws InvalidTopologyTemplateException { Collection<Map<String, Object>> hostGroups = - (Collection<Map<String, Object>>) properties.get(HOSTGROUPS_PROPERTY); + (Collection<Map<String, Object>>) properties.get(HOSTGROUPS_PROPERTY); if (hostGroups == null || hostGroups.isEmpty()) { throw new InvalidTopologyTemplateException("'host_groups' element must be included in cluster create body"); @@ -334,11 +337,11 @@ public class ProvisionClusterRequest extends BaseClusterRequest { processHostCountAndPredicate(hostGroupProperties, hostGroupInfo); processGroupHosts(name, (Collection<Map<String, String>>) - hostGroupProperties.get(HOSTGROUP_HOSTS_PROPERTY), hostGroupInfo); + hostGroupProperties.get(HOSTGROUP_HOSTS_PROPERTY), hostGroupInfo); // don't set the parent configuration hostGroupInfo.setConfiguration(configurationFactory.getConfiguration( - (Collection<Map<String, String>>) hostGroupProperties.get(CONFIGURATIONS_PROPERTY))); + (Collection<Map<String, String>>) hostGroupProperties.get(CONFIGURATIONS_PROPERTY))); } /** @@ -350,20 +353,20 @@ public class ProvisionClusterRequest extends BaseClusterRequest { * @throws InvalidTopologyTemplateException specified host group properties fail validation */ private void processHostCountAndPredicate(Map<String, Object> hostGroupProperties, HostGroupInfo hostGroupInfo) - throws InvalidTopologyTemplateException { + throws InvalidTopologyTemplateException { if (hostGroupProperties.containsKey(HOSTGROUP_HOST_COUNT_PROPERTY)) { hostGroupInfo.setRequestedCount(Integer.valueOf(String.valueOf( - hostGroupProperties.get(HOSTGROUP_HOST_COUNT_PROPERTY)))); + hostGroupProperties.get(HOSTGROUP_HOST_COUNT_PROPERTY)))); LOG.info("Stored expected hosts count {} for group {}", - hostGroupInfo.getRequestedHostCount(), hostGroupInfo.getHostGroupName()); + hostGroupInfo.getRequestedHostCount(), hostGroupInfo.getHostGroupName()); } if (hostGroupProperties.containsKey(HOSTGROUP_HOST_PREDICATE_PROPERTY)) { if (hostGroupInfo.getRequestedHostCount() == 0) { throw new InvalidTopologyTemplateException(String.format( - "Host group '%s' must not specify 'host_predicate' without 'host_count'", - hostGroupInfo.getHostGroupName())); + "Host group '%s' must not specify 'host_predicate' without 'host_count'", + hostGroupInfo.getHostGroupName())); } String hostPredicate = String.valueOf(hostGroupProperties.get(HOSTGROUP_HOST_PREDICATE_PROPERTY)); @@ -373,7 +376,7 @@ public class ProvisionClusterRequest extends BaseClusterRequest { LOG.info("Compiled host predicate {} for group {}", hostPredicate, hostGroupInfo.getHostGroupName()); } catch (InvalidQueryException e) { throw new InvalidTopologyTemplateException( - String.format("Unable to compile host predicate '%s': %s", hostPredicate, e), e); + String.format("Unable to compile host predicate '%s': %s", hostPredicate, e), e); } } } @@ -388,17 +391,17 @@ public class ProvisionClusterRequest extends BaseClusterRequest { * @throws InvalidTopologyTemplateException specified host group properties fail validation */ private void processGroupHosts(String name, Collection<Map<String, String>> hosts, HostGroupInfo hostGroupInfo) - throws InvalidTopologyTemplateException { + throws InvalidTopologyTemplateException { if (hosts != null) { if (hostGroupInfo.getRequestedHostCount() != 0) { throw new InvalidTopologyTemplateException(String.format( - "Host group '%s' must not contain both a 'hosts' element and a 'host_count' value", name)); + "Host group '%s' must not contain both a 'hosts' element and a 'host_count' value", name)); } if (hostGroupInfo.getPredicate() != null) { throw new InvalidTopologyTemplateException(String.format( - "Host group '%s' must not contain both a 'hosts' element and a 'host_predicate' value", name)); + "Host group '%s' must not contain both a 'hosts' element and a 'host_predicate' value", name)); } for (Map<String, String> hostProperties : hosts) { @@ -408,15 +411,15 @@ public class ProvisionClusterRequest extends BaseClusterRequest { if (hostProperties.containsKey(HOSTGROUP_HOST_RACK_INFO_PROPERTY)) { hostGroupInfo.addHostRackInfo( - hostProperties.get(HOSTGROUP_HOST_FQDN_PROPERTY), - hostProperties.get(HOSTGROUP_HOST_RACK_INFO_PROPERTY)); + hostProperties.get(HOSTGROUP_HOST_FQDN_PROPERTY), + hostProperties.get(HOSTGROUP_HOST_RACK_INFO_PROPERTY)); } } } if (hostGroupInfo.getRequestedHostCount() == 0) { throw new InvalidTopologyTemplateException(String.format( - "Host group '%s' must contain at least one 'hosts/fqdn' or a 'host_count' value", name)); + "Host group '%s' must contain at least one 'hosts/fqdn' or a 'host_count' value", name)); } } http://git-wip-us.apache.org/repos/asf/ambari/blob/403d5932/ambari-server/src/main/java/org/apache/ambari/server/topology/RequiredPasswordValidator.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/topology/RequiredPasswordValidator.java b/ambari-server/src/main/java/org/apache/ambari/server/topology/RequiredPasswordValidator.java deleted file mode 100644 index 41812c3..0000000 --- a/ambari-server/src/main/java/org/apache/ambari/server/topology/RequiredPasswordValidator.java +++ /dev/null @@ -1,156 +0,0 @@ -/** - * 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 distribut - * ed 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. - */ - -package org.apache.ambari.server.topology; - -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; - -import org.apache.ambari.server.controller.internal.Stack; -import org.apache.ambari.server.state.PropertyInfo; - -/** - * Validates that all required passwords are provided. - */ -public class RequiredPasswordValidator implements TopologyValidator { - - private String defaultPassword; - - public RequiredPasswordValidator(String defaultPassword) { - this.defaultPassword = defaultPassword; - } - - /** - * Validate that all required password properties have been set or that 'default_password' is specified. - * - * @throws InvalidTopologyException if required password properties are missing and no - * default is specified via 'default_password' - */ - public void validate(ClusterTopology topology) throws InvalidTopologyException { - Map<String, Map<String, Collection<String>>> missingPasswords = validateRequiredPasswords(topology); - - if (! missingPasswords.isEmpty()) { - throw new InvalidTopologyException("Missing required password properties. Specify a value for these " + - "properties in the cluster or host group configurations or include 'default_password' field in request. " + - missingPasswords); - } - } - - /** - * Validate all configurations. Validation is done on the operational configuration of each - * host group. An operational configuration is achieved by overlaying host group configuration - * on top of cluster configuration which overlays the default stack configurations. - * - * @return map of required properties which are missing. Empty map if none are missing. - * - * @throws IllegalArgumentException if blueprint contains invalid information - */ - - //todo: this is copied/pasted from Blueprint and is currently only used by validatePasswordProperties() - //todo: seems that we should have some common place for this code so it can be used by BP and here? - private Map<String, Map<String, Collection<String>>> validateRequiredPasswords(ClusterTopology topology) { - - Map<String, Map<String, Collection<String>>> missingProperties = - new HashMap<String, Map<String, Collection<String>>>(); - - for (Map.Entry<String, HostGroupInfo> groupEntry: topology.getHostGroupInfo().entrySet()) { - String hostGroupName = groupEntry.getKey(); - Map<String, Map<String, String>> groupProperties = - groupEntry.getValue().getConfiguration().getFullProperties(3); - - Collection<String> processedServices = new HashSet<String>(); - Blueprint blueprint = topology.getBlueprint(); - Stack stack = blueprint.getStack(); - - HostGroup hostGroup = blueprint.getHostGroup(hostGroupName); - for (String component : hostGroup.getComponentNames()) { - //for now, AMBARI is not recognized as a service in Stacks - if (component.equals("AMBARI_SERVER")) { - continue; - } - - String serviceName = stack.getServiceForComponent(component); - if (processedServices.add(serviceName)) { - //todo: do I need to subtract excluded configs? - Collection<Stack.ConfigProperty> requiredProperties = - stack.getRequiredConfigurationProperties(serviceName, PropertyInfo.PropertyType.PASSWORD); - - for (Stack.ConfigProperty property : requiredProperties) { - String category = property.getType(); - String name = property.getName(); - if (! propertyExists(topology, groupProperties, category, name)) { - Map<String, Collection<String>> missingHostGroupPropsMap = missingProperties.get(hostGroupName); - if (missingHostGroupPropsMap == null) { - missingHostGroupPropsMap = new HashMap<String, Collection<String>>(); - missingProperties.put(hostGroupName, missingHostGroupPropsMap); - } - Collection<String> missingHostGroupTypeProps = missingHostGroupPropsMap.get(category); - if (missingHostGroupTypeProps == null) { - missingHostGroupTypeProps = new HashSet<String>(); - missingHostGroupPropsMap.put(category, missingHostGroupTypeProps); - } - missingHostGroupTypeProps.add(name); - } - } - } - } - } - return missingProperties; - } - - private boolean propertyExists(ClusterTopology topology, Map<String, Map<String, String>> props, String type, String property) { - Map<String, String> typeProps = props.get(type); - return (typeProps != null && typeProps.containsKey(property)) || setDefaultPassword(topology, type, property); - } - - /** - * Attempt to set the default password in cluster configuration for missing password property. - * - * @param configType configuration type - * @param property password property name - * - * @return true if password was set, otherwise false. Currently the password will always be set - * unless it is null - */ - private boolean setDefaultPassword(ClusterTopology topology, String configType, String property) { - boolean setDefaultPassword = false; - if (defaultPassword != null && ! defaultPassword.trim().isEmpty()) { - topology.getConfiguration().setProperty(configType, property, defaultPassword); - setDefaultPassword = true; - } - return setDefaultPassword; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - RequiredPasswordValidator that = (RequiredPasswordValidator) o; - - return defaultPassword == null ? that.defaultPassword == null : defaultPassword.equals(that.defaultPassword); - } - - @Override - public int hashCode() { - return defaultPassword != null ? defaultPassword.hashCode() : 0; - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/403d5932/ambari-server/src/main/java/org/apache/ambari/server/topology/validators/ClusterConfigTypeValidator.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/topology/validators/ClusterConfigTypeValidator.java b/ambari-server/src/main/java/org/apache/ambari/server/topology/validators/ClusterConfigTypeValidator.java new file mode 100644 index 0000000..18d08b9 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/topology/validators/ClusterConfigTypeValidator.java @@ -0,0 +1,76 @@ +/* + * Licensed 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. + */ +package org.apache.ambari.server.topology.validators; + +import java.util.HashSet; +import java.util.Set; + +import org.apache.ambari.server.topology.ClusterTopology; +import org.apache.ambari.server.topology.InvalidTopologyException; +import org.apache.ambari.server.topology.TopologyValidator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Validates configuration types related to services specified in the blueprint. + * If the cluster creation template contains configuration types that are not related to services in the blueprint the + * validator fails interrupting the cluster provisioning. + */ +public class ClusterConfigTypeValidator implements TopologyValidator { + private static final Logger LOGGER = LoggerFactory.getLogger(ClusterConfigTypeValidator.class); + + @Override + public void validate(ClusterTopology topology) throws InvalidTopologyException { + + if (topology.getConfiguration() == null) { + LOGGER.debug("No configuration is set into the topology"); + return; + } + + if (topology.getConfiguration().getProperties() == null) { + LOGGER.debug("No properties is set into the topology configuration"); + return; + } + + // config types in from the request + Set<String> clusterConfigTypes = topology.getConfiguration().getProperties().keySet(); + LOGGER.debug("Cluster config types: {}", clusterConfigTypes); + + if (clusterConfigTypes == null || clusterConfigTypes.isEmpty()) { + LOGGER.debug("No config types to be checked."); + return; + } + + // collecting all config types for services in the blueprint (from the related stack) + Set<String> serviceConfigTypes = new HashSet<>(); + for (String serviceName : topology.getBlueprint().getServices()) { + serviceConfigTypes.addAll(topology.getBlueprint().getStack().getConfigurationTypes(serviceName)); + } + + // identifying invalid config types + Set<String> configTypeIntersection = new HashSet<String>(serviceConfigTypes); + + // if the intersection is changed, there's been some wrong config type provided in the cluster configuration + if (configTypeIntersection.retainAll(clusterConfigTypes)) { + LOGGER.debug("Valid config types: {}", configTypeIntersection); + + // get the wrong config types + Set<String> invalidConfigTypes = new HashSet<>(clusterConfigTypes); + invalidConfigTypes.removeAll(configTypeIntersection); + + LOGGER.error("The following config typess are wrong: {}", invalidConfigTypes); + throw new InvalidTopologyException("The following configuration types are invalid: " + invalidConfigTypes.toString()); + } + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/403d5932/ambari-server/src/main/java/org/apache/ambari/server/topology/validators/RequiredPasswordValidator.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/topology/validators/RequiredPasswordValidator.java b/ambari-server/src/main/java/org/apache/ambari/server/topology/validators/RequiredPasswordValidator.java new file mode 100644 index 0000000..3cc9b16 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/topology/validators/RequiredPasswordValidator.java @@ -0,0 +1,157 @@ +package org.apache.ambari.server.topology.validators; + +/* + * Licensed 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. + */ + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + +import org.apache.ambari.server.controller.internal.Stack; +import org.apache.ambari.server.state.PropertyInfo; +import org.apache.ambari.server.topology.Blueprint; +import org.apache.ambari.server.topology.ClusterTopology; +import org.apache.ambari.server.topology.HostGroup; +import org.apache.ambari.server.topology.HostGroupInfo; +import org.apache.ambari.server.topology.InvalidTopologyException; +import org.apache.ambari.server.topology.TopologyValidator; + +/** + * Validates that all required passwords are provided. + */ +public class RequiredPasswordValidator implements TopologyValidator { + + private String defaultPassword; + + public RequiredPasswordValidator(String defaultPassword) { + this.defaultPassword = defaultPassword; + } + + /** + * Validate that all required password properties have been set or that 'default_password' is specified. + * + * @throws InvalidTopologyException if required password properties are missing and no + * default is specified via 'default_password' + */ + public void validate(ClusterTopology topology) throws InvalidTopologyException { + Map<String, Map<String, Collection<String>>> missingPasswords = validateRequiredPasswords(topology); + + if (! missingPasswords.isEmpty()) { + throw new InvalidTopologyException("Missing required password properties. Specify a value for these " + + "properties in the cluster or host group configurations or include 'default_password' field in request. " + + missingPasswords); + } + } + + /** + * Validate all configurations. Validation is done on the operational configuration of each + * host group. An operational configuration is achieved by overlaying host group configuration + * on top of cluster configuration which overlays the default stack configurations. + * + * @return map of required properties which are missing. Empty map if none are missing. + * + * @throws IllegalArgumentException if blueprint contains invalid information + */ + + //todo: this is copied/pasted from Blueprint and is currently only used by validatePasswordProperties() + //todo: seems that we should have some common place for this code so it can be used by BP and here? + private Map<String, Map<String, Collection<String>>> validateRequiredPasswords(ClusterTopology topology) { + + Map<String, Map<String, Collection<String>>> missingProperties = + new HashMap<String, Map<String, Collection<String>>>(); + + for (Map.Entry<String, HostGroupInfo> groupEntry: topology.getHostGroupInfo().entrySet()) { + String hostGroupName = groupEntry.getKey(); + Map<String, Map<String, String>> groupProperties = + groupEntry.getValue().getConfiguration().getFullProperties(3); + + Collection<String> processedServices = new HashSet<String>(); + Blueprint blueprint = topology.getBlueprint(); + Stack stack = blueprint.getStack(); + + HostGroup hostGroup = blueprint.getHostGroup(hostGroupName); + for (String component : hostGroup.getComponentNames()) { + //for now, AMBARI is not recognized as a service in Stacks + if (component.equals("AMBARI_SERVER")) { + continue; + } + + String serviceName = stack.getServiceForComponent(component); + if (processedServices.add(serviceName)) { + //todo: do I need to subtract excluded configs? + Collection<Stack.ConfigProperty> requiredProperties = + stack.getRequiredConfigurationProperties(serviceName, PropertyInfo.PropertyType.PASSWORD); + + for (Stack.ConfigProperty property : requiredProperties) { + String category = property.getType(); + String name = property.getName(); + if (! propertyExists(topology, groupProperties, category, name)) { + Map<String, Collection<String>> missingHostGroupPropsMap = missingProperties.get(hostGroupName); + if (missingHostGroupPropsMap == null) { + missingHostGroupPropsMap = new HashMap<String, Collection<String>>(); + missingProperties.put(hostGroupName, missingHostGroupPropsMap); + } + Collection<String> missingHostGroupTypeProps = missingHostGroupPropsMap.get(category); + if (missingHostGroupTypeProps == null) { + missingHostGroupTypeProps = new HashSet<String>(); + missingHostGroupPropsMap.put(category, missingHostGroupTypeProps); + } + missingHostGroupTypeProps.add(name); + } + } + } + } + } + return missingProperties; + } + + private boolean propertyExists(ClusterTopology topology, Map<String, Map<String, String>> props, String type, String property) { + Map<String, String> typeProps = props.get(type); + return (typeProps != null && typeProps.containsKey(property)) || setDefaultPassword(topology, type, property); + } + + /** + * Attempt to set the default password in cluster configuration for missing password property. + * + * @param configType configuration type + * @param property password property name + * + * @return true if password was set, otherwise false. Currently the password will always be set + * unless it is null + */ + private boolean setDefaultPassword(ClusterTopology topology, String configType, String property) { + boolean setDefaultPassword = false; + if (defaultPassword != null && ! defaultPassword.trim().isEmpty()) { + topology.getConfiguration().setProperty(configType, property, defaultPassword); + setDefaultPassword = true; + } + return setDefaultPassword; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + RequiredPasswordValidator that = (RequiredPasswordValidator) o; + + return defaultPassword == null ? that.defaultPassword == null : defaultPassword.equals(that.defaultPassword); + } + + @Override + public int hashCode() { + return defaultPassword != null ? defaultPassword.hashCode() : 0; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/403d5932/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequestTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequestTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequestTest.java index c64da62..d92193f 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequestTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequestTest.java @@ -49,9 +49,9 @@ import org.apache.ambari.server.topology.BlueprintFactory; import org.apache.ambari.server.topology.Configuration; import org.apache.ambari.server.topology.HostGroupInfo; import org.apache.ambari.server.topology.InvalidTopologyTemplateException; -import org.apache.ambari.server.topology.RequiredPasswordValidator; import org.apache.ambari.server.topology.TopologyRequest; import org.apache.ambari.server.topology.TopologyValidator; +import org.apache.ambari.server.topology.validators.RequiredPasswordValidator; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -112,7 +112,7 @@ public class ProvisionClusterRequestTest { assertSame(blueprint, provisionClusterRequest.getBlueprint()); Map<String, HostGroupInfo> hostGroupInfo = provisionClusterRequest.getHostGroupInfo(); assertEquals(1, hostGroupInfo.size()); - assertEquals(1, provisionClusterRequest.getTopologyValidators().size()); + assertEquals(2, provisionClusterRequest.getTopologyValidators().size()); // group1 // host info @@ -164,7 +164,7 @@ public class ProvisionClusterRequestTest { assertSame(blueprint, provisionClusterRequest.getBlueprint()); Map<String, HostGroupInfo> hostGroupInfo = provisionClusterRequest.getHostGroupInfo(); assertEquals(1, hostGroupInfo.size()); - assertEquals(1, provisionClusterRequest.getTopologyValidators().size()); + assertEquals(2, provisionClusterRequest.getTopologyValidators().size()); // group2 HostGroupInfo group2Info = hostGroupInfo.get("group2"); @@ -216,7 +216,7 @@ public class ProvisionClusterRequestTest { assertSame(blueprint, provisionClusterRequest.getBlueprint()); Map<String, HostGroupInfo> hostGroupInfo = provisionClusterRequest.getHostGroupInfo(); assertEquals(2, hostGroupInfo.size()); - assertEquals(1, provisionClusterRequest.getTopologyValidators().size()); + assertEquals(2, provisionClusterRequest.getTopologyValidators().size()); // group1 // host info @@ -374,7 +374,7 @@ public class ProvisionClusterRequestTest { TopologyRequest request = new ProvisionClusterRequest(properties, null); List<TopologyValidator> validators = request.getTopologyValidators(); - assertEquals(1, validators.size()); + assertEquals(2, validators.size()); TopologyValidator pwdValidator = validators.get(0); TopologyValidator noDefaultPwdValidator = new RequiredPasswordValidator(null); @@ -388,7 +388,7 @@ public class ProvisionClusterRequestTest { TopologyRequest request = new ProvisionClusterRequest(properties, null); List<TopologyValidator> validators = request.getTopologyValidators(); - assertEquals(1, validators.size()); + assertEquals(2, validators.size()); TopologyValidator pwdValidator = validators.get(0); TopologyValidator defaultPwdValidator = new RequiredPasswordValidator("pwd"); http://git-wip-us.apache.org/repos/asf/ambari/blob/403d5932/ambari-server/src/test/java/org/apache/ambari/server/topology/RequiredPasswordValidatorTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/topology/RequiredPasswordValidatorTest.java b/ambari-server/src/test/java/org/apache/ambari/server/topology/RequiredPasswordValidatorTest.java index 8ab3a43..a077a7e 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/topology/RequiredPasswordValidatorTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/topology/RequiredPasswordValidatorTest.java @@ -34,6 +34,7 @@ import java.util.Map; import org.apache.ambari.server.controller.internal.Stack; import org.apache.ambari.server.state.PropertyInfo; +import org.apache.ambari.server.topology.validators.RequiredPasswordValidator; import org.junit.After; import org.junit.Before; import org.junit.Test; http://git-wip-us.apache.org/repos/asf/ambari/blob/403d5932/ambari-server/src/test/java/org/apache/ambari/server/topology/validators/ClusterConfigTypeValidatorTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/topology/validators/ClusterConfigTypeValidatorTest.java b/ambari-server/src/test/java/org/apache/ambari/server/topology/validators/ClusterConfigTypeValidatorTest.java new file mode 100644 index 0000000..24fa8b8 --- /dev/null +++ b/ambari-server/src/test/java/org/apache/ambari/server/topology/validators/ClusterConfigTypeValidatorTest.java @@ -0,0 +1,110 @@ +package org.apache.ambari.server.topology.validators; + +/* + * Licensed 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. + */ + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; + +import org.apache.ambari.server.controller.internal.Stack; +import org.apache.ambari.server.topology.Blueprint; +import org.apache.ambari.server.topology.ClusterTopology; +import org.apache.ambari.server.topology.Configuration; +import org.apache.ambari.server.topology.InvalidTopologyException; +import org.easymock.EasyMock; +import org.easymock.EasyMockRule; +import org.easymock.EasyMockSupport; +import org.easymock.Mock; +import org.easymock.TestSubject; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import nl.jqno.equalsverifier.EqualsVerifier; + +public class ClusterConfigTypeValidatorTest extends EasyMockSupport { + + @Rule + public EasyMockRule mocks = new EasyMockRule(this); + + + @Mock + private Configuration clusterConfigurationMock; + + @Mock + private Map<String, Map<String, String>> clusterConfigurationMapMock; + + @Mock + private Blueprint blueprintMock; + + @Mock + private Stack stackMock; + + @Mock + private ClusterTopology clusterTopologyMock; + + @TestSubject + private ClusterConfigTypeValidator clusterConfigTypeValidator = new ClusterConfigTypeValidator(); + + @Before + public void before() { + EasyMock.expect(clusterTopologyMock.getConfiguration()).andReturn(clusterConfigurationMock).anyTimes(); + EasyMock.expect(clusterConfigurationMock.getProperties()).andReturn(clusterConfigurationMapMock).anyTimes(); + + EasyMock.expect(clusterTopologyMock.getBlueprint()).andReturn(blueprintMock).anyTimes(); + EasyMock.expect(blueprintMock.getStack()).andReturn(stackMock).anyTimes(); + } + + @After + public void after() { + resetAll(); + } + + @Test(expected = InvalidTopologyException.class) + public void testShouldValidationFailWhenInvalidConfigGroupSpecifiedInCCTemplate() throws Exception { + // given + EasyMock.expect(clusterConfigurationMapMock.keySet()).andReturn(new HashSet<String>(Arrays.asList("oozie-site"))); + EasyMock.expect(blueprintMock.getServices()).andReturn(new HashSet<String>(Arrays.asList("YARN", "HDFS"))); + + EasyMock.expect(stackMock.getConfigurationTypes("HDFS")).andReturn(Arrays.asList("core-site")); + EasyMock.expect(stackMock.getConfigurationTypes("YARN")).andReturn(Arrays.asList("yarn-site")); + + replayAll(); + + //when + clusterConfigTypeValidator.validate(clusterTopologyMock); + + // then + // Exception is thrown + } + + @Test + public void testShouldValidationPassIfNoConfigTypesSpecifiedInCCTemplate() throws Exception { + //GIVEN + EasyMock.expect(clusterConfigurationMapMock.keySet()).andReturn(Collections.<String>emptySet()); + replayAll(); + + //WHEN + clusterConfigTypeValidator.validate(clusterTopologyMock); + + } + + @Test + public void testEquals() throws Exception { + EqualsVerifier.forClass(ClusterConfigTypeValidator.class).usingGetClass().verify(); + } +}