blob: b6638faa5fe56c8e0a3193c370ff7af16c49ef3c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ambari.server.serveraction.kerberos;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.actionmanager.HostRoleStatus;
import org.apache.ambari.server.agent.CommandReport;
import org.apache.ambari.server.controller.KerberosHelper;
import org.apache.ambari.server.controller.RootComponent;
import org.apache.ambari.server.controller.RootService;
import org.apache.ambari.server.controller.UpdateConfigurationPolicy;
import org.apache.ambari.server.state.Cluster;
import org.apache.ambari.server.state.ServiceComponentHost;
import org.apache.ambari.server.state.kerberos.KerberosComponentDescriptor;
import org.apache.ambari.server.state.kerberos.KerberosDescriptor;
import org.apache.ambari.server.state.kerberos.KerberosIdentityDescriptor;
import org.apache.ambari.server.state.kerberos.KerberosServiceDescriptor;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* PrepareKerberosIdentitiesServerAction is a ServerAction implementation that prepares metadata needed
* to process Kerberos identities (principals and keytabs files).
*/
public class PrepareKerberosIdentitiesServerAction extends AbstractPrepareKerberosServerAction {
private final static Logger LOG = LoggerFactory.getLogger(PrepareKerberosIdentitiesServerAction.class);
/**
* Called to execute this action. Upon invocation, calls
* {@link KerberosServerAction#processIdentities(Map)}
* to iterate through the Kerberos identity metadata and call
* {@link PrepareKerberosIdentitiesServerAction#processIdentities(Map)}
* for each identity to process.
*
* @param requestSharedDataContext a Map to be used a shared data among all ServerActions related
* to a given request
* @return a CommandReport indicating the result of this action
* @throws AmbariException
* @throws InterruptedException
*/
@Override
public CommandReport execute(ConcurrentMap<String, Object> requestSharedDataContext) throws
AmbariException, InterruptedException {
Cluster cluster = getCluster();
if (cluster == null) {
throw new AmbariException("Missing cluster object");
}
KerberosHelper kerberosHelper = getKerberosHelper();
KerberosDescriptor kerberosDescriptor = getKerberosDescriptor(cluster, false);
Map<String, String> commandParameters = getCommandParameters();
OperationType operationType = getOperationType(getCommandParameters());
Map<String, ? extends Collection<String>> serviceComponentFilter = getServiceComponentFilter();
Collection<String> hostFilter = getHostFilter();
Collection<String> identityFilter = getIdentityFilter();
// If the operationType is default, use the getServiceComponentHostsToProcess method to determine
// which ServiceComponentHosts to process based on the filters. However if we are regenerating
// keytabs for a specific set of components, build the identity filter below so we can
// customized what needs to be done.
List<ServiceComponentHost> schToProcess = kerberosHelper.getServiceComponentHostsToProcess(cluster, kerberosDescriptor,
(operationType == OperationType.DEFAULT) ? serviceComponentFilter : null, hostFilter);
String dataDirectory = getCommandParameterValue(commandParameters, DATA_DIRECTORY);
Map<String, Map<String, String>> kerberosConfigurations = new HashMap<>();
int schCount = schToProcess.size();
if (schCount == 0) {
actionLog.writeStdOut("There are no components to process");
} else if (schCount == 1) {
actionLog.writeStdOut(String.format("Processing %d component", schCount));
} else {
actionLog.writeStdOut(String.format("Processing %d components", schCount));
}
Set<String> services = cluster.getServices().keySet();
Map<String, Set<String>> propertiesToRemove = new HashMap<>();
Map<String, Set<String>> propertiesToIgnore = new HashMap<>();
boolean includeAmbariIdentity = "true".equalsIgnoreCase(getCommandParameterValue(commandParameters, KerberosServerAction.INCLUDE_AMBARI_IDENTITY));
// If we are including the Ambari identity; then ensure that if a host filter is set, do not the Ambari service identity.
includeAmbariIdentity &= (hostFilter == null);
if (serviceComponentFilter != null) {
// If we are including the Ambari identity; then ensure that if a service/component filter is set,
// it contains the AMBARI/AMBARI_SERVER component; else do not include the Ambari service identity.
includeAmbariIdentity &= (serviceComponentFilter.get(RootService.AMBARI.name()) != null)
&& serviceComponentFilter.get(RootService.AMBARI.name()).contains(RootComponent.AMBARI_SERVER.name());
if ((operationType != OperationType.DEFAULT)) {
// Update the identity filter, if necessary
identityFilter = updateIdentityFilter(kerberosDescriptor, identityFilter, serviceComponentFilter);
}
}
// Calculate the current host-specific configurations. These will be used to replace
// variables within the Kerberos descriptor data
Map<String, Map<String, String>> configurations = kerberosHelper.calculateConfigurations(cluster, null, kerberosDescriptor, false, false);
processServiceComponentHosts(cluster, kerberosDescriptor, schToProcess, identityFilter, dataDirectory,
configurations, kerberosConfigurations, includeAmbariIdentity, propertiesToIgnore);
UpdateConfigurationPolicy updateConfigurationPolicy = getUpdateConfigurationPolicy(commandParameters);
if (updateConfigurationPolicy != UpdateConfigurationPolicy.NONE) {
if (updateConfigurationPolicy.invokeStackAdvisor()) {
kerberosHelper.applyStackAdvisorUpdates(cluster, services, configurations, kerberosConfigurations,
propertiesToIgnore, propertiesToRemove, true);
}
// TODO: Determine if we need to do this again since it is done a few lines above.
Map<String, Map<String, String>> calculatedConfigurations = kerberosHelper.calculateConfigurations(cluster, null, kerberosDescriptor, false, false);
if (updateConfigurationPolicy.applyIdentityChanges()) {
processAuthToLocalRules(cluster, calculatedConfigurations, kerberosDescriptor, schToProcess, kerberosConfigurations, getDefaultRealm(commandParameters), false);
}
processConfigurationChanges(dataDirectory, kerberosConfigurations, propertiesToRemove, kerberosDescriptor, updateConfigurationPolicy);
}
return createCommandReport(0, HostRoleStatus.COMPLETED, "{}", actionLog.getStdOut(), actionLog.getStdErr());
}
/**
* Calls {@link KerberosHelper#getKerberosDescriptor(Cluster, boolean)}
*
* @param cluster cluster instance
* @param includePreconfigureData <code>true</code> to include the preconfigure data; <code>false</code> otherwise
* @return the kerberos descriptor associated with the specified cluster
* @throws AmbariException if unable to obtain the descriptor
* @see KerberosHelper#getKerberosDescriptor(Cluster, boolean)
*/
protected KerberosDescriptor getKerberosDescriptor(Cluster cluster, boolean includePreconfigureData)
throws AmbariException {
return getKerberosHelper().getKerberosDescriptor(cluster, includePreconfigureData);
}
/**
* Conditionally calls {@link KerberosHelper#setAuthToLocalRules(Cluster, KerberosDescriptor, String, Map, Map, Map, boolean)}
* if there are ServiceComponentHosts to process
*
* @param cluster the cluster
* @param calculatedConfiguration the configurations for the current cluster, used for replacements
* @param kerberosDescriptor the current Kerberos descriptor
* @param schToProcess a list of ServiceComponentHosts to process
* @param kerberosConfigurations the Kerberos-specific configuration map
* @param defaultRealm the default realm
* @param includePreconfiguredData true to include services flagged to be pre-configured; false otherwise
* @throws AmbariException
* @see KerberosHelper#setAuthToLocalRules(Cluster, KerberosDescriptor, String, Map, Map, Map, boolean)
*/
void processAuthToLocalRules(Cluster cluster, Map<String, Map<String, String>> calculatedConfiguration,
KerberosDescriptor kerberosDescriptor,
List<ServiceComponentHost> schToProcess,
Map<String, Map<String, String>> kerberosConfigurations,
String defaultRealm, boolean includePreconfiguredData)
throws AmbariException {
if (!schToProcess.isEmpty()) {
actionLog.writeStdOut("Creating auth-to-local rules");
Map<String, Set<String>> services = new HashMap<>();
for (ServiceComponentHost sch : schToProcess) {
Set<String> components = services.get(sch.getServiceName());
if (components == null) {
components = new HashSet<>();
services.put(sch.getServiceName(), components);
}
components.add(sch.getServiceComponentName());
}
KerberosHelper kerberosHelper = getKerberosHelper();
kerberosHelper.setAuthToLocalRules(cluster, kerberosDescriptor, defaultRealm, services,
calculatedConfiguration, kerberosConfigurations, includePreconfiguredData);
}
}
/**
* Iterate through the identities in the Kerberos descriptor to find the relevant identities to
* add to the identity filter.
* <p>
* The set of identities to include in the filter are determined by whether they are explicit
* identities set in a component or service in the supplied service/component filter.
*
* @param kerberosDescriptor the Kerberos descriptor
* @param identityFilter the existing identity filter
* @param serviceComponentFilter the service/component filter
* @return a new collection of paths (including any existing paths) to act as the updated identity filter
*/
private Collection<String> updateIdentityFilter(KerberosDescriptor kerberosDescriptor,
Collection<String> identityFilter,
Map<String, ? extends Collection<String>> serviceComponentFilter) {
Set<String> updatedFilter = (identityFilter == null) ? new HashSet<>() : new HashSet<>(identityFilter);
Map<String, KerberosServiceDescriptor> serviceDescriptors = kerberosDescriptor.getServices();
if (serviceDescriptors != null) {
for (KerberosServiceDescriptor serviceDescriptor : serviceDescriptors.values()) {
String serviceName = serviceDescriptor.getName();
if (serviceComponentFilter.containsKey("*") || serviceComponentFilter.containsKey(serviceName)) {
Collection<String> componentFilter = serviceComponentFilter.get(serviceName);
boolean anyComponent = ((componentFilter == null) || componentFilter.contains("*"));
// Only include the service-wide identities if the component filter is null contains "*", which indicates
// that all component for the given service are to be processed.
if (anyComponent) {
addIdentitiesToFilter(serviceDescriptor.getIdentities(), updatedFilter, true);
}
Map<String, KerberosComponentDescriptor> componentDescriptors = serviceDescriptor.getComponents();
if (componentDescriptors != null) {
for (KerberosComponentDescriptor componentDescriptor : componentDescriptors.values()) {
String componentName = componentDescriptor.getName();
if (anyComponent || (componentFilter.contains(componentName))) {
addIdentitiesToFilter(componentDescriptor.getIdentities(), updatedFilter, true);
}
}
}
}
}
}
return updatedFilter;
}
/**
* Add the path of each identity in the collection of identities to the supplied identity filter
* if that identity is not a reference to another identity or if references are allowed.
*
* @param identityDescriptors the collection of identity descriptors to process
* @param identityFilter the identity filter to modify
* @param skipReferences
*/
private void addIdentitiesToFilter(List<KerberosIdentityDescriptor> identityDescriptors,
Collection<String> identityFilter, boolean skipReferences) {
if (!CollectionUtils.isEmpty(identityDescriptors)) {
for (KerberosIdentityDescriptor identityDescriptor : identityDescriptors) {
if (!skipReferences || !identityDescriptor.isReference()) {
String identityPath = identityDescriptor.getPath();
if (!StringUtils.isEmpty(identityPath)) {
identityFilter.add(identityPath);
// Find and add the references TO this identity to ensure the new/updated keytab file is
// sent to the appropriate host(s)
addIdentitiesToFilter(identityDescriptor.findReferences(), identityFilter, false);
}
}
}
}
}
}