2626import static org .junit .Assert .assertNull ;
2727import static org .junit .Assert .assertTrue ;
2828import static org .junit .Assert .fail ;
29+ import static org .mockito .Mockito .doReturn ;
2930import static org .mockito .Mockito .mock ;
31+ import static org .mockito .Mockito .spy ;
3032import static org .mockito .Mockito .when ;
3133import static org .mockito .ArgumentMatchers .anyString ;
3234import static org .mockito .ArgumentMatchers .any ;
@@ -123,6 +125,8 @@ public class TestAuxServices {
123125 .getSimpleName ());
124126 private File manifest = new File (rootDir , "manifest.txt" );
125127 private ObjectMapper mapper = new ObjectMapper ();
128+ private static final FsPermission WRITABLE_BY_OWNER = FsPermission .createImmutable ((short ) 0755 );
129+ private static final FsPermission WRITABLE_BY_GROUP = FsPermission .createImmutable ((short ) 0775 );
126130
127131 @ Parameterized .Parameters
128132 public static Collection <Boolean > getParams () {
@@ -268,6 +272,24 @@ private void writeManifestFile(AuxServiceRecords services, Configuration
268272 mapper .writeValue (manifest , services );
269273 }
270274
275+ /**
276+ * Creates a spy object of AuxServices for test cases which assume that we have proper
277+ * file system permissions by default.
278+ *
279+ * Permission checking iterates through the parents of the manifest file until it
280+ * reaches the system root, so without mocking this the success of the initialization
281+ * would heavily depend on the environment where the test is running.
282+ *
283+ * @return a spy object of AuxServices
284+ */
285+ private AuxServices getSpyAuxServices (AuxiliaryLocalPathHandler auxiliaryLocalPathHandler ,
286+ Context nmContext , DeletionService deletionService ) throws IOException {
287+ AuxServices auxServices = spy (new AuxServices (auxiliaryLocalPathHandler ,
288+ nmContext , deletionService ));
289+ doReturn (true ).when (auxServices ).checkManifestPermissions (any (FileStatus .class ));
290+ return auxServices ;
291+ }
292+
271293 @ SuppressWarnings ("resource" )
272294 @ Test
273295 public void testRemoteAuxServiceClassPath () throws Exception {
@@ -317,7 +339,7 @@ public void testRemoteAuxServiceClassPath() throws Exception {
317339 YarnConfiguration .NM_AUX_SERVICE_REMOTE_CLASSPATH , "ServiceC" ),
318340 testJar .getAbsolutePath ());
319341 }
320- aux = new AuxServices (MOCK_AUX_PATH_HANDLER ,
342+ aux = getSpyAuxServices (MOCK_AUX_PATH_HANDLER ,
321343 mockContext2 , mockDelService2 );
322344 aux .init (conf );
323345 Assert .fail ("The permission of the jar is wrong."
@@ -339,7 +361,7 @@ public void testRemoteAuxServiceClassPath() throws Exception {
339361 YarnConfiguration .NM_AUX_SERVICE_REMOTE_CLASSPATH , "ServiceC" ),
340362 testJar .getAbsolutePath ());
341363 }
342- aux = new AuxServices (MOCK_AUX_PATH_HANDLER ,
364+ aux = getSpyAuxServices (MOCK_AUX_PATH_HANDLER ,
343365 mockContext2 , mockDelService2 );
344366 aux .init (conf );
345367 aux .start ();
@@ -356,7 +378,7 @@ public void testRemoteAuxServiceClassPath() throws Exception {
356378
357379 // initialize the same auxservice again, and make sure that we did not
358380 // re-download the jar from remote directory.
359- aux = new AuxServices (MOCK_AUX_PATH_HANDLER ,
381+ aux = getSpyAuxServices (MOCK_AUX_PATH_HANDLER ,
360382 mockContext2 , mockDelService2 );
361383 aux .init (conf );
362384 aux .start ();
@@ -377,7 +399,7 @@ public void testRemoteAuxServiceClassPath() throws Exception {
377399 FileTime fileTime = FileTime .fromMillis (time );
378400 Files .setLastModifiedTime (Paths .get (testJar .getAbsolutePath ()),
379401 fileTime );
380- aux = new AuxServices (MOCK_AUX_PATH_HANDLER ,
402+ aux = getSpyAuxServices (MOCK_AUX_PATH_HANDLER ,
381403 mockContext2 , mockDelService2 );
382404 aux .init (conf );
383405 aux .start ();
@@ -416,7 +438,7 @@ public void testCustomizedAuxServiceClassPath() throws Exception {
416438 "ServiceC" ), ServiceC .class , Service .class );
417439 }
418440 @ SuppressWarnings ("resource" )
419- AuxServices aux = new AuxServices (MOCK_AUX_PATH_HANDLER ,
441+ AuxServices aux = getSpyAuxServices (MOCK_AUX_PATH_HANDLER ,
420442 MOCK_CONTEXT , MOCK_DEL_SERVICE );
421443 aux .init (conf );
422444 aux .start ();
@@ -469,7 +491,7 @@ public void testCustomizedAuxServiceClassPath() throws Exception {
469491 when (mockDirsHandler .getLocalPathForWrite (anyString ())).thenReturn (
470492 rootAuxServiceDirPath );
471493 when (mockContext2 .getLocalDirsHandler ()).thenReturn (mockDirsHandler );
472- aux = new AuxServices (MOCK_AUX_PATH_HANDLER ,
494+ aux = getSpyAuxServices (MOCK_AUX_PATH_HANDLER ,
473495 mockContext2 , MOCK_DEL_SERVICE );
474496 aux .init (conf );
475497 aux .start ();
@@ -645,7 +667,7 @@ private Configuration getABConf(String aName, String bName,
645667 @ Test
646668 public void testAuxServices () throws IOException {
647669 Configuration conf = getABConf ();
648- final AuxServices aux = new AuxServices (MOCK_AUX_PATH_HANDLER ,
670+ final AuxServices aux = getSpyAuxServices (MOCK_AUX_PATH_HANDLER ,
649671 MOCK_CONTEXT , MOCK_DEL_SERVICE );
650672 aux .init (conf );
651673
@@ -673,7 +695,7 @@ public void testAuxServices() throws IOException {
673695 @ Test
674696 public void testAuxServicesMeta () throws IOException {
675697 Configuration conf = getABConf ();
676- final AuxServices aux = new AuxServices (MOCK_AUX_PATH_HANDLER ,
698+ final AuxServices aux = getSpyAuxServices (MOCK_AUX_PATH_HANDLER ,
677699 MOCK_CONTEXT , MOCK_DEL_SERVICE );
678700 aux .init (conf );
679701
@@ -705,7 +727,7 @@ public void testAuxServicesMeta() throws IOException {
705727 public void testAuxUnexpectedStop () throws IOException {
706728 // AuxServices no longer expected to stop when services stop
707729 Configuration conf = getABConf ();
708- final AuxServices aux = new AuxServices (MOCK_AUX_PATH_HANDLER ,
730+ final AuxServices aux = getSpyAuxServices (MOCK_AUX_PATH_HANDLER ,
709731 MOCK_CONTEXT , MOCK_DEL_SERVICE );
710732 aux .init (conf );
711733 aux .start ();
@@ -721,7 +743,7 @@ public void testAuxUnexpectedStop() throws IOException {
721743 public void testValidAuxServiceName () throws IOException {
722744 Configuration conf = getABConf ("Asrv1" , "Bsrv_2" , ServiceA .class ,
723745 ServiceB .class );
724- final AuxServices aux = new AuxServices (MOCK_AUX_PATH_HANDLER ,
746+ final AuxServices aux = getSpyAuxServices (MOCK_AUX_PATH_HANDLER ,
725747 MOCK_CONTEXT , MOCK_DEL_SERVICE );
726748 try {
727749 aux .init (conf );
@@ -730,7 +752,7 @@ public void testValidAuxServiceName() throws IOException {
730752 }
731753
732754 //Test bad auxService Name
733- final AuxServices aux1 = new AuxServices (MOCK_AUX_PATH_HANDLER ,
755+ final AuxServices aux1 = getSpyAuxServices (MOCK_AUX_PATH_HANDLER ,
734756 MOCK_CONTEXT , MOCK_DEL_SERVICE );
735757 if (useManifest ) {
736758 AuxServiceRecord serviceA =
@@ -760,7 +782,7 @@ public void testAuxServiceRecoverySetup() throws IOException {
760782 conf .setBoolean (YarnConfiguration .NM_RECOVERY_ENABLED , true );
761783 conf .set (YarnConfiguration .NM_RECOVERY_DIR , TEST_DIR .toString ());
762784 try {
763- final AuxServices aux = new AuxServices (MOCK_AUX_PATH_HANDLER ,
785+ final AuxServices aux = getSpyAuxServices (MOCK_AUX_PATH_HANDLER ,
764786 MOCK_CONTEXT , MOCK_DEL_SERVICE );
765787 aux .init (conf );
766788 Assert .assertEquals (2 , aux .getServices ().size ());
@@ -888,59 +910,114 @@ public void testAuxServicesConfChange() throws IOException {
888910 }
889911
890912 @ Test
891- public void testAuxServicesManifestPermissions () throws IOException {
913+ public void testAuxServicesInitWithManifestOwnerAndPermissionCheck () throws IOException {
892914 Assume .assumeTrue (useManifest );
893915 Configuration conf = getABConf ();
894- FileSystem fs = FileSystem .get (conf );
895- fs .setPermission (new Path (manifest .getAbsolutePath ()), FsPermission
896- .createImmutable ((short ) 0777 ));
897- AuxServices aux = new AuxServices (MOCK_AUX_PATH_HANDLER ,
898- MOCK_CONTEXT , MOCK_DEL_SERVICE );
899- aux .init (conf );
900- assertEquals (0 , aux .getServices ().size ());
901-
902- fs .setPermission (new Path (manifest .getAbsolutePath ()), FsPermission
903- .createImmutable ((short ) 0775 ));
904- aux = new AuxServices (MOCK_AUX_PATH_HANDLER ,
905- MOCK_CONTEXT , MOCK_DEL_SERVICE );
916+ AuxServices aux = spy (new AuxServices (MOCK_AUX_PATH_HANDLER ,
917+ MOCK_CONTEXT , MOCK_DEL_SERVICE ));
918+ doReturn (false ).when (aux ).checkManifestPermissions (any (FileStatus .class ));
906919 aux .init (conf );
907920 assertEquals (0 , aux .getServices ().size ());
908921
909- fs .setPermission (new Path (manifest .getAbsolutePath ()), FsPermission
910- .createImmutable ((short ) 0755 ));
911- fs .setPermission (new Path (rootDir .getAbsolutePath ()), FsPermission
912- .createImmutable ((short ) 0775 ));
913- aux = new AuxServices (MOCK_AUX_PATH_HANDLER ,
914- MOCK_CONTEXT , MOCK_DEL_SERVICE );
915- aux .init (conf );
916- assertEquals (0 , aux .getServices ().size ());
917-
918- fs .setPermission (new Path (rootDir .getAbsolutePath ()), FsPermission
919- .createImmutable ((short ) 0755 ));
920- aux = new AuxServices (MOCK_AUX_PATH_HANDLER ,
922+ aux = getSpyAuxServices (MOCK_AUX_PATH_HANDLER ,
921923 MOCK_CONTEXT , MOCK_DEL_SERVICE );
922924 aux .init (conf );
923925 assertEquals (2 , aux .getServices ().size ());
924926
925927 conf .set (YarnConfiguration .YARN_ADMIN_ACL , "" );
926- aux = new AuxServices (MOCK_AUX_PATH_HANDLER ,
928+ aux = getSpyAuxServices (MOCK_AUX_PATH_HANDLER ,
927929 MOCK_CONTEXT , MOCK_DEL_SERVICE );
928930 aux .init (conf );
929931 assertEquals (0 , aux .getServices ().size ());
930932
931933 conf .set (YarnConfiguration .YARN_ADMIN_ACL , UserGroupInformation
932934 .getCurrentUser ().getShortUserName ());
933- aux = new AuxServices (MOCK_AUX_PATH_HANDLER ,
935+ aux = getSpyAuxServices (MOCK_AUX_PATH_HANDLER ,
934936 MOCK_CONTEXT , MOCK_DEL_SERVICE );
935937 aux .init (conf );
936938 assertEquals (2 , aux .getServices ().size ());
937939 }
938940
941+ @ Test
942+ public void testCheckManifestPermissionsWhenFileIsOnlyWritableByOwner () throws IOException {
943+ Assume .assumeTrue (useManifest );
944+ final AuxServices aux = spy (new AuxServices (MOCK_AUX_PATH_HANDLER ,
945+ MOCK_CONTEXT , MOCK_DEL_SERVICE ));
946+ FileStatus manifestFileStatus = mock (FileStatus .class );
947+ Path manifestPath = mock (Path .class );
948+
949+ when (manifestFileStatus .getPermission ()).thenReturn (WRITABLE_BY_OWNER );
950+ when (manifestFileStatus .getPath ()).thenReturn (manifestPath );
951+
952+ assertTrue (aux .checkManifestPermissions (manifestFileStatus ));
953+ }
954+
955+ @ Test
956+ public void testCheckManifestPermissionsWhenFileIsWritableByGroup () throws IOException {
957+ Assume .assumeTrue (useManifest );
958+ final AuxServices aux = spy (new AuxServices (MOCK_AUX_PATH_HANDLER ,
959+ MOCK_CONTEXT , MOCK_DEL_SERVICE ));
960+ FileStatus manifestFileStatus = mock (FileStatus .class );
961+ Path manifestPath = mock (Path .class );
962+
963+ when (manifestFileStatus .getPermission ()).thenReturn (WRITABLE_BY_GROUP );
964+ when (manifestFileStatus .getPath ()).thenReturn (manifestPath );
965+
966+ assertFalse (aux .checkManifestPermissions (manifestFileStatus ));
967+ }
968+
969+ @ Test
970+ public void testCheckManifestPermissionsWhenParentIsWritableByGroup () throws IOException {
971+ Assume .assumeTrue (useManifest );
972+ final AuxServices aux = spy (new AuxServices (MOCK_AUX_PATH_HANDLER ,
973+ MOCK_CONTEXT , MOCK_DEL_SERVICE ));
974+
975+ FileStatus manifestFileStatus = mock (FileStatus .class );
976+ FileStatus parentFolderStatus = mock (FileStatus .class );
977+ when (manifestFileStatus .getPermission ()).thenReturn (WRITABLE_BY_OWNER );
978+ when (parentFolderStatus .getPermission ()).thenReturn (WRITABLE_BY_GROUP );
979+
980+ Path manifestPath = mock (Path .class );
981+ Path parentPath = mock (Path .class );
982+ when (manifestFileStatus .getPath ()).thenReturn (manifestPath );
983+ when (manifestPath .getParent ()).thenReturn (parentPath );
984+
985+ FileSystem manifestFs = mock (FileSystem .class );
986+ when (manifestFs .getFileStatus (parentPath )).thenReturn (parentFolderStatus );
987+ doReturn (manifestFs ).when (aux ).getManifestFS ();
988+
989+ assertFalse (aux .checkManifestPermissions (manifestFileStatus ));
990+ }
991+
992+ @ Test
993+ public void testCheckManifestPermissionsWhenParentAndFileIsWritableByOwner () throws IOException {
994+ Assume .assumeTrue (useManifest );
995+ final AuxServices aux = spy (new AuxServices (MOCK_AUX_PATH_HANDLER ,
996+ MOCK_CONTEXT , MOCK_DEL_SERVICE ));
997+
998+ FileStatus manifestFileStatus = mock (FileStatus .class );
999+ FileStatus parentFolderStatus = mock (FileStatus .class );
1000+ when (manifestFileStatus .getPermission ()).thenReturn (WRITABLE_BY_OWNER );
1001+ when (parentFolderStatus .getPermission ()).thenReturn (WRITABLE_BY_OWNER );
1002+
1003+ Path manifestPath = mock (Path .class );
1004+ Path parentPath = mock (Path .class );
1005+ when (manifestFileStatus .getPath ()).thenReturn (manifestPath );
1006+ when (parentFolderStatus .getPath ()).thenReturn (parentPath );
1007+ when (manifestPath .getParent ()).thenReturn (parentPath );
1008+
1009+ FileSystem manifestFs = mock (FileSystem .class );
1010+ when (manifestFs .getFileStatus (parentPath )).thenReturn (parentFolderStatus );
1011+ doReturn (manifestFs ).when (aux ).getManifestFS ();
1012+
1013+ assertTrue (aux .checkManifestPermissions (manifestFileStatus ));
1014+ }
1015+
9391016 @ Test
9401017 public void testRemoveManifest () throws IOException {
9411018 Assume .assumeTrue (useManifest );
9421019 Configuration conf = getABConf ();
943- final AuxServices aux = new AuxServices (MOCK_AUX_PATH_HANDLER ,
1020+ final AuxServices aux = getSpyAuxServices (MOCK_AUX_PATH_HANDLER ,
9441021 MOCK_CONTEXT , MOCK_DEL_SERVICE );
9451022 aux .init (conf );
9461023 assertEquals (2 , aux .getServices ().size ());
@@ -953,7 +1030,7 @@ public void testRemoveManifest() throws IOException {
9531030 public void testManualReload () throws IOException {
9541031 Assume .assumeTrue (useManifest );
9551032 Configuration conf = getABConf ();
956- final AuxServices aux = new AuxServices (MOCK_AUX_PATH_HANDLER ,
1033+ final AuxServices aux = getSpyAuxServices (MOCK_AUX_PATH_HANDLER ,
9571034 MOCK_CONTEXT , MOCK_DEL_SERVICE );
9581035 aux .init (conf );
9591036 try {
0 commit comments