Skip to content

Commit 1f179c2

Browse files
authored
YARN-9511. Refactoring TestAuxServices to eliminate flakyness (apache#7598)
1 parent 90a6f92 commit 1f179c2

File tree

2 files changed

+138
-43
lines changed
  • hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src

2 files changed

+138
-43
lines changed

hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/AuxServices.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,8 @@ public synchronized void reload(AuxServiceRecords services) throws
516516
loadServices(services, getConfig(), true);
517517
}
518518

519-
private boolean checkManifestPermissions(FileStatus status) throws
519+
@VisibleForTesting
520+
boolean checkManifestPermissions(FileStatus status) throws
520521
IOException {
521522
if ((status.getPermission().toShort() & 0022) != 0) {
522523
LOG.error("Manifest file and parents must not be writable by group or " +
@@ -528,7 +529,7 @@ private boolean checkManifestPermissions(FileStatus status) throws
528529
if (parent == null) {
529530
return true;
530531
}
531-
return checkManifestPermissions(manifestFS.getFileStatus(parent));
532+
return checkManifestPermissions(getManifestFS().getFileStatus(parent));
532533
}
533534

534535
private boolean checkManifestOwnerAndPermissions(FileStatus status) throws
@@ -964,6 +965,10 @@ protected static void setSystemClasses(AuxServiceRecord service, String
964965
service.getConfiguration().setProperty(SYSTEM_CLASSES, systemClasses);
965966
}
966967

968+
protected FileSystem getManifestFS() {
969+
return manifestFS;
970+
}
971+
967972
/**
968973
* Class which is used by the {@link Timer} class to periodically execute the
969974
* manifest reload.

hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/containermanager/TestAuxServices.java

Lines changed: 131 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@
2828
import static org.junit.jupiter.api.Assertions.assertTrue;
2929
import static org.junit.jupiter.api.Assertions.fail;
3030
import static org.junit.jupiter.api.Assumptions.assumeTrue;
31+
import static org.mockito.Mockito.doReturn;
3132
import static org.mockito.Mockito.mock;
33+
import static org.mockito.Mockito.spy;
3234
import static org.mockito.Mockito.when;
3335
import static org.mockito.ArgumentMatchers.anyString;
3436
import static org.mockito.ArgumentMatchers.any;
@@ -122,6 +124,8 @@ public class TestAuxServices {
122124
.getSimpleName());
123125
private File manifest = new File(rootDir, "manifest.txt");
124126
private ObjectMapper mapper = new ObjectMapper();
127+
private static final FsPermission WRITABLE_BY_OWNER = FsPermission.createImmutable((short) 0755);
128+
private static final FsPermission WRITABLE_BY_GROUP = FsPermission.createImmutable((short) 0775);
125129

126130
public static Collection<Boolean> getParams() {
127131
return Arrays.asList(false, true);
@@ -266,6 +270,24 @@ private void writeManifestFile(AuxServiceRecords services, Configuration
266270
mapper.writeValue(manifest, services);
267271
}
268272

273+
/**
274+
* Creates a spy object of AuxServices for test cases which assume that we have proper
275+
* file system permissions by default.
276+
*
277+
* Permission checking iterates through the parents of the manifest file until it
278+
* reaches the system root, so without mocking this the success of the initialization
279+
* would heavily depend on the environment where the test is running.
280+
*
281+
* @return a spy object of AuxServices
282+
*/
283+
private AuxServices getSpyAuxServices(AuxiliaryLocalPathHandler auxiliaryLocalPathHandler,
284+
Context nmContext, DeletionService deletionService) throws IOException {
285+
AuxServices auxServices = spy(new AuxServices(auxiliaryLocalPathHandler,
286+
nmContext, deletionService));
287+
doReturn(true).when(auxServices).checkManifestPermissions(any(FileStatus.class));
288+
return auxServices;
289+
}
290+
269291
@SuppressWarnings("resource")
270292
@ParameterizedTest
271293
@MethodSource("getParams")
@@ -317,7 +339,7 @@ public void testRemoteAuxServiceClassPath(boolean pUseManifest) 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
fail("The permission of the jar is wrong."
@@ -339,7 +361,7 @@ public void testRemoteAuxServiceClassPath(boolean pUseManifest) 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(boolean pUseManifest) 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(boolean pUseManifest) 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();
@@ -419,7 +441,7 @@ public void testCustomizedAuxServiceClassPath(boolean pUseManifest) throws Excep
419441
"ServiceC"), ServiceC.class, Service.class);
420442
}
421443
@SuppressWarnings("resource")
422-
AuxServices aux = new AuxServices(MOCK_AUX_PATH_HANDLER,
444+
AuxServices aux = getSpyAuxServices(MOCK_AUX_PATH_HANDLER,
423445
MOCK_CONTEXT, MOCK_DEL_SERVICE);
424446
aux.init(conf);
425447
aux.start();
@@ -472,7 +494,7 @@ public void testCustomizedAuxServiceClassPath(boolean pUseManifest) throws Excep
472494
when(mockDirsHandler.getLocalPathForWrite(anyString())).thenReturn(
473495
rootAuxServiceDirPath);
474496
when(mockContext2.getLocalDirsHandler()).thenReturn(mockDirsHandler);
475-
aux = new AuxServices(MOCK_AUX_PATH_HANDLER,
497+
aux = getSpyAuxServices(MOCK_AUX_PATH_HANDLER,
476498
mockContext2, MOCK_DEL_SERVICE);
477499
aux.init(conf);
478500
aux.start();
@@ -654,7 +676,7 @@ private Configuration getABConf(String aName, String bName,
654676
public void testAuxServices(boolean pUseManifest) throws IOException {
655677
initTestAuxServices(pUseManifest);
656678
Configuration conf = getABConf();
657-
final AuxServices aux = new AuxServices(MOCK_AUX_PATH_HANDLER,
679+
final AuxServices aux = getSpyAuxServices(MOCK_AUX_PATH_HANDLER,
658680
MOCK_CONTEXT, MOCK_DEL_SERVICE);
659681
aux.init(conf);
660682

@@ -684,7 +706,7 @@ public void testAuxServices(boolean pUseManifest) throws IOException {
684706
public void testAuxServicesMeta(boolean pUseManifest) throws IOException {
685707
initTestAuxServices(pUseManifest);
686708
Configuration conf = getABConf();
687-
final AuxServices aux = new AuxServices(MOCK_AUX_PATH_HANDLER,
709+
final AuxServices aux = getSpyAuxServices(MOCK_AUX_PATH_HANDLER,
688710
MOCK_CONTEXT, MOCK_DEL_SERVICE);
689711
aux.init(conf);
690712

@@ -718,7 +740,7 @@ public void testAuxUnexpectedStop(boolean pUseManifest) throws IOException {
718740
initTestAuxServices(pUseManifest);
719741
// AuxServices no longer expected to stop when services stop
720742
Configuration conf = getABConf();
721-
final AuxServices aux = new AuxServices(MOCK_AUX_PATH_HANDLER,
743+
final AuxServices aux = getSpyAuxServices(MOCK_AUX_PATH_HANDLER,
722744
MOCK_CONTEXT, MOCK_DEL_SERVICE);
723745
aux.init(conf);
724746
aux.start();
@@ -736,7 +758,7 @@ public void testValidAuxServiceName(boolean pUseManifest) throws IOException {
736758
initTestAuxServices(pUseManifest);
737759
Configuration conf = getABConf("Asrv1", "Bsrv_2", ServiceA.class,
738760
ServiceB.class);
739-
final AuxServices aux = new AuxServices(MOCK_AUX_PATH_HANDLER,
761+
final AuxServices aux = getSpyAuxServices(MOCK_AUX_PATH_HANDLER,
740762
MOCK_CONTEXT, MOCK_DEL_SERVICE);
741763
try {
742764
aux.init(conf);
@@ -745,7 +767,7 @@ public void testValidAuxServiceName(boolean pUseManifest) throws IOException {
745767
}
746768

747769
//Test bad auxService Name
748-
final AuxServices aux1 = new AuxServices(MOCK_AUX_PATH_HANDLER,
770+
final AuxServices aux1 = getSpyAuxServices(MOCK_AUX_PATH_HANDLER,
749771
MOCK_CONTEXT, MOCK_DEL_SERVICE);
750772
if (useManifest) {
751773
AuxServiceRecord serviceA =
@@ -776,7 +798,7 @@ public void testAuxServiceRecoverySetup(boolean pUseManifest) throws IOException
776798
conf.setBoolean(YarnConfiguration.NM_RECOVERY_ENABLED, true);
777799
conf.set(YarnConfiguration.NM_RECOVERY_DIR, TEST_DIR.toString());
778800
try {
779-
final AuxServices aux = new AuxServices(MOCK_AUX_PATH_HANDLER,
801+
final AuxServices aux = getSpyAuxServices(MOCK_AUX_PATH_HANDLER,
780802
MOCK_CONTEXT, MOCK_DEL_SERVICE);
781803
aux.init(conf);
782804
assertEquals(2, aux.getServices().size());
@@ -906,62 +928,130 @@ public void testAuxServicesConfChange(boolean pUseManifest) throws IOException {
906928

907929
@ParameterizedTest
908930
@MethodSource("getParams")
909-
public void testAuxServicesManifestPermissions(boolean pUseManifest) throws IOException {
931+
public void testAuxServicesInitWithManifestOwnerAndPermissionCheck(boolean pUseManifest)
932+
throws IOException {
910933
initTestAuxServices(pUseManifest);
911934
assumeTrue(useManifest);
912935
Configuration conf = getABConf();
913-
FileSystem fs = FileSystem.get(conf);
914-
fs.setPermission(new Path(manifest.getAbsolutePath()), FsPermission
915-
.createImmutable((short) 0777));
916-
AuxServices aux = new AuxServices(MOCK_AUX_PATH_HANDLER,
917-
MOCK_CONTEXT, MOCK_DEL_SERVICE);
936+
AuxServices aux = spy(new AuxServices(MOCK_AUX_PATH_HANDLER,
937+
MOCK_CONTEXT, MOCK_DEL_SERVICE));
938+
doReturn(false).when(aux).checkManifestPermissions(any(FileStatus.class));
918939
aux.init(conf);
919940
assertEquals(0, aux.getServices().size());
920941

921-
fs.setPermission(new Path(manifest.getAbsolutePath()), FsPermission
922-
.createImmutable((short) 0775));
923-
aux = new AuxServices(MOCK_AUX_PATH_HANDLER,
924-
MOCK_CONTEXT, MOCK_DEL_SERVICE);
925-
aux.init(conf);
926-
assertEquals(0, aux.getServices().size());
927-
928-
fs.setPermission(new Path(manifest.getAbsolutePath()), FsPermission
929-
.createImmutable((short) 0755));
930-
fs.setPermission(new Path(rootDir.getAbsolutePath()), FsPermission
931-
.createImmutable((short) 0775));
932-
aux = new AuxServices(MOCK_AUX_PATH_HANDLER,
933-
MOCK_CONTEXT, MOCK_DEL_SERVICE);
934-
aux.init(conf);
935-
assertEquals(0, aux.getServices().size());
936-
937-
fs.setPermission(new Path(rootDir.getAbsolutePath()), FsPermission
938-
.createImmutable((short) 0755));
939-
aux = new AuxServices(MOCK_AUX_PATH_HANDLER,
942+
aux = getSpyAuxServices(MOCK_AUX_PATH_HANDLER,
940943
MOCK_CONTEXT, MOCK_DEL_SERVICE);
941944
aux.init(conf);
942945
assertEquals(2, aux.getServices().size());
943946

944947
conf.set(YarnConfiguration.YARN_ADMIN_ACL, "");
945-
aux = new AuxServices(MOCK_AUX_PATH_HANDLER,
948+
aux = getSpyAuxServices(MOCK_AUX_PATH_HANDLER,
946949
MOCK_CONTEXT, MOCK_DEL_SERVICE);
947950
aux.init(conf);
948951
assertEquals(0, aux.getServices().size());
949952

950953
conf.set(YarnConfiguration.YARN_ADMIN_ACL, UserGroupInformation
951954
.getCurrentUser().getShortUserName());
952-
aux = new AuxServices(MOCK_AUX_PATH_HANDLER,
955+
aux = getSpyAuxServices(MOCK_AUX_PATH_HANDLER,
953956
MOCK_CONTEXT, MOCK_DEL_SERVICE);
954957
aux.init(conf);
955958
assertEquals(2, aux.getServices().size());
956959
}
957960

961+
@ParameterizedTest
962+
@MethodSource("getParams")
963+
public void testCheckManifestPermissionsWhenFileIsOnlyWritableByOwner(boolean pUseManifest)
964+
throws IOException {
965+
initTestAuxServices(pUseManifest);
966+
assumeTrue(useManifest);
967+
final AuxServices aux = spy(new AuxServices(MOCK_AUX_PATH_HANDLER,
968+
MOCK_CONTEXT, MOCK_DEL_SERVICE));
969+
FileStatus manifestFileStatus = mock(FileStatus.class);
970+
Path manifestPath = mock(Path.class);
971+
972+
when(manifestFileStatus.getPermission()).thenReturn(WRITABLE_BY_OWNER);
973+
when(manifestFileStatus.getPath()).thenReturn(manifestPath);
974+
975+
assertTrue(aux.checkManifestPermissions(manifestFileStatus));
976+
}
977+
978+
@ParameterizedTest
979+
@MethodSource("getParams")
980+
public void testCheckManifestPermissionsWhenFileIsWritableByGroup(boolean pUseManifest)
981+
throws IOException {
982+
initTestAuxServices(pUseManifest);
983+
assumeTrue(useManifest);
984+
final AuxServices aux = spy(new AuxServices(MOCK_AUX_PATH_HANDLER,
985+
MOCK_CONTEXT, MOCK_DEL_SERVICE));
986+
FileStatus manifestFileStatus = mock(FileStatus.class);
987+
Path manifestPath = mock(Path.class);
988+
989+
when(manifestFileStatus.getPermission()).thenReturn(WRITABLE_BY_GROUP);
990+
when(manifestFileStatus.getPath()).thenReturn(manifestPath);
991+
992+
assertFalse(aux.checkManifestPermissions(manifestFileStatus));
993+
}
994+
995+
@ParameterizedTest
996+
@MethodSource("getParams")
997+
public void testCheckManifestPermissionsWhenParentIsWritableByGroup(boolean pUseManifest)
998+
throws IOException {
999+
initTestAuxServices(pUseManifest);
1000+
assumeTrue(useManifest);
1001+
final AuxServices aux = spy(new AuxServices(MOCK_AUX_PATH_HANDLER,
1002+
MOCK_CONTEXT, MOCK_DEL_SERVICE));
1003+
1004+
FileStatus manifestFileStatus = mock(FileStatus.class);
1005+
FileStatus parentFolderStatus = mock(FileStatus.class);
1006+
when(manifestFileStatus.getPermission()).thenReturn(WRITABLE_BY_OWNER);
1007+
when(parentFolderStatus.getPermission()).thenReturn(WRITABLE_BY_GROUP);
1008+
1009+
Path manifestPath = mock(Path.class);
1010+
Path parentPath = mock(Path.class);
1011+
when(manifestFileStatus.getPath()).thenReturn(manifestPath);
1012+
when(manifestPath.getParent()).thenReturn(parentPath);
1013+
1014+
FileSystem manifestFs = mock(FileSystem.class);
1015+
when(manifestFs.getFileStatus(parentPath)).thenReturn(parentFolderStatus);
1016+
doReturn(manifestFs).when(aux).getManifestFS();
1017+
1018+
assertFalse(aux.checkManifestPermissions(manifestFileStatus));
1019+
}
1020+
1021+
@ParameterizedTest
1022+
@MethodSource("getParams")
1023+
public void testCheckManifestPermissionsWhenParentAndFileIsWritableByOwner(boolean pUseManifest)
1024+
throws IOException {
1025+
initTestAuxServices(pUseManifest);
1026+
assumeTrue(useManifest);
1027+
final AuxServices aux = spy(new AuxServices(MOCK_AUX_PATH_HANDLER,
1028+
MOCK_CONTEXT, MOCK_DEL_SERVICE));
1029+
1030+
FileStatus manifestFileStatus = mock(FileStatus.class);
1031+
FileStatus parentFolderStatus = mock(FileStatus.class);
1032+
when(manifestFileStatus.getPermission()).thenReturn(WRITABLE_BY_OWNER);
1033+
when(parentFolderStatus.getPermission()).thenReturn(WRITABLE_BY_OWNER);
1034+
1035+
Path manifestPath = mock(Path.class);
1036+
Path parentPath = mock(Path.class);
1037+
when(manifestFileStatus.getPath()).thenReturn(manifestPath);
1038+
when(parentFolderStatus.getPath()).thenReturn(parentPath);
1039+
when(manifestPath.getParent()).thenReturn(parentPath);
1040+
1041+
FileSystem manifestFs = mock(FileSystem.class);
1042+
when(manifestFs.getFileStatus(parentPath)).thenReturn(parentFolderStatus);
1043+
doReturn(manifestFs).when(aux).getManifestFS();
1044+
1045+
assertTrue(aux.checkManifestPermissions(manifestFileStatus));
1046+
}
1047+
9581048
@ParameterizedTest
9591049
@MethodSource("getParams")
9601050
public void testRemoveManifest(boolean pUseManifest) throws IOException {
9611051
initTestAuxServices(pUseManifest);
9621052
assumeTrue(useManifest);
9631053
Configuration conf = getABConf();
964-
final AuxServices aux = new AuxServices(MOCK_AUX_PATH_HANDLER,
1054+
final AuxServices aux = getSpyAuxServices(MOCK_AUX_PATH_HANDLER,
9651055
MOCK_CONTEXT, MOCK_DEL_SERVICE);
9661056
aux.init(conf);
9671057
assertEquals(2, aux.getServices().size());
@@ -976,7 +1066,7 @@ public void testManualReload(boolean pUseManifest) throws IOException {
9761066
initTestAuxServices(pUseManifest);
9771067
assumeTrue(useManifest);
9781068
Configuration conf = getABConf();
979-
final AuxServices aux = new AuxServices(MOCK_AUX_PATH_HANDLER,
1069+
final AuxServices aux = getSpyAuxServices(MOCK_AUX_PATH_HANDLER,
9801070
MOCK_CONTEXT, MOCK_DEL_SERVICE);
9811071
aux.init(conf);
9821072
try {

0 commit comments

Comments
 (0)