You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by js...@apache.org on 2014/12/17 21:08:52 UTC

[1/2] ambari git commit: AMBARI-8700. Create orchestrator to manage enabling and disabling Kerberos on a cluster

Repository: ambari
Updated Branches:
  refs/heads/trunk 191232e29 -> ef341466e


http://git-wip-us.apache.org/repos/asf/ambari/blob/ef341466/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerTest.java
index 9dd967a..eb14c20 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerTest.java
@@ -9353,6 +9353,7 @@ public class AmbariManagementControllerTest {
     injector.injectMembers(capture(controllerCapture));
     expect(injector.getInstance(Gson.class)).andReturn(null);
     expect(injector.getInstance(MaintenanceStateHelper.class)).andReturn(maintHelper);
+    expect(injector.getInstance(KerberosHelper.class)).andReturn(createNiceMock(KerberosHelper.class));
 
     // getServices
     expect(clusters.getCluster("cluster1")).andReturn(cluster);
@@ -9396,6 +9397,7 @@ public class AmbariManagementControllerTest {
     injector.injectMembers(capture(controllerCapture));
     expect(injector.getInstance(Gson.class)).andReturn(null);
     expect(injector.getInstance(MaintenanceStateHelper.class)).andReturn(maintHelper);
+    expect(injector.getInstance(KerberosHelper.class)).andReturn(createNiceMock(KerberosHelper.class));
 
     // getServices
     expect(clusters.getCluster("cluster1")).andReturn(cluster);
@@ -9454,6 +9456,7 @@ public class AmbariManagementControllerTest {
     injector.injectMembers(capture(controllerCapture));
     expect(injector.getInstance(Gson.class)).andReturn(null);
     expect(injector.getInstance(MaintenanceStateHelper.class)).andReturn(maintHelper);
+    expect(injector.getInstance(KerberosHelper.class)).andReturn(createNiceMock(KerberosHelper.class));
 
     // getServices
     expect(clusters.getCluster("cluster1")).andReturn(cluster).times(4);

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef341466/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java
new file mode 100644
index 0000000..8804550
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java
@@ -0,0 +1,350 @@
+/*
+ * 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.controller;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.actionmanager.ActionManager;
+import org.apache.ambari.server.actionmanager.HostRoleCommand;
+import org.apache.ambari.server.actionmanager.RequestFactory;
+import org.apache.ambari.server.actionmanager.Stage;
+import org.apache.ambari.server.actionmanager.StageFactory;
+import org.apache.ambari.server.api.services.AmbariMetaInfo;
+import org.apache.ambari.server.controller.internal.RequestStageContainer;
+import org.apache.ambari.server.metadata.RoleCommandOrder;
+import org.apache.ambari.server.orm.DBAccessor;
+import org.apache.ambari.server.security.SecurityHelper;
+import org.apache.ambari.server.serveraction.kerberos.KerberosCredential;
+import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.Config;
+import org.apache.ambari.server.state.ConfigHelper;
+import org.apache.ambari.server.state.Host;
+import org.apache.ambari.server.state.HostState;
+import org.apache.ambari.server.state.MaintenanceState;
+import org.apache.ambari.server.state.SecurityState;
+import org.apache.ambari.server.state.Service;
+import org.apache.ambari.server.state.ServiceComponent;
+import org.apache.ambari.server.state.ServiceComponentHost;
+import org.apache.ambari.server.state.StackId;
+import org.apache.ambari.server.state.cluster.ClusterFactory;
+import org.apache.ambari.server.state.cluster.ClustersImpl;
+import org.apache.ambari.server.state.host.HostFactory;
+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.KerberosKeytabDescriptor;
+import org.apache.ambari.server.state.kerberos.KerberosPrincipalDescriptor;
+import org.apache.ambari.server.state.kerberos.KerberosServiceDescriptor;
+import org.apache.ambari.server.state.stack.OsFamily;
+import org.easymock.EasyMockSupport;
+import org.easymock.IAnswer;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import javax.persistence.EntityManager;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.easymock.EasyMock.*;
+
+public class KerberosHelperTest extends EasyMockSupport {
+
+  private static Injector injector;
+
+  @Before
+  public void setUp() throws Exception {
+    injector = Guice.createInjector(new AbstractModule() {
+
+      @Override
+      protected void configure() {
+        bind(EntityManager.class).toInstance(createNiceMock(EntityManager.class));
+        bind(DBAccessor.class).toInstance(createNiceMock(DBAccessor.class));
+        bind(ClusterFactory.class).toInstance(createNiceMock(ClusterFactory.class));
+        bind(HostFactory.class).toInstance(createNiceMock(HostFactory.class));
+        bind(SecurityHelper.class).toInstance(createNiceMock(SecurityHelper.class));
+        bind(OsFamily.class).toInstance(createNiceMock(OsFamily.class));
+        bind(AmbariCustomCommandExecutionHelper.class).toInstance(createNiceMock(AmbariCustomCommandExecutionHelper.class));
+        bind(MaintenanceStateHelper.class).toInstance(createNiceMock(MaintenanceStateHelper.class));
+        bind(AmbariManagementController.class).toInstance(createNiceMock(AmbariManagementController.class));
+        bind(AmbariMetaInfo.class).toInstance(createNiceMock(AmbariMetaInfo.class));
+        bind(ActionManager.class).toInstance(createNiceMock(ActionManager.class));
+        bind(RequestFactory.class).toInstance(createNiceMock(RequestFactory.class));
+        bind(StageFactory.class).toInstance(createNiceMock(StageFactory.class));
+        bind(Clusters.class).toInstance(createNiceMock(ClustersImpl.class));
+        bind(ConfigHelper.class).toInstance(createNiceMock(ConfigHelper.class));
+      }
+    });
+  }
+
+  @After
+  public void tearDown() throws Exception {
+
+  }
+
+  @Test(expected = AmbariException.class)
+  public void testMissingClusterEnv() throws Exception {
+    KerberosHelper kerberosHelper = injector.getInstance(KerberosHelper.class);
+
+    Cluster cluster = createNiceMock(Cluster.class);
+    KerberosDescriptor kerberosDescriptor = createNiceMock(KerberosDescriptor.class);
+    RequestStageContainer requestStageContainer = createNiceMock(RequestStageContainer.class);
+
+    replayAll();
+    kerberosHelper.toggleKerberos(cluster, kerberosDescriptor, requestStageContainer);
+    verifyAll();
+  }
+
+  @Test(expected = AmbariException.class)
+  public void testMissingKrb5Conf() throws Exception {
+    KerberosHelper kerberosHelper = injector.getInstance(KerberosHelper.class);
+
+    final Map<String, String> clusterEnvProperties = createNiceMock(Map.class);
+    expect(clusterEnvProperties.get("security_enabled")).andReturn("true").once();
+    expect(clusterEnvProperties.get("kerberos_domain")).andReturn("FOOBAR.COM").once();
+
+    final Config clusterEnvConfig = createNiceMock(Config.class);
+    expect(clusterEnvConfig.getProperties()).andReturn(clusterEnvProperties).once();
+
+    final Cluster cluster = createNiceMock(Cluster.class);
+    expect(cluster.getDesiredConfigByType("cluster-env")).andReturn(clusterEnvConfig).once();
+
+    final KerberosDescriptor kerberosDescriptor = createNiceMock(KerberosDescriptor.class);
+
+    replayAll();
+    kerberosHelper.toggleKerberos(cluster, kerberosDescriptor, null);
+    verifyAll();
+  }
+
+  @Test
+  public void testEnableKerberos() throws Exception {
+    KerberosHelper kerberosHelper = injector.getInstance(KerberosHelper.class);
+
+    final ServiceComponentHost sch1 = createNiceMock(ServiceComponentHost.class);
+    expect(sch1.getServiceName()).andReturn("SERVICE1").once();
+    expect(sch1.getServiceComponentName()).andReturn("COMPONENT1").once();
+    expect(sch1.getSecurityState()).andReturn(SecurityState.UNSECURED).anyTimes();
+
+    final ServiceComponentHost sch2 = createNiceMock(ServiceComponentHost.class);
+    expect(sch2.getServiceName()).andReturn("SERVICE2").once();
+    expect(sch2.getServiceComponentName()).andReturn("COMPONENT2").once();
+    expect(sch2.getSecurityState()).andReturn(SecurityState.UNSECURED).anyTimes();
+
+    final Host host = createNiceMock(Host.class);
+    expect(host.getHostName()).andReturn("host1").once();
+    expect(host.getState()).andReturn(HostState.HEALTHY).once();
+
+    final Service service1 = createStrictMock(Service.class);
+    expect(service1.getName()).andReturn("SERVICE1").anyTimes();
+    expect(service1.getServiceComponents())
+        .andReturn(Collections.<String, ServiceComponent>emptyMap())
+        .once();
+    service1.setSecurityState(SecurityState.SECURED_KERBEROS);
+    expectLastCall().once();
+
+    final Service service2 = createStrictMock(Service.class);
+    expect(service2.getName()).andReturn("SERVICE2").anyTimes();
+    expect(service2.getServiceComponents())
+        .andReturn(Collections.<String, ServiceComponent>emptyMap())
+        .once();
+    service2.setSecurityState(SecurityState.SECURED_KERBEROS);
+    expectLastCall().once();
+
+    final Map<String, String> clusterEnvProperties = createNiceMock(Map.class);
+    expect(clusterEnvProperties.get("security_enabled")).andReturn("true").once();
+    expect(clusterEnvProperties.get("kerberos_domain")).andReturn("FOOBAR.COM").once();
+
+    final Config clusterEnvConfig = createNiceMock(Config.class);
+    expect(clusterEnvConfig.getProperties()).andReturn(clusterEnvProperties).once();
+
+    final Map<String, String> krb5ConfProperties = createNiceMock(Map.class);
+    expect(krb5ConfProperties.get("kdc_type")).andReturn("mit-kdc").once();
+
+    final Config krb5ConfConfig = createNiceMock(Config.class);
+    expect(krb5ConfConfig.getProperties()).andReturn(krb5ConfProperties).once();
+
+    final MaintenanceStateHelper maintenanceStateHelper = injector.getInstance(MaintenanceStateHelper.class);
+    expect(maintenanceStateHelper.getEffectiveState(anyObject(ServiceComponentHost.class)))
+        .andReturn(MaintenanceState.OFF).anyTimes();
+
+    final Cluster cluster = createNiceMock(Cluster.class);
+    expect(cluster.getDesiredConfigByType("cluster-env")).andReturn(clusterEnvConfig).once();
+    expect(cluster.getDesiredConfigByType("krb5-conf")).andReturn(krb5ConfConfig).once();
+    expect(cluster.getClusterName()).andReturn("c1").anyTimes();
+    expect(cluster.getServices())
+        .andReturn(new HashMap<String, Service>() {
+          {
+            put("SERVICE1", service1);
+            put("SERVICE2", service2);
+          }
+        })
+        .anyTimes();
+    expect(cluster.getServiceComponentHosts("host1"))
+        .andReturn(new ArrayList<ServiceComponentHost>() {
+          {
+            add(sch1);
+            add(sch2);
+          }
+        })
+        .once();
+    expect(cluster.getCurrentStackVersion())
+        .andReturn(new StackId("HDP", "2.2"))
+        .anyTimes();
+    expect(cluster.getSessionAttributes()).andReturn(new HashMap<String, Object>(){{
+      put("kerberos_admin/" + KerberosCredential.KEY_NAME_PRINCIPAL, "principal");
+      put("kerberos_admin/" + KerberosCredential.KEY_NAME_PASSWORD, "password");
+      put("kerberos_admin/" + KerberosCredential.KEY_NAME_KEYTAB, "keytab");
+    }}).anyTimes();
+
+    final Clusters clusters = injector.getInstance(Clusters.class);
+    expect(clusters.getHostsForCluster("c1"))
+        .andReturn(new HashMap<String, Host>() {
+          {
+            put("host1", host);
+          }
+        })
+        .once();
+
+    final AmbariManagementController ambariManagementController = injector.getInstance(AmbariManagementController.class);
+    expect(ambariManagementController.findConfigurationTagsWithOverrides(cluster, "host1"))
+        .andReturn(Collections.<String, Map<String, String>>emptyMap())
+        .once();
+    expect(ambariManagementController.getRoleCommandOrder(cluster))
+        .andReturn(createNiceMock(RoleCommandOrder.class))
+        .once();
+
+    final ConfigHelper configHelper = injector.getInstance(ConfigHelper.class);
+    expect(configHelper.getEffectiveConfigProperties(anyObject(Cluster.class), anyObject(Map.class)))
+        .andReturn(new HashMap<String, Map<String, String>>() {
+          {
+            put("cluster-env", new HashMap<String, String>() {{
+              put("kerberos_domain", "FOOBAR.COM");
+            }});
+          }
+        })
+        .once();
+    expect(configHelper.getEffectiveConfigAttributes(anyObject(Cluster.class), anyObject(Map.class)))
+        .andReturn(Collections.<String, Map<String, Map<String, String>>>emptyMap())
+        .once();
+
+    final KerberosPrincipalDescriptor principalDescriptor1 = createNiceMock(KerberosPrincipalDescriptor.class);
+    expect(principalDescriptor1.getValue()).andReturn("component1/_HOST@${realm").once();
+    expect(principalDescriptor1.getConfiguration()).andReturn("service1-site/component1.kerberos.principal").once();
+
+    final KerberosPrincipalDescriptor principalDescriptor2 = createNiceMock(KerberosPrincipalDescriptor.class);
+    expect(principalDescriptor2.getValue()).andReturn("component2/_HOST@${realm").once();
+    expect(principalDescriptor2.getConfiguration()).andReturn("service2-site/component2.kerberos.principal").once();
+
+    final KerberosKeytabDescriptor keytabDescriptor1 = createNiceMock(KerberosKeytabDescriptor.class);
+    expect(keytabDescriptor1.getFile()).andReturn("${keytab_dir}/service1.keytab").once();
+    expect(keytabDescriptor1.getOwnerName()).andReturn("service1").once();
+    expect(keytabDescriptor1.getOwnerAccess()).andReturn("rw").once();
+    expect(keytabDescriptor1.getGroupName()).andReturn("hadoop").once();
+    expect(keytabDescriptor1.getGroupAccess()).andReturn("").once();
+    expect(keytabDescriptor1.getConfiguration()).andReturn("service1-site/component1.keytab.file").once();
+
+    final KerberosKeytabDescriptor keytabDescriptor2 = createNiceMock(KerberosKeytabDescriptor.class);
+    expect(keytabDescriptor2.getFile()).andReturn("${keytab_dir}/service2.keytab").once();
+    expect(keytabDescriptor2.getOwnerName()).andReturn("service2").once();
+    expect(keytabDescriptor2.getOwnerAccess()).andReturn("rw").once();
+    expect(keytabDescriptor2.getGroupName()).andReturn("hadoop").once();
+    expect(keytabDescriptor2.getGroupAccess()).andReturn("").once();
+    expect(keytabDescriptor2.getConfiguration()).andReturn("service2-site/component2.keytab.file").once();
+
+    final KerberosIdentityDescriptor identityDescriptor1 = createNiceMock(KerberosIdentityDescriptor.class);
+    expect(identityDescriptor1.getPrincipalDescriptor()).andReturn(principalDescriptor1).once();
+    expect(identityDescriptor1.getKeytabDescriptor()).andReturn(keytabDescriptor1).once();
+
+    final KerberosIdentityDescriptor identityDescriptor2 = createNiceMock(KerberosIdentityDescriptor.class);
+    expect(identityDescriptor2.getPrincipalDescriptor()).andReturn(principalDescriptor2).once();
+    expect(identityDescriptor2.getKeytabDescriptor()).andReturn(keytabDescriptor2).once();
+
+    final KerberosComponentDescriptor componentDescriptor1 = createNiceMock(KerberosComponentDescriptor.class);
+    expect(componentDescriptor1.getIdentities(true)).
+        andReturn(new ArrayList<KerberosIdentityDescriptor>() {{
+          add(identityDescriptor1);
+        }}).once();
+
+    final KerberosComponentDescriptor componentDescriptor2 = createNiceMock(KerberosComponentDescriptor.class);
+    expect(componentDescriptor2.getIdentities(true)).
+        andReturn(new ArrayList<KerberosIdentityDescriptor>() {{
+          add(identityDescriptor2);
+        }}).once();
+
+    final KerberosServiceDescriptor serviceDescriptor1 = createNiceMock(KerberosServiceDescriptor.class);
+    expect(serviceDescriptor1.getComponent("COMPONENT1")).andReturn(componentDescriptor1).once();
+
+    final KerberosServiceDescriptor serviceDescriptor2 = createNiceMock(KerberosServiceDescriptor.class);
+    expect(serviceDescriptor2.getComponent("COMPONENT2")).andReturn(componentDescriptor2).once();
+
+    final KerberosDescriptor kerberosDescriptor = createNiceMock(KerberosDescriptor.class);
+    expect(kerberosDescriptor.getService("SERVICE1")).andReturn(serviceDescriptor1).once();
+    expect(kerberosDescriptor.getService("SERVICE2")).andReturn(serviceDescriptor2).once();
+
+    final StageFactory stageFactory = injector.getInstance(StageFactory.class);
+    expect(stageFactory.createNew(anyLong(), anyObject(String.class), anyObject(String.class),
+        anyLong(), anyObject(String.class), anyObject(String.class), anyObject(String.class),
+        anyObject(String.class)))
+        .andAnswer(new IAnswer<Stage>() {
+          @Override
+          public Stage answer() throws Throwable {
+            Stage stage = createNiceMock(Stage.class);
+
+            expect(stage.getHostRoleCommands())
+                .andReturn(Collections.<String, Map<String, HostRoleCommand>>emptyMap())
+                .anyTimes();
+            replay(stage);
+            return stage;
+          }
+        })
+        .anyTimes();
+
+    // This is a STRICT mock to help ensure that the end result is what we want.
+    final RequestStageContainer requestStageContainer = createStrictMock(RequestStageContainer.class);
+    // Create Principals Stage
+    expect(requestStageContainer.getId()).andReturn(1L).once();
+    requestStageContainer.addStages(anyObject(List.class));
+    expectLastCall().once();
+    // Create Keytabs Stage
+    expect(requestStageContainer.getId()).andReturn(1L).once();
+    requestStageContainer.addStages(anyObject(List.class));
+    expectLastCall().once();
+    // TODO: Add more of these when more stages are added.
+    // Clean-up/Finalize Stage
+    expect(requestStageContainer.getId()).andReturn(1L).once();
+    requestStageContainer.addStages(anyObject(List.class));
+    expectLastCall().once();
+
+    replayAll();
+
+    // Needed by infrastructure
+    injector.getInstance(AmbariMetaInfo.class).init();
+
+    kerberosHelper.toggleKerberos(cluster, kerberosDescriptor, requestStageContainer);
+
+    verifyAll();
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef341466/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ComponentResourceProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ComponentResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ComponentResourceProviderTest.java
index a66add3..5fb2831 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ComponentResourceProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/ComponentResourceProviderTest.java
@@ -53,6 +53,7 @@ import org.apache.ambari.server.StackAccessException;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.controller.AmbariManagementController;
 import org.apache.ambari.server.controller.AmbariManagementControllerImpl;
+import org.apache.ambari.server.controller.KerberosHelper;
 import org.apache.ambari.server.controller.MaintenanceStateHelper;
 import org.apache.ambari.server.controller.RequestStatusResponse;
 import org.apache.ambari.server.controller.ServiceComponentRequest;
@@ -798,6 +799,7 @@ public class ComponentResourceProviderTest {
     injector.injectMembers(capture(controllerCapture));
     expect(injector.getInstance(Gson.class)).andReturn(null);
     expect(injector.getInstance(MaintenanceStateHelper.class)).andReturn(maintHelper);
+    expect(injector.getInstance(KerberosHelper.class)).andReturn(createNiceMock(KerberosHelper.class));
 
     // getComponents
     expect(clusters.getCluster("cluster1")).andReturn(cluster);

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef341466/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostResourceProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostResourceProviderTest.java
index 2e80e24..a00afce 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostResourceProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostResourceProviderTest.java
@@ -26,6 +26,7 @@ import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.controller.AmbariManagementController;
 import org.apache.ambari.server.controller.AmbariManagementControllerImpl;
 import org.apache.ambari.server.controller.HostResponse;
+import org.apache.ambari.server.controller.KerberosHelper;
 import org.apache.ambari.server.controller.MaintenanceStateHelper;
 import org.apache.ambari.server.controller.RequestStatusResponse;
 import org.apache.ambari.server.controller.HostRequest;
@@ -957,6 +958,7 @@ public class HostResourceProviderTest {
     injector.injectMembers(capture(controllerCapture));
     expect(injector.getInstance(Gson.class)).andReturn(null);
     expect(injector.getInstance(MaintenanceStateHelper.class)).andReturn(maintHelper);
+    expect(injector.getInstance(KerberosHelper.class)).andReturn(createNiceMock(KerberosHelper.class));
 
     // getHosts
     expect(clusters.getCluster("cluster1")).andReturn(cluster);
@@ -1006,6 +1008,7 @@ public class HostResourceProviderTest {
     injector.injectMembers(capture(controllerCapture));
     expect(injector.getInstance(Gson.class)).andReturn(null);
     expect(injector.getInstance(MaintenanceStateHelper.class)).andReturn(maintHelper);
+    expect(injector.getInstance(KerberosHelper.class)).andReturn(createNiceMock(KerberosHelper.class));
 
     // getHosts
     expect(clusters.getCluster("cluster1")).andReturn(cluster);
@@ -1054,6 +1057,7 @@ public class HostResourceProviderTest {
     injector.injectMembers(capture(controllerCapture));
     expect(injector.getInstance(Gson.class)).andReturn(null);
     expect(injector.getInstance(MaintenanceStateHelper.class)).andReturn(maintHelper);
+    expect(injector.getInstance(KerberosHelper.class)).andReturn(createNiceMock(KerberosHelper.class));
 
     // getHosts
     expect(clusters.getCluster("cluster1")).andReturn(cluster);
@@ -1118,6 +1122,7 @@ public class HostResourceProviderTest {
     injector.injectMembers(capture(controllerCapture));
     expect(injector.getInstance(Gson.class)).andReturn(null);
     expect(injector.getInstance(MaintenanceStateHelper.class)).andReturn(maintHelper);
+    expect(injector.getInstance(KerberosHelper.class)).andReturn(createNiceMock(KerberosHelper.class));
 
     // getHosts
     expect(clusters.getCluster("cluster1")).andReturn(cluster).times(4);

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef341466/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerActionTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerActionTest.java b/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerActionTest.java
index fd1017b..d720da2 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerActionTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerActionTest.java
@@ -45,7 +45,6 @@ import static org.mockito.Mockito.when;
 
 public class KerberosServerActionTest {
 
-
   Map<String, String> commandParams = new HashMap<String, String>();
   File temporaryDirectory;
   private Injector injector;
@@ -110,7 +109,6 @@ public class KerberosServerActionTest {
     }
     builder.close();
 
-
     commandParams.put(KerberosServerAction.DATA_DIRECTORY, temporaryDirectory.getAbsolutePath());
     commandParams.put(KerberosServerAction.DEFAULT_REALM, "REALM.COM");
     commandParams.put(KerberosServerAction.KDC_TYPE, KDCType.MIT_KDC.toString());


[2/2] ambari git commit: AMBARI-8700. Create orchestrator to manage enabling and disabling Kerberos on a cluster

Posted by js...@apache.org.
AMBARI-8700. Create orchestrator to manage enabling and disabling Kerberos on a cluster


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

Branch: refs/heads/trunk
Commit: ef341466e49d8f7f2cc144bb63416fb25a77f6d3
Parents: 191232e
Author: Robert Levas <rl...@hortonworks.com>
Authored: Wed Dec 17 15:07:38 2014 -0500
Committer: John Speidel <js...@hortonworks.com>
Committed: Wed Dec 17 15:08:43 2014 -0500

----------------------------------------------------------------------
 .../AmbariManagementControllerImpl.java         |   24 +-
 .../server/controller/ClusterRequest.java       |   31 +-
 .../server/controller/KerberosHelper.java       | 1117 ++++++++++++++++++
 .../internal/ClusterResourceProvider.java       |  114 ++
 .../kerberos/FinalizeKerberosServerAction.java  |   94 ++
 .../kerberos/KerberosServerAction.java          |  137 ++-
 .../AmbariManagementControllerImplTest.java     |   17 +
 .../AmbariManagementControllerTest.java         |    3 +
 .../server/controller/KerberosHelperTest.java   |  350 ++++++
 .../internal/ComponentResourceProviderTest.java |    2 +
 .../internal/HostResourceProviderTest.java      |    5 +
 .../kerberos/KerberosServerActionTest.java      |    2 -
 12 files changed, 1828 insertions(+), 68 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/ef341466/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
index 435c828..f58fa9d 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
@@ -207,6 +207,11 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
 
   private MaintenanceStateHelper maintenanceStateHelper;
 
+  /**
+   * The KerberosHelper to help setup for enabling for disabling Kerberos
+   */
+  private KerberosHelper kerberosHelper;
+
   final private String masterHostname;
   final private Integer masterPort;
   final private String masterProtocol;
@@ -246,7 +251,7 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
     LOG.info("Initializing the AmbariManagementControllerImpl");
     masterHostname =  InetAddress.getLocalHost().getCanonicalHostName();
     maintenanceStateHelper = injector.getInstance(MaintenanceStateHelper.class);
-
+    kerberosHelper = injector.getInstance(KerberosHelper.class);
     if(configs != null)
     {
       if (configs.getApiSSLAuthentication()) {
@@ -1316,7 +1321,22 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
       saveClusterUpdate(request, clusterResponse);
     }
 
-    return null;
+    RequestStageContainer requestStageContainer = null;
+    Map<String, Service> services = cluster.getServices();
+    if ((services != null) && services.containsKey("KERBEROS")) {
+      // Handle either adding or removing Kerberos from the cluster. This may generate multiple stages
+      // or not depending the current state of the cluster.  The main configuration used to determine
+      // whether Kerberos is to be added or removed is cluster-config/security_enabled.
+      requestStageContainer = kerberosHelper.toggleKerberos(cluster,
+          request.getKerberosDescriptor(), null);
+    }
+
+    if (requestStageContainer != null) {
+      requestStageContainer.persist();
+      return requestStageContainer.getRequestStatusResponse();
+    } else {
+      return null;
+    }
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef341466/ambari-server/src/main/java/org/apache/ambari/server/controller/ClusterRequest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/ClusterRequest.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/ClusterRequest.java
index 5dd0be0..8d2cce1 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/ClusterRequest.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/ClusterRequest.java
@@ -18,6 +18,8 @@
 
 package org.apache.ambari.server.controller;
 
+import org.apache.ambari.server.state.kerberos.KerberosDescriptor;
+
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -42,6 +44,11 @@ public class ClusterRequest {
   private ServiceConfigVersionRequest serviceConfigVersionRequest = null;
 
   /**
+   * A KerberosDescriptor parsed from the request payload.
+   */
+  private KerberosDescriptor kerberosDescriptor;
+
+  /**
    * The cluster session attributes.
    */
   private final Map<String, Object> sessionAttributes;
@@ -56,12 +63,13 @@ public class ClusterRequest {
   
   public ClusterRequest(Long clusterId, String clusterName, 
       String provisioningState, String stackVersion, Set<String> hostNames) {
-    this(clusterId, clusterName, provisioningState, stackVersion, hostNames, null);
+    this(clusterId, clusterName, provisioningState, stackVersion, hostNames, null, null);
   }
 
   public ClusterRequest(Long clusterId, String clusterName,
                         String provisioningState, String stackVersion,
-                        Set<String> hostNames, Map<String, Object> sessionAttributes) {
+                        Set<String> hostNames, KerberosDescriptor kerberosDescriptor,
+                        Map<String, Object> sessionAttributes) {
     super();
     this.clusterId         = clusterId;
     this.clusterName       = clusterName;
@@ -69,6 +77,7 @@ public class ClusterRequest {
     this.stackVersion      = stackVersion;
     this.hostNames         = hostNames;
     this.sessionAttributes = sessionAttributes;
+    this.kerberosDescriptor = kerberosDescriptor;
   }
 
 
@@ -165,6 +174,24 @@ public class ClusterRequest {
     return configs;
   }
 
+  /**
+   * Returns the KerberosDescriptor for this ClusterRequest
+   *
+   * @return a KerberosDescriptor or null if one was not specified
+   */
+  public KerberosDescriptor getKerberosDescriptor() {
+    return kerberosDescriptor;
+  }
+
+  /**
+   * Sets a KerberosDescriptor for this ClusterRequest
+   *
+   * @param kerberosDescriptor a KerberosDescriptor
+   */
+  public void setKerberosDescriptor(KerberosDescriptor kerberosDescriptor) {
+    this.kerberosDescriptor = kerberosDescriptor;
+  }
+
   @Override
   public String toString() {
     StringBuilder sb = new StringBuilder();

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef341466/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelper.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelper.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelper.java
new file mode 100644
index 0000000..625c0f6
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelper.java
@@ -0,0 +1,1117 @@
+/*
+ * 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.controller;
+
+import com.google.inject.Inject;
+import com.google.inject.persist.Transactional;
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.Role;
+import org.apache.ambari.server.RoleCommand;
+import org.apache.ambari.server.actionmanager.ActionManager;
+import org.apache.ambari.server.actionmanager.RequestFactory;
+import org.apache.ambari.server.actionmanager.Stage;
+import org.apache.ambari.server.actionmanager.StageFactory;
+import org.apache.ambari.server.api.services.AmbariMetaInfo;
+import org.apache.ambari.server.configuration.Configuration;
+import org.apache.ambari.server.controller.internal.RequestStageContainer;
+import org.apache.ambari.server.metadata.RoleCommandOrder;
+import org.apache.ambari.server.serveraction.ServerAction;
+import org.apache.ambari.server.serveraction.kerberos.CreateKeytabFilesServerAction;
+import org.apache.ambari.server.serveraction.kerberos.CreatePrincipalsServerAction;
+import org.apache.ambari.server.serveraction.kerberos.FinalizeKerberosServerAction;
+import org.apache.ambari.server.serveraction.kerberos.KDCType;
+import org.apache.ambari.server.serveraction.kerberos.KerberosActionDataFile;
+import org.apache.ambari.server.serveraction.kerberos.KerberosActionDataFileBuilder;
+import org.apache.ambari.server.serveraction.kerberos.KerberosConfigDataFile;
+import org.apache.ambari.server.serveraction.kerberos.KerberosConfigDataFileBuilder;
+import org.apache.ambari.server.serveraction.kerberos.KerberosCredential;
+import org.apache.ambari.server.serveraction.kerberos.KerberosServerAction;
+import org.apache.ambari.server.stageplanner.RoleGraph;
+import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.Config;
+import org.apache.ambari.server.state.ConfigHelper;
+import org.apache.ambari.server.state.Host;
+import org.apache.ambari.server.state.HostState;
+import org.apache.ambari.server.state.MaintenanceState;
+import org.apache.ambari.server.state.SecurityState;
+import org.apache.ambari.server.state.Service;
+import org.apache.ambari.server.state.ServiceComponentHost;
+import org.apache.ambari.server.state.StackId;
+import org.apache.ambari.server.state.kerberos.KerberosComponentDescriptor;
+import org.apache.ambari.server.state.kerberos.KerberosConfigurationDescriptor;
+import org.apache.ambari.server.state.kerberos.KerberosDescriptor;
+import org.apache.ambari.server.state.kerberos.KerberosIdentityDescriptor;
+import org.apache.ambari.server.state.kerberos.KerberosKeytabDescriptor;
+import org.apache.ambari.server.state.kerberos.KerberosPrincipalDescriptor;
+import org.apache.ambari.server.state.kerberos.KerberosServiceDescriptor;
+import org.apache.ambari.server.state.svccomphost.ServiceComponentHostServerActionEvent;
+import org.apache.ambari.server.utils.StageUtils;
+import org.apache.commons.io.FileUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+public class KerberosHelper {
+  private static final String BASE_LOG_DIR = "/tmp/ambari";
+
+  private static final Logger LOG = LoggerFactory.getLogger(KerberosHelper.class);
+
+  @Inject
+  private AmbariCustomCommandExecutionHelper customCommandExecutionHelper;
+
+  @Inject
+  private MaintenanceStateHelper maintenanceStateHelper;
+
+  @Inject
+  private AmbariManagementController ambariManagementController;
+
+  @Inject
+  private AmbariMetaInfo ambariMetaInfo;
+
+  @Inject
+  private ActionManager actionManager;
+
+  @Inject
+  private RequestFactory requestFactory;
+
+  @Inject
+  private StageFactory stageFactory;
+
+  @Inject
+  private Clusters clusters;
+
+  @Inject
+  private ConfigHelper configHelper;
+
+  /**
+   * The Handler implementation that provides the logic to enable Kerberos
+   */
+  private Handler enableKerberosHandler = new EnableKerberosHandler();
+
+  /**
+   * The Handler implementation that provides the logic to disable Kerberos
+   */
+  private Handler disableKerberosHandler = new DisableKerberosHandler();
+
+
+  /**
+   * Toggles Kerberos security to enable it or remove it depending on the state of the cluster.
+   * <p/>
+   * The "cluster-env" configuration set is used to determine the security state of the cluster.
+   * If the "security_enabled" property is set to "true" than an attempt will be make to enable
+   * Kerberos; if "false" an attempt will be made to disable Kerberos.
+   * Also, the "kerberos_domain" is used as the default Kerberos realm for the cluster.
+   * <p/>
+   * The "krb5-conf" configuration type ios used to obtain information about the relevant KDC.
+   * The "kdc_type" property is used to determine what type of KDC is being used so that the
+   * appropriate actions maybe taken in order interact with it properly.
+   * <p/>
+   * This process is idempotent such that it may be called several times, each time only affecting
+   * items that need to be brought up to date.
+   *
+   * @param cluster               the relevant Cluster
+   * @param kerberosDescriptor    a KerberosDescriptor containing updates to the descriptor already
+   *                              configured for the cluster
+   * @param requestStageContainer a RequestStageContainer to place generated stages, if needed -
+   *                              if null a new RequestStageContainer will be created.
+   * @return the updated or a new RequestStageContainer containing the stages that need to be
+   * executed to complete this task; or null if no stages need to be executed.
+   * @throws AmbariException
+   */
+  public RequestStageContainer toggleKerberos(Cluster cluster, KerberosDescriptor kerberosDescriptor,
+                                              RequestStageContainer requestStageContainer)
+      throws AmbariException {
+
+    if (cluster == null) {
+      String message = "The cluster object is not available";
+      LOG.error(message);
+      throw new AmbariException(message);
+    }
+
+    Config configClusterEnv = cluster.getDesiredConfigByType("cluster-env");
+    if (configClusterEnv == null) {
+      String message = "The 'cluster-env' configuration is not available";
+      LOG.error(message);
+      throw new AmbariException(message);
+    }
+
+    Map<String, String> clusterEnvProperties = configClusterEnv.getProperties();
+    if (clusterEnvProperties == null) {
+      String message = "The 'cluster-env' configuration properties are not available";
+      LOG.error(message);
+      throw new AmbariException(message);
+    }
+
+    String securityEnabled = clusterEnvProperties.get("security_enabled");
+    if ((securityEnabled == null) || securityEnabled.isEmpty()) {
+      LOG.warn("Missing 'securityEnabled' property of cluster-env, unable to determine the cluster's security state. This may be ok.");
+    } else {
+      String defaultRealm = clusterEnvProperties.get("kerberos_domain");
+
+      Config configKrb5Conf = cluster.getDesiredConfigByType("krb5-conf");
+      if (configKrb5Conf == null) {
+        String message = "The 'krb5-conf' configuration is not available";
+        LOG.error(message);
+        throw new AmbariException(message);
+      }
+
+      Map<String, String> krb5ConfProperties = configKrb5Conf.getProperties();
+      if (krb5ConfProperties == null) {
+        String message = "The 'krb5-conf' configuration properties are not available";
+        LOG.error(message);
+        throw new AmbariException(message);
+      }
+
+      KDCType kdcType = null;
+      String kdcTypeProperty = krb5ConfProperties.get("kdc_type");
+      if (kdcTypeProperty != null) {
+        try {
+          kdcType = KDCType.translate(kdcTypeProperty);
+        } catch (IllegalArgumentException e) {
+          String message = String.format("Invalid 'kdc_type' value: %s", kdcTypeProperty);
+          LOG.error(message);
+          throw new AmbariException(message);
+        }
+      }
+
+      if (kdcType == null) {
+        // Set the KDCType to the the MIT_KDC as a fallback.
+        kdcType = KDCType.MIT_KDC;
+      }
+
+      if ("true".equalsIgnoreCase(securityEnabled)) {
+        LOG.info("Configuring Kerberos for realm {} on cluster, {}", defaultRealm, cluster.getClusterName());
+        requestStageContainer = handle(cluster, kerberosDescriptor, defaultRealm, kdcType, requestStageContainer, enableKerberosHandler);
+      } else if ("false".equalsIgnoreCase(securityEnabled)) {
+        LOG.info("Disabling Kerberos from cluster, {}", cluster.getClusterName());
+        requestStageContainer = handle(cluster, kerberosDescriptor, defaultRealm, kdcType, requestStageContainer, disableKerberosHandler);
+      } else {
+        String message = String.format("Invalid value for `security_enabled` property of cluster-env: %s", securityEnabled);
+        LOG.error(message);
+        throw new AmbariException(message);
+      }
+    }
+
+    return requestStageContainer;
+  }
+
+  /**
+   * Performs operations needed to enable to disable Kerberos on the relevant cluster.
+   * <p/>
+   * Iterates through the components installed on the relevant cluster and attempts to enable or
+   * disable Kerberos as needed.
+   * <p/>
+   * The supplied Handler instance handles the logic on whether this process enables or disables
+   * Kerberos.
+   *
+   * @param cluster               the relevant Cluster
+   * @param kerberosDescriptor    the (derived) KerberosDescriptor
+   * @param realm                 the default Kerberos realm for the Cluster
+   * @param kdcType               the relevant KDC type (MIT KDC or Active Directory)
+   * @param requestStageContainer a RequestStageContainer to place generated stages, if needed -
+   *                              if null a new RequestStageContainer will be created.
+   * @return the updated or a new RequestStageContainer containing the stages that need to be
+   * executed to complete this task; or null if no stages need to be executed.
+   * @throws AmbariException
+   */
+  @Transactional
+  private RequestStageContainer handle(Cluster cluster,
+                                       KerberosDescriptor kerberosDescriptor,
+                                       String realm, KDCType kdcType,
+                                       RequestStageContainer requestStageContainer,
+                                       Handler handler) throws AmbariException {
+
+    Map<String, Service> services = cluster.getServices();
+
+    if ((services != null) && !services.isEmpty()) {
+      SecurityState desiredSecurityState = handler.getNewServiceSecurityState();
+      String clusterName = cluster.getClusterName();
+      Map<String, Host> hosts = clusters.getHostsForCluster(clusterName);
+
+      if ((hosts != null) && !hosts.isEmpty()) {
+        List<ServiceComponentHost> serviceComponentHostsToProcess = new ArrayList<ServiceComponentHost>();
+        File indexFile;
+        kerberosDescriptor = buildKerberosDescriptor(cluster.getCurrentStackVersion(), kerberosDescriptor);
+        KerberosActionDataFileBuilder kerberosActionDataFileBuilder = null;
+        Map<String, String> kerberosDescriptorProperties = kerberosDescriptor.getProperties();
+        Map<String, Map<String, String>> kerberosConfigurations = new HashMap<String, Map<String, String>>();
+
+        // Create a temporary directory to store metadata needed to complete this task.  Information
+        // such as which principals and keytabs files to create as well as what configurations need
+        // to be update are stored in data files in this directory. Any keytab files are stored in
+        // this directory until they are distributed to their appropriate hosts.
+        File dataDirectory;
+        try {
+          dataDirectory = createTemporaryDirectory();
+        } catch (IOException e) {
+          String message = "Failed to create the temporary data directory.";
+          LOG.error(message, e);
+          throw new AmbariException(message, e);
+        }
+
+        // Create the file used to store details about principals and keytabs to create
+        indexFile = new File(dataDirectory, KerberosActionDataFile.DATA_FILE_NAME);
+
+        try {
+          // Iterate over the hosts in the cluster to find the components installed in each.  For each
+          // component (aka service component host - sch) determine the configuration updates and
+          // and the principals an keytabs to create.
+          for (Host host : hosts.values()) {
+            // Only process "healthy" hosts.  When an unhealthy host becomes healthy, it will be
+            // processed the next time this executes.
+            if (host.getState() == HostState.HEALTHY) {
+              String hostname = host.getHostName();
+
+              // Get a list of components on the current host
+              List<ServiceComponentHost> serviceComponentHosts = cluster.getServiceComponentHosts(hostname);
+
+              if ((serviceComponentHosts != null) && !serviceComponentHosts.isEmpty()) {
+                // Calculate the current host-specific configurations. These will be used to replace
+                // variables within the Kerberos descriptor data
+                Map<String, Map<String, String>> configurations = calculateConfigurations(cluster, hostname);
+
+                // If any properties are set in the calculated KerberosDescriptor, add them into the
+                // Map of configurations as an un-categorized type (using an empty string)
+                if (kerberosDescriptorProperties != null) {
+                  if (configurations == null) {
+                    configurations = new HashMap<String, Map<String, String>>();
+                  }
+
+                  if (configurations.get("") == null) {
+                    configurations.put("", kerberosDescriptorProperties);
+                  } else {
+                    configurations.get("").putAll(kerberosDescriptorProperties);
+                  }
+                }
+
+                // Iterate over the components installed on the current host to get the service and
+                // component-level Kerberos descriptors in order to determine which principals,
+                // keytab files, and configurations need to be created or updated.
+                for (ServiceComponentHost sch : serviceComponentHosts) {
+                  String serviceName = sch.getServiceName();
+                  KerberosServiceDescriptor serviceDescriptor = kerberosDescriptor.getService(serviceName);
+
+                  if (serviceDescriptor != null) {
+                    KerberosComponentDescriptor componentDescriptor = serviceDescriptor.getComponent(sch.getServiceComponentName());
+
+                    if (componentDescriptor != null) {
+                      int identitiesAdded = 0;
+                      // Test to see if this component should be process by querying the handler
+                      if (handler.shouldProcess(desiredSecurityState, sch)) {
+                        // Calculate the set of configurations to update and replace any variables
+                        // using the previously calculated Map of configurations for the host.
+                        mergeConfigurations(kerberosConfigurations,
+                            componentDescriptor.getConfigurations(true), configurations);
+
+                        // Lazily create the KerberosActionDataFileBuilder instance...
+                        if (kerberosActionDataFileBuilder == null) {
+                          kerberosActionDataFileBuilder = new KerberosActionDataFileBuilder(indexFile);
+                        }
+
+                        // Add service-level principals (and keytabs)
+                        identitiesAdded += addIdentities(kerberosActionDataFileBuilder,
+                            serviceDescriptor.getIdentities(true), sch, configurations);
+
+                        // Add component-level principals (and keytabs)
+                        identitiesAdded += addIdentities(kerberosActionDataFileBuilder,
+                            componentDescriptor.getIdentities(true), sch, configurations);
+
+                        if (identitiesAdded > 0) {
+                          serviceComponentHostsToProcess.add(sch);
+                        }
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          }
+        } catch (IOException e) {
+          String message = String.format("Failed to write index file - %s", indexFile.getAbsolutePath());
+          LOG.error(message);
+          throw new AmbariException(message, e);
+        } finally {
+          if (kerberosActionDataFileBuilder != null) {
+            // Make sure the data file is closed
+            try {
+              kerberosActionDataFileBuilder.close();
+            } catch (IOException e) {
+              LOG.warn("Failed to close the index file writer", e);
+            }
+          }
+        }
+
+        // If there are ServiceComponentHosts to process, make sure the administrator credentials
+        // are available
+        if (!serviceComponentHostsToProcess.isEmpty()) {
+          if (getEncryptedAdministratorCredentials(cluster) == null) {
+            try {
+              FileUtils.deleteDirectory(dataDirectory);
+            } catch (IOException e) {
+              LOG.warn(String.format("The data directory (%s) was not deleted due to an error condition - {%s}",
+                  dataDirectory.getAbsolutePath(), e.getMessage()), e);
+            }
+            throw new AmbariException("Missing KDC administrator credentials");
+          }
+        }
+
+        // Always set up the necessary stages to perform the tasks needed to complete the operation.
+        // Some stages may be no-ops, this is expected.
+        // Gather data needed to create stages and tasks...
+        Map<String, Set<String>> clusterHostInfo = StageUtils.getClusterHostInfo(hosts, cluster);
+        String clusterHostInfoJson = StageUtils.getGson().toJson(clusterHostInfo);
+        Map<String, String> hostParams = customCommandExecutionHelper.createDefaultHostParams(cluster);
+        String hostParamsJson = StageUtils.getGson().toJson(hostParams);
+        String ambariServerHostname = StageUtils.getHostName();
+        ServiceComponentHostServerActionEvent event = new ServiceComponentHostServerActionEvent(
+            "AMBARI_SERVER",
+            ambariServerHostname, // TODO: Choose a random hostname from the cluster. All tasks for the AMBARI_SERVER service will be executed on this Ambari server
+            System.currentTimeMillis());
+        RoleCommandOrder roleCommandOrder = ambariManagementController.getRoleCommandOrder(cluster);
+
+        // If a RequestStageContainer does not already exist, create a new one...
+        if (requestStageContainer == null) {
+          requestStageContainer = new RequestStageContainer(
+              actionManager.getNextRequestId(),
+              null,
+              requestFactory,
+              actionManager);
+        }
+
+        // Use the handler implementation to setup the relevant stages.
+        int lastStageId = handler.createStages(cluster, hosts, kerberosConfigurations,
+            clusterHostInfoJson, hostParamsJson, event, roleCommandOrder, realm, kdcType.toString(),
+            dataDirectory, requestStageContainer);
+
+        // Add the cleanup stage...
+
+        Map<String, String> commandParameters = new HashMap<String, String>();
+        commandParameters.put(KerberosServerAction.DATA_DIRECTORY, dataDirectory.getAbsolutePath());
+
+        Stage stage = createServerActionStage(++lastStageId,
+            cluster,
+            requestStageContainer.getId(),
+            "Finalize Operations",
+            clusterHostInfoJson,
+            "{}",
+            hostParamsJson,
+            FinalizeKerberosServerAction.class,
+            event,
+            commandParameters,
+            300);
+
+        RoleGraph roleGraph = new RoleGraph(roleCommandOrder);
+        roleGraph.build(stage);
+        requestStageContainer.addStages(roleGraph.getStages());
+
+        // If all goes well, set the appropriate states on the relevant ServiceComponentHosts
+        for (ServiceComponentHost sch : serviceComponentHostsToProcess) {
+          // Update the desired and current states for the ServiceComponentHost
+          // using new state information from the the handler implementation
+          sch.setDesiredSecurityState(handler.getNewDesiredSCHSecurityState());
+          sch.setSecurityState(handler.getNewSCHSecurityState());
+        }
+      }
+
+      // If all goes well, set all services to _desire_ to be secured or unsecured, depending on handler
+      for (Service service : services.values()) {
+        service.setSecurityState(desiredSecurityState);
+      }
+    }
+
+    return requestStageContainer;
+  }
+
+  /**
+   * Creates a temporary directory within the system temporary directory
+   * <p/>
+   * The resulting directory is to be removed by the caller when desired.
+   *
+   * @return a File pointing to the new temporary directory, or null if one was not created
+   * @throws java.io.IOException if a new temporary directory cannot be created
+   */
+  private File createTemporaryDirectory() throws IOException {
+    String tempDirectoryPath = System.getProperty("java.io.tmpdir");
+    if (tempDirectoryPath == null) {
+      throw new IOException("The System property 'java.io.tmpdir' does not specify a temporary directory");
+    }
+
+    File directory;
+    int tries = 0;
+    long now = System.currentTimeMillis();
+
+    do {
+      directory = new File(tempDirectoryPath, String.format("%s%d-%d.d",
+          KerberosServerAction.DATA_DIRECTORY_PREFIX, now, tries));
+
+      if ((directory.exists()) || !directory.mkdirs()) {
+        directory = null; // Rest and try again...
+      } else {
+        LOG.debug("Created temporary directory: {}", directory.getAbsolutePath());
+      }
+    } while ((directory == null) && (++tries < 100));
+
+    if (directory == null) {
+      throw new IOException(String.format("Failed to create a temporary directory in %s", tempDirectoryPath));
+    }
+
+    return directory;
+  }
+
+  /**
+   * Build a composite Kerberos descriptor using the default descriptor data, existing cluster
+   * descriptor data (future), and the supplied descriptor updates from the request
+   *
+   * @param currentStackVersion the current cluster's StackId
+   * @param kerberosDescriptor  a KerberosDescriptor containing updates from the request payload
+   * @return a KerberosDescriptor containing existing data with requested changes
+   * @throws AmbariException
+   */
+  private KerberosDescriptor buildKerberosDescriptor(StackId currentStackVersion,
+                                                     KerberosDescriptor kerberosDescriptor)
+      throws AmbariException {
+    KerberosDescriptor defaultKerberosDescriptor = ambariMetaInfo.getKerberosDescriptor(
+        currentStackVersion.getStackName(),
+        currentStackVersion.getStackVersion()
+    );
+
+    if (defaultKerberosDescriptor == null) {
+      return kerberosDescriptor;
+    } else {
+      if (kerberosDescriptor != null) {
+        defaultKerberosDescriptor.update(kerberosDescriptor);
+      }
+      return defaultKerberosDescriptor;
+    }
+  }
+
+  /**
+   * Merges configuration from a Map of configuration updates into a main configurations Map.  Each
+   * property in the updates Map is processed to replace variables using the replacement Map.
+   * <p/>
+   * See {@link org.apache.ambari.server.state.kerberos.KerberosDescriptor#replaceVariables(String, java.util.Map)}
+   * for information on variable replacement.
+   *
+   * @param configurations a Map of configurations
+   * @param updates        a Map of configuration updates
+   * @param replacements   a Map of (grouped) replacement values
+   * @return the merged Map
+   * @throws AmbariException
+   */
+  private Map<String, Map<String, String>> mergeConfigurations(Map<String, Map<String, String>> configurations,
+                                                               Map<String, KerberosConfigurationDescriptor> updates,
+                                                               Map<String, Map<String, String>> replacements)
+      throws AmbariException {
+
+    if ((updates != null) && !updates.isEmpty()) {
+      if (configurations == null) {
+        configurations = new HashMap<String, Map<String, String>>();
+      }
+
+      for (Map.Entry<String, KerberosConfigurationDescriptor> entry : updates.entrySet()) {
+        String type = entry.getKey();
+        KerberosConfigurationDescriptor configurationDescriptor = entry.getValue();
+
+        if (configurationDescriptor != null) {
+          Map<String, String> updatedProperties = configurationDescriptor.getProperties();
+
+          if (updatedProperties != null) {
+            Map<String, String> existingProperties = configurations.get(type);
+            if (existingProperties == null) {
+              existingProperties = new HashMap<String, String>();
+              configurations.put(type, existingProperties);
+            }
+
+            for (Map.Entry<String, String> property : updatedProperties.entrySet()) {
+              existingProperties.put(property.getKey(),
+                  KerberosDescriptor.replaceVariables(property.getValue(), replacements));
+            }
+          }
+        }
+      }
+    }
+
+    return configurations;
+  }
+
+  /**
+   * Adds identities to the KerberosActionDataFileBuilder.
+   *
+   * @param kerberosActionDataFileBuilder a KerberosActionDataFileBuilder to use for storing identity
+   *                                      records
+   * @param identities                    a List of KerberosIdentityDescriptors to add to the data
+   *                                      file
+   * @param sch                           the relevant ServiceComponentHost
+   * @param configurations                a Map of configurations to use a replacements for variables
+   *                                      in identity fields
+   * @return an integer indicating the number of identities added to the data file
+   * @throws java.io.IOException if an error occurs while writing a record to the data file
+   */
+  private int addIdentities(KerberosActionDataFileBuilder kerberosActionDataFileBuilder,
+                            List<KerberosIdentityDescriptor> identities,
+                            ServiceComponentHost sch,
+                            Map<String, Map<String, String>> configurations) throws IOException {
+    int identitiesAdded = 0;
+
+    if (identities != null) {
+      for (KerberosIdentityDescriptor identity : identities) {
+        KerberosPrincipalDescriptor principalDescriptor = identity.getPrincipalDescriptor();
+        String principal = null;
+        String principalConfiguration = null;
+
+        if (principalDescriptor != null) {
+          principal = KerberosDescriptor.replaceVariables(principalDescriptor.getValue(), configurations);
+          principalConfiguration = KerberosDescriptor.replaceVariables(principalDescriptor.getConfiguration(), configurations);
+        }
+
+        if (principal != null) {
+          KerberosKeytabDescriptor keytabDescriptor = identity.getKeytabDescriptor();
+          String keytabFilePath = null;
+          String keytabFileOwnerName = null;
+          String keytabFileOwnerAccess = null;
+          String keytabFileGroupName = null;
+          String keytabFileGroupAccess = null;
+          String keytabFileConfiguration = null;
+
+          if (keytabDescriptor != null) {
+            keytabFilePath = KerberosDescriptor.replaceVariables(keytabDescriptor.getFile(), configurations);
+            keytabFileOwnerName = KerberosDescriptor.replaceVariables(keytabDescriptor.getOwnerName(), configurations);
+            keytabFileOwnerAccess = KerberosDescriptor.replaceVariables(keytabDescriptor.getOwnerAccess(), configurations);
+            keytabFileGroupName = KerberosDescriptor.replaceVariables(keytabDescriptor.getGroupName(), configurations);
+            keytabFileGroupAccess = KerberosDescriptor.replaceVariables(keytabDescriptor.getGroupAccess(), configurations);
+            keytabFileConfiguration = KerberosDescriptor.replaceVariables(keytabDescriptor.getConfiguration(), configurations);
+          }
+
+          kerberosActionDataFileBuilder.addRecord(sch.getHostName(),
+              sch.getServiceName(),
+              sch.getServiceComponentName(),
+              principal,
+              principalConfiguration,
+              keytabFilePath,
+              keytabFileOwnerName,
+              keytabFileOwnerAccess,
+              keytabFileGroupName,
+              keytabFileGroupAccess,
+              keytabFileConfiguration);
+
+          identitiesAdded++;
+        }
+      }
+    }
+
+    return identitiesAdded;
+  }
+
+  /**
+   * Calculates the map of configurations relative to the cluster and host.
+   * <p/>
+   * This was borrowed from {@link org.apache.ambari.server.actionmanager.ExecutionCommandWrapper#getExecutionCommand()}
+   *
+   * @param cluster  the relevant Cluster
+   * @param hostname the relevant hostname
+   * @return a Map of calculated configuration types
+   * @throws AmbariException
+   */
+  private Map<String, Map<String, String>> calculateConfigurations(Cluster cluster, String hostname) throws AmbariException {
+    // For a configuration type, both tag and an actual configuration can be stored
+    // Configurations from the tag is always expanded and then over-written by the actual
+    // global:version1:{a1:A1,b1:B1,d1:D1} + global:{a1:A2,c1:C1,DELETED_d1:x} ==>
+    // global:{a1:A2,b1:B1,c1:C1}
+    Map<String, Map<String, String>> configurations = new HashMap<String, Map<String, String>>();
+
+    Map<String, Map<String, String>> configurationTags = ambariManagementController.findConfigurationTagsWithOverrides(cluster, hostname);
+    Map<String, Map<String, Map<String, String>>> configurationAttributes = new TreeMap<String, Map<String, Map<String, String>>>();
+
+    if (configurationTags.get(Configuration.GLOBAL_CONFIG_TAG) != null) {
+      configHelper.applyCustomConfig(
+          configurations, Configuration.GLOBAL_CONFIG_TAG,
+          Configuration.RCA_ENABLED_PROPERTY, "false", false);
+    }
+
+    Map<String, Map<String, String>> configProperties = configHelper.getEffectiveConfigProperties(cluster, configurationTags);
+
+    // Apply the configurations saved with the Execution Cmd on top of
+    // derived configs - This will take care of all the hacks
+    for (Map.Entry<String, Map<String, String>> entry : configProperties.entrySet()) {
+      String type = entry.getKey();
+      Map<String, String> allLevelMergedConfig = entry.getValue();
+      Map<String, String> configuration = configurations.get(type);
+
+      if (configuration == null) {
+        configuration = new HashMap<String, String>(allLevelMergedConfig);
+      } else {
+        Map<String, String> mergedConfig = configHelper.getMergedConfig(allLevelMergedConfig, configuration);
+        configuration.clear();
+        configuration.putAll(mergedConfig);
+      }
+
+      configurations.put(type, configuration);
+    }
+
+    Map<String, Map<String, Map<String, String>>> configAttributes =
+        configHelper.getEffectiveConfigAttributes(cluster, configurationTags);
+
+    for (Map.Entry<String, Map<String, Map<String, String>>> attributesOccurrence : configAttributes.entrySet()) {
+      String type = attributesOccurrence.getKey();
+      Map<String, Map<String, String>> attributes = attributesOccurrence.getValue();
+
+      if (!configurationAttributes.containsKey(type)) {
+        configurationAttributes.put(type, new TreeMap<String, Map<String, String>>());
+      }
+      configHelper.cloneAttributesMap(attributes, configurationAttributes.get(type));
+    }
+
+    return configurations;
+  }
+
+  /**
+   * Creates a new stage
+   *
+   * @param id              the new stage's id
+   * @param cluster         the relevant Cluster
+   * @param requestId       the relevant request Id
+   * @param requestContext  a String describing the stage
+   * @param clusterHostInfo JSON-encoded clusterHostInfo structure
+   * @param commandParams   JSON-encoded command parameters
+   * @param hostParams      JSON-encoded host parameters
+   * @return a newly created Stage
+   */
+  private Stage createNewStage(long id, Cluster cluster, long requestId,
+                               String requestContext, String clusterHostInfo,
+                               String commandParams, String hostParams) {
+    Stage stage = stageFactory.createNew(requestId,
+        BASE_LOG_DIR + File.pathSeparator + requestId,
+        cluster.getClusterName(),
+        cluster.getClusterId(),
+        requestContext,
+        clusterHostInfo,
+        commandParams,
+        hostParams);
+
+    stage.setStageId(id);
+    return stage;
+  }
+
+  /**
+   * Creates a new stage with a single task describing the ServerAction class to invoke and the other
+   * task-related information.
+   *
+   * @param id                the new stage's id
+   * @param cluster           the relevant Cluster
+   * @param requestId         the relevant request Id
+   * @param requestContext    a String describing the stage
+   * @param clusterHostInfo   JSON-encoded clusterHostInfo structure
+   * @param commandParams     JSON-encoded command parameters
+   * @param hostParams        JSON-encoded host parameters
+   * @param actionClass       The ServeAction class that implements the action to invoke
+   * @param event             The relevant ServiceComponentHostServerActionEvent
+   * @param commandParameters a Map of command parameters to attach to the task added to the new
+   *                          stage
+   * @param timeout           the timeout for the task/action
+   * @return a newly created Stage
+   */
+  private Stage createServerActionStage(long id, Cluster cluster, long requestId,
+                                        String requestContext, String clusterHostInfo,
+                                        String commandParams, String hostParams,
+                                        Class<? extends ServerAction> actionClass,
+                                        ServiceComponentHostServerActionEvent event,
+                                        Map<String, String> commandParameters, Integer timeout) {
+
+    Stage stage = createNewStage(id, cluster, requestId, requestContext, clusterHostInfo, commandParams, hostParams);
+    stage.addServerActionCommand(actionClass.getName(),
+        Role.AMBARI_SERVER_ACTION,
+        RoleCommand.EXECUTE,
+        cluster.getClusterName(),
+        event,
+        commandParameters,
+        timeout);
+
+    return stage;
+  }
+
+  /**
+   * Using the session data from the relevant Cluster object, creates a KerberosCredential,
+   * serializes, and than encrypts it.
+   * <p/>
+   * Since the relevant data is stored in the HTTPSession (which relies on ThreadLocalStorage),
+   * it needs to be retrieved now and placed in the action's command parameters so it will be
+   * available when needed.  Because command parameters are stored in plaintext in the Ambari database,
+   * this (sensitive) data needs to be encrypted, however it needs to be done using a key the can be
+   * recreated sometime later when the data needs to be access. Since it is expected that the Cluster
+   * object will be able now and later, the hashcode of this object is used to build the key - it
+   * is expected that the same instance will be retrieved from the Clusters instance, thus yielding
+   * the same hashcode value.
+   * <p/>
+   * If the Ambari server architecture changes, this will need to be revisited.
+   *
+   * @param cluster the relevant Cluster
+   * @return a serialized and encrypted KerberosCredential, or null if administrator data is not found
+   * @throws AmbariException
+   */
+  private String getEncryptedAdministratorCredentials(Cluster cluster) throws AmbariException {
+    String encryptedAdministratorCredentials = null;
+
+    Map<String, Object> sessionAttributes = cluster.getSessionAttributes();
+    if (sessionAttributes != null) {
+      KerberosCredential credential = KerberosCredential.fromMap(sessionAttributes, "kerberos_admin/");
+      if (credential != null) {
+        byte[] key = Integer.toHexString(cluster.hashCode()).getBytes();
+        encryptedAdministratorCredentials = credential.encrypt(key);
+      }
+    }
+
+    return encryptedAdministratorCredentials;
+  }
+
+  /**
+   * Handler is an interface that needs to be implemented by toggle handler classes to do the
+   * "right" thing for the task at hand.
+   */
+  private interface Handler {
+    /**
+     * Tests the Service and ServiceComponentHost to see if they are in the appropriate security
+     * state to be processed for the relevant task.
+     *
+     * @param desiredSecurityState the SecurityState to be transitioned to
+     * @param sch                  the ServiceComponentHost to test
+     * @return true if both the Service and ServiceComponentHost are in the appropriate security
+     * state to be processed; otherwise false
+     * @throws AmbariException of an error occurs while testing
+     */
+    boolean shouldProcess(SecurityState desiredSecurityState, ServiceComponentHost sch) throws AmbariException;
+
+    /**
+     * Returns the new SecurityState to be set as the ServiceComponentHost's _desired_ SecurityState.
+     *
+     * @return a SecurityState to be set as the ServiceComponentHost's _desired_ SecurityState
+     */
+    SecurityState getNewDesiredSCHSecurityState();
+
+    /**
+     * Returns the new SecurityState to be set as the ServiceComponentHost's _current_ SecurityState.
+     *
+     * @return a SecurityState to be set as the ServiceComponentHost's _current_ SecurityState
+     */
+    SecurityState getNewSCHSecurityState();
+
+
+    /**
+     * Returns the new SecurityState to be set as the Service's SecurityState.
+     *
+     * @return a SecurityState to be set as the Service's SecurityState
+     */
+    SecurityState getNewServiceSecurityState();
+
+    /**
+     * Creates the necessary stages to complete the relevant task and stores them in the supplied
+     * or a newly created RequestStageContainer.
+     * <p/>
+     * If the supplied RequestStageContainer is null, a new one must be created and filled.
+     * {@link org.apache.ambari.server.controller.internal.RequestStageContainer#persist()} should
+     * not be called since it is not known if the set of states for this container is complete.
+     *
+     * @param cluster                the relevant Cluster
+     * @param hosts                  the relevant Map of Hosts
+     * @param kerberosConfigurations the compiled KerberosConfigurations for the entire Kerberos
+     *                               descriptor hierarchy with all variables replaced
+     * @param clusterHostInfo        JSON-encoded clusterHostInfo structure
+     * @param hostParams             JSON-encoded host parameters
+     * @param event                  a ServiceComponentHostServerActionEvent to pass to any created tasks
+     * @param roleCommandOrder       the RoleCommandOrder to use to generate the RoleGraph for any newly created Stages
+     * @param realm                  a String declaring the cluster's Kerberos realm
+     * @param kdcType                a relevant KDCType
+     * @param dataDirectory          a File pointing to the (temporary) data directory
+     * @param requestStageContainer  a RequestStageContainer to store the new stages in, if null a
+     *                               new RequestStageContainer will be created
+     * @return the last stage id generated, or -1 if no stages were created
+     * @throws AmbariException if an error occurs while creating the relevant stages
+     */
+    int createStages(Cluster cluster, Map<String, Host> hosts,
+                     Map<String, Map<String, String>> kerberosConfigurations,
+                     String clusterHostInfo, String hostParams,
+                     ServiceComponentHostServerActionEvent event,
+                     RoleCommandOrder roleCommandOrder,
+                     String realm, String kdcType, File dataDirectory,
+                     RequestStageContainer requestStageContainer)
+        throws AmbariException;
+
+  }
+
+  /**
+   * EnableKerberosHandler is an implementation of the Handler interface used to enable Kerberos
+   * on the relevant cluster
+   * <p/>
+   * This implementation attempts to set the Service and ServiceComponentHost _desired_ security
+   * states to {@link org.apache.ambari.server.state.SecurityState#SECURED_KERBEROS} and the
+   * ServiceComponentHost _current_ security state to {@link org.apache.ambari.server.state.SecurityState#SECURING}.
+   * <p/>
+   * To complete the process, this implementation creates the following stages:
+   * <ol>
+   * <li>create principals</li>
+   * <li>create keytab files</li>
+   * <li>distribute keytab files to the appropriate hosts</li>
+   * <li>update relevant configurations</li>
+   * <li>restart services</li>
+   * </ol>
+   */
+  private class EnableKerberosHandler implements Handler {
+    @Override
+    public boolean shouldProcess(SecurityState desiredSecurityState, ServiceComponentHost sch) throws AmbariException {
+      return (desiredSecurityState == SecurityState.SECURED_KERBEROS) &&
+          (maintenanceStateHelper.getEffectiveState(sch) == MaintenanceState.OFF) &&
+          (sch.getSecurityState() != SecurityState.SECURED_KERBEROS) &&
+          (sch.getSecurityState() != SecurityState.SECURING);
+    }
+
+    @Override
+    public SecurityState getNewDesiredSCHSecurityState() {
+      return SecurityState.SECURED_KERBEROS;
+    }
+
+    @Override
+    public SecurityState getNewSCHSecurityState() {
+      // TODO (rlevas): Set this to SecurityState.SECURING
+      // when the required infrastructure is in place
+      // See AMBARI-8343 and associated JIRAs (like AMBARI-8477)
+      return SecurityState.SECURED_KERBEROS;
+    }
+
+    @Override
+    public SecurityState getNewServiceSecurityState() {
+      return SecurityState.SECURED_KERBEROS;
+    }
+
+    @Override
+    public int createStages(Cluster cluster, Map<String, Host> hosts,
+                            Map<String, Map<String, String>> kerberosConfigurations,
+                            String clusterHostInfoJson, String hostParamsJson,
+                            ServiceComponentHostServerActionEvent event,
+                            RoleCommandOrder roleCommandOrder, String realm, String kdcType,
+                            File dataDirectory, RequestStageContainer requestStageContainer) throws AmbariException {
+      // If there are principals, keytabs, and configurations to process, setup the following sages:
+      //  1) generate principals
+      //  2) generate keytab files
+      //  3) distribute keytab files
+      //  4) update configurations
+      //  4) restart services
+
+      RoleGraph roleGraph;
+      Stage stage;
+      int stageId = -1;
+
+      // If a RequestStageContainer does not already exist, create a new one...
+      if (requestStageContainer == null) {
+        requestStageContainer = new RequestStageContainer(
+            actionManager.getNextRequestId(),
+            null,
+            requestFactory,
+            actionManager);
+      }
+
+      // 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()) {
+        File configFile = new File(dataDirectory, KerberosConfigDataFile.DATA_FILE_NAME);
+        KerberosConfigDataFileBuilder kerberosConfDataFileBuilder = null;
+        try {
+          kerberosConfDataFileBuilder = new KerberosConfigDataFileBuilder(configFile);
+
+          for (Map.Entry<String, Map<String, String>> entry : kerberosConfigurations.entrySet()) {
+            String type = entry.getKey();
+            Map<String, String> properties = entry.getValue();
+
+            if (properties != null) {
+              for (Map.Entry<String, String> configTypeEntry : properties.entrySet()) {
+                kerberosConfDataFileBuilder.addRecord(type,
+                    configTypeEntry.getKey(),
+                    configTypeEntry.getValue());
+              }
+            }
+          }
+        } catch (IOException e) {
+          String message = String.format("Failed to write kerberos configurations file - %s", configFile.getAbsolutePath());
+          LOG.error(message);
+          throw new AmbariException(message, e);
+        } finally {
+          if (kerberosConfDataFileBuilder != null) {
+            try {
+              kerberosConfDataFileBuilder.close();
+            } catch (IOException e) {
+              LOG.warn("Failed to close the kerberos configurations file writer", e);
+            }
+          }
+        }
+      }
+
+      Map<String, String> commandParameters = new HashMap<String, String>();
+      commandParameters.put(KerberosServerAction.DATA_DIRECTORY, dataDirectory.getAbsolutePath());
+      commandParameters.put(KerberosServerAction.DEFAULT_REALM, realm);
+      commandParameters.put(KerberosServerAction.KDC_TYPE, kdcType);
+      commandParameters.put(KerberosServerAction.ADMINISTRATOR_CREDENTIAL, getEncryptedAdministratorCredentials(cluster));
+
+      // *****************************************************************
+      // Create stage to create principals
+      stage = createServerActionStage(++stageId,
+          cluster,
+          requestStageContainer.getId(),
+          "Create Principals",
+          clusterHostInfoJson,
+          "{}",
+          hostParamsJson,
+          CreatePrincipalsServerAction.class,
+          event,
+          commandParameters,
+          1200);
+
+      roleGraph = new RoleGraph(roleCommandOrder);
+      roleGraph.build(stage);
+      requestStageContainer.addStages(roleGraph.getStages());
+
+      // *****************************************************************
+      // Create stage to generate keytabs
+      stage = createServerActionStage(++stageId,
+          cluster,
+          requestStageContainer.getId(),
+          "Create Keytabs",
+          clusterHostInfoJson,
+          "{}",
+          hostParamsJson,
+          CreateKeytabFilesServerAction.class,
+          event,
+          commandParameters,
+          1200);
+
+      roleGraph = new RoleGraph(roleCommandOrder);
+      roleGraph.build(stage);
+      requestStageContainer.addStages(roleGraph.getStages());
+
+      // *****************************************************************
+      // Create stage to distribute keytabs
+      // TODO (dilli): Implement this
+        /*
+        stage = createServerActionStage(++stageId,
+            cluster,
+            requestStageContainer.getId(),
+            "Distribute Kerberos keytabs to services",
+            clusterHostInfoJson,
+            "{}",
+            hostParamsJson,
+          DISTRIBUTE_KEYTABS.class,
+          event,
+          commandParameters,
+          1200);
+
+        roleGraph = new RoleGraph(roleCommandOrder);
+        roleGraph.build(stage);
+        requestStageContainer.addStages(roleGraph.getStages());
+        */
+
+      // *****************************************************************
+      // Create stage to update configurations
+      // TODO: (dilli): implement this
+        /*
+        stage = createServerActionStage(++stageId,
+            cluster,
+            requestStageContainer.getId(),
+            "Distribute Kerberos keytabs to services",
+            clusterHostInfoJson,
+            "{}",
+            hostParamsJson,
+            UPDATE_SERVICE_CONFIGURATIONS.class
+            event,
+            commandParameters,
+            1200);
+
+        roleGraph = new RoleGraph(roleCommandOrder);
+        roleGraph.build(stage);
+        requestStageContainer.addStages(roleGraph.getStages());
+        */
+
+      return stageId;
+    }
+  }
+
+  /**
+   * DisableKerberosHandler is an implementation of the Handler interface used to disable Kerberos
+   * on the relevant cluster
+   * <p/>
+   * This implementation attempts to set the Service and ServiceComponentHost _desired_ security
+   * states to {@link org.apache.ambari.server.state.SecurityState#UNSECURED} and the ServiceComponentHost
+   * _current_ security state to {@link org.apache.ambari.server.state.SecurityState#UNSECURING}.
+   * <p/>
+   * To complete the process, this implementation creates the following stages:
+   * <ol>
+   * <li>update relevant configurations</li>
+   * <li>delete keytab files</li>
+   * <li>remove principals</li>
+   * <li>restart services</li>
+   * </ol>
+   */
+  private class DisableKerberosHandler implements Handler {
+    @Override
+    public boolean shouldProcess(SecurityState desiredSecurityState, ServiceComponentHost sch) throws AmbariException {
+      return (desiredSecurityState == SecurityState.UNSECURED) &&
+          (maintenanceStateHelper.getEffectiveState(sch) == MaintenanceState.OFF) &&
+          (sch.getSecurityState() != SecurityState.UNSECURED) &&
+          (sch.getSecurityState() != SecurityState.UNSECURING);
+    }
+
+    @Override
+    public SecurityState getNewDesiredSCHSecurityState() {
+      return SecurityState.UNSECURED;
+    }
+
+    @Override
+    public SecurityState getNewSCHSecurityState() {
+      // TODO (rlevas): Set this to SecurityState.UNSECURING
+      // when the required infrastructure is in place
+      // See AMBARI-8343 and associated JIRAs (like AMBARI-8477)
+      return SecurityState.UNSECURED;
+    }
+
+    @Override
+    public SecurityState getNewServiceSecurityState() {
+      return SecurityState.UNSECURED;
+    }
+
+    @Override
+    public int createStages(Cluster cluster, Map<String, Host> hosts,
+                            Map<String, Map<String, String>> kerberosConfigurations,
+                            String clusterHostInfoJson, String hostParamsJson,
+                            ServiceComponentHostServerActionEvent event,
+                            RoleCommandOrder roleCommandOrder, String realm, String kdcType,
+                            File dataDirectory, RequestStageContainer requestStageContainer) {
+      // TODO (rlevas): If there are principals, keytabs, and configurations to process, setup the following sages:
+      //  1) remove principals
+      //  2) remove keytab files
+      //  3) update configurations
+      //  3) restart services
+      return -1;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef341466/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java
index 551698a..f9aca1d 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java
@@ -17,6 +17,7 @@
  */
 package org.apache.ambari.server.controller.internal;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
@@ -50,6 +51,7 @@ import org.apache.ambari.server.state.Config;
 import org.apache.ambari.server.state.ConfigHelper;
 import org.apache.ambari.server.state.ConfigImpl;
 import org.apache.ambari.server.state.StackId;
+import org.apache.ambari.server.state.kerberos.KerberosDescriptor;
 
 /**
  * Resource provider for cluster resources.
@@ -317,6 +319,14 @@ public class ClusterResourceProvider extends BaseBlueprintProcessor {
     baseUnsupported.remove("default_password");
     baseUnsupported.remove("configurations");
 
+    // Allow property Ids that start with "kerberos_descriptor/"
+    Iterator<String> iterator = baseUnsupported.iterator();
+    while (iterator.hasNext()) {
+      if (iterator.next().startsWith("kerberos_descriptor/")) {
+        iterator.remove();
+      }
+    }
+
     return checkConfigPropertyIds(baseUnsupported, "Clusters");
   }
 
@@ -354,12 +364,15 @@ public class ClusterResourceProvider extends BaseBlueprintProcessor {
    * @return the cluster request object
    */
   private ClusterRequest getRequest(Map<String, Object> properties) {
+    KerberosDescriptor kerberosDescriptor = new KerberosDescriptor(createKerberosPropertyMap(properties));
+
     ClusterRequest cr = new ClusterRequest(
         (Long) properties.get(CLUSTER_ID_PROPERTY_ID),
         (String) properties.get(CLUSTER_NAME_PROPERTY_ID),
         (String) properties.get(CLUSTER_PROVISIONING_STATE_PROPERTY_ID),
         (String) properties.get(CLUSTER_VERSION_PROPERTY_ID),
         null,
+        kerberosDescriptor,
         getSessionAttributes(properties));
 
     List<ConfigurationRequest> configRequests = getConfigurationRequests("Clusters", properties);
@@ -377,6 +390,107 @@ public class ClusterResourceProvider extends BaseBlueprintProcessor {
   }
 
   /**
+   * Recursively attempts to "normalize" a property value into either a single Object, a Map or a
+   * Collection of items depending on the type of Object that is supplied.
+   * <p/>
+   * If the supplied value is a Map, attempts to render a Map of keys to "normalized" values. This
+   * may yield a Map of Maps or a Map of Collections, or a Map of values.
+   * <p/>
+   * If the supplied value is a Collection, attempts to render a Collection of Maps, Collections, or values
+   * <p/>
+   * Else, assumes the value is a simple value
+   *
+   * @param property an Object to "normalize"
+   * @return the normalized object or the input value if it is not a Map or Collection.
+   */
+  private Object normalizeKerberosProperty(Object property) {
+    if (property instanceof Map) {
+      Map<?, ?> properties = (Map) property;
+      Map<String, Object> map = new HashMap<String, Object>(properties.size());
+
+      for (Map.Entry<?, ?> entry : properties.entrySet()) {
+        normalizeKerberosProperty(entry.getKey().toString(), entry.getValue(), map);
+      }
+
+      return map;
+    } else if (property instanceof Collection) {
+      Collection properties = (Collection) property;
+      Collection<Object> collection = new ArrayList<Object>(properties.size());
+
+      for (Object item : properties) {
+        collection.add(normalizeKerberosProperty(item));
+      }
+
+      return collection;
+    } else {
+      return property;
+    }
+  }
+
+  /**
+   * Recursively attempts to "normalize" a property value into either a single Object, a Map or a
+   * Collection of items; and places the result into the supplied Map under a specified key.
+   * <p/>
+   * See {@link #normalizeKerberosProperty(Object)} for more information "normalizing" a property value
+   *
+   * If the key (propertyName) indicates a hierarchy by separating names with a '/', the supplied map
+   * will be updated to handle the hierarchy. For example, if the propertyName value is "parent/child"
+   * then the map will be updated to contain an entry where the key is named "parent" and the value
+   * is a Map containing an entry with a name of "child" and value that is the normalized version of
+   * the specified value (propertyValue).
+   *
+   * @param propertyName a String declaring the name of the supplied property value
+   * @param propertyValue an Object containing the property value
+   * @param map a Map to store the results within
+   * @see #normalizeKerberosProperty(Object)
+   */
+  private void normalizeKerberosProperty(String propertyName, Object propertyValue, Map<String, Object> map) {
+    String[] keys = propertyName.split("/");
+    Map<String, Object> currentMap = map;
+
+    if (keys.length > 0) {
+      for (int i = 0; i < keys.length - 1; i++) {
+        String key = keys[i];
+
+        Object value = currentMap.get(key);
+
+        if (value instanceof Map) {
+          currentMap = (Map<String, Object>) value;
+        } else {
+          Map<String, Object> temp = new HashMap<String, Object>();
+          currentMap.put(key, temp);
+          currentMap = temp;
+        }
+      }
+
+      currentMap.put(keys[keys.length - 1], normalizeKerberosProperty(propertyValue));
+    }
+  }
+
+  /**
+   * Given a Map of Strings to Objects, attempts to expand all properties into a tree of Maps to
+   * effectively represent a Kerberos descriptor.
+   *
+   * @param properties a Map of properties to process
+   * @return a Map containing the expanded hierarchy of data
+   * @see #normalizeKerberosProperty(String, Object, java.util.Map)
+   */
+  private Map<String, Object> createKerberosPropertyMap(Map<String, Object> properties) {
+    Map<String, Object> kerberosPropertyMap = new HashMap<String, Object>();
+
+    if (properties != null) {
+      for (Map.Entry<String, Object> entry : properties.entrySet()) {
+        String key = entry.getKey();
+        if (key.startsWith("kerberos_descriptor/")) {
+          normalizeKerberosProperty(key.replace("kerberos_descriptor/", ""), entry.getValue(), kerberosPropertyMap);
+        }
+      }
+    }
+
+    return kerberosPropertyMap;
+  }
+
+  /**
    * Get the map of session attributes from the given property map.
    *
    * @param properties  the property map from the request

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef341466/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/FinalizeKerberosServerAction.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/FinalizeKerberosServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/FinalizeKerberosServerAction.java
new file mode 100644
index 0000000..2c6b33e
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/FinalizeKerberosServerAction.java
@@ -0,0 +1,94 @@
+/*
+ * 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 org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.actionmanager.HostRoleStatus;
+import org.apache.ambari.server.agent.CommandReport;
+import org.apache.commons.io.FileUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentMap;
+
+public class FinalizeKerberosServerAction extends KerberosServerAction {
+  private final static Logger LOG = LoggerFactory.getLogger(FinalizeKerberosServerAction.class);
+
+  /**
+   * Processes an identity as necessary.
+   * <p/>
+   * This method is not used since the {@link #processIdentities(java.util.Map)} is not invoked
+   *
+   * @param identityRecord           a Map containing the data for the current identity record
+   * @param evaluatedPrincipal       a String indicating the relevant principal
+   * @param operationHandler         a KerberosOperationHandler used to perform Kerberos-related
+   *                                 tasks for specific Kerberos implementations
+   *                                 (MIT, Active Directory, etc...)
+   * @param requestSharedDataContext a Map to be used a shared data among all ServerActions related
+   *                                 to a given request
+   * @return null, always
+   * @throws AmbariException
+   */
+  @Override
+  protected CommandReport processIdentity(Map<String, String> identityRecord, String evaluatedPrincipal,
+                                          KerberosOperationHandler operationHandler,
+                                          Map<String, Object> requestSharedDataContext)
+      throws AmbariException {
+
+    return null;
+  }
+
+  /**
+   *
+   * @param requestSharedDataContext a Map to be used a shared data among all ServerActions related
+   *                                 to a given request
+   * @return
+   * @throws AmbariException
+   * @throws InterruptedException
+   */
+  @Override
+  public CommandReport execute(ConcurrentMap<String, Object> requestSharedDataContext) throws AmbariException, InterruptedException {
+    String dataDirectoryPath = getCommandParameterValue(DATA_DIRECTORY);
+
+    // Make sure this is a relevant directory. We don't want to accidentally allow _ANY_ directory
+    // to be deleted.
+    if ((dataDirectoryPath != null) && dataDirectoryPath.contains("/" + DATA_DIRECTORY_PREFIX)) {
+      File dataDirectory = new File(dataDirectoryPath);
+      File dataDirectoryParent = dataDirectory.getParentFile();
+
+      // Make sure this directory has a parent and it is writeable, else we wont be able to
+      // delete the directory
+      if ((dataDirectoryParent != null) && dataDirectory.isDirectory() &&
+          dataDirectoryParent.isDirectory() && dataDirectoryParent.canWrite()) {
+        try {
+          FileUtils.deleteDirectory(dataDirectory);
+        } catch (IOException e) {
+          // We should log this exception, but don't let it fail the process since if we got to this
+          // KerberosServerAction it is expected that the the overall process was a success.
+          LOG.warn(String.format("The data directory (%s) was not deleted due to an error condition - {%s}",
+              dataDirectory.getAbsolutePath(), e.getMessage()), e);
+        }
+      }
+    }
+
+    return createCommandReport(0, HostRoleStatus.COMPLETED, "{}", null, null);  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef341466/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerAction.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerAction.java
index a3a1b5b..2df1829 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerAction.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerAction.java
@@ -68,6 +68,12 @@ public abstract class KerberosServerAction extends AbstractServerAction {
    */
   public static final String KDC_TYPE = "kdc_type";
 
+  /**
+   * The prefix to use for the data directory name.
+   */
+  public static final String DATA_DIRECTORY_PREFIX = ".ambari_";
+
+
   /*
    * Kerberos action shared data entry names
    */
@@ -180,8 +186,9 @@ public abstract class KerberosServerAction extends AbstractServerAction {
   protected KerberosCredential getAdministratorCredential(Map<String, String> commandParameters) throws AmbariException {
     Cluster cluster = clusters.getCluster(getExecutionCommand().getClusterName());
 
-    if(cluster == null)
+    if (cluster == null) {
       throw new AmbariException("Failed get the Cluster object");
+    }
 
     // Create the key like we did when we encrypted the data, based on the Cluster objects hashcode.
     byte[] key = Integer.toHexString(cluster.hashCode()).getBytes();
@@ -239,73 +246,79 @@ public abstract class KerberosServerAction extends AbstractServerAction {
       if (dataDirectoryPath != null) {
         File dataDirectory = new File(dataDirectoryPath);
 
-        if (!dataDirectory.exists() || !dataDirectory.isDirectory()) {
-          String message = String.format("Failed to process the identities, the data directory does not exist: %s",
-              dataDirectory.getAbsolutePath());
-          LOG.error(message);
-          throw new AmbariException(message);
-        }
-        // The "index" file is expected to be in the specified data directory and named "index.dat"
-        File indexFile = new File(dataDirectory, DATA_FILE_NAME);
-
-        if (!indexFile.canRead()) {
-          String message = String.format("Failed to process the identities, cannot read the index file: %s",
-              indexFile.getAbsolutePath());
-          LOG.error(message);
-          throw new AmbariException(message);
-        }
-        // Create the data file reader to parse and iterate through the records
-        KerberosActionDataFileReader reader = null;
-        KerberosOperationHandler handler = KerberosOperationHandlerFactory.getKerberosOperationHandler(kdcType);
-
-        if (handler == null) {
-          String message = String.format("Failed to process the identities, cannot read the index file: %s",
-              indexFile.getAbsolutePath());
-          LOG.error(message);
-          throw new AmbariException(message);
-        }
-
-        handler.open(administratorCredential, defaultRealm);
-
-        try {
-          reader = new KerberosActionDataFileReader(indexFile);
-          for (Map<String, String> record : reader) {
-            // Process the current record
-            commandReport = processRecord(record, defaultRealm, handler, requestSharedDataContext);
+        // If the data directory exists, attempt to process further, else assume there is no work to do
+        if (dataDirectory.exists()) {
+          if (!dataDirectory.isDirectory() || !dataDirectory.canRead()) {
+            String message = String.format("Failed to process the identities, the data directory is not accessible: %s",
+                dataDirectory.getAbsolutePath());
+            LOG.error(message);
+            throw new AmbariException(message);
+          }
+          // The "index" file may or may not exist in the data directory, depending on if there
+          // is work to do or not.
+          File indexFile = new File(dataDirectory, DATA_FILE_NAME);
+
+          if (indexFile.exists()) {
+            if (!indexFile.canRead()) {
+              String message = String.format("Failed to process the identities, cannot read the index file: %s",
+                  indexFile.getAbsolutePath());
+              LOG.error(message);
+              throw new AmbariException(message);
+            }
 
-            // If the principal processor returns a CommandReport, than it is time to stop since
-            // an error condition has probably occurred, else all is assumed to be well.
-            if (commandReport != null) {
-              break;
+            KerberosOperationHandler handler = KerberosOperationHandlerFactory.getKerberosOperationHandler(kdcType);
+            if (handler == null) {
+              String message = String.format("Failed to process the identities, a KDC operation handler was not found for the KDC type of : %s",
+                  kdcType.toString());
+              LOG.error(message);
+              throw new AmbariException(message);
             }
-          }
-        } catch (AmbariException e) {
-          // Catch this separately from IOException since the reason it was thrown was not the same
-          // Note: AmbariException is an IOException, so there may be some confusion
-          throw new AmbariException(e.getMessage(), e);
-        } catch (IOException e) {
-          String message = String.format("Failed to process the identities, cannot read the index file: %s",
-              indexFile.getAbsolutePath());
-          LOG.error(message, e);
-          throw new AmbariException(message, e);
-        } finally {
-          if (reader != null) {
-            // The reader needs to be closed, if it fails to close ignore the exception since
-            // there is little we can or care to do about it now.
+
+            // Create the data file reader to parse and iterate through the records
+            KerberosActionDataFileReader reader = null;
             try {
-              reader.close();
+              handler.open(administratorCredential, defaultRealm);
+
+              reader = new KerberosActionDataFileReader(indexFile);
+              for (Map<String, String> record : reader) {
+                // Process the current record
+                commandReport = processRecord(record, defaultRealm, handler, requestSharedDataContext);
+
+                // If the principal processor returns a CommandReport, than it is time to stop since
+                // an error condition has probably occurred, else all is assumed to be well.
+                if (commandReport != null) {
+                  break;
+                }
+              }
+            } catch (AmbariException e) {
+              // Catch this separately from IOException since the reason it was thrown was not the same
+              // Note: AmbariException is an IOException, so there may be some confusion
+              throw new AmbariException(e.getMessage(), e);
             } catch (IOException e) {
-              // Ignore this...
+              String message = String.format("Failed to process the identities, cannot read the index file: %s",
+                  indexFile.getAbsolutePath());
+              LOG.error(message, e);
+              throw new AmbariException(message, e);
+            } finally {
+              if (reader != null) {
+                // The reader needs to be closed, if it fails to close ignore the exception since
+                // there is little we can or care to do about it now.
+                try {
+                  reader.close();
+                } catch (IOException e) {
+                  // Ignore this...
+                }
+              }
+
+              // The KerberosOperationHandler needs to be closed, if it fails to close ignore the
+              // exception since there is little we can or care to do about it now.
+              try {
+                handler.close();
+              } catch (AmbariException e) {
+                // Ignore this...
+              }
             }
           }
-
-          // The KerberosOperationHandler needs to be closed, if it fails to close ignore the
-          // exception since there is little we can or care to do about it now.
-          try {
-            handler.close();
-          } catch (AmbariException e) {
-            // Ignore this...
-          }
         }
       }
     }

http://git-wip-us.apache.org/repos/asf/ambari/blob/ef341466/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerImplTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerImplTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerImplTest.java
index 4db01b9..5208a2d 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerImplTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerImplTest.java
@@ -120,6 +120,7 @@ public class AmbariManagementControllerImplTest {
     injector.injectMembers(capture(controllerCapture));
     expect(injector.getInstance(Gson.class)).andReturn(null);
     expect(injector.getInstance(MaintenanceStateHelper.class)).andReturn(null);
+    expect(injector.getInstance(KerberosHelper.class)).andReturn(createNiceMock(KerberosHelper.class));
 
     //replay
     replay(injector);
@@ -194,6 +195,7 @@ public class AmbariManagementControllerImplTest {
     injector.injectMembers(capture(controllerCapture));
     expect(injector.getInstance(Gson.class)).andReturn(null);
     expect(injector.getInstance(MaintenanceStateHelper.class)).andReturn(null);
+    expect(injector.getInstance(KerberosHelper.class)).andReturn(createNiceMock(KerberosHelper.class));
 
     // getCluster
     expect(clusters.getCluster("cluster1")).andReturn(cluster);
@@ -409,6 +411,7 @@ public class AmbariManagementControllerImplTest {
     injector.injectMembers(capture(controllerCapture));
     expect(injector.getInstance(Gson.class)).andReturn(null);
     expect(injector.getInstance(MaintenanceStateHelper.class)).andReturn(null);
+    expect(injector.getInstance(KerberosHelper.class)).andReturn(createNiceMock(KerberosHelper.class));
 
     // getCluster
     expect(clusters.getCluster("cluster1")).andThrow(new ClusterNotFoundException("cluster1"));
@@ -462,6 +465,7 @@ public class AmbariManagementControllerImplTest {
     injector.injectMembers(capture(controllerCapture));
     expect(injector.getInstance(Gson.class)).andReturn(null);
     expect(injector.getInstance(MaintenanceStateHelper.class)).andReturn(null);
+    expect(injector.getInstance(KerberosHelper.class)).andReturn(createNiceMock(KerberosHelper.class));
 
     // getCluster
     expect(clusters.getCluster("cluster1")).andThrow(new ClusterNotFoundException("cluster1"));
@@ -507,6 +511,7 @@ public class AmbariManagementControllerImplTest {
     injector.injectMembers(capture(controllerCapture));
     expect(injector.getInstance(Gson.class)).andReturn(null);
     expect(injector.getInstance(MaintenanceStateHelper.class)).andReturn(null);
+    expect(injector.getInstance(KerberosHelper.class)).andReturn(createNiceMock(KerberosHelper.class));
     expect(clusterRequest.getClusterName()).andReturn("clusterNew").times(4);
     expect(clusterRequest.getClusterId()).andReturn(1L).times(4);
     expect(clusters.getClusterById(1L)).andReturn(cluster);
@@ -546,6 +551,7 @@ public class AmbariManagementControllerImplTest {
     injector.injectMembers(capture(controllerCapture));
     expect(injector.getInstance(Gson.class)).andReturn(null);
     expect(injector.getInstance(MaintenanceStateHelper.class)).andReturn(null);
+    expect(injector.getInstance(KerberosHelper.class)).andReturn(createNiceMock(KerberosHelper.class));
     expect(clusterRequest.getClusterName()).andReturn("clusterNew").times(4);
     expect(clusterRequest.getClusterId()).andReturn(1L).times(4);
     expect(clusters.getClusterById(1L)).andReturn(cluster);
@@ -599,6 +605,7 @@ public class AmbariManagementControllerImplTest {
 
     expect(injector.getInstance(Gson.class)).andReturn(null);
     expect(injector.getInstance(MaintenanceStateHelper.class)).andReturn(maintHelper).anyTimes();
+    expect(injector.getInstance(KerberosHelper.class)).andReturn(createNiceMock(KerberosHelper.class));
 
     // getHostComponent
     expect(clusters.getCluster("cluster1")).andReturn(cluster);
@@ -666,6 +673,7 @@ public class AmbariManagementControllerImplTest {
     injector.injectMembers(capture(controllerCapture));
     expect(injector.getInstance(Gson.class)).andReturn(null);
     expect(injector.getInstance(MaintenanceStateHelper.class)).andReturn(maintHelper);
+    expect(injector.getInstance(KerberosHelper.class)).andReturn(createNiceMock(KerberosHelper.class));
 
     // getHostComponent
     expect(clusters.getCluster("cluster1")).andReturn(cluster);
@@ -750,6 +758,7 @@ public class AmbariManagementControllerImplTest {
     injector.injectMembers(capture(controllerCapture));
     expect(injector.getInstance(Gson.class)).andReturn(null);
     expect(injector.getInstance(MaintenanceStateHelper.class)).andReturn(stateHelper).anyTimes();
+    expect(injector.getInstance(KerberosHelper.class)).andReturn(createNiceMock(KerberosHelper.class));
 
 
     // getHostComponent
@@ -851,6 +860,7 @@ public class AmbariManagementControllerImplTest {
     injector.injectMembers(capture(controllerCapture));
     expect(injector.getInstance(Gson.class)).andReturn(null);
     expect(injector.getInstance(MaintenanceStateHelper.class)).andReturn(maintHelper).anyTimes();
+    expect(injector.getInstance(KerberosHelper.class)).andReturn(createNiceMock(KerberosHelper.class));
 
     // getHostComponent
     expect(clusters.getCluster("cluster1")).andReturn(cluster).times(3);
@@ -955,6 +965,7 @@ public class AmbariManagementControllerImplTest {
     injector.injectMembers(capture(controllerCapture));
     expect(injector.getInstance(Gson.class)).andReturn(null);
     expect(injector.getInstance(MaintenanceStateHelper.class)).andReturn(maintHelper).anyTimes();
+    expect(injector.getInstance(KerberosHelper.class)).andReturn(createNiceMock(KerberosHelper.class));
 
     // getHostComponent
     expect(clusters.getCluster("cluster1")).andReturn(cluster).times(3);
@@ -1061,6 +1072,7 @@ public class AmbariManagementControllerImplTest {
     injector.injectMembers(capture(controllerCapture));
     expect(injector.getInstance(Gson.class)).andReturn(null);
     expect(injector.getInstance(MaintenanceStateHelper.class)).andReturn(maintHelper).anyTimes();
+    expect(injector.getInstance(KerberosHelper.class)).andReturn(createNiceMock(KerberosHelper.class));
 
     // getHostComponent
     expect(clusters.getCluster("cluster1")).andReturn(cluster).times(3);
@@ -1144,6 +1156,7 @@ public class AmbariManagementControllerImplTest {
     injector.injectMembers(capture(controllerCapture));
     expect(injector.getInstance(Gson.class)).andReturn(null);
     expect(injector.getInstance(MaintenanceStateHelper.class)).andReturn(maintHelper);
+    expect(injector.getInstance(KerberosHelper.class)).andReturn(createNiceMock(KerberosHelper.class));
 
     // getHostComponent
     expect(clusters.getCluster("cluster1")).andReturn(cluster);
@@ -1198,6 +1211,7 @@ public class AmbariManagementControllerImplTest {
     injector.injectMembers(capture(controllerCapture));
     expect(injector.getInstance(Gson.class)).andReturn(null);
     expect(injector.getInstance(MaintenanceStateHelper.class)).andReturn(maintHelper);
+    expect(injector.getInstance(KerberosHelper.class)).andReturn(createNiceMock(KerberosHelper.class));
 
     // getHostComponent
     expect(clusters.getCluster("cluster1")).andThrow(new ClusterNotFoundException("cluster1"));
@@ -1259,6 +1273,7 @@ public class AmbariManagementControllerImplTest {
     injector.injectMembers(capture(controllerCapture));
     expect(injector.getInstance(Gson.class)).andReturn(null);
     expect(injector.getInstance(MaintenanceStateHelper.class)).andReturn(maintHelper).anyTimes();
+    expect(injector.getInstance(KerberosHelper.class)).andReturn(createNiceMock(KerberosHelper.class));
 
     // getHostComponent
     expect(clusters.getCluster("cluster1")).andReturn(cluster);
@@ -1345,6 +1360,7 @@ public class AmbariManagementControllerImplTest {
     injector.injectMembers(capture(controllerCapture));
     expect(injector.getInstance(Gson.class)).andReturn(null);
     expect(injector.getInstance(MaintenanceStateHelper.class)).andReturn(maintHelper).anyTimes();
+    expect(injector.getInstance(KerberosHelper.class)).andReturn(createNiceMock(KerberosHelper.class));
 
     // getHostComponent
     expect(clusters.getCluster("cluster1")).andReturn(cluster);
@@ -1429,6 +1445,7 @@ public class AmbariManagementControllerImplTest {
     injector.injectMembers(capture(controllerCapture));
     expect(injector.getInstance(Gson.class)).andReturn(null);
     expect(injector.getInstance(MaintenanceStateHelper.class)).andReturn(maintHelper).anyTimes();
+    expect(injector.getInstance(KerberosHelper.class)).andReturn(createNiceMock(KerberosHelper.class));
 
     replay(maintHelper, injector, clusters, serviceInfo);