From dc8e01e752b729906b67bcd57570facb36bd5bc7 Mon Sep 17 00:00:00 2001 From: Vinod Kumar Vavilapalli Date: Thu, 13 Feb 2014 21:25:34 +0000 Subject: [PATCH] YARN-1676. Modified RM HA handling of user-to-group mappings to be available across RM failover by making using of a remote configuration-provider. Contributed by Xuan Gong. svn merge --ignore-ancestry -c 1568041 ../../trunk/ git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/branches/branch-2@1568042 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/hadoop/security/Groups.java | 15 +++ hadoop-yarn-project/CHANGES.txt | 4 + .../server/resourcemanager/AdminService.java | 17 +-- .../resourcemanager/ResourceManager.java | 4 + .../resourcemanager/TestRMAdminService.java | 120 ++++++++++++++++++ 5 files changed, 152 insertions(+), 8 deletions(-) diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/Groups.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/Groups.java index 097bc30dfe3..ea18b94dea7 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/Groups.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/Groups.java @@ -27,6 +27,7 @@ import java.util.concurrent.ConcurrentHashMap; import org.apache.hadoop.HadoopIllegalArgumentException; import org.apache.hadoop.classification.InterfaceAudience; +import org.apache.hadoop.classification.InterfaceAudience.Private; import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.CommonConfigurationKeys; @@ -240,4 +241,18 @@ public class Groups { } return GROUPS; } + + /** + * Create new groups used to map user-to-groups with loaded configuration. + * @param conf + * @return the groups being used to map user-to-groups. + */ + @Private + public static synchronized Groups + getUserToGroupsMappingServiceWithLoadedConfiguration( + Configuration conf) { + + GROUPS = new Groups(conf); + return GROUPS; + } } diff --git a/hadoop-yarn-project/CHANGES.txt b/hadoop-yarn-project/CHANGES.txt index e2020f25383..ba7cb9eee78 100644 --- a/hadoop-yarn-project/CHANGES.txt +++ b/hadoop-yarn-project/CHANGES.txt @@ -169,6 +169,10 @@ Release 2.4.0 - UNRELEASED YARN-1345. Remove FINAL_SAVING state from YarnApplicationAttemptState (Zhijie Shen via jianhe) + YARN-1676. Modified RM HA handling of user-to-group mappings to + be available across RM failover by making using of a remote + configuration-provider. (Xuan Gong via vinodkv) + OPTIMIZATIONS BUG FIXES diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/AdminService.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/AdminService.java index da479b47ee8..6ebf90a6b5f 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/AdminService.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/AdminService.java @@ -381,21 +381,22 @@ public class AdminService extends CompositeService implements @Override public RefreshUserToGroupsMappingsResponse refreshUserToGroupsMappings( RefreshUserToGroupsMappingsRequest request) - throws YarnException, StandbyException { - UserGroupInformation user = checkAcls("refreshUserToGroupsMappings"); + throws YarnException, IOException { + String argName = "refreshUserToGroupsMappings"; + UserGroupInformation user = checkAcls(argName); - // TODO (YARN-1459): Revisit handling user-groups on Standby RM if (!isRMActive()) { - RMAuditLogger.logFailure(user.getShortUserName(), - "refreshUserToGroupsMapping", + RMAuditLogger.logFailure(user.getShortUserName(), argName, adminAcl.toString(), "AdminService", "ResourceManager is not active. Can not refresh user-groups."); throwStandbyException(); } - Groups.getUserToGroupsMappingService().refresh(); - RMAuditLogger.logSuccess(user.getShortUserName(), - "refreshUserToGroupsMappings", "AdminService"); + Groups.getUserToGroupsMappingService( + getConfiguration(getConfig(), + YarnConfiguration.CORE_SITE_CONFIGURATION_FILE)).refresh(); + + RMAuditLogger.logSuccess(user.getShortUserName(), argName, "AdminService"); return recordFactory.newRecordInstance( RefreshUserToGroupsMappingsResponse.class); diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceManager.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceManager.java index 1040cc5c526..5ef58a74d8e 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceManager.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceManager.java @@ -33,6 +33,7 @@ import org.apache.hadoop.http.HttpConfig; import org.apache.hadoop.http.HttpConfig.Policy; import org.apache.hadoop.metrics2.lib.DefaultMetricsSystem; import org.apache.hadoop.metrics2.source.JvmMetrics; +import org.apache.hadoop.security.Groups; import org.apache.hadoop.security.SecurityUtil; import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.service.AbstractService; @@ -198,6 +199,9 @@ public class ResourceManager extends CompositeService implements Recoverable { this.conf = this.configurationProvider.getConfiguration(this.conf, YarnConfiguration.CORE_SITE_CONFIGURATION_FILE); + // Do refreshUserToGroupsMappings with loaded core-site.xml + Groups.getUserToGroupsMappingServiceWithLoadedConfiguration(this.conf) + .refresh(); } // register the handlers for all AlwaysOn services using setupDispatcher(). diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestRMAdminService.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestRMAdminService.java index ee008e93b43..9746664dba8 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestRMAdminService.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestRMAdminService.java @@ -24,10 +24,17 @@ import java.io.DataOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.CommonConfigurationKeys; import org.apache.hadoop.fs.CommonConfigurationKeysPublic; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; +import org.apache.hadoop.security.GroupMappingServiceProvider; +import org.apache.hadoop.security.Groups; +import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.security.authorize.AccessControlList; import org.apache.hadoop.security.authorize.ProxyUsers; import org.apache.hadoop.security.authorize.ServiceAuthorizationManager; @@ -37,6 +44,7 @@ import org.apache.hadoop.yarn.server.api.protocolrecords.RefreshAdminAclsRequest import org.apache.hadoop.yarn.server.api.protocolrecords.RefreshQueuesRequest; import org.apache.hadoop.yarn.server.api.protocolrecords.RefreshServiceAclsRequest; import org.apache.hadoop.yarn.server.api.protocolrecords.RefreshSuperUserGroupsConfigurationRequest; +import org.apache.hadoop.yarn.server.api.protocolrecords.RefreshUserToGroupsMappingsRequest; import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler; import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacitySchedulerConfiguration; import org.junit.After; @@ -366,6 +374,84 @@ public class TestRMAdminService { .get("hadoop.proxyuser.test.hosts").contains("test_hosts")); } + @Test + public void testRefreshUserToGroupsMappingsWithLocalConfigurationProvider() { + rm = new MockRM(configuration); + rm.init(configuration); + rm.start(); + try { + rm.adminService + .refreshUserToGroupsMappings(RefreshUserToGroupsMappingsRequest + .newInstance()); + } catch (Exception ex) { + fail("Using localConfigurationProvider. Should not get any exception."); + } + } + + @Test + public void + testRefreshUserToGroupsMappingsWithFileSystemBasedConfigurationProvider() + throws IOException, YarnException { + configuration.set(YarnConfiguration.RM_CONFIGURATION_PROVIDER_CLASS, + "org.apache.hadoop.yarn.FileSystemBasedConfigurationProvider"); + try { + rm = new MockRM(configuration); + rm.init(configuration); + rm.start(); + fail("Should throw an exception"); + } catch (Exception ex) { + // Expect exception here + } + + String user = UserGroupInformation.getCurrentUser().getUserName(); + List groupWithInit = + new ArrayList(Groups.getUserToGroupsMappingService( + configuration).getGroups(user)); + + // upload default configurations + uploadDefaultConfiguration(); + Configuration conf = new Configuration(); + conf.setClass(CommonConfigurationKeys.HADOOP_SECURITY_GROUP_MAPPING, + MockUnixGroupsMapping.class, + GroupMappingServiceProvider.class); + uploadConfiguration(conf, "core-site.xml"); + + try { + rm = new MockRM(configuration); + rm.init(configuration); + rm.start(); + } catch (Exception ex) { + fail("Should not get any exceptions"); + } + + // Make sure RM will use the updated GroupMappingServiceProvider + List groupBefore = + new ArrayList(Groups.getUserToGroupsMappingService( + configuration).getGroups(user)); + Assert.assertTrue(groupBefore.contains("test_group_A") + && groupBefore.contains("test_group_B") + && groupBefore.contains("test_group_C") && groupBefore.size() == 3); + Assert.assertTrue(groupWithInit.size() != groupBefore.size()); + Assert.assertFalse(groupWithInit.contains("test_group_A") + || groupWithInit.contains("test_group_B") + || groupWithInit.contains("test_group_C")); + + // update the groups + MockUnixGroupsMapping.updateGroups(); + + rm.adminService + .refreshUserToGroupsMappings(RefreshUserToGroupsMappingsRequest + .newInstance()); + List groupAfter = + Groups.getUserToGroupsMappingService(configuration).getGroups(user); + + // should get the updated groups + Assert.assertTrue(groupAfter.contains("test_group_D") + && groupAfter.contains("test_group_E") + && groupAfter.contains("test_group_F") && groupAfter.size() == 3); + + } + private String writeConfigurationXML(Configuration conf, String confXMLName) throws IOException { DataOutputStream output = null; @@ -418,4 +504,38 @@ public class TestRMAdminService { .addResource(YarnConfiguration.HADOOP_POLICY_CONFIGURATION_FILE); uploadConfiguration(hadoopPolicyConf, "hadoop-policy.xml"); } + + private static class MockUnixGroupsMapping implements + GroupMappingServiceProvider { + + @SuppressWarnings("serial") + private static List group = new ArrayList() {{ + add("test_group_A"); + add("test_group_B"); + add("test_group_C"); + }}; + + @Override + public List getGroups(String user) throws IOException { + return group; + } + + @Override + public void cacheGroupsRefresh() throws IOException { + // Do nothing + } + + @Override + public void cacheGroupsAdd(List groups) throws IOException { + // Do nothing + } + + public static void updateGroups() { + group.clear(); + group.add("test_group_D"); + group.add("test_group_E"); + group.add("test_group_F"); + } + } + }