blob: 5c56588259f8fe9d42fe0eb4003e7c84e58585d9 [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 com.google.inject.Inject;
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.state.Cluster;
import org.apache.ambari.server.state.ConfigHelper;
import org.apache.ambari.server.state.PropertyInfo;
import org.apache.ambari.server.state.SecurityState;
import org.apache.ambari.server.state.ServiceComponentHost;
import org.apache.ambari.server.state.StackId;
import org.apache.ambari.server.state.kerberos.KerberosDescriptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
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 java.util.regex.Matcher;
/**
* PrepareEnableKerberosServerAction is a ServerAction implementation that prepares metadata needed
* to enable Kerberos on the cluster.
*/
public class PrepareDisableKerberosServerAction extends AbstractPrepareKerberosServerAction {
private final static Logger LOG = LoggerFactory.getLogger(PrepareDisableKerberosServerAction.class);
/**
* KerberosHelper
*/
@Inject
private KerberosHelper kerberosHelper;
@Inject
private ConfigHelper configHelper;
@Inject
private KerberosConfigDataFileWriterFactory kerberosConfigDataFileWriterFactory;
/**
* Called to execute this action. Upon invocation, calls
* {@link KerberosServerAction#processIdentities(Map)}
* to iterate through the Kerberos identity metadata and call
* {@link PrepareDisableKerberosServerAction#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");
}
KerberosDescriptor kerberosDescriptor = kerberosHelper.getKerberosDescriptor(cluster);
Collection<String> identityFilter = getIdentityFilter();
List<ServiceComponentHost> schToProcess = kerberosHelper.getServiceComponentHostsToProcess(cluster,
kerberosDescriptor,
getServiceComponentFilter(),
null, identityFilter,
new KerberosHelper.Command<Boolean, ServiceComponentHost>() {
@Override
public Boolean invoke(ServiceComponentHost sch) throws AmbariException {
return (sch.getDesiredSecurityState() == SecurityState.UNSECURED) && (sch.getSecurityState() != SecurityState.UNSECURED);
}
});
Map<String, Map<String, String>> kerberosConfigurations = new HashMap<String, Map<String, String>>();
Map<String, String> commandParameters = getCommandParameters();
String dataDirectory = getCommandParameterValue(commandParameters, DATA_DIRECTORY);
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));
}
Map<String, Map<String, String>> propertiesToInsert = new HashMap<>();
processServiceComponentHosts(cluster, kerberosDescriptor, schToProcess, identityFilter, dataDirectory,
kerberosConfigurations, propertiesToInsert, null, false, true);
// Add auth-to-local configurations to the set of changes
Set<String> authToLocalProperties = kerberosDescriptor.getAllAuthToLocalProperties();
if(authToLocalProperties != null) {
for (String authToLocalProperty : authToLocalProperties) {
Matcher m = KerberosDescriptor.AUTH_TO_LOCAL_PROPERTY_SPECIFICATION_PATTERN.matcher(authToLocalProperty);
if (m.matches()) {
String configType = m.group(1);
String propertyName = m.group(2);
if (configType == null) {
configType = "";
}
// Add existing auth_to_local configuration, if set
Map<String, String> configuration = kerberosConfigurations.get(configType);
if (configuration != null) {
configuration.put(propertyName, "DEFAULT");
}
}
}
}
actionLog.writeStdOut("Determining configuration changes");
// Ensure the cluster-env/security_enabled flag is set properly
Map<String, String> clusterEnvProperties = kerberosConfigurations.get(KerberosHelper.SECURITY_ENABLED_CONFIG_TYPE);
if (clusterEnvProperties == null) {
clusterEnvProperties = new HashMap<String, String>();
kerberosConfigurations.put(KerberosHelper.SECURITY_ENABLED_CONFIG_TYPE, clusterEnvProperties);
}
clusterEnvProperties.put(KerberosHelper.SECURITY_ENABLED_PROPERTY_NAME, "false");
// If there are configurations to set, create a (temporary) data file to store the configuration
// updates and fill it will the relevant configurations.
if (!kerberosConfigurations.isEmpty()) {
if(dataDirectory == null) {
String message = "The data directory has not been set. Generated data can not be stored.";
LOG.error(message);
throw new AmbariException(message);
}
Map<String, Collection<String>> configurationsToRemove = new HashMap<String, Collection<String>>();
File configFile = new File(dataDirectory, KerberosConfigDataFileWriter.DATA_FILE_NAME);
KerberosConfigDataFileWriter kerberosConfDataFileWriter = null;
// Fill the configurationsToRemove map with all Kerberos-related configurations. Values
// needed to be kept will have new values from the stack definition and thus pruned from
// this map.
for (Map.Entry<String, Map<String, String>> entry : kerberosConfigurations.entrySet()) {
configurationsToRemove.put(entry.getKey(), new HashSet<String>(entry.getValue().keySet()));
}
// Remove cluster-env from the set of configurations to remove since it has no default set
// or properties and the logic below will remove all from this set - which is not desirable.
configurationsToRemove.remove("cluster-env");
// Update kerberosConfigurations with properties recommended by stack advisor
for (Map.Entry<String, Map<String, String>> typeEntry : propertiesToInsert.entrySet()) {
String configType = typeEntry.getKey();
Map<String, String> propertiesMap = typeEntry.getValue();
Map<String, String> kerberosPropertiesMap = kerberosConfigurations.get(configType);
if (kerberosPropertiesMap == null) {
kerberosConfigurations.put(configType, propertiesMap);
} else {
for (Map.Entry<String, String> propertyEntry : propertiesMap.entrySet()) {
kerberosPropertiesMap.put(propertyEntry.getKey(), propertyEntry.getValue());
}
}
}
if (!schToProcess.isEmpty()) {
Set<String> visitedServices = new HashSet<String>();
for (ServiceComponentHost sch : schToProcess) {
String serviceName = sch.getServiceName();
if (!visitedServices.contains(serviceName)) {
StackId stackVersion = sch.getStackVersion();
visitedServices.add(serviceName);
if (stackVersion != null) {
Set<PropertyInfo> serviceProperties = configHelper.getServiceProperties(stackVersion, serviceName, true);
if (serviceProperties != null) {
for (PropertyInfo propertyInfo : serviceProperties) {
String filename = propertyInfo.getFilename();
if (filename != null) {
String type = ConfigHelper.fileNameToConfigType(filename);
String propertyName = propertyInfo.getName();
Map<String, String> kerberosConfiguration = kerberosConfigurations.get(type);
if ((kerberosConfiguration != null) && (kerberosConfiguration.containsKey(propertyName))) {
kerberosConfiguration.put(propertyName, propertyInfo.getValue());
}
// Remove the relevant from the set of properties (for the given type) to remove
Collection<String> propertiesToRemove = configurationsToRemove.get(type);
if (propertiesToRemove != null) {
propertiesToRemove.remove(propertyName);
}
}
}
}
}
}
}
}
actionLog.writeStdOut(String.format("Writing configuration changes metadata file to %s", configFile.getAbsolutePath()));
try {
kerberosConfDataFileWriter = kerberosConfigDataFileWriterFactory.createKerberosConfigDataFileWriter(configFile);
for (Map.Entry<String, Map<String, String>> entry : kerberosConfigurations.entrySet()) {
String type = entry.getKey();
Map<String, String> properties = entry.getValue();
Collection<String> propertiesToRemove = configurationsToRemove.get(type);
if (properties != null) {
for (Map.Entry<String, String> configTypeEntry : properties.entrySet()) {
String propertyName = configTypeEntry.getKey();
// Ignore properties that should be removed
if ((propertiesToRemove == null) || !propertiesToRemove.contains(propertyName)) {
String value = configTypeEntry.getValue();
String operation = (value == null)
? KerberosConfigDataFileWriter.OPERATION_TYPE_REMOVE
: KerberosConfigDataFileWriter.OPERATION_TYPE_SET;
kerberosConfDataFileWriter.addRecord(type, propertyName, value, operation);
}
}
}
}
// Declare which properties to remove from the configurations
for (Map.Entry<String, Collection<String>> entry : configurationsToRemove.entrySet()) {
String type = entry.getKey();
Collection<String> properties = entry.getValue();
if (properties != null) {
for (String propertyName : properties) {
kerberosConfDataFileWriter.addRecord(type, propertyName, null, KerberosConfigDataFileWriter.OPERATION_TYPE_REMOVE);
}
}
}
} catch (IOException e) {
String message = String.format("Failed to write kerberos configurations file - %s", configFile.getAbsolutePath());
LOG.error(message, e);
actionLog.writeStdOut(message);
actionLog.writeStdErr(message + "\n" + e.getLocalizedMessage());
throw new AmbariException(message, e);
} finally {
if (kerberosConfDataFileWriter != null) {
try {
kerberosConfDataFileWriter.close();
} catch (IOException e) {
String message = "Failed to close the kerberos configurations file writer";
LOG.warn(message, e);
actionLog.writeStdOut(message);
actionLog.writeStdErr(message + "\n" + e.getLocalizedMessage());
}
}
}
}
return createCommandReport(0, HostRoleStatus.COMPLETED, "{}", actionLog.getStdOut(), actionLog.getStdErr());
}
}