Skip to content

Commit 414d401

Browse files
YARN-10958. Use correct configuration for Group service init in CSMappingPlacementRule (#3560)
* YARN-10958. Initial commit * Fix javadoc + behaviour * Fix review comments * fix checkstyle + blanks * fix checkstyle + blanks * Fix checkstyle + blanks
1 parent 3e11354 commit 414d401

File tree

3 files changed

+148
-1
lines changed
  • hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security
  • hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src

3 files changed

+148
-1
lines changed

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/Groups.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,4 +493,9 @@ public static synchronized Groups getUserToGroupsMappingService(
493493
GROUPS = new Groups(conf);
494494
return GROUPS;
495495
}
496+
497+
@VisibleForTesting
498+
public static void reset() {
499+
GROUPS = null;
500+
}
496501
}

hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/placement/CSMappingPlacementRule.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ public boolean initialize(ResourceScheduler scheduler) throws IOException {
133133
overrideWithQueueMappings = conf.getOverrideWithQueueMappings();
134134

135135
if (groups == null) {
136-
groups = Groups.getUserToGroupsMappingService(conf);
136+
groups = Groups.getUserToGroupsMappingService(csContext.getConf());
137137
}
138138

139139
MappingRuleValidationContext validationContext = buildValidationContext();
@@ -535,4 +535,9 @@ private String cleanName(String name) {
535535
return name;
536536
}
537537
}
538+
539+
@VisibleForTesting
540+
public Groups getGroups() {
541+
return groups;
542+
}
538543
}

hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/placement/csmappingrule/TestCSMappingPlacementRule.java

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
package org.apache.hadoop.yarn.server.resourcemanager.placement.csmappingrule;
2020

21+
import org.apache.hadoop.conf.Configuration;
22+
import org.apache.hadoop.security.GroupMappingServiceProvider;
2123
import org.apache.hadoop.thirdparty.com.google.common.collect.ImmutableMap;
2224
import org.apache.hadoop.thirdparty.com.google.common.collect.ImmutableSet;
2325
import org.apache.hadoop.security.Groups;
@@ -51,6 +53,7 @@
5153
import static junit.framework.TestCase.assertNull;
5254
import static junit.framework.TestCase.assertTrue;
5355
import static junit.framework.TestCase.fail;
56+
import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SECURITY_GROUP_MAPPING;
5457
import static org.mockito.ArgumentMatchers.isNull;
5558
import static org.mockito.Mockito.mock;
5659
import static org.mockito.Mockito.when;
@@ -771,4 +774,138 @@ public void testSecondaryGroupNameCleanup() throws IOException {
771774
"Application should have been placed to root.groups.sec_dot_test_dot_grp",
772775
engine, app, "test.user", "root.groups.sec_dot_test_dot_grp");
773776
}
777+
778+
/**
779+
* 1. Invoke Groups.reset(). This make sure that the underlying singleton {@link Groups#GROUPS}
780+
* is set to null.<br>
781+
* 2. Create a Configuration object in which the property "hadoop.security.group.mapping"
782+
* refers to an existing a test implementation.<br>
783+
* 3. Create a mock CapacityScheduler where getConf() and getConfiguration() contain different
784+
* settings for "hadoop.security.group.mapping". Since getConf() is the service config, this
785+
* should return the config object created in step #2.<br>
786+
* 4. Create an instance of CSMappingPlacementRule with a single primary group rule.<br>
787+
* 5. Run the placement evaluation.<br>
788+
* 6. Expected: The returned queue matches what is supposed to be coming from the test group
789+
* mapping service ("testuser" --> "testGroup1").<br>
790+
* 7. Modify "hadoop.security.group.mapping" in the config object created in step #2.
791+
* This step is required to guarantee that the CSMappingPlacementRule doesn't try to recreate
792+
* the group mapping implementation and uses the one that was previously created.<br>
793+
* 8. Call Groups.refresh() which changes the group mapping ("testuser" --> "testGroup0"). This
794+
* requires that the test group mapping service implement GroupMappingServiceProvider
795+
* .cacheGroupsRefresh().<br>
796+
* 9. Create a new instance of CSMappingPlacementRule. This is important as we want to test
797+
* that even this new {@link CSMappingPlacementRule} instance uses the same group mapping
798+
* instance.<br>
799+
* 10. Run the placement evaluation again<br>
800+
* 11. Expected: with the same user, the target queue has changed to 'testGroup0'.<br>
801+
* <p>
802+
* These all looks convoluted, but the steps above make sure all the following conditions are met:
803+
* <p>
804+
* 1. CSMappingPlacementRule will force the initialization of groups.<br>
805+
* 2. We select the correct configuration for group service init.<br>
806+
* 3. We don't create a new Groups instance if the singleton is initialized, so we cover the
807+
* original problem described in YARN-10597.<br>
808+
*/
809+
@Test
810+
public void testPlacementEngineSelectsCorrectConfigurationForGroupMapping() throws IOException {
811+
Groups.reset();
812+
final String user = "testuser";
813+
814+
//Create service-wide configuration object
815+
Configuration yarnConf = new Configuration();
816+
yarnConf.setClass(HADOOP_SECURITY_GROUP_MAPPING, MockUnixGroupsMapping.class,
817+
GroupMappingServiceProvider.class);
818+
819+
//Create CS configuration object with a single, primary group mapping rule
820+
List<MappingRule> mappingRules = new ArrayList<>();
821+
mappingRules.add(
822+
new MappingRule(
823+
MappingRuleMatchers.createUserMatcher(user),
824+
(new MappingRuleActions.PlaceToQueueAction(
825+
"root.man.%primary_group", true))
826+
.setFallbackReject()));
827+
CapacitySchedulerConfiguration csConf = new CapacitySchedulerConfiguration() {
828+
@Override
829+
public List<MappingRule> getMappingRules() {
830+
return mappingRules;
831+
}
832+
};
833+
csConf.setOverrideWithQueueMappings(true);
834+
//Intentionally add a dummy implementation class -
835+
// The "HADOOP_SECURITY_GROUP_MAPPING" should not be read from the
836+
// CapacitySchedulerConfiguration instance!
837+
csConf.setClass(HADOOP_SECURITY_GROUP_MAPPING, String.class, Object.class);
838+
839+
CapacityScheduler cs = createMockCS(yarnConf, csConf);
840+
841+
//Create app, submit to placement engine, expecting queue=testGroup1
842+
CSMappingPlacementRule engine = initPlacementEngine(cs);
843+
ApplicationSubmissionContext app = createApp("app");
844+
assertPlace(engine, app, user, "root.man.testGroup1");
845+
846+
//Intentionally add a dummy implementation class!
847+
// The "HADOOP_SECURITY_GROUP_MAPPING" should not be read from the
848+
// CapacitySchedulerConfiguration instance!
849+
//This makes sure that the Groups instance is not recreated by CSMappingPlacementRule
850+
yarnConf.setClass(HADOOP_SECURITY_GROUP_MAPPING, String.class, Object.class);
851+
852+
//Refresh the groups, this makes testGroup0 as primary group for "testUser"
853+
engine.getGroups().refresh();
854+
855+
//Create app, submit to placement engine, expecting queue=testGroup0 (the new primary group)
856+
engine = initPlacementEngine(cs);
857+
assertPlace(engine, app, user, "root.man.testGroup0");
858+
}
859+
860+
private CSMappingPlacementRule initPlacementEngine(CapacityScheduler cs) throws IOException {
861+
CSMappingPlacementRule engine = new CSMappingPlacementRule();
862+
engine.setFailOnConfigError(true);
863+
engine.initialize(cs);
864+
return engine;
865+
}
866+
867+
private CapacityScheduler createMockCS(Configuration conf,
868+
CapacitySchedulerConfiguration csConf) {
869+
CapacitySchedulerQueueManager qm =
870+
mock(CapacitySchedulerQueueManager.class);
871+
createQueueHierarchy(qm);
872+
873+
CapacityScheduler cs = mock(CapacityScheduler.class);
874+
when(cs.getConfiguration()).thenReturn(csConf);
875+
when(cs.getConf()).thenReturn(conf);
876+
when(cs.getCapacitySchedulerQueueManager()).thenReturn(qm);
877+
return cs;
878+
}
879+
880+
public static class MockUnixGroupsMapping implements GroupMappingServiceProvider {
881+
882+
public MockUnixGroupsMapping() {
883+
GROUP.clear();
884+
GROUP.add("testGroup1");
885+
GROUP.add("testGroup2");
886+
GROUP.add("testGroup3");
887+
}
888+
889+
private static final List<String> GROUP = new ArrayList<>();
890+
891+
@Override
892+
public List<String> getGroups(String user) throws IOException {
893+
return GROUP;
894+
}
895+
896+
@Override
897+
public void cacheGroupsRefresh() {
898+
GROUP.add(0, "testGroup0");
899+
}
900+
901+
@Override
902+
public void cacheGroupsAdd(List<String> groups) {
903+
// Do nothing
904+
}
905+
906+
@Override
907+
public Set<String> getGroupsSet(String user) {
908+
return ImmutableSet.copyOf(GROUP);
909+
}
910+
}
774911
}

0 commit comments

Comments
 (0)