You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by st...@apache.org on 2017/06/08 13:41:55 UTC

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

Repository: ambari
Updated Branches:
  refs/heads/branch-2.5 e615beb8e -> afeb6f3a3


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/afeb6f3a
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/afeb6f3a
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/afeb6f3a

Branch: refs/heads/branch-2.5
Commit: afeb6f3a3402652db5dbda2c598c77b74ef0988e
Parents: e615beb
Author: Attila Magyar <am...@hortonworks.com>
Authored: Thu Jun 8 15:41:37 2017 +0200
Committer: Toader, Sebastian <st...@hortonworks.com>
Committed: Thu Jun 8 15:41:37 2017 +0200

----------------------------------------------------------------------
 .../security/kerberos/kerberos_descriptor.md    |  5 +-
 .../server/controller/KerberosHelperImpl.java   | 19 +++--
 .../state/kerberos/KerberosDescriptor.java      | 51 ++++++++++++++
 .../kerberos/VariableReplacementHelper.java     | 23 +++++-
 .../stacks/HDP/2.6/services/YARN/kerberos.json  |  4 +-
 .../server/controller/KerberosHelperTest.java   | 43 ++++++------
 .../state/kerberos/KerberosDescriptorTest.java  |  9 +++
 .../kerberos/VariableReplacementHelperTest.java | 73 ++++++++++++--------
 8 files changed, 169 insertions(+), 58 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/afeb6f3a/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/afeb6f3a/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 846bc3d..802c93d 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
@@ -1276,10 +1276,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/afeb6f3a/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 e7be589..86a5e01 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.TreeMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+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/afeb6f3a/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 77333b8..0dd551c 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
@@ -42,7 +42,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, ...)
@@ -57,6 +57,7 @@ public class VariableReplacementHelper {
       put("each", new EachFunction());
       put("toLower", new ToLowerFunction());
       put("append", new AppendFunction());
+      put("principalPrimary", new PrincipalPrimary());
     }
   };
 
@@ -377,4 +378,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/afeb6f3a/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/afeb6f3a/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 2162e76..9b9f087 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
@@ -1009,27 +1009,27 @@ 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.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.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.getServiceComponents())
         .andReturn(Collections.<String, ServiceComponent>emptyMap())
-        .times(1);
+        .anyTimes();
     service2.setSecurityState(SecurityState.SECURED_KERBEROS);
     expectLastCall().once();
 
@@ -1094,7 +1094,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.
@@ -1301,7 +1301,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.
@@ -1499,7 +1499,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.
@@ -1646,7 +1646,7 @@ public class KerberosHelperTest extends EasyMockSupport {
     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.getName()).andReturn("SERVICE1").anyTimes();
@@ -1722,7 +1722,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;
@@ -2263,7 +2263,7 @@ public class KerberosHelperTest extends EasyMockSupport {
     expect(kerberosDescriptor.getIdentities(eq(true), anyObject(Map.class))).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);
@@ -2695,7 +2695,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);
@@ -2822,7 +2822,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);
@@ -2858,9 +2858,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() {
@@ -3110,7 +3111,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.
@@ -3312,7 +3313,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.
@@ -3489,7 +3490,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
@@ -3659,7 +3660,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.
@@ -3969,7 +3970,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/afeb6f3a/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 d484ee5..747558d 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/afeb6f3a/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 857047b..159ad69 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
@@ -24,6 +24,8 @@ import org.apache.ambari.server.AmbariException;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 
+import static junit.framework.Assert.assertEquals;
+
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
@@ -72,62 +74,62 @@ public class VariableReplacementHelperTest {
       }
     };
 
-    Assert.assertEquals("concrete",
+    assertEquals("concrete",
         helper.replaceVariables("concrete", configurations));
 
-    Assert.assertEquals("Hello World",
+    assertEquals("Hello World",
         helper.replaceVariables("${global_variable}", configurations));
 
-    Assert.assertEquals("Replacement1",
+    assertEquals("Replacement1",
         helper.replaceVariables("${config-type/variable.name}", configurations));
 
-    Assert.assertEquals("Replacement1|Replacement2",
+    assertEquals("Replacement1|Replacement2",
         helper.replaceVariables("${config-type/variable.name}|${config-type2/variable.name}", configurations));
 
-    Assert.assertEquals("Replacement1|Replacement2|${config-type3/variable.name}",
+    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",
+    assertEquals("Replacement2|Replacement2",
         helper.replaceVariables("${config-type/variable.name1}|${config-type2/variable.name}", configurations));
 
-    Assert.assertEquals("Replacement1_reference",
+    assertEquals("Replacement1_reference",
         helper.replaceVariables("${config-type/variable.name}_reference", configurations));
 
-    Assert.assertEquals("dash",
+    assertEquals("dash",
         helper.replaceVariables("${variable-name}", configurations));
 
-    Assert.assertEquals("underscore",
+    assertEquals("underscore",
         helper.replaceVariables("${variable_name}", configurations));
 
-    Assert.assertEquals("config_type_dot",
+    assertEquals("config_type_dot",
         helper.replaceVariables("${config_type/variable.name}", configurations));
 
-    Assert.assertEquals("config_type_dash",
+    assertEquals("config_type_dash",
         helper.replaceVariables("${config_type/variable-name}", configurations));
 
-    Assert.assertEquals("config_type_underscore",
+    assertEquals("config_type_underscore",
         helper.replaceVariables("${config_type/variable_name}", configurations));
 
-    Assert.assertEquals("config.type_dot",
+    assertEquals("config.type_dot",
         helper.replaceVariables("${config.type/variable.name}", configurations));
 
-    Assert.assertEquals("config.type_dash",
+    assertEquals("config.type_dash",
         helper.replaceVariables("${config.type/variable-name}", configurations));
 
-    Assert.assertEquals("config.type_underscore",
+    assertEquals("config.type_underscore",
         helper.replaceVariables("${config.type/variable_name}", configurations));
 
-    Assert.assertEquals("dot",
+    assertEquals("dot",
         helper.replaceVariables("${variable.name}", configurations));
 
     // Replacement yields an empty string
-    Assert.assertEquals("",
+    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}",
+      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) {
@@ -146,13 +148,13 @@ 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",
+    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}",
+    assertEquals("Hello my realm is {EXAMPLE.COM}",
         helper.replaceVariables("Hello my realm is {${realm}}", configurations));
 
-    Assert.assertEquals("$c6401.ambari.apache.org",
+    assertEquals("$c6401.ambari.apache.org",
         helper.replaceVariables("$${host}", configurations));
   }
 
@@ -178,10 +180,10 @@ public class VariableReplacementHelperTest {
       }
     };
 
-    Assert.assertEquals("test=thrift://one:9083\\,thrift://two:9083\\,thrift://three:9083\\,thrift://four:9083",
+    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",
+    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;
@@ -191,25 +193,25 @@ public class VariableReplacementHelperTest {
     actual = new LinkedList<String>(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<String>(Arrays.asList("four", "hello", "one", "there", "three", "two"));
     actual = new LinkedList<String>(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<String>(Arrays.asList("four", "hello", "one", "there", "three", "two"));
     actual = new LinkedList<String>(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<String>(Arrays.asList("four", "hello", "one", "one", "there", "three", "two"));
     actual = new LinkedList<String>(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 {
@@ -220,7 +222,22 @@ public class VariableReplacementHelperTest {
       // Ignore this is expected.
     }
 
-    Assert.assertEquals("test=unit.test", helper.replaceVariables("test=${realm|toLower()}", configurations));
+    assertEquals("test=unit.test", helper.replaceVariables("test=${realm|toLower()}", 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