18
18
19
19
package org .apache .hadoop .yarn .server .resourcemanager .placement .csmappingrule ;
20
20
21
+ import org .apache .hadoop .conf .Configuration ;
22
+ import org .apache .hadoop .security .GroupMappingServiceProvider ;
21
23
import org .apache .hadoop .thirdparty .com .google .common .collect .ImmutableMap ;
22
24
import org .apache .hadoop .thirdparty .com .google .common .collect .ImmutableSet ;
23
25
import org .apache .hadoop .security .Groups ;
51
53
import static junit .framework .TestCase .assertNull ;
52
54
import static junit .framework .TestCase .assertTrue ;
53
55
import static junit .framework .TestCase .fail ;
56
+ import static org .apache .hadoop .fs .CommonConfigurationKeysPublic .HADOOP_SECURITY_GROUP_MAPPING ;
54
57
import static org .mockito .ArgumentMatchers .isNull ;
55
58
import static org .mockito .Mockito .mock ;
56
59
import static org .mockito .Mockito .when ;
@@ -771,4 +774,138 @@ public void testSecondaryGroupNameCleanup() throws IOException {
771
774
"Application should have been placed to root.groups.sec_dot_test_dot_grp" ,
772
775
engine , app , "test.user" , "root.groups.sec_dot_test_dot_grp" );
773
776
}
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
+ }
774
911
}
0 commit comments