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 2017/06/08 19:22:56 UTC

[42/50] [abbrv] ambari git commit: AMBARI-20877.Custom RM principal causes zookeeper HA state store to be inaccessible. (Attila Magyar via stoader)

AMBARI-20877.Custom RM principal causes zookeeper HA state store to be inaccessible. (Attila Magyar via stoader)


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

Branch: refs/heads/branch-feature-AMBARI-20859
Commit: 89797ea2c56ae0387899890756527e1019ffd3f3
Parents: e2fbd0f
Author: Attila Magyar <am...@hortonworks.com>
Authored: Thu Jun 8 15:46:51 2017 +0200
Committer: Toader, Sebastian <st...@hortonworks.com>
Committed: Thu Jun 8 15:46:51 2017 +0200

----------------------------------------------------------------------
 .../security/kerberos/kerberos_descriptor.md    |   5 +-
 .../server/controller/KerberosHelperImpl.java   |  19 ++-
 .../state/kerberos/KerberosDescriptor.java      |  51 ++++++++
 .../kerberos/VariableReplacementHelper.java     |  23 +++-
 .../YARN/3.0.0.3.0/kerberos.json                |   4 +-
 .../stacks/HDP/2.6/services/YARN/kerberos.json  |   4 +-
 .../server/controller/KerberosHelperTest.java   | 129 +++++++++---------
 .../state/kerberos/KerberosDescriptorTest.java  |   9 ++
 .../kerberos/VariableReplacementHelperTest.java | 130 +++++++++++--------
 9 files changed, 242 insertions(+), 132 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/89797ea2/ambari-server/docs/security/kerberos/kerberos_descriptor.md
----------------------------------------------------------------------
diff --git a/ambari-server/docs/security/kerberos/kerberos_descriptor.md b/ambari-server/docs/security/kerberos/kerberos_descriptor.md
index 54af50f..a59564c 100644
--- a/ambari-server/docs/security/kerberos/kerberos_descriptor.md
+++ b/ambari-server/docs/security/kerberos/kerberos_descriptor.md
@@ -288,8 +288,9 @@ the configuration type and containing values for each relevant property.
 
 Each property name and value may be a concrete value or contain variables to be replaced using values
 from the stack-level `properties` block or any available configuration. Properties from the `properties`
-block are referenced by name (`${property_name}`) and configuration properties are reference by
-configuration specification (`${config-type/property_name}`).
+block are referenced by name (`${property_name}`), configuration properties are reference by
+configuration specification (`${config-type/property_name}`) and kerberos principals are referenced by the principal path
+(`principals/SERVICE/COMPONENT/principal_name`).
 
 ```
 "configurations" : [

http://git-wip-us.apache.org/repos/asf/ambari/blob/89797ea2/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java
----------------------------------------------------------------------
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 87c826d..61674cf 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
@@ -1241,10 +1241,21 @@ public class KerberosHelperImpl implements KerberosHelper {
   @Override
   public Map<String, Map<String, String>> calculateConfigurations(Cluster cluster, String hostname,
                                                                   Map<String, String> kerberosDescriptorProperties)
-      throws AmbariException {
-    return addAdditionalConfigurations(cluster,
-        calculateExistingConfigurations(cluster, hostname),
-        hostname, kerberosDescriptorProperties);
+      throws AmbariException
+  {
+    Map<String, Map<String, String>> configuration = addAdditionalConfigurations(cluster,
+      calculateExistingConfigurations(cluster, hostname),
+      hostname, kerberosDescriptorProperties);
+    configuration.put("principals", principalNames(cluster, configuration));
+    return configuration;
+  }
+
+  private Map<String, String> principalNames(Cluster cluster, Map<String, Map<String, String>> configuration) throws AmbariException {
+    Map<String, String> result = new HashMap<>();
+    for (Map.Entry<String, String> each : getKerberosDescriptor(cluster).principals().entrySet()) {
+      result.put(each.getKey(), variableReplacementHelper.replaceVariables(each.getValue(), configuration));
+    }
+    return result;
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/ambari/blob/89797ea2/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosDescriptor.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosDescriptor.java b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosDescriptor.java
index a1b9e5c..f9dfa4a 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosDescriptor.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosDescriptor.java
@@ -20,12 +20,16 @@ package org.apache.ambari.server.state.kerberos;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeMap;
 
+import org.apache.ambari.server.AmbariException;
+import org.apache.commons.lang.StringUtils;
+
 /**
  * KerberosDescriptor is an implementation of an AbstractKerberosDescriptorContainer that
  * encapsulates an entire Kerberos descriptor hierarchy.
@@ -418,4 +422,51 @@ public class KerberosDescriptor extends AbstractKerberosDescriptorContainer {
 
     return authToLocalProperties;
   }
+
+  /**
+   * Get a map of principals, where the key is the principal path (SERVICE/COMPONENT/principal_name or SERVICE/principal_name) and the value is the principal.
+   *
+   * For example if the kerberos principal of the HISTORYSERVER is defined in the kerberos.json:
+   * "name": "history_server_jhs",
+   *   "principal": {
+   *   "value": "jhs/_HOST@${realm}",
+   *   "type" : "service",
+   * },
+   * Then "jhs/_HOST@EXAMPLE.COM" will be put into the map under the "MAPREDUCE2/HISTORYSERVER/history_server_jhs" key.
+   */
+  public Map<String, String> principals() throws AmbariException {
+    Map<String,String> result = new HashMap<>();
+    for (AbstractKerberosDescriptorContainer each : nullToEmpty(getChildContainers())) {
+      if ((each instanceof KerberosServiceDescriptor)) {
+        collectFromComponents(each.getName(), nullToEmpty(((KerberosServiceDescriptor) each).getComponents()).values(), result);
+        collectFromIdentities(each.getName(), "", nullToEmpty(each.getIdentities()), result);
+      }
+    }
+    return result;
+  }
+
+  private static void collectFromComponents(String service, Collection<KerberosComponentDescriptor> components, Map<String, String> result) {
+    for (KerberosComponentDescriptor each : components) {
+      collectFromIdentities(service, each.getName(), nullToEmpty(each.getIdentities()), result);
+    }
+  }
+
+  private static void collectFromIdentities(String service, String component, Collection<KerberosIdentityDescriptor> identities, Map<String, String> result) {
+    for (KerberosIdentityDescriptor each : identities) {
+      if (each.getPrincipalDescriptor() != null && !each.getReferencedServiceName().isPresent() && !each.getName().startsWith("/")) {
+        String path = StringUtils.isBlank(component)
+          ? String.format("%s/%s", service, each.getName())
+          : String.format("%s/%s/%s", service, component, each.getName());
+        result.put(path, each.getPrincipalDescriptor().getName());
+      }
+    }
+  }
+
+  private static <T> Collection<T> nullToEmpty(Collection<T> collection) {
+    return collection == null ? Collections.<T>emptyList() : collection;
+  }
+
+  private static <K,V> Map<K,V> nullToEmpty(Map<K,V> collection) {
+    return collection == null ? Collections.<K,V>emptyMap() : collection;
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/89797ea2/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/VariableReplacementHelper.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/VariableReplacementHelper.java b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/VariableReplacementHelper.java
index b9e2841..a83f080 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/VariableReplacementHelper.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/VariableReplacementHelper.java
@@ -43,7 +43,7 @@ public class VariableReplacementHelper {
   /**
    * a regular expression Pattern used to find "variable" placeholders in strings
    */
-  private static final Pattern PATTERN_VARIABLE = Pattern.compile("\\$\\{(?:([\\w\\-\\.]+)/)?([\\w\\-\\.]+)(?:\\s*\\|\\s*(.+?))?\\}");
+  private static final Pattern PATTERN_VARIABLE = Pattern.compile("\\$\\{(?:([\\w\\-\\.]+)/)?([\\w\\-\\./]+)(?:\\s*\\|\\s*(.+?))?\\}");
 
   /**
    * a regular expression Pattern used to parse "function" declarations: name(arg1, arg2, ...)
@@ -59,6 +59,7 @@ public class VariableReplacementHelper {
       put("toLower", new ToLowerFunction());
       put("replace", new ReplaceValue());
       put("append", new AppendFunction());
+      put("principalPrimary", new PrincipalPrimary());
     }
   };
 
@@ -411,4 +412,24 @@ public class VariableReplacementHelper {
       return sourceData;
     }
   }
+
+  /**
+   * Get the primary part of a Kerberos principal.
+   * The format of a typical Kerberos principal is primary/instance@REALM.
+   */
+  private static class PrincipalPrimary implements Function {
+    @Override
+    public String perform(String[] args, String data, Map<String, Map<String, String>> replacementsMap) {
+      if (data == null) {
+        return null;
+      }
+      if (data.contains("/")) {
+        return data.split("/")[0];
+      } else if (data.contains("@")) {
+        return data.split("@")[0];
+      } else {
+        return data;
+      }
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/89797ea2/ambari-server/src/main/resources/common-services/YARN/3.0.0.3.0/kerberos.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/common-services/YARN/3.0.0.3.0/kerberos.json b/ambari-server/src/main/resources/common-services/YARN/3.0.0.3.0/kerberos.json
index ae4db4f..b1501b8 100644
--- a/ambari-server/src/main/resources/common-services/YARN/3.0.0.3.0/kerberos.json
+++ b/ambari-server/src/main/resources/common-services/YARN/3.0.0.3.0/kerberos.json
@@ -32,9 +32,9 @@
             "yarn.resourcemanager.proxyuser.*.hosts": "",
             "yarn.resourcemanager.proxyuser.*.users": "",
             "yarn.resourcemanager.proxy-user-privileges.enabled": "true",
-            "yarn.resourcemanager.zk-acl" : "sasl:rm:rwcda",
+            "yarn.resourcemanager.zk-acl" : "sasl:${principals/YARN/RESOURCEMANAGER/resource_manager_rm|principalPrimary()}:rwcda",
             "hadoop.registry.secure" : "true",
-            "hadoop.registry.system.accounts" : "sasl:yarn,sasl:mapred,sasl:hadoop,sasl:hdfs,sasl:rm,sasl:hive",
+            "hadoop.registry.system.accounts" : "sasl:${principals/YARN/APP_TIMELINE_SERVER/app_timeline_server_yarn|principalPrimary()},sasl:${principals/MAPREDUCE2/HISTORYSERVER/history_server_jhs|principalPrimary()},sasl:${principals/HDFS/NAMENODE/hdfs|principalPrimary()},sasl:${principals/YARN/RESOURCEMANAGER/resource_manager_rm|principalPrimary()},sasl:${principals/HIVE/HIVE_SERVER/hive_server_hive|principalPrimary()}",
             "hadoop.registry.client.auth" : "kerberos",
             "hadoop.registry.jaas.context" : "Client"
           }

http://git-wip-us.apache.org/repos/asf/ambari/blob/89797ea2/ambari-server/src/main/resources/stacks/HDP/2.6/services/YARN/kerberos.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/stacks/HDP/2.6/services/YARN/kerberos.json b/ambari-server/src/main/resources/stacks/HDP/2.6/services/YARN/kerberos.json
index ae4db4f..b1501b8 100644
--- a/ambari-server/src/main/resources/stacks/HDP/2.6/services/YARN/kerberos.json
+++ b/ambari-server/src/main/resources/stacks/HDP/2.6/services/YARN/kerberos.json
@@ -32,9 +32,9 @@
             "yarn.resourcemanager.proxyuser.*.hosts": "",
             "yarn.resourcemanager.proxyuser.*.users": "",
             "yarn.resourcemanager.proxy-user-privileges.enabled": "true",
-            "yarn.resourcemanager.zk-acl" : "sasl:rm:rwcda",
+            "yarn.resourcemanager.zk-acl" : "sasl:${principals/YARN/RESOURCEMANAGER/resource_manager_rm|principalPrimary()}:rwcda",
             "hadoop.registry.secure" : "true",
-            "hadoop.registry.system.accounts" : "sasl:yarn,sasl:mapred,sasl:hadoop,sasl:hdfs,sasl:rm,sasl:hive",
+            "hadoop.registry.system.accounts" : "sasl:${principals/YARN/APP_TIMELINE_SERVER/app_timeline_server_yarn|principalPrimary()},sasl:${principals/MAPREDUCE2/HISTORYSERVER/history_server_jhs|principalPrimary()},sasl:${principals/HDFS/NAMENODE/hdfs|principalPrimary()},sasl:${principals/YARN/RESOURCEMANAGER/resource_manager_rm|principalPrimary()},sasl:${principals/HIVE/HIVE_SERVER/hive_server_hive|principalPrimary()}",
             "hadoop.registry.client.auth" : "kerberos",
             "hadoop.registry.jaas.context" : "Client"
           }

http://git-wip-us.apache.org/repos/asf/ambari/blob/89797ea2/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
index 98241eb..18a6754 100644
--- 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
@@ -1001,30 +1001,30 @@ public class KerberosHelperTest extends EasyMockSupport {
     expect(serviceComponentKerberosClient.getName()).andReturn(Role.KERBEROS_CLIENT.name()).anyTimes();
     expect(serviceComponentKerberosClient.getServiceComponentHosts()).andReturn(Collections.singletonMap("host1", schKerberosClient)).anyTimes();
 
-    final Service serviceKerberos = createStrictMock(Service.class);
+    final Service serviceKerberos = createNiceMock(Service.class);
     expect(serviceKerberos.getDesiredStackId()).andReturn(stackId).anyTimes();
     expect(serviceKerberos.getName()).andReturn(Service.Type.KERBEROS.name()).anyTimes();
     expect(serviceKerberos.getServiceComponents())
         .andReturn(Collections.singletonMap(Role.KERBEROS_CLIENT.name(), serviceComponentKerberosClient))
-        .times(1);
+        .anyTimes();
     serviceKerberos.setSecurityState(SecurityState.SECURED_KERBEROS);
     expectLastCall().once();
 
-    final Service service1 = createStrictMock(Service.class);
+    final Service service1 = createNiceMock(Service.class);
     expect(service1.getDesiredStackId()).andReturn(stackId).anyTimes();
     expect(service1.getName()).andReturn("SERVICE1").anyTimes();
     expect(service1.getServiceComponents())
         .andReturn(Collections.<String, ServiceComponent>emptyMap())
-        .times(1);
+        .anyTimes();
     service1.setSecurityState(SecurityState.SECURED_KERBEROS);
     expectLastCall().once();
 
-    final Service service2 = createStrictMock(Service.class);
+    final Service service2 = createNiceMock(Service.class);
     expect(service2.getName()).andReturn("SERVICE2").anyTimes();
     expect(service2.getDesiredStackId()).andReturn(stackId).anyTimes();
     expect(service2.getServiceComponents())
         .andReturn(Collections.<String, ServiceComponent>emptyMap())
-        .times(1);
+        .anyTimes();
     service2.setSecurityState(SecurityState.SECURED_KERBEROS);
     expectLastCall().once();
 
@@ -1090,7 +1090,7 @@ public class KerberosHelperTest extends EasyMockSupport {
     expect(kerberosDescriptor.getService("SERVICE1")).andReturn(serviceDescriptor1).once();
     expect(kerberosDescriptor.getService("SERVICE2")).andReturn(serviceDescriptor2).once();
 
-    setupKerberosDescriptor(kerberosDescriptor, 1);
+    setupKerberosDescriptor(kerberosDescriptor);
     setupStageFactory();
 
     // This is a STRICT mock to help ensure that the end result is what we want.
@@ -1203,7 +1203,7 @@ public class KerberosHelperTest extends EasyMockSupport {
     expect(serviceComponentKerberosClient.getServiceComponentHosts()).andReturn(Collections.singletonMap("host1", schKerberosClient)).anyTimes();
 
     final Service serviceKerberos = createNiceMock(Service.class);
-    expect(serviceKerberos.getDesiredStackId()).andReturn(new StackId("HDP-2.2"));
+    expect(serviceKerberos.getDesiredStackId()).andReturn(new StackId("HDP-2.2")).anyTimes();
     expect(serviceKerberos.getName()).andReturn(Service.Type.KERBEROS.name()).anyTimes();
     expect(serviceKerberos.getServiceComponents())
         .andReturn(Collections.singletonMap(Role.KERBEROS_CLIENT.name(), serviceComponentKerberosClient))
@@ -1212,7 +1212,7 @@ public class KerberosHelperTest extends EasyMockSupport {
     expectLastCall().once();
 
     final Service service1 = createNiceMock(Service.class);
-    expect(service1.getDesiredStackId()).andReturn(new StackId("HDP-2.2"));
+    expect(service1.getDesiredStackId()).andReturn(new StackId("HDP-2.2")).anyTimes();
     expect(service1.getName()).andReturn("SERVICE1").anyTimes();
     expect(service1.getServiceComponents())
         .andReturn(Collections.<String, ServiceComponent>emptyMap())
@@ -1221,7 +1221,7 @@ public class KerberosHelperTest extends EasyMockSupport {
     expectLastCall().once();
 
     final Service service2 = createNiceMock(Service.class);
-    expect(service2.getDesiredStackId()).andReturn(new StackId("HDP-2.2"));
+    expect(service2.getDesiredStackId()).andReturn(new StackId("HDP-2.2")).anyTimes();
     expect(service2.getName()).andReturn("SERVICE2").anyTimes();
     expect(service2.getServiceComponents())
         .andReturn(Collections.<String, ServiceComponent>emptyMap())
@@ -1287,7 +1287,7 @@ public class KerberosHelperTest extends EasyMockSupport {
     expect(kerberosDescriptor.getService("SERVICE1")).andReturn(serviceDescriptor1).atLeastOnce();
     expect(kerberosDescriptor.getService("SERVICE2")).andReturn(serviceDescriptor2).atLeastOnce();
 
-    setupKerberosDescriptor(kerberosDescriptor, 1);
+    setupKerberosDescriptor(kerberosDescriptor);
     setupStageFactory();
 
     // This is a STRICT mock to help ensure that the end result is what we want.
@@ -1426,25 +1426,25 @@ public class KerberosHelperTest extends EasyMockSupport {
     expect(serviceComponentKerberosClient.getServiceComponentHosts()).andReturn(map).anyTimes();
 
     final Service serviceKerberos = createStrictMock(Service.class);
-    expect(serviceKerberos.getDesiredStackId()).andReturn(new StackId("HDP-2.2"));
+    expect(serviceKerberos.getDesiredStackId()).andReturn(new StackId("HDP-2.2")).anyTimes();
     expect(serviceKerberos.getName()).andReturn(Service.Type.KERBEROS.name()).anyTimes();
     expect(serviceKerberos.getServiceComponents())
         .andReturn(Collections.singletonMap(Role.KERBEROS_CLIENT.name(), serviceComponentKerberosClient))
-        .times(1);
+        .anyTimes();
 
     final Service service1 = createStrictMock(Service.class);
-    expect(service1.getDesiredStackId()).andReturn(new StackId("HDP-2.2"));
+    expect(service1.getDesiredStackId()).andReturn(new StackId("HDP-2.2")).anyTimes();
     expect(service1.getName()).andReturn("SERVICE1").anyTimes();
     expect(service1.getServiceComponents())
         .andReturn(Collections.<String, ServiceComponent>emptyMap())
-        .times(1);
+        .anyTimes();
 
     final Service service2 = createStrictMock(Service.class);
-    expect(service2.getDesiredStackId()).andReturn(new StackId("HDP-2.2"));
+    expect(service2.getDesiredStackId()).andReturn(new StackId("HDP-2.2")).anyTimes();
     expect(service2.getName()).andReturn("SERVICE2").anyTimes();
     expect(service2.getServiceComponents())
         .andReturn(Collections.<String, ServiceComponent>emptyMap())
-        .times(1);
+        .anyTimes();
 
     final Map<String, String> kerberosEnvProperties = createMock(Map.class);
     expect(kerberosEnvProperties.get("kdc_type")).andReturn("mit-kdc").anyTimes();
@@ -1509,7 +1509,7 @@ public class KerberosHelperTest extends EasyMockSupport {
     expect(kerberosDescriptor.getService("SERVICE1")).andReturn(serviceDescriptor1).atLeastOnce();
     expect(kerberosDescriptor.getService("SERVICE2")).andReturn(serviceDescriptor2).atLeastOnce();
 
-    setupKerberosDescriptor(kerberosDescriptor, 1);
+    setupKerberosDescriptor(kerberosDescriptor);
     setupStageFactory();
 
     final RequestStageContainer requestStageContainer;
@@ -2050,7 +2050,7 @@ public class KerberosHelperTest extends EasyMockSupport {
     expect(kerberosDescriptor.getIdentities(eq(true), EasyMock.<Map<String, Object>>anyObject())).andReturn(null).atLeastOnce();
     expect(kerberosDescriptor.getAuthToLocalProperties()).andReturn(Collections.singleton("core-site/auth.to.local")).atLeastOnce();
 
-    setupKerberosDescriptor(kerberosDescriptor, 2);
+    setupKerberosDescriptor(kerberosDescriptor);
 
     RecommendationResponse.BlueprintConfigurations coreSiteRecommendation = createNiceMock(RecommendationResponse
         .BlueprintConfigurations.class);
@@ -2485,7 +2485,7 @@ public class KerberosHelperTest extends EasyMockSupport {
       expectLastCall().once();
     }
 
-    setupKerberosDescriptor(kerberosDescriptor, 1);
+    setupKerberosDescriptor(kerberosDescriptor);
 
     Map<String, Map<String, String>> existingConfigurations = new HashMap<>();
     existingConfigurations.put("kerberos-env", propertiesKerberosEnv);
@@ -2612,7 +2612,7 @@ public class KerberosHelperTest extends EasyMockSupport {
     expect(kerberosDescriptor.getProperties()).andReturn(kerberosDescriptorProperties);
     expect(kerberosDescriptor.getService("SERVICE1")).andReturn(service1KerberosDescriptor).times(1);
 
-    setupKerberosDescriptor(kerberosDescriptor, 1);
+    setupKerberosDescriptor(kerberosDescriptor);
 
     Map<String, Map<String, String>> existingConfigurations = new HashMap<>();
     existingConfigurations.put("kerberos-env", propertiesKerberosEnv);
@@ -2648,9 +2648,10 @@ public class KerberosHelperTest extends EasyMockSupport {
     assertEquals(0, capturedPrincipalsForKeytab.size());
   }
 
-  private void setupKerberosDescriptor(KerberosDescriptor kerberosDescriptor, int expectedCalls) throws Exception {
+  private void setupKerberosDescriptor(KerberosDescriptor kerberosDescriptor) throws Exception {
     // cluster.getCurrentStackVersion expectation is already specified in main test method
-    expect(metaInfo.getKerberosDescriptor("HDP", "2.2")).andReturn(kerberosDescriptor).times(expectedCalls);
+    expect(metaInfo.getKerberosDescriptor("HDP", "2.2")).andReturn(kerberosDescriptor).anyTimes();
+    expect(kerberosDescriptor.principals()).andReturn(Collections.<String, String>emptyMap()).anyTimes();
   }
 
   private void setupStageFactory() {
@@ -2736,25 +2737,25 @@ public class KerberosHelperTest extends EasyMockSupport {
     ).anyTimes();
 
     final Service serviceKerberos = createStrictMock(Service.class);
-    expect(serviceKerberos.getDesiredStackId()).andReturn(new StackId("HDP-2.2"));
+    expect(serviceKerberos.getDesiredStackId()).andReturn(new StackId("HDP-2.2")).anyTimes();
     expect(serviceKerberos.getName()).andReturn(Service.Type.KERBEROS.name()).anyTimes();
     expect(serviceKerberos.getServiceComponents())
         .andReturn(Collections.singletonMap(Role.KERBEROS_CLIENT.name(), serviceComponentKerberosClient))
-        .times(1);
+        .anyTimes();
 
     final Service service1 = createStrictMock(Service.class);
-    expect(service1.getDesiredStackId()).andReturn(new StackId("HDP-2.2"));
+    expect(service1.getDesiredStackId()).andReturn(new StackId("HDP-2.2")).anyTimes();
     expect(service1.getName()).andReturn("SERVICE1").anyTimes();
     expect(service1.getServiceComponents())
         .andReturn(Collections.<String, ServiceComponent>emptyMap())
-        .times(1);
+        .anyTimes();
 
     final Service service2 = createStrictMock(Service.class);
-    expect(service2.getDesiredStackId()).andReturn(new StackId("HDP-2.2"));
+    expect(service2.getDesiredStackId()).andReturn(new StackId("HDP-2.2")).anyTimes();
     expect(service2.getName()).andReturn("SERVICE2").anyTimes();
     expect(service2.getServiceComponents())
         .andReturn(Collections.<String, ServiceComponent>emptyMap())
-        .times(1);
+        .anyTimes();
 
     final Map<String, String> kerberosEnvProperties = createMock(Map.class);
     expect(kerberosEnvProperties.get("kdc_type")).andReturn("mit-kdc").anyTimes();
@@ -2904,7 +2905,7 @@ public class KerberosHelperTest extends EasyMockSupport {
       expect(kerberosDescriptor.getService("SERVICE1")).andReturn(serviceDescriptor1).times(1);
     }
 
-    setupKerberosDescriptor(kerberosDescriptor, 1);
+    setupKerberosDescriptor(kerberosDescriptor);
     setupStageFactory();
 
     // This is a STRICT mock to help ensure that the end result is what we want.
@@ -2995,25 +2996,25 @@ public class KerberosHelperTest extends EasyMockSupport {
     expect(serviceComponentKerberosClient.getServiceComponentHosts()).andReturn(Collections.singletonMap("host1", schKerberosClient)).anyTimes();
 
     final Service serviceKerberos = createStrictMock(Service.class);
-    expect(serviceKerberos.getDesiredStackId()).andReturn(new StackId("HDP-2.2"));
+    expect(serviceKerberos.getDesiredStackId()).andReturn(new StackId("HDP-2.2")).anyTimes();
     expect(serviceKerberos.getName()).andReturn(Service.Type.KERBEROS.name()).anyTimes();
     expect(serviceKerberos.getServiceComponents())
         .andReturn(Collections.singletonMap(Role.KERBEROS_CLIENT.name(), serviceComponentKerberosClient))
-        .times(1);
+        .anyTimes();
 
     final Service service1 = createStrictMock(Service.class);
-    expect(service1.getDesiredStackId()).andReturn(new StackId("HDP-2.2"));
+    expect(service1.getDesiredStackId()).andReturn(new StackId("HDP-2.2")).anyTimes();
     expect(service1.getName()).andReturn("SERVICE1").anyTimes();
     expect(service1.getServiceComponents())
         .andReturn(Collections.<String, ServiceComponent>emptyMap())
-        .times(1);
+        .anyTimes();
 
     final Service service2 = createStrictMock(Service.class);
-    expect(service2.getDesiredStackId()).andReturn(new StackId("HDP-2.2"));
+    expect(service2.getDesiredStackId()).andReturn(new StackId("HDP-2.2")).anyTimes();
     expect(service2.getName()).andReturn("SERVICE2").anyTimes();
     expect(service2.getServiceComponents())
         .andReturn(Collections.<String, ServiceComponent>emptyMap())
-        .times(1);
+        .anyTimes();
 
     final Map<String, String> kerberosEnvProperties = createMock(Map.class);
     expect(kerberosEnvProperties.get("kdc_type")).andReturn("mit-kdc").anyTimes();
@@ -3110,7 +3111,7 @@ public class KerberosHelperTest extends EasyMockSupport {
     expect(kerberosDescriptor.getService("SERVICE1")).andReturn(serviceDescriptor1).times(1);
     expect(kerberosDescriptor.getService("SERVICE3")).andReturn(serviceDescriptor3).times(1);
 
-    setupKerberosDescriptor(kerberosDescriptor, 1);
+    setupKerberosDescriptor(kerberosDescriptor);
     setupStageFactory();
 
     // This is a STRICT mock to help ensure that the end result is what we want.
@@ -3210,26 +3211,26 @@ public class KerberosHelperTest extends EasyMockSupport {
       expect(serviceComponentKerberosClient.getName()).andReturn(Role.KERBEROS_CLIENT.name()).anyTimes();
       expect(serviceComponentKerberosClient.getServiceComponentHosts()).andReturn(Collections.singletonMap("host1", schKerberosClient)).anyTimes();
 
-      final Service serviceKerberos = createStrictMock(Service.class);
-      expect(serviceKerberos.getDesiredStackId()).andReturn(new StackId("HDP-2.2"));
+      final Service serviceKerberos = createNiceMock(Service.class);
+      expect(serviceKerberos.getDesiredStackId()).andReturn(new StackId("HDP-2.2")).anyTimes();
       expect(serviceKerberos.getName()).andReturn(Service.Type.KERBEROS.name()).anyTimes();
       expect(serviceKerberos.getServiceComponents())
           .andReturn(Collections.singletonMap(Role.KERBEROS_CLIENT.name(), serviceComponentKerberosClient))
-          .times(2);
+          .anyTimes();
 
-      final Service service1 = createStrictMock(Service.class);
-      expect(service1.getDesiredStackId()).andReturn(new StackId("HDP-2.2"));
+      final Service service1 = createNiceMock(Service.class);
+      expect(service1.getDesiredStackId()).andReturn(new StackId("HDP-2.2")).anyTimes();
       expect(service1.getName()).andReturn("SERVICE1").anyTimes();
       expect(service1.getServiceComponents())
           .andReturn(Collections.<String, ServiceComponent>emptyMap())
-          .times(2);
+          .anyTimes();
 
-      final Service service2 = createStrictMock(Service.class);
-      expect(service2.getDesiredStackId()).andReturn(new StackId("HDP-2.2"));
+      final Service service2 = createNiceMock(Service.class);
+      expect(service2.getDesiredStackId()).andReturn(new StackId("HDP-2.2")).anyTimes();
       expect(service2.getName()).andReturn("SERVICE2").anyTimes();
       expect(service2.getServiceComponents())
           .andReturn(Collections.<String, ServiceComponent>emptyMap())
-          .times(2);
+          .anyTimes();
 
 
       expect(cluster.getClusterName()).andReturn("c1").anyTimes();
@@ -3291,7 +3292,7 @@ public class KerberosHelperTest extends EasyMockSupport {
       final KerberosDescriptor kerberosDescriptor = createStrictMock(KerberosDescriptor.class);
       expect(kerberosDescriptor.getProperties()).andReturn(null).once();
 
-      setupKerberosDescriptor(kerberosDescriptor, 1);
+      setupKerberosDescriptor(kerberosDescriptor);
       setupStageFactory();
 
       // Preparation Stage
@@ -3384,26 +3385,26 @@ public class KerberosHelperTest extends EasyMockSupport {
     expect(serviceComponentKerberosClient.getName()).andReturn(Role.KERBEROS_CLIENT.name()).anyTimes();
     expect(serviceComponentKerberosClient.getServiceComponentHosts()).andReturn(Collections.singletonMap("host1", schKerberosClient)).anyTimes();
 
-    final Service serviceKerberos = createStrictMock(Service.class);
-    expect(serviceKerberos.getDesiredStackId()).andReturn(new StackId("HDP-2.2"));
+    final Service serviceKerberos = createNiceMock(Service.class);
+    expect(serviceKerberos.getDesiredStackId()).andReturn(new StackId("HDP-2.2")).anyTimes();
     expect(serviceKerberos.getName()).andReturn(Service.Type.KERBEROS.name()).anyTimes();
     expect(serviceKerberos.getServiceComponents())
         .andReturn(Collections.singletonMap(Role.KERBEROS_CLIENT.name(), serviceComponentKerberosClient))
-        .times(2);
+        .anyTimes();
 
-    final Service service1 = createStrictMock(Service.class);
-    expect(service1.getDesiredStackId()).andReturn(new StackId("HDP-2.2"));
+    final Service service1 = createNiceMock(Service.class);
+    expect(service1.getDesiredStackId()).andReturn(new StackId("HDP-2.2")).anyTimes();
     expect(service1.getName()).andReturn("SERVICE1").anyTimes();
     expect(service1.getServiceComponents())
         .andReturn(Collections.<String, ServiceComponent>emptyMap())
-        .times(2);
+        .anyTimes();
 
-    final Service service2 = createStrictMock(Service.class);
-    expect(service2.getDesiredStackId()).andReturn(new StackId("HDP-2.2"));
+    final Service service2 = createNiceMock(Service.class);
+    expect(service2.getDesiredStackId()).andReturn(new StackId("HDP-2.2")).anyTimes();
     expect(service2.getName()).andReturn("SERVICE2").anyTimes();
     expect(service2.getServiceComponents())
         .andReturn(Collections.<String, ServiceComponent>emptyMap())
-        .times(2);
+        .anyTimes();
 
     final Map<String, String> kerberosEnvProperties = createMock(Map.class);
     expect(kerberosEnvProperties.get("kdc_type")).andReturn("mit-kdc").anyTimes();
@@ -3466,7 +3467,7 @@ public class KerberosHelperTest extends EasyMockSupport {
     final KerberosDescriptor kerberosDescriptor = createStrictMock(KerberosDescriptor.class);
     expect(kerberosDescriptor.getProperties()).andReturn(null).once();
 
-    setupKerberosDescriptor(kerberosDescriptor, 1);
+    setupKerberosDescriptor(kerberosDescriptor);
     setupStageFactory();
 
     // This is a STRICT mock to help ensure that the end result is what we want.
@@ -3558,22 +3559,22 @@ public class KerberosHelperTest extends EasyMockSupport {
     expect(serviceComponentKerberosClient.getName()).andReturn(Role.KERBEROS_CLIENT.name()).anyTimes();
     expect(serviceComponentKerberosClient.getServiceComponentHosts()).andReturn(Collections.singletonMap("host1", schKerberosClient1)).anyTimes();
 
-    final Service serviceKerberos = createStrictMock(Service.class);
-    expect(serviceKerberos.getDesiredStackId()).andReturn(new StackId("HDP-2.2"));
+    final Service serviceKerberos = createNiceMock(Service.class);
+    expect(serviceKerberos.getDesiredStackId()).andReturn(new StackId("HDP-2.2")).anyTimes();
     expect(serviceKerberos.getName()).andReturn(Service.Type.KERBEROS.name()).anyTimes();
     expect(serviceKerberos.getServiceComponents())
         .andReturn(Collections.singletonMap(Role.KERBEROS_CLIENT.name(), serviceComponentKerberosClient))
         .anyTimes();
 
-    final Service service1 = createStrictMock(Service.class);
-    expect(service1.getDesiredStackId()).andReturn(new StackId("HDP-2.2"));
+    final Service service1 = createNiceMock(Service.class);
+    expect(service1.getDesiredStackId()).andReturn(new StackId("HDP-2.2")).anyTimes();
     expect(service1.getName()).andReturn("SERVICE1").anyTimes();
     expect(service1.getServiceComponents())
         .andReturn(Collections.<String, ServiceComponent>emptyMap())
         .anyTimes();
 
-    final Service service2 = createStrictMock(Service.class);
-    expect(service2.getDesiredStackId()).andReturn(new StackId("HDP-2.2"));
+    final Service service2 = createNiceMock(Service.class);
+    expect(service2.getDesiredStackId()).andReturn(new StackId("HDP-2.2")).anyTimes();
     expect(service2.getName()).andReturn("SERVICE2").anyTimes();
     expect(service2.getServiceComponents())
         .andReturn(Collections.<String, ServiceComponent>emptyMap())
@@ -3779,7 +3780,7 @@ public class KerberosHelperTest extends EasyMockSupport {
     expect(kerberosDescriptor.getService("SERVICE1")).andReturn(serviceDescriptor1).anyTimes();
     expect(kerberosDescriptor.getService("SERVICE2")).andReturn(serviceDescriptor2).anyTimes();
 
-    setupKerberosDescriptor(kerberosDescriptor, 1);
+    setupKerberosDescriptor(kerberosDescriptor);
 
     replayAll();
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/89797ea2/ambari-server/src/test/java/org/apache/ambari/server/state/kerberos/KerberosDescriptorTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/kerberos/KerberosDescriptorTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/kerberos/KerberosDescriptorTest.java
index a63da61..7fb5624 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/state/kerberos/KerberosDescriptorTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/kerberos/KerberosDescriptorTest.java
@@ -493,4 +493,13 @@ public class KerberosDescriptorTest {
     }});
     Assert.assertEquals(1, identities.size());
   }
+
+  @Test
+  public void testCollectPrincipalNames() throws Exception {
+    URL systemResourceURL = ClassLoader.getSystemResource("kerberos/test_get_referenced_identity_descriptor.json");
+    KerberosDescriptor descriptor = KERBEROS_DESCRIPTOR_FACTORY.createInstance(new File(systemResourceURL.getFile()));
+    Map<String, String> principalsPerComponent = descriptor.principals();
+    Assert.assertEquals("service2_component1@${realm}", principalsPerComponent.get("SERVICE2/SERVICE2_COMPONENT1/service2_component1_identity"));
+    Assert.assertEquals("service1@${realm}", principalsPerComponent.get("SERVICE1/service1_identity"));
+  }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/89797ea2/ambari-server/src/test/java/org/apache/ambari/server/state/kerberos/VariableReplacementHelperTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/kerberos/VariableReplacementHelperTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/kerberos/VariableReplacementHelperTest.java
index f00f694..e46294a 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/state/kerberos/VariableReplacementHelperTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/kerberos/VariableReplacementHelperTest.java
@@ -18,6 +18,8 @@
 
 package org.apache.ambari.server.state.kerberos;
 
+import static junit.framework.Assert.assertEquals;
+
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
@@ -72,63 +74,63 @@ public class VariableReplacementHelperTest {
       }
     };
 
-    Assert.assertEquals("concrete",
-        helper.replaceVariables("concrete", configurations));
+    assertEquals("concrete",
+      helper.replaceVariables("concrete", configurations));
 
-    Assert.assertEquals("Hello World",
-        helper.replaceVariables("${global_variable}", configurations));
+    assertEquals("Hello World",
+      helper.replaceVariables("${global_variable}", configurations));
 
-    Assert.assertEquals("Replacement1",
-        helper.replaceVariables("${config-type/variable.name}", configurations));
+    assertEquals("Replacement1",
+      helper.replaceVariables("${config-type/variable.name}", configurations));
 
-    Assert.assertEquals("Replacement1|Replacement2",
-        helper.replaceVariables("${config-type/variable.name}|${config-type2/variable.name}", configurations));
+    assertEquals("Replacement1|Replacement2",
+      helper.replaceVariables("${config-type/variable.name}|${config-type2/variable.name}", configurations));
 
-    Assert.assertEquals("Replacement1|Replacement2|${config-type3/variable.name}",
-        helper.replaceVariables("${config-type/variable.name}|${config-type2/variable.name}|${config-type3/variable.name}", configurations));
+    assertEquals("Replacement1|Replacement2|${config-type3/variable.name}",
+      helper.replaceVariables("${config-type/variable.name}|${config-type2/variable.name}|${config-type3/variable.name}", configurations));
 
-    Assert.assertEquals("Replacement2|Replacement2",
-        helper.replaceVariables("${config-type/variable.name1}|${config-type2/variable.name}", configurations));
+    assertEquals("Replacement2|Replacement2",
+      helper.replaceVariables("${config-type/variable.name1}|${config-type2/variable.name}", configurations));
 
-    Assert.assertEquals("Replacement1_reference",
-        helper.replaceVariables("${config-type/variable.name}_reference", configurations));
+    assertEquals("Replacement1_reference",
+      helper.replaceVariables("${config-type/variable.name}_reference", configurations));
 
-    Assert.assertEquals("dash",
-        helper.replaceVariables("${variable-name}", configurations));
+    assertEquals("dash",
+      helper.replaceVariables("${variable-name}", configurations));
 
-    Assert.assertEquals("underscore",
-        helper.replaceVariables("${variable_name}", configurations));
+    assertEquals("underscore",
+      helper.replaceVariables("${variable_name}", configurations));
 
-    Assert.assertEquals("config_type_dot",
-        helper.replaceVariables("${config_type/variable.name}", configurations));
+    assertEquals("config_type_dot",
+      helper.replaceVariables("${config_type/variable.name}", configurations));
 
-    Assert.assertEquals("config_type_dash",
-        helper.replaceVariables("${config_type/variable-name}", configurations));
+    assertEquals("config_type_dash",
+      helper.replaceVariables("${config_type/variable-name}", configurations));
 
-    Assert.assertEquals("config_type_underscore",
-        helper.replaceVariables("${config_type/variable_name}", configurations));
+    assertEquals("config_type_underscore",
+      helper.replaceVariables("${config_type/variable_name}", configurations));
 
-    Assert.assertEquals("config.type_dot",
-        helper.replaceVariables("${config.type/variable.name}", configurations));
+    assertEquals("config.type_dot",
+      helper.replaceVariables("${config.type/variable.name}", configurations));
 
-    Assert.assertEquals("config.type_dash",
-        helper.replaceVariables("${config.type/variable-name}", configurations));
+    assertEquals("config.type_dash",
+      helper.replaceVariables("${config.type/variable-name}", configurations));
 
-    Assert.assertEquals("config.type_underscore",
-        helper.replaceVariables("${config.type/variable_name}", configurations));
+    assertEquals("config.type_underscore",
+      helper.replaceVariables("${config.type/variable_name}", configurations));
 
-    Assert.assertEquals("dot",
-        helper.replaceVariables("${variable.name}", configurations));
+    assertEquals("dot",
+      helper.replaceVariables("${variable.name}", configurations));
 
     // Replacement yields an empty string
-    Assert.assertEquals("",
-        helper.replaceVariables("${config-type/variable.name2}", configurations));
+    assertEquals("",
+      helper.replaceVariables("${config-type/variable.name2}", configurations));
 
 
     // This might cause an infinite loop... we assume protection is in place...
     try {
-      Assert.assertEquals("${config-type2/self_reference}",
-          helper.replaceVariables("${config-type2/self_reference}", configurations));
+      assertEquals("${config-type2/self_reference}",
+        helper.replaceVariables("${config-type2/self_reference}", configurations));
       Assert.fail(String.format("%s expected to be thrown", AmbariException.class.getName()));
     } catch (AmbariException e) {
       // This is expected...
@@ -146,14 +148,14 @@ public class VariableReplacementHelperTest {
       }
     };
 
-    Assert.assertEquals("hive.metastore.local=false,hive.metastore.uris=thrift://c6401.ambari.apache.org:9083,hive.metastore.sasl.enabled=true,hive.metastore.execute.setugi=true,hive.metastore.warehouse.dir=/apps/hive/warehouse,hive.exec.mode.local.auto=false,hive.metastore.kerberos.principal=hive/_HOST@EXAMPLE.COM",
-        helper.replaceVariables("hive.metastore.local=false,hive.metastore.uris=thrift://${host}:9083,hive.metastore.sasl.enabled=true,hive.metastore.execute.setugi=true,hive.metastore.warehouse.dir=/apps/hive/warehouse,hive.exec.mode.local.auto=false,hive.metastore.kerberos.principal=hive/_HOST@${realm}", configurations));
+    assertEquals("hive.metastore.local=false,hive.metastore.uris=thrift://c6401.ambari.apache.org:9083,hive.metastore.sasl.enabled=true,hive.metastore.execute.setugi=true,hive.metastore.warehouse.dir=/apps/hive/warehouse,hive.exec.mode.local.auto=false,hive.metastore.kerberos.principal=hive/_HOST@EXAMPLE.COM",
+      helper.replaceVariables("hive.metastore.local=false,hive.metastore.uris=thrift://${host}:9083,hive.metastore.sasl.enabled=true,hive.metastore.execute.setugi=true,hive.metastore.warehouse.dir=/apps/hive/warehouse,hive.exec.mode.local.auto=false,hive.metastore.kerberos.principal=hive/_HOST@${realm}", configurations));
 
-    Assert.assertEquals("Hello my realm is {EXAMPLE.COM}",
-        helper.replaceVariables("Hello my realm is {${realm}}", configurations));
+    assertEquals("Hello my realm is {EXAMPLE.COM}",
+      helper.replaceVariables("Hello my realm is {${realm}}", configurations));
 
-    Assert.assertEquals("$c6401.ambari.apache.org",
-        helper.replaceVariables("$${host}", configurations));
+    assertEquals("$c6401.ambari.apache.org",
+      helper.replaceVariables("$${host}", configurations));
   }
 
   @Test
@@ -168,7 +170,7 @@ public class VariableReplacementHelperTest {
         put("kafka-broker", new HashMap<String, String>() {{
           put("listeners", "PLAINTEXT://localhost:6667");
         }});
-        
+
         put("clusterHostInfo", new HashMap<String, String>() {{
           put("hive_metastore_host", "host1.unit.test, host2.unit.test , host3.unit.test"); // spaces are there on purpose.
         }});
@@ -182,11 +184,11 @@ public class VariableReplacementHelperTest {
       }
     };
 
-    Assert.assertEquals("test=thrift://one:9083\\,thrift://two:9083\\,thrift://three:9083\\,thrift://four:9083",
-        helper.replaceVariables("test=${delimited.data|each(thrift://%s:9083, \\\\,, \\s*\\,\\s*)}", configurations));
+    assertEquals("test=thrift://one:9083\\,thrift://two:9083\\,thrift://three:9083\\,thrift://four:9083",
+      helper.replaceVariables("test=${delimited.data|each(thrift://%s:9083, \\\\,, \\s*\\,\\s*)}", configurations));
 
-    Assert.assertEquals("hive.metastore.local=false,hive.metastore.uris=thrift://host1.unit.test:9083\\,thrift://host2.unit.test:9083\\,thrift://host3.unit.test:9083,hive.metastore.sasl.enabled=true,hive.metastore.execute.setugi=true,hive.metastore.warehouse.dir=/apps/hive/warehouse,hive.exec.mode.local.auto=false,hive.metastore.kerberos.principal=hive/_HOST@UNIT.TEST",
-        helper.replaceVariables("hive.metastore.local=false,hive.metastore.uris=${clusterHostInfo/hive_metastore_host | each(thrift://%s:9083, \\\\,, \\s*\\,\\s*)},hive.metastore.sasl.enabled=true,hive.metastore.execute.setugi=true,hive.metastore.warehouse.dir=/apps/hive/warehouse,hive.exec.mode.local.auto=false,hive.metastore.kerberos.principal=hive/_HOST@${realm}", configurations));
+    assertEquals("hive.metastore.local=false,hive.metastore.uris=thrift://host1.unit.test:9083\\,thrift://host2.unit.test:9083\\,thrift://host3.unit.test:9083,hive.metastore.sasl.enabled=true,hive.metastore.execute.setugi=true,hive.metastore.warehouse.dir=/apps/hive/warehouse,hive.exec.mode.local.auto=false,hive.metastore.kerberos.principal=hive/_HOST@UNIT.TEST",
+      helper.replaceVariables("hive.metastore.local=false,hive.metastore.uris=${clusterHostInfo/hive_metastore_host | each(thrift://%s:9083, \\\\,, \\s*\\,\\s*)},hive.metastore.sasl.enabled=true,hive.metastore.execute.setugi=true,hive.metastore.warehouse.dir=/apps/hive/warehouse,hive.exec.mode.local.auto=false,hive.metastore.kerberos.principal=hive/_HOST@${realm}", configurations));
 
     List<String> expected;
     List<String> actual;
@@ -195,38 +197,52 @@ public class VariableReplacementHelperTest {
     actual = new LinkedList<>(Arrays.asList(helper.replaceVariables("${foobar-site/hello | append(foobar-site/data, \\,, true)}", configurations).split(",")));
     Collections.sort(expected);
     Collections.sort(actual);
-    Assert.assertEquals(expected, actual);
+    assertEquals(expected, actual);
 
     expected = new LinkedList<>(Arrays.asList("four", "hello", "one", "there", "three", "two"));
     actual = new LinkedList<>(Arrays.asList(helper.replaceVariables("${foobar-site/hello_there | append(foobar-site/data, \\,, true)}", configurations).split(",")));
     Collections.sort(expected);
     Collections.sort(actual);
-    Assert.assertEquals(expected, actual);
+    assertEquals(expected, actual);
 
     expected = new LinkedList<>(Arrays.asList("four", "hello", "one", "there", "three", "two"));
     actual = new LinkedList<>(Arrays.asList(helper.replaceVariables("${foobar-site/hello_there_one | append(foobar-site/data, \\,, true)}", configurations).split(",")));
     Collections.sort(expected);
     Collections.sort(actual);
-    Assert.assertEquals(expected, actual);
+    assertEquals(expected, actual);
 
     expected = new LinkedList<>(Arrays.asList("four", "hello", "one", "one", "there", "three", "two"));
     actual = new LinkedList<>(Arrays.asList(helper.replaceVariables("${foobar-site/hello_there_one | append(foobar-site/data, \\,, false)}", configurations).split(",")));
     Collections.sort(expected);
     Collections.sort(actual);
-    Assert.assertEquals(expected, actual);
+    assertEquals(expected, actual);
 
     // Test invalid number of arguments.
     try {
       helper.replaceVariables("${foobar-site/hello_there_one | append(foobar-site/data, \\,)}", configurations);
       Assert.fail("Expected IllegalArgumentException");
-    }
-    catch (IllegalArgumentException e) {
+    } catch (IllegalArgumentException e) {
       // Ignore this is expected.
     }
 
-    Assert.assertEquals("test=unit.test", helper.replaceVariables("test=${realm|toLower()}", configurations));
-  
-    Assert.assertEquals("PLAINTEXTSASL://localhost:6667", helper.replaceVariables("${kafka-broker/listeners|replace(\\bPLAINTEXT\\b,PLAINTEXTSASL)}", configurations)); 
+    assertEquals("test=unit.test", helper.replaceVariables("test=${realm|toLower()}", configurations));
+
+    assertEquals("PLAINTEXTSASL://localhost:6667", helper.replaceVariables("${kafka-broker/listeners|replace(\\bPLAINTEXT\\b,PLAINTEXTSASL)}", configurations));
   }
 
-}
+  @Test
+  public void testReplacePrincipalWithPrimary() throws AmbariException {
+    Map<String, Map<String, String>> config = new HashMap<String, Map<String, String>>() {
+      {
+        put("principals", new HashMap<String, String>() {{
+          put("resource_manager_rm", "rm/HOST@EXAMPLE.COM");
+          put("hive_server_hive", "hive@EXAMPLE.COM");
+          put("hdfs", "hdfs");
+        }});
+      }
+    };
+    assertEquals("hdfs", helper.replaceVariables("${principals/hdfs|principalPrimary()}", config));
+    assertEquals("rm", helper.replaceVariables("${principals/resource_manager_rm|principalPrimary()}", config));
+    assertEquals("hive", helper.replaceVariables("${principals/hive_server_hive|principalPrimary()}", config));
+  }
+}
\ No newline at end of file