/* * 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.io.File; import java.io.IOException; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; 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.AmbariManagementController; import org.apache.ambari.server.serveraction.AbstractServerAction; import org.apache.ambari.server.state.Cluster; import org.apache.ambari.server.state.Clusters; import org.apache.ambari.server.state.ConfigHelper; import org.apache.ambari.server.state.SecurityType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.inject.Inject; /** * UpdateKerberosConfigServerAction is implementation of ServerAction that updates service configs * while enabling Kerberos */ public class UpdateKerberosConfigsServerAction extends AbstractServerAction { private final static Logger LOG = LoggerFactory.getLogger(UpdateKerberosConfigsServerAction.class); @Inject private AmbariManagementController controller; @Inject private ConfigHelper configHelper; /** * The KerberosConfigDataFileReaderFactory to use to obtain KerberosConfigDataFileReader instances */ @Inject private KerberosConfigDataFileReaderFactory kerberosConfigDataFileReaderFactory; /** * Executes this ServerAction * <p/> * This is typically called by the ServerActionExecutor in it's own thread, but there is no * guarantee that this is the case. It is expected that the ExecutionCommand and HostRoleCommand * properties are set before calling this method. * * @param requestSharedDataContext a Map to be used a shared data among all ServerActions related * to a given request * @return a CommandReport declaring the status of the task * @throws org.apache.ambari.server.AmbariException * @throws InterruptedException */ @Override public CommandReport execute(ConcurrentMap<String, Object> requestSharedDataContext) throws AmbariException, InterruptedException { CommandReport commandReport = null; String clusterName = getExecutionCommand().getClusterName(); Clusters clusters = controller.getClusters(); Cluster cluster = clusters.getCluster(clusterName); String authenticatedUserName = getCommandParameterValue(getCommandParameters(), KerberosServerAction.AUTHENTICATED_USER_NAME); String dataDirectoryPath = getCommandParameterValue(getCommandParameters(), KerberosServerAction.DATA_DIRECTORY); HashMap<String, Map<String, String>> propertiesToSet = new HashMap<>(); HashMap<String, Collection<String>> propertiesToRemove = new HashMap<>(); // If the data directory path is set, attempt to process further, else assume there is no work to do if (dataDirectoryPath != null) { File dataDirectory = new File(dataDirectoryPath); // If the data directory exists, attempt to process further, else assume there is no work to do if (dataDirectory.exists()) { KerberosConfigDataFileReader configReader = null; Set<String> configTypes = new HashSet<>(); try { // If the config data file exists, iterate over the records to find the (explicit) // configuration settings to update File configFile = new File(dataDirectory, KerberosConfigDataFileReader.DATA_FILE_NAME); if (configFile.exists()) { configReader = kerberosConfigDataFileReaderFactory.createKerberosConfigDataFileReader(configFile); for (Map<String, String> record : configReader) { String configType = record.get(KerberosConfigDataFileReader.CONFIGURATION_TYPE); String configKey = record.get(KerberosConfigDataFileReader.KEY); String configOp = record.get(KerberosConfigDataFileReader.OPERATION); configTypes.add(configType); if (KerberosConfigDataFileReader.OPERATION_TYPE_REMOVE.equals(configOp)) { removeConfigTypeProp(propertiesToRemove, configType, configKey); } else { String configVal = record.get(KerberosConfigDataFileReader.VALUE); addConfigTypePropVal(propertiesToSet, configType, configKey, configVal); } } } // ensure that cluster-env/security_enabled have proper value final String securityEnabled = cluster.getSecurityType() == SecurityType.KERBEROS ? "true" : "false"; if(!configTypes.contains("cluster-env")) { configTypes.add("cluster-env"); } Map<String, String> clusterEnvProperties = propertiesToSet.get("cluster-env"); if(clusterEnvProperties == null) { clusterEnvProperties = new HashMap<>(); propertiesToSet.put("cluster-env", clusterEnvProperties); } clusterEnvProperties.put("security_enabled", securityEnabled); if (!configTypes.isEmpty()) { String configNote = getCommandParameterValue(getCommandParameters(), KerberosServerAction.UPDATE_CONFIGURATION_NOTE); if((configNote == null) || configNote.isEmpty()) { configNote = cluster.getSecurityType() == SecurityType.KERBEROS ? "Enabling Kerberos" : "Disabling Kerberos"; } for (String configType : configTypes) { configHelper.updateConfigType(cluster, controller, configType, propertiesToSet.get(configType), propertiesToRemove.get(configType), authenticatedUserName, configNote); } } } catch (IOException e) { String message = "Could not update services configs to enable kerberos"; actionLog.writeStdErr(message); LOG.error(message, e); commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr()); } finally { if (configReader != null && !configReader.isClosed()) { try { configReader.close(); } catch (Throwable t) { // ignored } } } } } return (commandReport == null) ? createCommandReport(0, HostRoleStatus.COMPLETED, "{}", actionLog.getStdOut(), actionLog.getStdErr()) : commandReport; } /** * Gets a property from the given commandParameters * * @param commandParameters map of command parameters * @param propertyName property name to find value for * @return value of given proeprty name, would return <code>null</code> * if the provided commandParameters is null or if the requested property is not found * in commandParams */ private static String getCommandParameterValue(Map<String, String> commandParameters, String propertyName) { return ((commandParameters == null) || (propertyName == null)) ? null : commandParameters.get(propertyName); } /** * Adds a property to properties of a given service config type * * @param configurations a map of configurations * @param configType service config type * @param prop property to be added * @param val value for the property */ private void addConfigTypePropVal(HashMap<String, Map<String, String>> configurations, String configType, String prop, String val) { Map<String, String> configTypePropsVal = configurations.get(configType); if (configTypePropsVal == null) { configTypePropsVal = new HashMap<>(); configurations.put(configType, configTypePropsVal); } configTypePropsVal.put(prop, val); actionLog.writeStdOut(String.format("Setting property %s/%s: %s", configType, prop, (val == null) ? "<null>" : val)); } /** * Removes a property from the set of properties of a given service config type * * @param configurations a map of configurations * @param configType service config type * @param prop property to be removed */ private void removeConfigTypeProp(HashMap<String, Collection<String>> configurations, String configType, String prop) { Collection<String> configTypeProps = configurations.get(configType); if (configTypeProps == null) { configTypeProps = new HashSet<>(); configurations.put(configType, configTypeProps); } configTypeProps.add(prop); actionLog.writeStdOut(String.format("Removing property %s/%s", configType, prop)); } }