You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by rl...@apache.org on 2018/07/05 16:40:33 UTC

[ambari] branch trunk updated: [AMBARI-24229] Prevent Configuration Changes During Keytab Regeneration in an Upgrade

This is an automated email from the ASF dual-hosted git repository.

rlevas pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ambari.git


The following commit(s) were added to refs/heads/trunk by this push:
     new a100a99  [AMBARI-24229] Prevent Configuration Changes During Keytab Regeneration in an Upgrade
a100a99 is described below

commit a100a99037d26cdf4e262eaf3ee03dd6581027f0
Author: Robert Levas <rl...@users.noreply.github.com>
AuthorDate: Thu Jul 5 12:40:30 2018 -0400

    [AMBARI-24229] Prevent Configuration Changes During Keytab Regeneration in an Upgrade
    
    Test failure is unrelated (ambari-web).  :(
    
    * [AMBARI-24229] Prevent Configuration Changes During Keytab Regeneration in an Upgrade
    
    * [AMBARI-24229] Prevent Configuration Changes During Keytab Regeneration in an Upgrade
---
 .../api/resources/ClusterResourceDefinition.java   |   1 +
 .../ambari/server/controller/KerberosHelper.java   |  18 +-
 .../server/controller/KerberosHelperImpl.java      | 106 ++++--
 .../controller/UpdateConfigurationPolicy.java      | 116 ++++++
 .../internal/UpgradeResourceProvider.java          |   2 +
 .../AbstractPrepareKerberosServerAction.java       | 168 ++++++++-
 .../kerberos/KerberosServerAction.java             |  31 +-
 .../PrepareDisableKerberosServerAction.java        |   2 +-
 .../PrepareEnableKerberosServerAction.java         |   2 +-
 .../PrepareKerberosIdentitiesServerAction.java     |  27 +-
 .../internal/UpgradeResourceProviderTest.java      |  89 ++---
 .../AbstractPrepareKerberosServerActionTest.java   | 397 +++++++++++++++++++--
 12 files changed, 811 insertions(+), 148 deletions(-)

diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ClusterResourceDefinition.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ClusterResourceDefinition.java
index 9d0c169..bc0e3ae 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ClusterResourceDefinition.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ClusterResourceDefinition.java
@@ -88,6 +88,7 @@ public class ClusterResourceDefinition extends BaseResourceDefinition {
     directives.add(KerberosHelper.DIRECTIVE_HOSTS);
     directives.add(KerberosHelper.DIRECTIVE_COMPONENTS);
     directives.add(KerberosHelper.DIRECTIVE_IGNORE_CONFIGS);
+    directives.add(KerberosHelper.DIRECTIVE_CONFIG_UPDATE_POLICY);
     return directives;
   }
 
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
index 79b2269..3c4d6b2 100644
--- 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
@@ -61,10 +61,26 @@ public interface KerberosHelper {
    */
   String DIRECTIVE_COMPONENTS = "regenerate_components";
   /**
-   * directive used to pass host list to regenerate keytabs on
+   * directive used to indicate configurations are not to be updated (if set to "true") when regenerating
+   * keytab files
+   * @deprecated use {@link #DIRECTIVE_CONFIG_UPDATE_POLICY}
    */
   String DIRECTIVE_IGNORE_CONFIGS = "ignore_config_updates";
   /**
+   * directive used to indicate how to handle configuration updates when regenerating keytab files
+   *
+   * expected values:
+   * <ul>
+   * <li>none</li>
+   * <li>identities_only</li>
+   * <li>new_and_identities</li>
+   * <li>all</li>
+   * </ul>
+   *
+   * @see UpdateConfigurationPolicy
+   */
+  String DIRECTIVE_CONFIG_UPDATE_POLICY = "config_update_policy";
+  /**
    * directive used to indicate that the enable Kerberos operation should proceed even if the
    * cluster's security type is not changing
    */
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java
index ad4d411..5521a29 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java
@@ -279,36 +279,49 @@ public class KerberosHelperImpl implements KerberosHelper {
                 throw new AmbariException(String.format("Custom operation %s can only be requested with the security type cluster property: %s", operation.name(), SecurityType.KERBEROS.name()));
               }
 
+              KerberosServerAction.OperationType operationType;
+              if ("true".equalsIgnoreCase(value) || "all".equalsIgnoreCase(value)) {
+                operationType = KerberosServerAction.OperationType.RECREATE_ALL;
+              } else if ("missing".equalsIgnoreCase(value)) {
+                operationType = KerberosServerAction.OperationType.CREATE_MISSING;
+              } else {
+                throw new AmbariException(String.format("Unexpected directive value: %s", value));
+              }
+
               boolean retryAllowed = false;
               if (requestProperties.containsKey(ALLOW_RETRY)) {
                 String allowRetryString = requestProperties.get(ALLOW_RETRY);
                 retryAllowed = Boolean.parseBoolean(allowRetryString);
               }
 
-              CreatePrincipalsAndKeytabsHandler handler = null;
-
               Set<String> hostFilter = parseHostFilter(requestProperties);
               Map<String, Set<String>> serviceComponentFilter = parseComponentFilter(requestProperties);
 
-              boolean updateConfigurations = !requestProperties.containsKey(DIRECTIVE_IGNORE_CONFIGS)
-                || !"true".equalsIgnoreCase(requestProperties.get(DIRECTIVE_IGNORE_CONFIGS));
+              UpdateConfigurationPolicy updateConfigurationsPolicy = UpdateConfigurationPolicy.ALL;
+              if(requestProperties.containsKey(DIRECTIVE_CONFIG_UPDATE_POLICY)) {
+                String policyValue = requestProperties.get(DIRECTIVE_CONFIG_UPDATE_POLICY);
+                updateConfigurationsPolicy = UpdateConfigurationPolicy.translate(policyValue);
 
-              boolean forceAllHosts = (hostFilter == null) || (hostFilter.contains("*"));
-
-              if ("true".equalsIgnoreCase(value) || "all".equalsIgnoreCase(value)) {
-                handler = new CreatePrincipalsAndKeytabsHandler(KerberosServerAction.OperationType.RECREATE_ALL, updateConfigurations, forceAllHosts, true);
-              } else if ("missing".equalsIgnoreCase(value)) {
-                handler = new CreatePrincipalsAndKeytabsHandler(KerberosServerAction.OperationType.CREATE_MISSING, updateConfigurations, forceAllHosts, true);
+                if(updateConfigurationsPolicy== null) {
+                  throw new AmbariException(String.format("Unexpected comfiguration policy value: %s", policyValue));
+                }
+              }
+              else if(requestProperties.containsKey(DIRECTIVE_IGNORE_CONFIGS)) {
+                if("true".equalsIgnoreCase(requestProperties.get(DIRECTIVE_IGNORE_CONFIGS))) {
+                  // This really means to no update existing properties. However, we need to ensure
+                  // that Kerberos identity specific configurations are updated or added to make
+                  // sure all is consistent.
+                  updateConfigurationsPolicy = UpdateConfigurationPolicy.NEW_AND_IDENTITIES;
+                }
               }
 
-              if (handler != null) {
-                handler.setRetryAllowed(retryAllowed);
+              boolean forceAllHosts = (hostFilter == null) || (hostFilter.contains("*"));
 
-                requestStageContainer = handle(cluster, getKerberosDetails(cluster, manageIdentities),
+              CreatePrincipalsAndKeytabsHandler handler = new CreatePrincipalsAndKeytabsHandler(operationType, updateConfigurationsPolicy, forceAllHosts, true);
+              handler.setRetryAllowed(retryAllowed);
+
+              requestStageContainer = handle(cluster, getKerberosDetails(cluster, manageIdentities),
                   serviceComponentFilter, hostFilter, null, null, requestStageContainer, handler);
-              } else {
-                throw new AmbariException(String.format("Unexpected directive value: %s", value));
-              }
 
               break;
 
@@ -368,9 +381,17 @@ public class KerberosHelperImpl implements KerberosHelper {
                                                 Set<String> hostFilter, Collection<String> identityFilter, Set<String> hostsToForceKerberosOperations,
                                                 RequestStageContainer requestStageContainer, Boolean manageIdentities)
     throws AmbariException, KerberosOperationException {
-    return handle(cluster, getKerberosDetails(cluster, manageIdentities), serviceComponentFilter, hostFilter, identityFilter,
-      hostsToForceKerberosOperations, requestStageContainer, new CreatePrincipalsAndKeytabsHandler(KerberosServerAction.OperationType.DEFAULT, false, false,
-        false));
+    return handle(cluster,
+        getKerberosDetails(cluster, manageIdentities),
+        serviceComponentFilter,
+        hostFilter,
+        identityFilter,
+        hostsToForceKerberosOperations,
+        requestStageContainer,
+        new CreatePrincipalsAndKeytabsHandler(KerberosServerAction.OperationType.DEFAULT,
+            UpdateConfigurationPolicy.NONE,
+            false,
+            false));
   }
 
   @Override
@@ -1112,8 +1133,14 @@ public class KerberosHelperImpl implements KerberosHelper {
   public RequestStageContainer createTestIdentity(Cluster cluster, Map<String, String> commandParamsStage,
                                                   RequestStageContainer requestStageContainer)
     throws KerberosOperationException, AmbariException {
-    return handleTestIdentity(cluster, getKerberosDetails(cluster, null), commandParamsStage, requestStageContainer,
-      new CreatePrincipalsAndKeytabsHandler(KerberosServerAction.OperationType.DEFAULT, false, false, false));
+    return handleTestIdentity(cluster,
+        getKerberosDetails(cluster, null),
+        commandParamsStage,
+        requestStageContainer,
+        new CreatePrincipalsAndKeytabsHandler(KerberosServerAction.OperationType.DEFAULT,
+            UpdateConfigurationPolicy.NONE,
+            false,
+            false));
   }
 
   @Override
@@ -3891,7 +3918,7 @@ public class KerberosHelperImpl implements KerberosHelper {
       Map<String, String> commandParameters = new HashMap<>();
       commandParameters.put(KerberosServerAction.AUTHENTICATED_USER_NAME, ambariManagementController.getAuthName());
       commandParameters.put(KerberosServerAction.UPDATE_CONFIGURATION_NOTE, "Enabling Kerberos");
-      commandParameters.put(KerberosServerAction.UPDATE_CONFIGURATIONS, "true");
+      commandParameters.put(KerberosServerAction.UPDATE_CONFIGURATION_POLICY, UpdateConfigurationPolicy.ALL.name());
       commandParameters.put(KerberosServerAction.DEFAULT_REALM, kerberosDetails.getDefaultRealm());
       commandParameters.put(KerberosServerAction.INCLUDE_AMBARI_IDENTITY, (kerberosDetails.createAmbariPrincipal()) ? "true" : "false");
       commandParameters.put(KerberosServerAction.PRECONFIGURE_SERVICES, kerberosDetails.getPreconfigureServices());
@@ -3987,7 +4014,7 @@ public class KerberosHelperImpl implements KerberosHelper {
       Map<String, String> commandParameters = new HashMap<>();
       commandParameters.put(KerberosServerAction.AUTHENTICATED_USER_NAME, ambariManagementController.getAuthName());
       commandParameters.put(KerberosServerAction.UPDATE_CONFIGURATION_NOTE, "Disabling Kerberos");
-      commandParameters.put(KerberosServerAction.UPDATE_CONFIGURATIONS, "true");
+      commandParameters.put(KerberosServerAction.UPDATE_CONFIGURATION_POLICY, UpdateConfigurationPolicy.ALL.name());
       commandParameters.put(KerberosServerAction.DEFAULT_REALM, kerberosDetails.getDefaultRealm());
       if (dataDirectory != null) {
         commandParameters.put(KerberosServerAction.DATA_DIRECTORY, dataDirectory.getAbsolutePath());
@@ -4084,10 +4111,9 @@ public class KerberosHelperImpl implements KerberosHelper {
     private KerberosServerAction.OperationType operationType;
 
     /**
-     * A boolean value indicating whether to update service configurations (<code>true</code>)
-     * or ignore any potential configuration changes (<code>false</code>).
+     * A UpdateConfigurationPolicy indicating how to handle configuration changes.
      */
-    private boolean updateConfigurations;
+    private UpdateConfigurationPolicy updateConfigurationPolicy;
 
     /**
      * A boolean value indicating whether to include all hosts (<code>true</code>) when setting up
@@ -4107,19 +4133,20 @@ public class KerberosHelperImpl implements KerberosHelper {
      * CreatePrincipalsAndKeytabsHandler constructor to set whether this instance should be used to
      * regenerate all keytabs or just the ones that have not been distributed
      *
-     * @param operationType         The type of Kerberos operation being performed
-     * @param updateConfigurations  A boolean value indicating whether to update service configurations
-     *                              (<code>true</code>) or ignore any potential configuration changes
-     * @param forceAllHosts         A boolean value indicating whether to include all hosts (<code>true</code>)
-     *                              when setting up agent-side tasks or to select only the hosts found to be
-     *                              relevant (<code>false</code>)
-     * @param includeAmbariIdentity A boolean value indicating whether to include Ambari server
-     *                              identity (<code>true</code>) or ignore it (<code>false</code>)
+     * @param operationType             The type of Kerberos operation being performed
+     * @param updateConfigurationPolicy The policy to use when updating configurations
+     *                                  (<code>true</code>) or ignore any potential configuration changes
+     * @param forceAllHosts             A boolean value indicating whether to include all hosts (<code>true</code>)
+     *                                  when setting up agent-side tasks or to select only the hosts found to be
+     *                                  relevant (<code>false</code>)
+     * @param includeAmbariIdentity     A boolean value indicating whether to include Ambari server
+     *                                  identity (<code>true</code>) or ignore it (<code>false</code>)
      */
-    CreatePrincipalsAndKeytabsHandler(KerberosServerAction.OperationType operationType, boolean updateConfigurations,
+    CreatePrincipalsAndKeytabsHandler(KerberosServerAction.OperationType operationType,
+                                      UpdateConfigurationPolicy updateConfigurationPolicy,
                                       boolean forceAllHosts, boolean includeAmbariIdentity) {
       this.operationType = operationType;
-      this.updateConfigurations = updateConfigurations;
+      this.updateConfigurationPolicy = updateConfigurationPolicy;
       this.forceAllHosts = forceAllHosts;
       this.includeAmbariIdentity = includeAmbariIdentity;
     }
@@ -4176,9 +4203,9 @@ public class KerberosHelperImpl implements KerberosHelper {
       commandParameters.put(KerberosServerAction.OPERATION_TYPE, (operationType == null) ? KerberosServerAction.OperationType.DEFAULT.name() : operationType.name());
       commandParameters.put(KerberosServerAction.INCLUDE_AMBARI_IDENTITY, (processAmbariIdentity) ? "true" : "false");
 
-      if (updateConfigurations) {
+      if (updateConfigurationPolicy != UpdateConfigurationPolicy.NONE) {
         commandParameters.put(KerberosServerAction.UPDATE_CONFIGURATION_NOTE, "Updated Kerberos-related configurations");
-        commandParameters.put(KerberosServerAction.UPDATE_CONFIGURATIONS, "true");
+        commandParameters.put(KerberosServerAction.UPDATE_CONFIGURATION_POLICY, updateConfigurationPolicy.name());
       }
 
       List<String> hostsToInclude = calculateHosts(cluster, serviceComponentHosts, hostsWithValidKerberosClient, forceAllHosts);
@@ -4219,7 +4246,7 @@ public class KerberosHelperImpl implements KerberosHelper {
           roleCommandOrder, requestStageContainer, hostsToInclude);
       }
 
-      if (updateConfigurations) {
+      if (updateConfigurationPolicy != UpdateConfigurationPolicy.NONE) {
         // *****************************************************************
         // Create stage to update configurations of services
         addUpdateConfigurationsStage(cluster, clusterHostInfoJson, hostParamsJson, event, commandParameters,
@@ -4353,6 +4380,7 @@ public class KerberosHelperImpl implements KerberosHelper {
         }
 
         commandParameters.put(KerberosServerAction.KDC_TYPE, kerberosDetails.getKdcType().name());
+        commandParameters.put(KerberosServerAction.UPDATE_CONFIGURATION_POLICY, UpdateConfigurationPolicy.ALL.name());
 
         // *****************************************************************
         // Create stage to create principals
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/UpdateConfigurationPolicy.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/UpdateConfigurationPolicy.java
new file mode 100644
index 0000000..52f3e58
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/UpdateConfigurationPolicy.java
@@ -0,0 +1,116 @@
+/*
+ * 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;
+
+/**
+ * The update configuration policies
+ * <ul>
+ * <li>NONE - No configurations will be updated</li>
+ * <li>IDENTITIES_ONLY - New and updated configurations related to Kerberos identity information - principal, keytab file, and auth-to-local rule properties</li>
+ * <li>NEW_AND_IDENTITIES - Only new configurations declared by the Kerberos descriptor and stack advisor as well as the identity-related changes</li>
+ * <li>ALL - All configuration changes (default)</li>
+ * </ul>
+ */
+public enum UpdateConfigurationPolicy {
+  /**
+   * No configurations will be updated
+   */
+  NONE(false, false, false, false),
+
+  /**
+   * New and updated configurations related to Kerberos identity information - principal, keytab
+   * file, and auth-to-local rule properties
+   */
+  IDENTITIES_ONLY(false, true, false, false),
+
+  /**
+   * Only new configurations declared by the Kerberos descriptor and stack advisor as well as the
+   * identity-related changes
+   */
+  NEW_AND_IDENTITIES(true, true, true, false),
+
+  /**
+   * All configuration changes (default)
+   */
+  ALL(true, true, true, true);
+
+  private final boolean invokeStackAdvisor;
+  private final boolean applyIdentityChanges;
+  private final boolean applyAdditions;
+  private final boolean applyOtherChanges;
+
+  UpdateConfigurationPolicy(boolean invokeStackAdvisor, boolean applyIdentityChanges, boolean applyAdditions, boolean applyOtherChanges) {
+    this.invokeStackAdvisor = invokeStackAdvisor;
+    this.applyIdentityChanges = applyIdentityChanges;
+    this.applyAdditions = applyAdditions;
+    this.applyOtherChanges = applyOtherChanges;
+  }
+
+  public boolean invokeStackAdvisor() {
+    return invokeStackAdvisor;
+  }
+
+  public boolean applyIdentityChanges() {
+    return applyIdentityChanges;
+  }
+
+  public boolean applyAdditions() {
+    return applyAdditions;
+  }
+
+  public boolean applyOtherChanges() {
+    return applyOtherChanges;
+  }
+
+  /**
+   * Safely translates a {@link UpdateConfigurationPolicy} value to a {@link String} of all
+   * lowercase characters.
+   *
+   * @param value the value to translate
+   * @return <code>null</code> if the input is <code>null</code>; otherwise the String value of
+   * the policy enum converted to lowercase characters.
+   */
+  public static String translate(UpdateConfigurationPolicy value) {
+    return (value == null) ? null : value.name().toLowerCase();
+  }
+
+  /**
+   * Safely translates a {@link String} value to an {@link UpdateConfigurationPolicy}.
+   * <p>
+   * The input value will be trimmed and converted to all uppercase characters.  If "-"'s are used
+   * instead of "_"'s, they will be converted.
+   *
+   * @param stringValue the String to translate
+   * @return The translated {@link UpdateConfigurationPolicy} value; or <code>null</code> if a translation cannot be made
+   */
+  public static UpdateConfigurationPolicy translate(String stringValue) {
+    if (stringValue != null) {
+      stringValue = stringValue.trim().toUpperCase();
+
+      if (!stringValue.isEmpty()) {
+        try {
+          return valueOf(stringValue.replace('-', '_'));
+        } catch (IllegalArgumentException e) {
+          // ignore this and return null later...
+        }
+      }
+    }
+
+    return null;
+  }
+}
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java
index 322c0f9..287e1a5 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java
@@ -52,6 +52,7 @@ import org.apache.ambari.server.controller.AmbariManagementController;
 import org.apache.ambari.server.controller.ExecuteCommandJson;
 import org.apache.ambari.server.controller.KerberosHelper;
 import org.apache.ambari.server.controller.KerberosHelperImpl.SupportedCustomOperation;
+import org.apache.ambari.server.controller.UpdateConfigurationPolicy;
 import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
 import org.apache.ambari.server.controller.spi.NoSuchResourceException;
 import org.apache.ambari.server.controller.spi.Predicate;
@@ -857,6 +858,7 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
               Map<String, String> requestProperties = new HashMap<>();
               requestProperties.put(SupportedCustomOperation.REGENERATE_KEYTABS.name().toLowerCase(), "missing");
               requestProperties.put(KerberosHelper.ALLOW_RETRY, Boolean.TRUE.toString().toLowerCase());
+              requestProperties.put(KerberosHelper.DIRECTIVE_CONFIG_UPDATE_POLICY, UpdateConfigurationPolicy.NEW_AND_IDENTITIES.name());
 
               // add stages to the upgrade which will regenerate missing keytabs only
               req = s_kerberosHelper.get().executeCustomOperations(cluster, requestProperties, req, null);
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/AbstractPrepareKerberosServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/AbstractPrepareKerberosServerAction.java
index bcace83..ce1d808 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/AbstractPrepareKerberosServerAction.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/AbstractPrepareKerberosServerAction.java
@@ -34,15 +34,19 @@ import org.apache.ambari.server.agent.CommandReport;
 import org.apache.ambari.server.controller.KerberosHelper;
 import org.apache.ambari.server.controller.RootComponent;
 import org.apache.ambari.server.controller.RootService;
+import org.apache.ambari.server.controller.UpdateConfigurationPolicy;
 import org.apache.ambari.server.serveraction.kerberos.stageutils.ResolvedKerberosKeytab;
 import org.apache.ambari.server.serveraction.kerberos.stageutils.ResolvedKerberosPrincipal;
 import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.ConfigHelper;
 import org.apache.ambari.server.state.ServiceComponentHost;
+import org.apache.ambari.server.state.kerberos.AbstractKerberosDescriptorContainer;
 import org.apache.ambari.server.state.kerberos.KerberosComponentDescriptor;
 import org.apache.ambari.server.state.kerberos.KerberosDescriptor;
 import org.apache.ambari.server.state.kerberos.KerberosIdentityDescriptor;
 import org.apache.ambari.server.state.kerberos.KerberosServiceDescriptor;
 import org.apache.ambari.server.utils.StageUtils;
+import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -64,6 +68,9 @@ public abstract class AbstractPrepareKerberosServerAction extends KerberosServer
   @Inject
   private KerberosConfigDataFileWriterFactory kerberosConfigDataFileWriterFactory;
 
+  @Inject
+  private ConfigHelper configHelper;
+
   @Override
   protected CommandReport processIdentity(ResolvedKerberosPrincipal resolvedPrincipal, KerberosOperationHandler operationHandler, Map<String, String> kerberosConfiguration, Map<String, Object> requestSharedDataContext) throws AmbariException {
     throw new UnsupportedOperationException();
@@ -74,12 +81,12 @@ public abstract class AbstractPrepareKerberosServerAction extends KerberosServer
   }
 
   public void processServiceComponentHosts(Cluster cluster, KerberosDescriptor kerberosDescriptor,
-                                    List<ServiceComponentHost> schToProcess,
-                                    Collection<String> identityFilter, String dataDirectory,
-                                    Map<String, Map<String, String>> currentConfigurations,
-                                    Map<String, Map<String, String>> kerberosConfigurations,
-                                    boolean includeAmbariIdentity,
-                                    Map<String, Set<String>> propertiesToBeIgnored) throws AmbariException {
+                                           List<ServiceComponentHost> schToProcess,
+                                           Collection<String> identityFilter, String dataDirectory,
+                                           Map<String, Map<String, String>> currentConfigurations,
+                                           Map<String, Map<String, String>> kerberosConfigurations,
+                                           boolean includeAmbariIdentity,
+                                           Map<String, Set<String>> propertiesToBeIgnored) throws AmbariException {
     List<Component> components = new ArrayList<>();
     for (ServiceComponentHost each : schToProcess) {
       components.add(Component.fromServiceComponentHost(each));
@@ -266,15 +273,19 @@ public abstract class AbstractPrepareKerberosServerAction extends KerberosServer
    * If work is to be done, a data file containing the details is created so it they changes may be
    * processed in the appropriate stage.
    *
-   * @param dataDirectory          the directory in which to write the configuration changes data file
-   * @param kerberosConfigurations the Kerberos-specific configuration map
-   * @param propertiesToBeRemoved  a map of properties to be removed from the current configuration,
-   *                               grouped by configuration type.
+   * @param dataDirectory             the directory in which to write the configuration changes data file
+   * @param kerberosConfigurations    the Kerberos-specific configuration map
+   * @param propertiesToBeRemoved     a map of properties to be removed from the current configuration,
+   *                                  grouped by configuration type.
+   * @param kerberosDescriptor        the Kerberos descriptor
+   * @param updateConfigurationPolicy the policy used to determine which configurations to update
    * @throws AmbariException
    */
   protected void processConfigurationChanges(String dataDirectory,
                                              Map<String, Map<String, String>> kerberosConfigurations,
-                                             Map<String, Set<String>> propertiesToBeRemoved)
+                                             Map<String, Set<String>> propertiesToBeRemoved,
+                                             KerberosDescriptor kerberosDescriptor,
+                                             UpdateConfigurationPolicy updateConfigurationPolicy)
       throws AmbariException {
     actionLog.writeStdOut("Determining configuration changes");
 
@@ -287,6 +298,12 @@ public abstract class AbstractPrepareKerberosServerAction extends KerberosServer
         throw new AmbariException(message);
       }
 
+      // Determine what the relevant Kerberos identity-related properties are...
+      Map<String, Set<String>> kerberosIdentityProperties = getIdentityProperties(kerberosDescriptor, null);
+
+      // Determine what the existing properties are...
+      Map<String, Map<String, String>> existingProperties = configHelper.getEffectiveConfigProperties(getClusterName(), null);
+
       File configFile = new File(dataDirectory, KerberosConfigDataFileWriter.DATA_FILE_NAME);
       KerberosConfigDataFileWriter kerberosConfDataFileWriter = null;
 
@@ -300,10 +317,15 @@ public abstract class AbstractPrepareKerberosServerAction extends KerberosServer
 
           if (properties != null) {
             for (Map.Entry<String, String> configTypeEntry : properties.entrySet()) {
-              kerberosConfDataFileWriter.addRecord(type,
-                  configTypeEntry.getKey(),
-                  configTypeEntry.getValue(),
-                  KerberosConfigDataFileWriter.OPERATION_TYPE_SET);
+
+              // Determine if this configuration should be written or not...
+              String propertyName = configTypeEntry.getKey();
+              if (includeConfiguration(type, propertyName, updateConfigurationPolicy, existingProperties, kerberosIdentityProperties)) {
+                kerberosConfDataFileWriter.addRecord(type,
+                    propertyName,
+                    configTypeEntry.getValue(),
+                    KerberosConfigDataFileWriter.OPERATION_TYPE_SET);
+              }
             }
           }
         }
@@ -343,4 +365,120 @@ public abstract class AbstractPrepareKerberosServerAction extends KerberosServer
       }
     }
   }
+
+  /**
+   * Determine of the configuration should be included in the set of configurations to update.
+   *
+   * @param configType
+   * @param propertyName
+   * @param updateConfigurationPolicy
+   * @param existingProperties
+   * @param kerberosIdentityProperties
+   * @return
+   */
+  private boolean includeConfiguration(String configType, String propertyName,
+                                       UpdateConfigurationPolicy updateConfigurationPolicy,
+                                       Map<String, Map<String, String>> existingProperties,
+                                       Map<String, Set<String>> kerberosIdentityProperties) {
+
+    // Determine if the property represents a Kerberos identity-related property
+    boolean isIdentity;
+    if (kerberosIdentityProperties == null) {
+      isIdentity = false;
+    } else {
+      Set<String> propertyNames = kerberosIdentityProperties.get(configType);
+      isIdentity = !CollectionUtils.isEmpty(propertyNames) && propertyNames.contains(propertyName);
+    }
+
+    if (isIdentity) {
+      return updateConfigurationPolicy.applyIdentityChanges();
+    }
+
+    // Determine if the property is a new property
+    boolean isNew;
+    if (existingProperties == null) {
+      isNew = true;
+    } else {
+      Map<String, String> propertyNames = existingProperties.get(configType);
+      isNew = (propertyNames == null) || !propertyNames.containsKey(propertyName);
+    }
+
+    if (isNew) {
+      return updateConfigurationPolicy.applyAdditions();
+    }
+
+    // All other properties...
+    return updateConfigurationPolicy.applyOtherChanges();
+  }
+
+  /**
+   * Recursively processes a Kerberos descriptor container and it children to find the
+   * Kerberos identity-related properties.
+   * <p>
+   * Kerberos identity-related properties are those that contain the following information:
+   * <ul>
+   * <li>principal names</li>
+   * <li>keytab file paths</li>
+   * <li>auth-to-local rules</li>
+   * </ul>
+   *
+   * @param container          the AbstractKerberosDescriptorContainer to process
+   * @param identityProperties a map of config-types to sets of property names to append data
+   * @return a map of config-types to sets of property names
+   */
+  private Map<String, Set<String>> getIdentityProperties(AbstractKerberosDescriptorContainer container, Map<String, Set<String>> identityProperties) {
+    if (container != null) {
+      if (identityProperties == null) {
+        identityProperties = new HashMap<>();
+      }
+
+      // Process the Kerberos identities - principal and keytab file properties.
+      List<KerberosIdentityDescriptor> identityDescriptors;
+      try {
+        // There is no need to resolve references since we just need to get the set of configurations that can be changed.
+        identityDescriptors = container.getIdentities(false, null);
+      } catch (AmbariException e) {
+        LOG.error("An exception occurred getting the Kerberos identity descriptors.  No configurations will be identified.", e);
+        identityDescriptors = null;
+      }
+
+      if (identityDescriptors != null) {
+        Map<String, Map<String, String>> identityConfigurations = kerberosHelper.getIdentityConfigurations(identityDescriptors);
+
+        if (identityConfigurations != null) {
+          for (Map.Entry<String, Map<String, String>> entry : identityConfigurations.entrySet()) {
+            Map<String, String> properties = entry.getValue();
+            if (properties != null) {
+              Set<String> configProperties = identityProperties.computeIfAbsent(entry.getKey(), k -> new HashSet<>());
+              configProperties.addAll(properties.keySet());
+            }
+          }
+        }
+      }
+
+      // Process any auth-to-local rule properties
+      Map<String, Set<String>> authToLocalProperties = kerberosHelper.translateConfigurationSpecifications(container.getAuthToLocalProperties());
+      if (authToLocalProperties != null) {
+        for (Map.Entry<String, Set<String>> entry : authToLocalProperties.entrySet()) {
+          String configType = entry.getKey();
+          Set<String> propertyNames = entry.getValue();
+
+          if (propertyNames != null) {
+            Set<String> configProperties = identityProperties.computeIfAbsent(configType, k -> new HashSet<>());
+            configProperties.addAll(propertyNames);
+          }
+        }
+      }
+
+      // Process the children...
+      Collection<? extends AbstractKerberosDescriptorContainer> childContainers = container.getChildContainers();
+      if (childContainers != null) {
+        for (AbstractKerberosDescriptorContainer childContainer : childContainers) {
+          getIdentityProperties(childContainer, identityProperties);
+        }
+      }
+    }
+
+    return identityProperties;
+  }
 }
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 2c9aa8c..904fd01 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
@@ -31,6 +31,7 @@ import org.apache.ambari.server.actionmanager.HostRoleStatus;
 import org.apache.ambari.server.agent.CommandReport;
 import org.apache.ambari.server.agent.ExecutionCommand;
 import org.apache.ambari.server.controller.KerberosHelper;
+import org.apache.ambari.server.controller.UpdateConfigurationPolicy;
 import org.apache.ambari.server.orm.dao.HostDAO;
 import org.apache.ambari.server.orm.entities.HostEntity;
 import org.apache.ambari.server.security.credential.PrincipalKeyCredential;
@@ -100,10 +101,20 @@ public abstract class KerberosServerAction extends AbstractServerAction {
   public static final String KDC_TYPE = "kdc_type";
 
   /**
-   * A (command parameter) property name used to hold a boolean value indicating whether configurations
-   * should be process to see if they need to be updated
+   * A (command parameter) property name used to hold the value indicating how to process
+   * configurations updates. One of the of the following values is expected:
+   * <dl>
+   * <dt>none</dt>
+   * <dd>No configurations will be updated</dd>
+   * <dt>identities_only</dt>
+   * <dd>New and updated configurations related to Kerberos identity information - principal, keytab file, and auth-to-local rule properties</dd>
+   * <dt>new_and_identities</dt>
+   * <dd>Only new configurations declared by the Kerberos descriptor and stack advisor as well as the identity-related changes</dd>
+   * <dt>all</dt>
+   * <dd>All configuration changes (default)</dd>
+   * </dl>
    */
-  public static final String UPDATE_CONFIGURATIONS = "update_configurations";
+  public static final String UPDATE_CONFIGURATION_POLICY = "update_configuration_policy";
 
   /**
    * A (command parameter) property name used to hold the note to set when applying any
@@ -197,6 +208,20 @@ public abstract class KerberosServerAction extends AbstractServerAction {
   }
 
   /**
+   * Given a (command parameter) Map, attempts to safely retrieve the "update_configuration_policy" property.
+   *
+   * @param commandParameters a Map containing the dictionary of data to interrogate
+   * @return a UpdateConfigurationPolicy
+   */
+  protected static UpdateConfigurationPolicy getUpdateConfigurationPolicy(Map<String, String> commandParameters) {
+    String stringValue = getCommandParameterValue(commandParameters, UPDATE_CONFIGURATION_POLICY);
+    UpdateConfigurationPolicy value = UpdateConfigurationPolicy.translate(stringValue);
+
+    // Return UpdateConfigurationPolicy.ALL as a default value
+    return (value == null) ? UpdateConfigurationPolicy.ALL : value;
+  }
+
+  /**
    * Given a (command parameter) Map, attempts to safely retrieve the "default_realm" property.
    *
    * @param commandParameters a Map containing the dictionary of data to interrogate
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareDisableKerberosServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareDisableKerberosServerAction.java
index b9381b4..80b901b 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareDisableKerberosServerAction.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareDisableKerberosServerAction.java
@@ -203,7 +203,7 @@ public class PrepareDisableKerberosServerAction extends AbstractPrepareKerberosS
       kerberosHelper.applyStackAdvisorUpdates(cluster, services, configurations, kerberosConfigurations,
           propertiesToIgnore, configurationsToRemove, false);
 
-      processConfigurationChanges(dataDirectory, kerberosConfigurations, configurationsToRemove);
+      processConfigurationChanges(dataDirectory, kerberosConfigurations, configurationsToRemove, kerberosDescriptor, getUpdateConfigurationPolicy(commandParameters));
     }
 
     return createCommandReport(0, HostRoleStatus.COMPLETED, "{}", actionLog.getStdOut(), actionLog.getStdErr());
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareEnableKerberosServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareEnableKerberosServerAction.java
index 2d29bdc..f9b9717 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareEnableKerberosServerAction.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareEnableKerberosServerAction.java
@@ -128,7 +128,7 @@ public class PrepareEnableKerberosServerAction extends PrepareKerberosIdentities
     }
     clusterEnvProperties.put(KerberosHelper.SECURITY_ENABLED_PROPERTY_NAME, "true");
 
-    processConfigurationChanges(dataDirectory, kerberosConfigurations, propertiesToRemove);
+    processConfigurationChanges(dataDirectory, kerberosConfigurations, propertiesToRemove, kerberosDescriptor, getUpdateConfigurationPolicy(commandParameters));
 
     return createCommandReport(0, HostRoleStatus.COMPLETED, "{}", actionLog.getStdOut(), actionLog.getStdErr());
   }
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareKerberosIdentitiesServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareKerberosIdentitiesServerAction.java
index c7f2003..b6638fa 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareKerberosIdentitiesServerAction.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/PrepareKerberosIdentitiesServerAction.java
@@ -32,6 +32,7 @@ import org.apache.ambari.server.agent.CommandReport;
 import org.apache.ambari.server.controller.KerberosHelper;
 import org.apache.ambari.server.controller.RootComponent;
 import org.apache.ambari.server.controller.RootService;
+import org.apache.ambari.server.controller.UpdateConfigurationPolicy;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.ServiceComponentHost;
 import org.apache.ambari.server.state.kerberos.KerberosComponentDescriptor;
@@ -113,9 +114,9 @@ public class PrepareKerberosIdentitiesServerAction extends AbstractPrepareKerber
       // If we are including the Ambari identity; then ensure that if a service/component filter is set,
       // it contains the AMBARI/AMBARI_SERVER component; else do not include the Ambari service identity.
       includeAmbariIdentity &= (serviceComponentFilter.get(RootService.AMBARI.name()) != null)
-        && serviceComponentFilter.get(RootService.AMBARI.name()).contains(RootComponent.AMBARI_SERVER.name());
+          && serviceComponentFilter.get(RootService.AMBARI.name()).contains(RootComponent.AMBARI_SERVER.name());
 
-      if((operationType != OperationType.DEFAULT)) {
+      if ((operationType != OperationType.DEFAULT)) {
         // Update the identity filter, if necessary
         identityFilter = updateIdentityFilter(kerberosDescriptor, identityFilter, serviceComponentFilter);
       }
@@ -128,13 +129,22 @@ public class PrepareKerberosIdentitiesServerAction extends AbstractPrepareKerber
     processServiceComponentHosts(cluster, kerberosDescriptor, schToProcess, identityFilter, dataDirectory,
         configurations, kerberosConfigurations, includeAmbariIdentity, propertiesToIgnore);
 
-    kerberosHelper.applyStackAdvisorUpdates(cluster, services, configurations, kerberosConfigurations,
-        propertiesToIgnore, propertiesToRemove, true);
+    UpdateConfigurationPolicy updateConfigurationPolicy = getUpdateConfigurationPolicy(commandParameters);
 
-    if ("true".equalsIgnoreCase(getCommandParameterValue(commandParameters, UPDATE_CONFIGURATIONS))) {
+    if (updateConfigurationPolicy != UpdateConfigurationPolicy.NONE) {
+      if (updateConfigurationPolicy.invokeStackAdvisor()) {
+        kerberosHelper.applyStackAdvisorUpdates(cluster, services, configurations, kerberosConfigurations,
+            propertiesToIgnore, propertiesToRemove, true);
+      }
+
+      // TODO: Determine if we need to do this again since it is done a few lines above.
       Map<String, Map<String, String>> calculatedConfigurations = kerberosHelper.calculateConfigurations(cluster, null, kerberosDescriptor, false, false);
-      processAuthToLocalRules(cluster, calculatedConfigurations, kerberosDescriptor, schToProcess, kerberosConfigurations, getDefaultRealm(commandParameters), false);
-      processConfigurationChanges(dataDirectory, kerberosConfigurations, propertiesToRemove);
+
+      if (updateConfigurationPolicy.applyIdentityChanges()) {
+        processAuthToLocalRules(cluster, calculatedConfigurations, kerberosDescriptor, schToProcess, kerberosConfigurations, getDefaultRealm(commandParameters), false);
+      }
+
+      processConfigurationChanges(dataDirectory, kerberosConfigurations, propertiesToRemove, kerberosDescriptor, updateConfigurationPolicy);
     }
 
     return createCommandReport(0, HostRoleStatus.COMPLETED, "{}", actionLog.getStdOut(), actionLog.getStdErr());
@@ -247,7 +257,8 @@ public class PrepareKerberosIdentitiesServerAction extends AbstractPrepareKerber
   /**
    * Add the path of each identity in the collection of identities to the supplied identity filter
    * if that identity is not a reference to another identity or if references are allowed.
-   *  @param identityDescriptors the collection of identity descriptors to process
+   *
+   * @param identityDescriptors the collection of identity descriptors to process
    * @param identityFilter      the identity filter to modify
    * @param skipReferences
    */
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UpgradeResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UpgradeResourceProviderTest.java
index def2a9c..40ee9d8 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UpgradeResourceProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UpgradeResourceProviderTest.java
@@ -58,7 +58,9 @@ import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.controller.AmbariManagementController;
 import org.apache.ambari.server.controller.AmbariServer;
 import org.apache.ambari.server.controller.KerberosHelper;
+import org.apache.ambari.server.controller.KerberosHelperImpl;
 import org.apache.ambari.server.controller.ResourceProviderFactory;
+import org.apache.ambari.server.controller.UpdateConfigurationPolicy;
 import org.apache.ambari.server.controller.spi.Predicate;
 import org.apache.ambari.server.controller.spi.Request;
 import org.apache.ambari.server.controller.spi.RequestStatus;
@@ -120,6 +122,7 @@ import org.apache.ambari.server.view.ViewRegistry;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.easymock.Capture;
 import org.easymock.EasyMock;
 import org.easymock.EasyMockSupport;
 import org.junit.After;
@@ -188,7 +191,7 @@ public class UpgradeResourceProviderTest extends EasyMockSupport {
     expect(
         configHelper.getDefaultProperties(EasyMock.anyObject(StackId.class),
             EasyMock.anyString())).andReturn(
-      new HashMap<>()).anyTimes();
+        new HashMap<>()).anyTimes();
 
     expect(
         configHelper.getChangedConfigTypes(EasyMock.anyObject(Cluster.class), EasyMock.anyObject(ServiceConfigEntity.class),
@@ -322,10 +325,11 @@ public class UpgradeResourceProviderTest extends EasyMockSupport {
 
   /**
    * Obtain request id from the {@code RequestStatus}
+   *
    * @param requestStatus reqult of the {@code createResources}
    * @return id of the request
    */
-  private long getRequestId(RequestStatus requestStatus){
+  private long getRequestId(RequestStatus requestStatus) {
     assertEquals(1, requestStatus.getAssociatedResources().size());
     Resource r = requestStatus.getAssociatedResources().iterator().next();
     String id = r.getPropertyValue("Upgrade/request_id").toString();
@@ -507,9 +511,9 @@ public class UpgradeResourceProviderTest extends EasyMockSupport {
     propertyIds.add("Upgrade");
 
     Predicate predicate = new PredicateBuilder()
-      .property(UpgradeResourceProvider.UPGRADE_REQUEST_ID).equals("1").and()
-      .property(UpgradeResourceProvider.UPGRADE_CLUSTER_NAME).equals("c1")
-      .toPredicate();
+        .property(UpgradeResourceProvider.UPGRADE_REQUEST_ID).equals("1").and()
+        .property(UpgradeResourceProvider.UPGRADE_CLUSTER_NAME).equals("c1")
+        .toPredicate();
     Request request = PropertyHelper.getReadRequest(propertyIds);
 
     ResourceProvider upgradeResourceProvider = createProvider(amc);
@@ -529,9 +533,9 @@ public class UpgradeResourceProviderTest extends EasyMockSupport {
     propertyIds.add("UpgradeGroup");
 
     predicate = new PredicateBuilder()
-      .property(UpgradeGroupResourceProvider.UPGRADE_REQUEST_ID).equals("1").and()
-      .property(UpgradeGroupResourceProvider.UPGRADE_CLUSTER_NAME).equals("c1")
-      .toPredicate();
+        .property(UpgradeGroupResourceProvider.UPGRADE_REQUEST_ID).equals("1").and()
+        .property(UpgradeGroupResourceProvider.UPGRADE_CLUSTER_NAME).equals("c1")
+        .toPredicate();
     request = PropertyHelper.getReadRequest(propertyIds);
 
     ResourceProvider upgradeGroupResourceProvider = new UpgradeGroupResourceProvider(amc);
@@ -550,10 +554,10 @@ public class UpgradeResourceProviderTest extends EasyMockSupport {
     propertyIds.add("UpgradeItem");
 
     predicate = new PredicateBuilder()
-      .property(UpgradeItemResourceProvider.UPGRADE_GROUP_ID).equals("1").and()
-      .property(UpgradeItemResourceProvider.UPGRADE_REQUEST_ID).equals("1").and()
-      .property(UpgradeItemResourceProvider.UPGRADE_CLUSTER_NAME).equals("c1")
-      .toPredicate();
+        .property(UpgradeItemResourceProvider.UPGRADE_GROUP_ID).equals("1").and()
+        .property(UpgradeItemResourceProvider.UPGRADE_REQUEST_ID).equals("1").and()
+        .property(UpgradeItemResourceProvider.UPGRADE_CLUSTER_NAME).equals("c1")
+        .toPredicate();
     request = PropertyHelper.getReadRequest(propertyIds);
 
     ResourceProvider upgradeItemResourceProvider = new UpgradeItemResourceProvider(amc);
@@ -569,10 +573,10 @@ public class UpgradeResourceProviderTest extends EasyMockSupport {
     propertyIds.add("UpgradeItem");
 
     predicate = new PredicateBuilder()
-      .property(UpgradeItemResourceProvider.UPGRADE_GROUP_ID).equals("3").and()
-      .property(UpgradeItemResourceProvider.UPGRADE_REQUEST_ID).equals("1").and()
-      .property(UpgradeItemResourceProvider.UPGRADE_CLUSTER_NAME).equals("c1")
-      .toPredicate();
+        .property(UpgradeItemResourceProvider.UPGRADE_GROUP_ID).equals("3").and()
+        .property(UpgradeItemResourceProvider.UPGRADE_REQUEST_ID).equals("1").and()
+        .property(UpgradeItemResourceProvider.UPGRADE_CLUSTER_NAME).equals("c1")
+        .toPredicate();
     request = PropertyHelper.getReadRequest(propertyIds);
 
     upgradeItemResourceProvider = new UpgradeItemResourceProvider(amc);
@@ -622,9 +626,9 @@ public class UpgradeResourceProviderTest extends EasyMockSupport {
     propertyIds.add("Upgrade");
 
     Predicate predicate = new PredicateBuilder()
-      .property(UpgradeResourceProvider.UPGRADE_REQUEST_ID).equals("1").and()
-      .property(UpgradeResourceProvider.UPGRADE_CLUSTER_NAME).equals("c1")
-      .toPredicate();
+        .property(UpgradeResourceProvider.UPGRADE_REQUEST_ID).equals("1").and()
+        .property(UpgradeResourceProvider.UPGRADE_CLUSTER_NAME).equals("c1")
+        .toPredicate();
 
     request = PropertyHelper.getReadRequest(propertyIds);
     Set<Resource> resources = upgradeResourceProvider.getResources(request, predicate);
@@ -812,7 +816,6 @@ public class UpgradeResourceProviderTest extends EasyMockSupport {
   }
 
 
-
   /**
    * Test Downgrade from the partially completed upgrade
    */
@@ -851,10 +854,10 @@ public class UpgradeResourceProviderTest extends EasyMockSupport {
     boolean isZKGroupFound = false;
 
     // look only for testing groups
-    for (UpgradeGroupEntity group: groups) {
+    for (UpgradeGroupEntity group : groups) {
       if (group.getName().equalsIgnoreCase("hive")) {
         isHiveGroupFound = true;
-      } else if (group.getName().equalsIgnoreCase("zookeeper")){
+      } else if (group.getName().equalsIgnoreCase("zookeeper")) {
         isZKGroupFound = true;
       }
     }
@@ -883,10 +886,10 @@ public class UpgradeResourceProviderTest extends EasyMockSupport {
     RequestStatus status = upgradeResourceProvider.createResources(request);
     UpgradeEntity upgradeEntity = upgradeDao.findUpgradeByRequestId(getRequestId(status));
 
-    for (UpgradeGroupEntity group: upgradeEntity.getUpgradeGroups()) {
+    for (UpgradeGroupEntity group : upgradeEntity.getUpgradeGroups()) {
       if (group.getName().equalsIgnoreCase("hive")) {
         isHiveGroupFound = true;
-      } else if (group.getName().equalsIgnoreCase("zookeeper")){
+      } else if (group.getName().equalsIgnoreCase("zookeeper")) {
         isZKGroupFound = true;
       }
     }
@@ -1141,7 +1144,6 @@ public class UpgradeResourceProviderTest extends EasyMockSupport {
   }
 
 
-
   @Test
   public void testPercents() throws Exception {
     RequestStatus status = testCreateResources();
@@ -1291,11 +1293,11 @@ public class UpgradeResourceProviderTest extends EasyMockSupport {
     List<StageEntity> stageEntities = stageDAO.findByRequestId(entity.getRequestId());
     Gson gson = new Gson();
     for (StageEntity se : stageEntities) {
-      Map<String, String> map = gson.<Map<String, String>> fromJson(se.getCommandParamsStage(),Map.class);
+      Map<String, String> map = gson.<Map<String, String>>fromJson(se.getCommandParamsStage(), Map.class);
       assertTrue(map.containsKey("upgrade_direction"));
       assertEquals("upgrade", map.get("upgrade_direction"));
 
-      if(map.containsKey("upgrade_type")){
+      if (map.containsKey("upgrade_type")) {
         assertEquals("rolling_upgrade", map.get("upgrade_type"));
       }
     }
@@ -1541,7 +1543,7 @@ public class UpgradeResourceProviderTest extends EasyMockSupport {
     try {
       upgradeResourceProvider.createResources(request);
       Assert.fail("The request should have failed due to the missing Upgrade/host_order property");
-    } catch( SystemException systemException ){
+    } catch (SystemException systemException) {
       // expected
     }
 
@@ -1679,7 +1681,7 @@ public class UpgradeResourceProviderTest extends EasyMockSupport {
     requestProps.put(UpgradeResourceProvider.UPGRADE_REPO_VERSION_ID, String.valueOf(repoVersionEntity2200.getId()));
     requestProps.put(UpgradeResourceProvider.UPGRADE_PACK, "upgrade_test_host_ordered");
     requestProps.put(UpgradeResourceProvider.UPGRADE_TYPE, UpgradeType.HOST_ORDERED.toString());
-    requestProps.put(UpgradeResourceProvider.UPGRADE_SKIP_PREREQUISITE_CHECKS,Boolean.TRUE.toString());
+    requestProps.put(UpgradeResourceProvider.UPGRADE_SKIP_PREREQUISITE_CHECKS, Boolean.TRUE.toString());
     requestProps.put(UpgradeResourceProvider.UPGRADE_DIRECTION, Direction.UPGRADE.name());
     requestProps.put(UpgradeResourceProvider.UPGRADE_HOST_ORDERED_HOSTS, hostsOrder);
 
@@ -1726,8 +1728,8 @@ public class UpgradeResourceProviderTest extends EasyMockSupport {
     List<UpgradeHistoryEntity> histories = upgrade.getHistory();
     assertEquals(2, histories.size());
 
-    for( UpgradeHistoryEntity history : histories){
-      assertEquals( "ZOOKEEPER", history.getServiceName() );
+    for (UpgradeHistoryEntity history : histories) {
+      assertEquals("ZOOKEEPER", history.getServiceName());
       assertEquals(repoVersionEntity2110, history.getFromReposistoryVersion());
       assertEquals(repoVersionEntity2200, history.getTargetRepositoryVersion());
     }
@@ -1830,7 +1832,7 @@ public class UpgradeResourceProviderTest extends EasyMockSupport {
       @Override
       public String apply(UpgradeHistoryEntity input) {
         return input.getServiceName() + "/" + input.getComponentName();
-      };
+      }
     };
 
     for (UpgradeEntity upgrade : upgrades) {
@@ -1874,7 +1876,7 @@ public class UpgradeResourceProviderTest extends EasyMockSupport {
 
     Map<String, Object> requestProps = new HashMap<>();
     requestProps.put(UpgradeResourceProvider.UPGRADE_CLUSTER_NAME, "c1");
-    requestProps.put(UpgradeResourceProvider.UPGRADE_REPO_VERSION_ID,String.valueOf(repoVersionEntity2112.getId()));
+    requestProps.put(UpgradeResourceProvider.UPGRADE_REPO_VERSION_ID, String.valueOf(repoVersionEntity2112.getId()));
     requestProps.put(UpgradeResourceProvider.UPGRADE_PACK, "upgrade_test");
     requestProps.put(UpgradeResourceProvider.UPGRADE_SKIP_PREREQUISITE_CHECKS, "true");
     requestProps.put(UpgradeResourceProvider.UPGRADE_DIRECTION, Direction.UPGRADE.name());
@@ -1984,7 +1986,6 @@ public class UpgradeResourceProviderTest extends EasyMockSupport {
     assertTrue(foundConfigTask);
 
 
-
     // !!! test that a regular upgrade will pick up the config change
     cluster.setUpgradeEntity(null);
     repoVersionEntity2112.setType(RepositoryType.STANDARD);
@@ -2017,8 +2018,7 @@ public class UpgradeResourceProviderTest extends EasyMockSupport {
   }
 
 
-
-  private String parseSingleMessage(String msgStr){
+  private String parseSingleMessage(String msgStr) {
     JsonParser parser = new JsonParser();
     JsonArray msgArray = (JsonArray) parser.parse(msgStr);
     JsonObject msg = (JsonObject) msgArray.get(0);
@@ -2110,7 +2110,7 @@ public class UpgradeResourceProviderTest extends EasyMockSupport {
       if (command.getRole().equals(Role.ZOOKEEPER_SERVER) && command.getRoleCommand().equals(RoleCommand.CUSTOM_COMMAND)) {
         Map<String, String> commandParams = wrapper.getExecutionCommand().getCommandParams();
         assertTrue(commandParams.containsKey(KeyNames.COMMAND_TIMEOUT));
-        assertEquals("824",commandParams.get(KeyNames.COMMAND_TIMEOUT));
+        assertEquals("824", commandParams.get(KeyNames.COMMAND_TIMEOUT));
         found = true;
       }
     }
@@ -2178,6 +2178,8 @@ public class UpgradeResourceProviderTest extends EasyMockSupport {
    */
   @Test
   public void testCreateRegenerateKeytabStages() throws Exception {
+    Capture<Map<String, String>> requestPropertyMapCapture = EasyMock.newCapture();
+
     Map<String, Object> requestProps = new HashMap<>();
     requestProps.put(UpgradeResourceProvider.UPGRADE_CLUSTER_NAME, "c1");
     requestProps.put(UpgradeResourceProvider.UPGRADE_REPO_VERSION_ID, String.valueOf(repoVersionEntity2200.getId()));
@@ -2190,9 +2192,9 @@ public class UpgradeResourceProviderTest extends EasyMockSupport {
     RequestStageContainer requestStageContainer = createNiceMock(RequestStageContainer.class);
     expect(requestStageContainer.getStages()).andReturn(Lists.newArrayList()).once();
 
-    expect(kerberosHelperMock.executeCustomOperations(eq(cluster), EasyMock.anyObject(),
+    expect(kerberosHelperMock.executeCustomOperations(eq(cluster), EasyMock.capture(requestPropertyMapCapture),
         EasyMock.anyObject(RequestStageContainer.class), eq(null))).andReturn(
-            requestStageContainer).once();
+        requestStageContainer).once();
 
     replayAll();
 
@@ -2207,6 +2209,11 @@ public class UpgradeResourceProviderTest extends EasyMockSupport {
     }
 
     verifyAll();
+
+    Map<String, String> requestPropertyMap = requestPropertyMapCapture.getValue();
+    assertEquals("true", requestPropertyMap.get(KerberosHelper.ALLOW_RETRY));
+    assertEquals("missing", requestPropertyMap.get(KerberosHelperImpl.SupportedCustomOperation.REGENERATE_KEYTABS.name().toLowerCase()));
+    assertEquals(UpdateConfigurationPolicy.NEW_AND_IDENTITIES.name(), requestPropertyMap.get(KerberosHelper.DIRECTIVE_CONFIG_UPDATE_POLICY.toLowerCase()));
   }
 
   /**
@@ -2214,8 +2221,8 @@ public class UpgradeResourceProviderTest extends EasyMockSupport {
    */
   private class MockModule implements Module {
     /**
-   *
-   */
+     *
+     */
     @Override
     public void configure(Binder binder) {
       binder.bind(ConfigHelper.class).toInstance(configHelper);
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/AbstractPrepareKerberosServerActionTest.java b/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/AbstractPrepareKerberosServerActionTest.java
index d580e6a..47ccef5 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/AbstractPrepareKerberosServerActionTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/AbstractPrepareKerberosServerActionTest.java
@@ -20,14 +20,17 @@ package org.apache.ambari.server.serveraction.kerberos;
 
 import static org.easymock.EasyMock.anyBoolean;
 import static org.easymock.EasyMock.anyObject;
-import static org.easymock.EasyMock.createNiceMock;
+import static org.easymock.EasyMock.capture;
+import static org.easymock.EasyMock.eq;
 import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.replay;
-import static org.easymock.EasyMock.verify;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.newCapture;
 
 import java.io.File;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -36,78 +39,215 @@ import java.util.concurrent.ConcurrentMap;
 
 import javax.persistence.EntityManager;
 
-import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.actionmanager.ActionDBAccessor;
+import org.apache.ambari.server.actionmanager.ActionManager;
+import org.apache.ambari.server.actionmanager.HostRoleCommandFactory;
+import org.apache.ambari.server.actionmanager.RequestFactory;
+import org.apache.ambari.server.actionmanager.StageFactory;
 import org.apache.ambari.server.agent.CommandReport;
+import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.audit.AuditLogger;
+import org.apache.ambari.server.controller.AbstractRootServiceResponseFactory;
+import org.apache.ambari.server.controller.AmbariManagementController;
 import org.apache.ambari.server.controller.KerberosHelper;
+import org.apache.ambari.server.controller.KerberosHelperImpl;
+import org.apache.ambari.server.controller.UpdateConfigurationPolicy;
+import org.apache.ambari.server.hooks.HookContextFactory;
+import org.apache.ambari.server.hooks.HookService;
+import org.apache.ambari.server.metadata.RoleCommandOrderProvider;
+import org.apache.ambari.server.orm.dao.HostRoleCommandDAO;
+import org.apache.ambari.server.scheduler.ExecutionScheduler;
+import org.apache.ambari.server.security.encryption.CredentialStoreService;
+import org.apache.ambari.server.stack.StackManagerFactory;
+import org.apache.ambari.server.stageplanner.RoleGraphFactory;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.ConfigFactory;
+import org.apache.ambari.server.state.ConfigHelper;
 import org.apache.ambari.server.state.Host;
 import org.apache.ambari.server.state.Service;
+import org.apache.ambari.server.state.ServiceComponentFactory;
 import org.apache.ambari.server.state.ServiceComponentHost;
+import org.apache.ambari.server.state.ServiceComponentHostFactory;
+import org.apache.ambari.server.state.configgroup.ConfigGroupFactory;
 import org.apache.ambari.server.state.kerberos.KerberosComponentDescriptor;
 import org.apache.ambari.server.state.kerberos.KerberosDescriptor;
+import org.apache.ambari.server.state.kerberos.KerberosDescriptorFactory;
 import org.apache.ambari.server.state.kerberos.KerberosServiceDescriptor;
-import org.easymock.EasyMock;
+import org.apache.ambari.server.state.scheduler.RequestExecutionFactory;
+import org.apache.ambari.server.state.stack.OsFamily;
+import org.apache.ambari.server.topology.PersistedState;
+import org.apache.ambari.server.topology.tasks.ConfigureClusterTaskFactory;
+import org.easymock.Capture;
+import org.easymock.CaptureType;
+import org.easymock.EasyMockSupport;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
+import org.springframework.security.crypto.password.PasswordEncoder;
 
 import com.google.inject.AbstractModule;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.google.inject.Provider;
 
-public class AbstractPrepareKerberosServerActionTest {
-  private class PrepareKerberosServerAction extends AbstractPrepareKerberosServerAction{
+public class AbstractPrepareKerberosServerActionTest extends EasyMockSupport {
+  private static final String KERBEROS_DESCRIPTOR_JSON = "" +
+      "{" +
+      "  \"identities\": [" +
+      "    {" +
+      "      \"keytab\": {" +
+      "        \"file\": \"${keytab_dir}/spnego.service.keytab\"," +
+      "        \"group\": {" +
+      "          \"access\": \"r\"," +
+      "          \"name\": \"${cluster-env/user_group}\"" +
+      "        }," +
+      "        \"owner\": {" +
+      "          \"access\": \"r\"," +
+      "          \"name\": \"root\"" +
+      "        }" +
+      "      }," +
+      "      \"name\": \"spnego\"," +
+      "      \"principal\": {" +
+      "        \"configuration\": null," +
+      "        \"local_username\": null," +
+      "        \"type\": \"service\"," +
+      "        \"value\": \"HTTP/_HOST@${realm}\"" +
+      "      }" +
+      "    }" +
+      "  ]," +
+      "  \"services\": [" +
+      "    {" +
+      "      \"components\": [" +
+      "        {" +
+      "          \"identities\": [" +
+      "            {" +
+      "              \"name\": \"service_master_spnego_identity\"," +
+      "              \"reference\": \"/spnego\"" +
+      "            }" +
+      "          ]," +
+      "          \"name\": \"SERVICE_MASTER\"" +
+      "        }" +
+      "      ]," +
+      "      \"configurations\": [" +
+      "        {" +
+      "          \"service-site\": {" +
+      "            \"property1\": \"property1_updated_value\"," +
+      "            \"property2\": \"property2_updated_value\"" +
+      "          }" +
+      "        }" +
+      "      ]," +
+      "      \"identities\": [" +
+      "        {" +
+      "          \"name\": \"service_identity\"," +
+      "          \"keytab\": {" +
+      "            \"configuration\": \"service-site/keytab_file_path\"," +
+      "            \"file\": \"${keytab_dir}/service.service.keytab\"," +
+      "            \"group\": {" +
+      "              \"access\": \"r\"," +
+      "              \"name\": \"${cluster-env/user_group}\"" +
+      "            }," +
+      "            \"owner\": {" +
+      "              \"access\": \"r\"," +
+      "              \"name\": \"${service-env/service_user}\"" +
+      "            }" +
+      "          }," +
+      "          \"principal\": {" +
+      "            \"configuration\": \"service-site/principal_name\"," +
+      "            \"local_username\": \"${service-env/service_user}\"," +
+      "            \"type\": \"service\"," +
+      "            \"value\": \"${service-env/service_user}/_HOST@${realm}\"" +
+      "          }" +
+      "        }" +
+      "      ]," +
+      "      \"name\": \"SERVICE\"" +
+      "    }" +
+      "  ]," +
+      "  \"properties\": {" +
+      "    \"additional_realms\": \"\"," +
+      "    \"keytab_dir\": \"/etc/security/keytabs\"," +
+      "    \"principal_suffix\": \"-${cluster_name|toLower()}\"," +
+      "    \"realm\": \"${kerberos-env/realm}\"" +
+      "  }" +
+      "}";
+
+  private class TestKerberosServerAction extends AbstractPrepareKerberosServerAction {
+
+    @Override
+    protected String getClusterName() {
+      return "c1";
+    }
 
     @Override
-    public CommandReport execute(ConcurrentMap<String, Object> requestSharedDataContext) throws AmbariException, InterruptedException {
+    public CommandReport execute(ConcurrentMap<String, Object> requestSharedDataContext) {
       return null;
     }
   }
 
   private Injector injector;
-  private final PrepareKerberosServerAction prepareKerberosServerAction = new PrepareKerberosServerAction();
-
-  private final AuditLogger auditLogger = EasyMock.createNiceMock(AuditLogger.class);
-  private final Clusters clusters = EasyMock.createNiceMock(Clusters.class);
-  private final KerberosHelper kerberosHelper = EasyMock.createNiceMock(KerberosHelper.class);
-  private final KerberosIdentityDataFileWriterFactory kerberosIdentityDataFileWriterFactory = EasyMock.createNiceMock(KerberosIdentityDataFileWriterFactory.class);
+  private final AbstractPrepareKerberosServerAction testKerberosServerAction = new TestKerberosServerAction();
 
   @Before
   public void setUp() throws Exception {
     injector = Guice.createInjector(new AbstractModule() {
       @Override
       protected void configure() {
-        bind(KerberosHelper.class).toInstance(kerberosHelper);
-        bind(KerberosIdentityDataFileWriterFactory.class).toInstance(kerberosIdentityDataFileWriterFactory);
-        bind(Clusters.class).toInstance(clusters);
-        bind(AuditLogger.class).toInstance(auditLogger);
-        Provider<EntityManager> entityManagerProvider =  EasyMock.createNiceMock(Provider.class);
+        bind(AmbariMetaInfo.class).toInstance(createNiceMock(AmbariMetaInfo.class));
+        bind(KerberosHelper.class).to(KerberosHelperImpl.class);
+        bind(KerberosIdentityDataFileWriterFactory.class).toInstance(createNiceMock(KerberosIdentityDataFileWriterFactory.class));
+        bind(KerberosConfigDataFileWriterFactory.class).toInstance(createNiceMock(KerberosConfigDataFileWriterFactory.class));
+        bind(Clusters.class).toInstance(createNiceMock(Clusters.class));
+        bind(AuditLogger.class).toInstance(createNiceMock(AuditLogger.class));
+        bind(ConfigHelper.class).toInstance(createNiceMock(ConfigHelper.class));
+        bind(HostRoleCommandDAO.class).toInstance(createNiceMock(HostRoleCommandDAO.class));
+        bind(ActionManager.class).toInstance(createNiceMock(ActionManager.class));
+        bind(OsFamily.class).toInstance(createNiceMock(OsFamily.class));
+        bind(ExecutionScheduler.class).toInstance(createNiceMock(ExecutionScheduler.class));
+        bind(AmbariManagementController.class).toInstance(createNiceMock(AmbariManagementController.class));
+        bind(ActionDBAccessor.class).toInstance(createNiceMock(ActionDBAccessor.class));
+        bind(StackManagerFactory.class).toInstance(createNiceMock(StackManagerFactory.class));
+        bind(ConfigFactory.class).toInstance(createNiceMock(ConfigFactory.class));
+        bind(ConfigGroupFactory.class).toInstance(createNiceMock(ConfigGroupFactory.class));
+        bind(CredentialStoreService.class).toInstance(createNiceMock(CredentialStoreService.class));
+        bind(RequestExecutionFactory.class).toInstance(createNiceMock(RequestExecutionFactory.class));
+        bind(RequestFactory.class).toInstance(createNiceMock(RequestFactory.class));
+        bind(RoleCommandOrderProvider.class).toInstance(createNiceMock(RoleCommandOrderProvider.class));
+        bind(RoleGraphFactory.class).toInstance(createNiceMock(RoleGraphFactory.class));
+        bind(AbstractRootServiceResponseFactory.class).toInstance(createNiceMock(AbstractRootServiceResponseFactory.class));
+        bind(ServiceComponentFactory.class).toInstance(createNiceMock(ServiceComponentFactory.class));
+        bind(ServiceComponentHostFactory.class).toInstance(createNiceMock(ServiceComponentHostFactory.class));
+        bind(StageFactory.class).toInstance(createNiceMock(StageFactory.class));
+        bind(HostRoleCommandFactory.class).toInstance(createNiceMock(HostRoleCommandFactory.class));
+        bind(HookContextFactory.class).toInstance(createNiceMock(HookContextFactory.class));
+        bind(HookService.class).toInstance(createNiceMock(HookService.class));
+        bind(PasswordEncoder.class).toInstance(createNiceMock(PasswordEncoder.class));
+        bind(PersistedState.class).toInstance(createNiceMock(PersistedState.class));
+        bind(ConfigureClusterTaskFactory.class).toInstance(createNiceMock(ConfigureClusterTaskFactory.class));
+        Provider<EntityManager> entityManagerProvider = createNiceMock(Provider.class);
         bind(EntityManager.class).toProvider(entityManagerProvider);
       }
     });
 
-    injector.injectMembers(prepareKerberosServerAction);
+    injector.injectMembers(testKerberosServerAction);
   }
 
   /**
    * Test checks that {@code KerberosHelper.applyStackAdvisorUpdates} would be called with
    * full list of the services and not only list of services with KerberosDescriptior.
    * In this test HDFS service will have KerberosDescriptor, while Zookeeper not.
+   *
    * @throws Exception
    */
   @Test
   @SuppressWarnings("unchecked")
   public void testProcessServiceComponentHosts() throws Exception {
-    final Cluster cluster =  EasyMock.createNiceMock(Cluster.class);
-    final KerberosIdentityDataFileWriter kerberosIdentityDataFileWriter = EasyMock.createNiceMock(KerberosIdentityDataFileWriter.class);
-    final KerberosDescriptor kerberosDescriptor = EasyMock.createNiceMock(KerberosDescriptor.class);
-    final ServiceComponentHost serviceComponentHostHDFS = EasyMock.createNiceMock(ServiceComponentHost.class);
-    final ServiceComponentHost serviceComponentHostZK = EasyMock.createNiceMock(ServiceComponentHost.class);
-    final KerberosServiceDescriptor serviceDescriptor = EasyMock.createNiceMock(KerberosServiceDescriptor.class);
-    final KerberosComponentDescriptor componentDescriptor = EasyMock.createNiceMock(KerberosComponentDescriptor.class);
+    final Cluster cluster = createNiceMock(Cluster.class);
+    final KerberosIdentityDataFileWriter kerberosIdentityDataFileWriter = createNiceMock(KerberosIdentityDataFileWriter.class);
+    final KerberosDescriptor kerberosDescriptor = createNiceMock(KerberosDescriptor.class);
+    final ServiceComponentHost serviceComponentHostHDFS = createNiceMock(ServiceComponentHost.class);
+    final ServiceComponentHost serviceComponentHostZK = createNiceMock(ServiceComponentHost.class);
+    final KerberosServiceDescriptor serviceDescriptor = createNiceMock(KerberosServiceDescriptor.class);
+    final KerberosComponentDescriptor componentDescriptor = createNiceMock(KerberosComponentDescriptor.class);
 
     final String hdfsService = "HDFS";
     final String zookeeperService = "ZOOKEEPER";
@@ -118,20 +258,20 @@ public class AbstractPrepareKerberosServerActionTest {
     Collection<String> identityFilter = new ArrayList<>();
     Map<String, Map<String, String>> kerberosConfigurations = new HashMap<>();
     Map<String, Set<String>> propertiesToIgnore = new HashMap<>();
-    Map<String, String> descriptorProperties = new HashMap<>();
     Map<String, Map<String, String>> configurations = new HashMap<>();
 
     List<ServiceComponentHost> serviceComponentHosts = new ArrayList<ServiceComponentHost>() {{
       add(serviceComponentHostHDFS);
       add(serviceComponentHostZK);
     }};
-    Map<String, Service> clusterServices = new HashMap<String, Service>(){{
+    Map<String, Service> clusterServices = new HashMap<String, Service>() {{
       put(hdfsService, null);
       put(zookeeperService, null);
     }};
 
-    expect(kerberosDescriptor.getProperties()).andReturn(descriptorProperties).atLeastOnce();
-    expect(kerberosIdentityDataFileWriterFactory.createKerberosIdentityDataFileWriter((File)anyObject())).andReturn(kerberosIdentityDataFileWriter);
+    KerberosIdentityDataFileWriterFactory kerberosIdentityDataFileWriterFactory = injector.getInstance(KerberosIdentityDataFileWriterFactory.class);
+    expect(kerberosIdentityDataFileWriterFactory.createKerberosIdentityDataFileWriter(anyObject(File.class))).andReturn(kerberosIdentityDataFileWriter);
+
     // it's important to pass a copy of clusterServices
     expect(cluster.getServices()).andReturn(new HashMap<>(clusterServices)).atLeastOnce();
 
@@ -150,22 +290,201 @@ public class AbstractPrepareKerberosServerActionTest {
     expect(serviceDescriptor.getComponent(hdfsComponent)).andReturn(componentDescriptor).once();
     expect(componentDescriptor.getConfigurations(anyBoolean())).andReturn(null);
 
-    replay(kerberosDescriptor, kerberosHelper, kerberosIdentityDataFileWriterFactory,
-      cluster, serviceComponentHostHDFS, serviceComponentHostZK, serviceDescriptor, componentDescriptor);
+    replayAll();
 
-    prepareKerberosServerAction.processServiceComponentHosts(cluster,
-      kerberosDescriptor,
-      serviceComponentHosts,
-      identityFilter,
-      "",
+    injector.getInstance(AmbariMetaInfo.class).init();
+
+    testKerberosServerAction.processServiceComponentHosts(cluster,
+        kerberosDescriptor,
+        serviceComponentHosts,
+        identityFilter,
+        "",
         configurations, kerberosConfigurations,
         false, propertiesToIgnore);
 
-    verify(kerberosHelper);
+    verifyAll();
 
     // Ensure the host and hostname values were set in the configuration context
     Assert.assertEquals("host1", configurations.get("").get("host"));
     Assert.assertEquals("host1", configurations.get("").get("hostname"));
   }
 
+  @Test
+  public void testProcessConfigurationChanges() throws Exception {
+    // Existing property map....
+    Map<String, String> serviceSiteProperties = new HashMap<>();
+    serviceSiteProperties.put("property1", "property1_value");
+    serviceSiteProperties.put("principal_name", "principal_name_value");
+    serviceSiteProperties.put("keytab_file_path", "keytab_file_path_value");
+
+    Map<String, Map<String, String>> effectiveProperties = new HashMap<>();
+    effectiveProperties.put("service-site", serviceSiteProperties);
+
+    // Updated property map....
+    Map<String, String> updatedServiceSiteProperties = new HashMap<>();
+    updatedServiceSiteProperties.put("property1", "property1_updated_value");
+    updatedServiceSiteProperties.put("property2", "property2_updated_value");
+    updatedServiceSiteProperties.put("principal_name", "principal_name_updated_value");
+    updatedServiceSiteProperties.put("keytab_file_path", "keytab_file_path_updated_value");
+
+    Map<String, Map<String, String>> kerberosConfigurations = new HashMap<>();
+    kerberosConfigurations.put("service-site", updatedServiceSiteProperties);
+
+    KerberosDescriptor kerberosDescriptor = new KerberosDescriptorFactory().createInstance(KERBEROS_DESCRIPTOR_JSON);
+
+    ConfigHelper configHelper = injector.getInstance(ConfigHelper.class);
+    expect(configHelper.getEffectiveConfigProperties(eq("c1"), eq(null)))
+        .andReturn(effectiveProperties).anyTimes();
+
+    KerberosConfigDataFileWriterFactory factory = injector.getInstance(KerberosConfigDataFileWriterFactory.class);
+
+    ConfigWriterData dataCaptureAll = setupConfigWriter(factory);
+    ConfigWriterData dataCaptureIdentitiesOnly = setupConfigWriter(factory);
+    ConfigWriterData dataCaptureNewAndIdentities = setupConfigWriter(factory);
+    ConfigWriterData dataCaptureNone = setupConfigWriter(factory);
+
+    replayAll();
+
+    injector.getInstance(AmbariMetaInfo.class).init();
+
+    Map<String, String> expectedProperties;
+
+    // Update all configurations
+    testKerberosServerAction.processConfigurationChanges("test_directory",
+        kerberosConfigurations, Collections.emptyMap(), kerberosDescriptor, UpdateConfigurationPolicy.ALL);
+
+    expectedProperties = new HashMap<>();
+    expectedProperties.put("property1", "property1_updated_value");
+    expectedProperties.put("property2", "property2_updated_value");
+    expectedProperties.put("principal_name", "principal_name_updated_value");
+    expectedProperties.put("keytab_file_path", "keytab_file_path_updated_value");
+
+    verifyDataCapture(dataCaptureAll, Collections.singletonMap("service-site", expectedProperties));
+
+    // Update only identity configurations
+    testKerberosServerAction.processConfigurationChanges("test_directory",
+        kerberosConfigurations, Collections.emptyMap(), kerberosDescriptor, UpdateConfigurationPolicy.IDENTITIES_ONLY);
+
+    expectedProperties = new HashMap<>();
+    expectedProperties.put("principal_name", "principal_name_updated_value");
+    expectedProperties.put("keytab_file_path", "keytab_file_path_updated_value");
+
+    verifyDataCapture(dataCaptureIdentitiesOnly, Collections.singletonMap("service-site", expectedProperties));
+
+    // Update new and identity configurations
+    testKerberosServerAction.processConfigurationChanges("test_directory",
+        kerberosConfigurations, Collections.emptyMap(), kerberosDescriptor, UpdateConfigurationPolicy.NEW_AND_IDENTITIES);
+
+    expectedProperties = new HashMap<>();
+    expectedProperties.put("property2", "property2_updated_value");
+    expectedProperties.put("principal_name", "principal_name_updated_value");
+    expectedProperties.put("keytab_file_path", "keytab_file_path_updated_value");
+
+    verifyDataCapture(dataCaptureNewAndIdentities, Collections.singletonMap("service-site", expectedProperties));
+
+    // Update no configurations
+    testKerberosServerAction.processConfigurationChanges("test_directory",
+        kerberosConfigurations, Collections.emptyMap(), kerberosDescriptor, UpdateConfigurationPolicy.NONE);
+
+    verifyDataCapture(dataCaptureNone, Collections.emptyMap());
+
+    verifyAll();
+
+  }
+
+  private void verifyDataCapture(ConfigWriterData configWriterData, Map<String, Map<String, String>> expectedConfigurations) {
+
+    int expectedCaptures = 0;
+    Collection<Map<String, String>> expectedValuesCollection = expectedConfigurations.values();
+    for (Map<String, String> expectedValues : expectedValuesCollection) {
+      expectedCaptures += expectedValues.size();
+    }
+
+    Capture<String> captureConfigType = configWriterData.getCaptureConfigType();
+    if (expectedCaptures > 0) {
+      Assert.assertTrue(captureConfigType.hasCaptured());
+      List<String> valuesConfigType = captureConfigType.getValues();
+      Assert.assertEquals(expectedCaptures, valuesConfigType.size());
+    } else {
+      Assert.assertFalse(captureConfigType.hasCaptured());
+    }
+
+    Capture<String> capturePropertyName = configWriterData.getCapturePropertyName();
+    if (expectedCaptures > 0) {
+      Assert.assertTrue(capturePropertyName.hasCaptured());
+      List<String> valuesPropertyName = capturePropertyName.getValues();
+      Assert.assertEquals(expectedCaptures, valuesPropertyName.size());
+    } else {
+      Assert.assertFalse(capturePropertyName.hasCaptured());
+    }
+
+    Capture<String> capturePropertyValue = configWriterData.getCapturePropertyValue();
+    if (expectedCaptures > 0) {
+      Assert.assertTrue(capturePropertyValue.hasCaptured());
+      List<String> valuesPropertyValue = capturePropertyValue.getValues();
+      Assert.assertEquals(expectedCaptures, valuesPropertyValue.size());
+    } else {
+      Assert.assertFalse(capturePropertyValue.hasCaptured());
+    }
+
+    if (expectedCaptures > 0) {
+      int i = 0;
+      List<String> valuesConfigType = captureConfigType.getValues();
+      List<String> valuesPropertyName = capturePropertyName.getValues();
+      List<String> valuesPropertyValue = capturePropertyValue.getValues();
+
+      for (Map.Entry<String, Map<String, String>> entry : expectedConfigurations.entrySet()) {
+        String configType = entry.getKey();
+        Map<String, String> properties = entry.getValue();
+
+        for(Map.Entry<String, String> property:properties.entrySet()) {
+          Assert.assertEquals(configType, valuesConfigType.get(i));
+          Assert.assertEquals(property.getKey(), valuesPropertyName.get(i));
+          Assert.assertEquals(property.getValue(), valuesPropertyValue.get(i));
+          i++;
+        }
+      }
+    }
+
+  }
+
+  private ConfigWriterData setupConfigWriter(KerberosConfigDataFileWriterFactory factory) throws IOException {
+    Capture<String> captureConfigType = newCapture(CaptureType.ALL);
+    Capture<String> capturePropertyName = newCapture(CaptureType.ALL);
+    Capture<String> capturePropertyValue = newCapture(CaptureType.ALL);
+
+    KerberosConfigDataFileWriter mockWriter = createMock(KerberosConfigDataFileWriter.class);
+    mockWriter.addRecord(capture(captureConfigType), capture(capturePropertyName), capture(capturePropertyValue), eq(KerberosConfigDataFileWriter.OPERATION_TYPE_SET));
+    expectLastCall().anyTimes();
+    mockWriter.close();
+    expectLastCall().anyTimes();
+
+    expect(factory.createKerberosConfigDataFileWriter(anyObject(File.class))).andReturn(mockWriter).once();
+
+    return new ConfigWriterData(captureConfigType, capturePropertyName, capturePropertyValue);
+  }
+
+  private class ConfigWriterData {
+    private final Capture<String> captureConfigType;
+    private final Capture<String> capturePropertyName;
+    private final Capture<String> capturePropertyValue;
+
+    private ConfigWriterData(Capture<String> captureConfigType, Capture<String> capturePropertyName, Capture<String> capturePropertyValue) {
+      this.captureConfigType = captureConfigType;
+      this.capturePropertyName = capturePropertyName;
+      this.capturePropertyValue = capturePropertyValue;
+    }
+
+    Capture<String> getCaptureConfigType() {
+      return captureConfigType;
+    }
+
+    Capture<String> getCapturePropertyName() {
+      return capturePropertyName;
+    }
+
+    Capture<String> getCapturePropertyValue() {
+      return capturePropertyValue;
+    }
+  }
 }