You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by yu...@apache.org on 2014/12/18 20:10:47 UTC

ambari git commit: AMBARI-8358. update service configs for kerberos related configs when kerberos is enabled. (Dilli Arumugam via yusaku)

Repository: ambari
Updated Branches:
  refs/heads/trunk 6703610ec -> c5b9cc3d0


AMBARI-8358. update service configs for kerberos related configs when kerberos is enabled. (Dilli Arumugam via yusaku)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/c5b9cc3d
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/c5b9cc3d
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/c5b9cc3d

Branch: refs/heads/trunk
Commit: c5b9cc3d0c9f53b0e5af6479aaaacff50e2ec4b8
Parents: 6703610
Author: Yusaku Sako <yu...@hortonworks.com>
Authored: Thu Dec 18 11:09:21 2014 -0800
Committer: Yusaku Sako <yu...@hortonworks.com>
Committed: Thu Dec 18 11:09:21 2014 -0800

----------------------------------------------------------------------
 .../UpdateKerberosConfigsServerAction.java      | 282 +++++++++++++++++++
 .../UpdateKerberosConfigsServerActionTest.java  | 151 ++++++++++
 2 files changed, 433 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/c5b9cc3d/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/UpdateKerberosConfigsServerAction.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/UpdateKerberosConfigsServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/UpdateKerberosConfigsServerAction.java
new file mode 100644
index 0000000..a9dbbbd
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/UpdateKerberosConfigsServerAction.java
@@ -0,0 +1,282 @@
+/*
+ * 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.common.collect.Maps;
+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.AmbariManagementController;
+import org.apache.ambari.server.controller.ConfigurationRequest;
+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.Config;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * 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);
+
+  private HashMap<String, Map<String, String>> configtypesPropsVal = new HashMap();
+
+  @Inject
+  private AmbariManagementController controller;
+
+  /**
+   * 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 dataDir = getCommandParameterValue(getCommandParameters(), KerberosServerAction.DATA_DIRECTORY);
+    File indexFile = new File(dataDir + File.separator + KerberosActionDataFile.DATA_FILE_NAME);
+    File configFile = new File(dataDir + File.separator + KerberosConfigDataFile.DATA_FILE_NAME);
+
+    KerberosActionDataFileReader indexReader = null;
+    KerberosConfigDataFileReader configReader = null;
+
+    try {
+      indexReader = new KerberosActionDataFileReader(indexFile);
+      Iterator<Map<String, String>> indexRecords = indexReader.iterator();
+      while (indexRecords.hasNext()) {
+        Map<String, String> record = indexRecords.next();
+        String hostName = record.get(KerberosActionDataFile.HOSTNAME);
+        String principal = record.get(KerberosActionDataFile.PRINCIPAL);
+        String principalConfig = record.get(KerberosActionDataFile.PRINCIPAL_CONFIGURATION);
+        String[] principalTokens = principalConfig.split("/");
+        if (principalTokens.length == 2)  {
+          String principalConfigType = principalTokens[0];
+          String principalConfigProp = principalTokens[1];
+          addConfigTypePropVal(principalConfigType, principalConfigProp, principal);
+        }
+
+        String keytabPath = record.get(KerberosActionDataFile.KEYTAB_FILE_PATH);
+        String keytabConfig = record.get(KerberosActionDataFile.KEYTAB_FILE_CONFIGURATION);
+        String[] keytabTokens = keytabConfig.split("/");
+        if (keytabTokens.length == 2)  {
+          String keytabConfigType = keytabTokens[0];
+          String keytabConfigProp = keytabTokens[1];
+          addConfigTypePropVal(keytabConfigType, keytabConfigProp, keytabPath);
+        }
+      }
+
+      configReader = new KerberosConfigDataFileReader(configFile);
+      Iterator<Map<String, String>> configRecords = configReader.iterator();
+      while (configRecords.hasNext()) {
+        Map<String, String> record  = configRecords.next();
+        String configType = record.get(KerberosConfigDataFile.CONFIGURATION_TYPE);
+        String configKey = record.get(KerberosConfigDataFile.KEY);
+        String configVal = record.get(KerberosConfigDataFile.VALUE);
+        addConfigTypePropVal(configType, configKey, configVal);
+      }
+
+      for(Map.Entry<String, Map<String,String>> entry : configtypesPropsVal.entrySet()) {
+        Map<String, String> properties = entry.getValue();
+        updateConfigurationPropertiesForCluster(
+          cluster,
+          entry.getKey(),  // configType
+          properties,
+          true,    // updateIfExists
+          true,    // createNew
+          "update services configs to enable kerberos");
+      }
+
+    } catch (IOException e) {
+      String message = "Could not update services configs to enable kerberos";
+      LOG.error(message, e);
+      commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", "", message);
+    } finally {
+      if (indexReader != null && !indexReader.isClosed()) {
+        try {
+          indexReader.close();
+        } catch (Throwable t) {
+          // ignored
+        }
+      }
+      if (configReader != null && !configReader.isClosed()) {
+        try {
+          configReader.close();
+        } catch (Throwable t) {
+          // ignored
+        }
+      }
+    }
+    return (commandReport == null)
+      ? createCommandReport(0, HostRoleStatus.COMPLETED, "{}", null, null)
+      : commandReport;
+  }
+
+
+  /**
+   *
+   * Updates service config properties of a cluster
+   * @param cluster   the cluster for which to update service configs
+   * @param configType   service config type to be updated
+   * @param properties    map of service config properties
+   * @param updateIfExists    flag indicating whether to update if a property already exists
+   * @param createNewConfigType  flag indicating whether to create new service config
+   *                             if the config type does not exist
+   * @param note   a short note on change
+   * @throws AmbariException if the operation fails
+   */
+  private void updateConfigurationPropertiesForCluster(
+    Cluster cluster,
+    String configType,
+    Map<String, String> properties,
+    boolean updateIfExists,
+    boolean createNewConfigType,
+    String note)
+    throws AmbariException {
+
+    String newTag = "version" + System.currentTimeMillis();
+
+    if ((properties != null) && (properties.size() > 0)) {
+      Map<String, Config> all = cluster.getConfigsByType(configType);
+      if (all == null || !all.containsKey(newTag)) {
+        Map<String, String> oldConfigProperties;
+        Config oldConfig = cluster.getDesiredConfigByType(configType);
+
+        if (oldConfig == null && !createNewConfigType) {
+          LOG.info("Config " + configType + " not found. Assuming service not installed. " +
+            "Skipping configuration properties update");
+          return;
+        } else if (oldConfig == null) {
+          oldConfigProperties = new HashMap<String, String>();
+          newTag = "version1";
+        } else {
+          oldConfigProperties = oldConfig.getProperties();
+        }
+
+        Map<String, String> mergedProperties =
+          mergeProperties(oldConfigProperties, properties, updateIfExists);
+
+        if (!Maps.difference(oldConfigProperties, mergedProperties).areEqual()) {
+          LOG.info("Applying configuration with tag '{}' to " +
+            "cluster '{}'", newTag, cluster.getClusterName());
+
+          ConfigurationRequest cr = new ConfigurationRequest();
+          cr.setClusterName(cluster.getClusterName());
+          cr.setVersionTag(newTag);
+          cr.setType(configType);
+          cr.setProperties(mergedProperties);
+          cr.setServiceConfigVersionNote(note);
+          controller.createConfiguration(cr);
+
+          Config baseConfig = cluster.getConfig(cr.getType(), cr.getVersionTag());
+          if (baseConfig != null) {
+            String authName = "kerberization";
+
+            if (cluster.addDesiredConfig(authName, Collections.singleton(baseConfig)) != null) {
+              String oldConfigString = (oldConfig != null) ? " from='" + oldConfig.getTag() + "'" : "";
+              LOG.info("cluster '" + cluster.getClusterName() + "' "
+                + "changed by: '" + authName + "'; "
+                + "type='" + baseConfig.getType() + "' "
+                + "tag='" + baseConfig.getTag() + "'"
+                + oldConfigString);
+            }
+          }
+        } else {
+          LOG.info("No changes detected to config " + configType + ". Skipping configuration properties update");
+        }
+      }
+    }
+  }
+
+  /**
+   * Merges current properties and new properties
+   * @param originalProperties  current properties
+   * @param newProperties      new properties
+   * @param updateIfExists    flag indicating whether to update if a property already exists
+   * @return    merged properties
+   */
+  private static Map<String, String> mergeProperties(Map<String, String> originalProperties,
+                                                     Map<String, String> newProperties,
+                                                     boolean updateIfExists) {
+
+    Map<String, String> properties = new HashMap<String, String>(originalProperties);
+    for (Map.Entry<String, String> entry : newProperties.entrySet()) {
+      if (!properties.containsKey(entry.getKey()) || updateIfExists) {
+        properties.put(entry.getKey(), entry.getValue());
+      }
+    }
+    return properties;
+  }
+
+  /**
+   * 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 configtype   service config type
+   * @param prop     property to be added
+   * @param val   value for the proeprty
+   */
+  private void addConfigTypePropVal(String configtype, String prop, String val) {
+    Map<String, String> configtypePropsVal = configtypesPropsVal.get(configtype);
+    if (configtypePropsVal == null) {
+      configtypePropsVal = new HashMap<String, String>();
+      configtypesPropsVal.put(configtype, configtypePropsVal);
+    }
+    configtypePropsVal.put(prop, val);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/c5b9cc3d/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/UpdateKerberosConfigsServerActionTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/UpdateKerberosConfigsServerActionTest.java b/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/UpdateKerberosConfigsServerActionTest.java
new file mode 100644
index 0000000..888a919
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/UpdateKerberosConfigsServerActionTest.java
@@ -0,0 +1,151 @@
+/*
+ * 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.AbstractModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import org.apache.ambari.server.agent.ExecutionCommand;
+import org.apache.ambari.server.controller.AmbariManagementController;
+import org.apache.ambari.server.controller.ConfigurationRequest;
+import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.Clusters;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.easymock.EasyMock;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentMap;
+
+import static org.easymock.EasyMock.*;
+
+public class UpdateKerberosConfigsServerActionTest {
+
+  @Rule
+  public TemporaryFolder testFolder = new TemporaryFolder();
+  String dataDir;
+  private Injector injector;
+  private UpdateKerberosConfigsServerAction action;
+  final AmbariManagementController controller = EasyMock.createNiceMock(AmbariManagementController.class);
+  Clusters clusters = EasyMock.createNiceMock(Clusters.class);
+  Cluster cluster = EasyMock.createNiceMock(Cluster.class);
+
+  @Before
+  public void setup() throws Exception {
+    setupIndexDat();
+    setupConfigDat();
+
+    expect(controller.getClusters()).andReturn(clusters).once();
+
+    expect(controller.createConfiguration(anyObject(ConfigurationRequest.class))).andReturn(
+      null).once();
+
+    replay(controller);
+
+    expect(cluster.getConfigsByType("hdfs-site")).andReturn(null).once();
+    expect(cluster.getDesiredConfigByType("hdfs-site")).andReturn(null).once();
+    replay(cluster);
+
+    expect(clusters.getCluster(anyObject(String.class))).andReturn(cluster).once();
+    replay(clusters);
+
+    injector = Guice.createInjector(new AbstractModule() {
+
+      @Override
+      protected void configure() {
+        bind(AmbariManagementController.class).toInstance(controller);
+      }
+    });
+    action = injector.getInstance(UpdateKerberosConfigsServerAction.class);
+  }
+
+  @After
+  public void verifyCalls() throws Exception {
+    verify(controller);
+    verify(clusters);
+    verify(cluster);
+  }
+
+  private void setupIndexDat() throws Exception {
+
+    File indexFile;
+    KerberosActionDataFileBuilder kerberosActionDataFileBuilder = null;
+
+    dataDir = testFolder.getRoot().getAbsolutePath();
+    System.out.println("dataDir: " + dataDir);
+
+    indexFile = new File(dataDir, KerberosActionDataFile.DATA_FILE_NAME);
+    kerberosActionDataFileBuilder = new KerberosActionDataFileBuilder(indexFile);
+
+    kerberosActionDataFileBuilder.addRecord("c6403.ambari.apache.org", "HDFS", "DATANODE",
+      "dn/_HOST@_REALM", "hdfs-site/dfs.namenode.kerberos.principal",
+      "/etc/security/keytabs/dn.service.keytab",
+      "hdfs", "r", "hadoop", "", "hdfs-site/dfs.namenode.keytab.file");
+
+    kerberosActionDataFileBuilder.close();
+    File hostDirectory = new File(dataDir, "c6403.ambari.apache.org");
+
+    // Ensure the host directory exists...
+    if (hostDirectory.exists() || hostDirectory.mkdirs()) {
+      File file = new File(hostDirectory, DigestUtils.sha1Hex("/etc/security/keytabs/dn.service.keytab"));
+      if (!file.exists()) {
+        file.createNewFile();
+      }
+
+      FileWriter fw = new FileWriter(file.getAbsoluteFile());
+      BufferedWriter bw = new BufferedWriter(fw);
+      bw.write("hello");
+      bw.close();
+    }
+  }
+
+  private void setupConfigDat() throws Exception {
+    File configFile = new File(dataDir, KerberosConfigDataFile.DATA_FILE_NAME);
+    FileWriter fw = new FileWriter(configFile.getAbsoluteFile());
+    BufferedWriter bw = new BufferedWriter(fw);
+    bw.write("config,key,value\n");
+    bw.write("hdfs-site,hadoop.security.authentication,kerberos");
+    bw.close();
+  }
+
+  @Test
+  public void testUpdateConfig() throws Exception {
+    ExecutionCommand executionCommand = new ExecutionCommand();
+    Map<String, String> commandParams = new HashMap<String, String>();
+    commandParams.put(KerberosServerAction.DATA_DIRECTORY, dataDir);
+    executionCommand.setCommandParams(commandParams);
+
+    action.setExecutionCommand(executionCommand);
+
+    ConcurrentMap<String, Object> requestSharedDataContext = null;
+
+    action.execute(requestSharedDataContext);
+
+  }
+
+
+}