Skip to content

Commit be53969

Browse files
committed
YARN-8071. Add ability to specify nodemanager environment variables individually. Contributed by Jim Brennan
1 parent e3b7d7a commit be53969

File tree

4 files changed

+134
-19
lines changed
  • hadoop-yarn-project/hadoop-yarn

4 files changed

+134
-19
lines changed

hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/util/Apps.java

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,11 @@
2323
import static org.apache.hadoop.yarn.util.StringHelper.sjoin;
2424

2525
import java.io.File;
26-
import java.util.ArrayList;
2726
import java.util.HashMap;
2827
import java.util.Iterator;
28+
import java.util.HashSet;
2929
import java.util.Map;
30+
import java.util.Set;
3031
import java.util.regex.Matcher;
3132
import java.util.regex.Pattern;
3233

@@ -191,20 +192,36 @@ public static void setEnvFromInputProperty(Map<String, String> env,
191192
/**
192193
*
193194
* @param envString String containing env variable definitions
194-
* @param classPathSeparator String that separates the definitions
195-
* @return ArrayList of environment variable names
195+
* @return Set of environment variable names
196196
*/
197-
public static ArrayList<String> getEnvVarsFromInputString(String envString,
198-
String classPathSeparator) {
199-
ArrayList<String> envList = new ArrayList<>();
197+
private static Set<String> getEnvVarsFromInputString(String envString) {
198+
Set<String> envSet = new HashSet<>();
200199
if (envString != null && envString.length() > 0) {
201200
Matcher varValMatcher = VARVAL_SPLITTER.matcher(envString);
202201
while (varValMatcher.find()) {
203202
String envVar = varValMatcher.group(1);
204-
envList.add(envVar);
203+
envSet.add(envVar);
205204
}
206205
}
207-
return envList;
206+
return envSet;
207+
}
208+
209+
/**
210+
* Return the list of environment variable names specified in the
211+
* given property or default string and those specified individually
212+
* with the propname.VARNAME syntax (e.g., mapreduce.map.env.VARNAME=value).
213+
* @param propName the name of the property
214+
* @param defaultPropValue the default value for propName
215+
* @param conf configuration containing properties
216+
* @return Set of environment variable names
217+
*/
218+
public static Set<String> getEnvVarsFromInputProperty(
219+
String propName, String defaultPropValue, Configuration conf) {
220+
String envString = conf.get(propName, defaultPropValue);
221+
Set<String> varSet = getEnvVarsFromInputString(envString);
222+
Map<String, String> propMap = conf.getPropsWithPrefix(propName + ".");
223+
varSet.addAll(propMap.keySet());
224+
return varSet;
208225
}
209226

210227
/**

hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/resources/yarn-default.xml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1059,7 +1059,16 @@
10591059
</property>
10601060

10611061
<property>
1062-
<description>Environment variables that should be forwarded from the NodeManager's environment to the container's.</description>
1062+
<description>
1063+
Environment variables that should be forwarded from the NodeManager's
1064+
environment to the container's, specified as a comma separated list of
1065+
VARNAME=value pairs.
1066+
1067+
To define environment variables individually, you can specify
1068+
multiple properties of the form yarn.nodemanager.admin-env.VARNAME,
1069+
where VARNAME is the name of the environment variable. This is the only
1070+
way to add a variable when its value contains commas.
1071+
</description>
10631072
<name>yarn.nodemanager.admin-env</name>
10641073
<value>MALLOC_ARENA_MAX=$MALLOC_ARENA_MAX</value>
10651074
</property>

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

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1735,13 +1735,14 @@ public void sanitizeEnv(Map<String, String> environment, Path pwd,
17351735
addToEnvMap(environment, nmVars, "JVM_PID", "$$");
17361736
}
17371737

1738-
// variables here will be forced in, even if the container has specified them.
1739-
String nmAdminUserEnv = conf.get(
1740-
YarnConfiguration.NM_ADMIN_USER_ENV,
1741-
YarnConfiguration.DEFAULT_NM_ADMIN_USER_ENV);
1742-
Apps.setEnvFromInputString(environment, nmAdminUserEnv, File.pathSeparator);
1743-
nmVars.addAll(Apps.getEnvVarsFromInputString(nmAdminUserEnv,
1744-
File.pathSeparator));
1738+
// variables here will be forced in, even if the container has
1739+
// specified them.
1740+
String defEnvStr = conf.get(YarnConfiguration.DEFAULT_NM_ADMIN_USER_ENV);
1741+
Apps.setEnvFromInputProperty(environment,
1742+
YarnConfiguration.NM_ADMIN_USER_ENV, defEnvStr, conf,
1743+
File.pathSeparator);
1744+
nmVars.addAll(Apps.getEnvVarsFromInputProperty(
1745+
YarnConfiguration.NM_ADMIN_USER_ENV, defEnvStr, conf));
17451746

17461747
// TODO: Remove Windows check and use this approach on all platforms after
17471748
// additional testing. See YARN-358.

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

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,6 @@ protected String getNMEnvVar(String varname) {
491491
}
492492
}
493493

494-
495494
@Test (timeout = 20000)
496495
public void testInvalidEnvSyntaxDiagnostics() throws IOException {
497496

@@ -688,9 +687,10 @@ public void handle(Event event) {
688687
resources.put(userjar, lpaths);
689688

690689
Path nmp = new Path(testDir);
690+
Set<String> nmEnvTrack = new LinkedHashSet<>();
691691

692692
launch.sanitizeEnv(userSetEnv, pwd, appDirs, userLocalDirs, containerLogs,
693-
resources, nmp, Collections.emptySet());
693+
resources, nmp, nmEnvTrack);
694694

695695
List<String> result =
696696
getJarManifestClasspath(userSetEnv.get(Environment.CLASSPATH.name()));
@@ -709,7 +709,7 @@ public void handle(Event event) {
709709
dispatcher, exec, null, container, dirsHandler, containerManager);
710710

711711
launch.sanitizeEnv(userSetEnv, pwd, appDirs, userLocalDirs, containerLogs,
712-
resources, nmp, Collections.emptySet());
712+
resources, nmp, nmEnvTrack);
713713

714714
result =
715715
getJarManifestClasspath(userSetEnv.get(Environment.CLASSPATH.name()));
@@ -720,6 +720,94 @@ public void handle(Event event) {
720720

721721
}
722722

723+
@Test
724+
public void testSanitizeNMEnvVars() throws Exception {
725+
// Valid only for unix
726+
assumeNotWindows();
727+
ContainerLaunchContext containerLaunchContext =
728+
recordFactory.newRecordInstance(ContainerLaunchContext.class);
729+
ApplicationId appId = ApplicationId.newInstance(0, 0);
730+
ApplicationAttemptId appAttemptId =
731+
ApplicationAttemptId.newInstance(appId, 1);
732+
ContainerId cId = ContainerId.newContainerId(appAttemptId, 0);
733+
Map<String, String> userSetEnv = new HashMap<String, String>();
734+
Set<String> nmEnvTrack = new LinkedHashSet<>();
735+
userSetEnv.put(Environment.CONTAINER_ID.name(), "user_set_container_id");
736+
userSetEnv.put(Environment.NM_HOST.name(), "user_set_NM_HOST");
737+
userSetEnv.put(Environment.NM_PORT.name(), "user_set_NM_PORT");
738+
userSetEnv.put(Environment.NM_HTTP_PORT.name(), "user_set_NM_HTTP_PORT");
739+
userSetEnv.put(Environment.LOCAL_DIRS.name(), "user_set_LOCAL_DIR");
740+
userSetEnv.put(Environment.USER.key(), "user_set_" +
741+
Environment.USER.key());
742+
userSetEnv.put(Environment.LOGNAME.name(), "user_set_LOGNAME");
743+
userSetEnv.put(Environment.PWD.name(), "user_set_PWD");
744+
userSetEnv.put(Environment.HOME.name(), "user_set_HOME");
745+
userSetEnv.put(Environment.CLASSPATH.name(), "APATH");
746+
// This one should be appended to.
747+
String userMallocArenaMaxVal = "test_user_max_val";
748+
userSetEnv.put("MALLOC_ARENA_MAX", userMallocArenaMaxVal);
749+
containerLaunchContext.setEnvironment(userSetEnv);
750+
Container container = mock(Container.class);
751+
when(container.getContainerId()).thenReturn(cId);
752+
when(container.getLaunchContext()).thenReturn(containerLaunchContext);
753+
when(container.getLocalizedResources()).thenReturn(null);
754+
Dispatcher dispatcher = mock(Dispatcher.class);
755+
EventHandler<Event> eventHandler = new EventHandler<Event>() {
756+
public void handle(Event event) {
757+
Assert.assertTrue(event instanceof ContainerExitEvent);
758+
ContainerExitEvent exitEvent = (ContainerExitEvent) event;
759+
Assert.assertEquals(ContainerEventType.CONTAINER_EXITED_WITH_FAILURE,
760+
exitEvent.getType());
761+
}
762+
};
763+
when(dispatcher.getEventHandler()).thenReturn(eventHandler);
764+
765+
// these should eclipse anything in the user environment
766+
YarnConfiguration conf = new YarnConfiguration();
767+
String mallocArenaMaxVal = "test_nm_max_val";
768+
conf.set("yarn.nodemanager.admin-env",
769+
"MALLOC_ARENA_MAX=" + mallocArenaMaxVal);
770+
String testKey1 = "TEST_KEY1";
771+
String testVal1 = "testVal1";
772+
conf.set("yarn.nodemanager.admin-env." + testKey1, testVal1);
773+
String testKey2 = "TEST_KEY2";
774+
String testVal2 = "testVal2";
775+
conf.set("yarn.nodemanager.admin-env." + testKey2, testVal2);
776+
String testKey3 = "MOUNT_LIST";
777+
String testVal3 = "/home/a/b/c,/home/d/e/f,/home/g/e/h";
778+
conf.set("yarn.nodemanager.admin-env." + testKey3, testVal3);
779+
Map<String, String> environment = new HashMap<>();
780+
LinkedHashSet<String> nmVars = new LinkedHashSet<>();
781+
ContainerLaunch launch = new ContainerLaunch(distContext, conf,
782+
dispatcher, exec, null, container, dirsHandler, containerManager);
783+
String testDir = System.getProperty("test.build.data",
784+
"target/test-dir");
785+
Path pwd = new Path(testDir);
786+
List<Path> appDirs = new ArrayList<Path>();
787+
List<String> userLocalDirs = new ArrayList<>();
788+
List<String> containerLogs = new ArrayList<String>();
789+
Map<Path, List<String>> resources = new HashMap<Path, List<String>>();
790+
Path userjar = new Path("user.jar");
791+
List<String> lpaths = new ArrayList<String>();
792+
lpaths.add("userjarlink.jar");
793+
resources.put(userjar, lpaths);
794+
Path nmp = new Path(testDir);
795+
796+
launch.sanitizeEnv(userSetEnv, pwd, appDirs, userLocalDirs, containerLogs,
797+
resources, nmp, nmEnvTrack);
798+
Assert.assertTrue(userSetEnv.containsKey("MALLOC_ARENA_MAX"));
799+
Assert.assertTrue(userSetEnv.containsKey(testKey1));
800+
Assert.assertTrue(userSetEnv.containsKey(testKey2));
801+
Assert.assertTrue(userSetEnv.containsKey(testKey3));
802+
Assert.assertTrue(nmEnvTrack.contains("MALLOC_ARENA_MAX"));
803+
Assert.assertTrue(nmEnvTrack.contains("MOUNT_LIST"));
804+
Assert.assertEquals(userMallocArenaMaxVal + File.pathSeparator
805+
+ mallocArenaMaxVal, userSetEnv.get("MALLOC_ARENA_MAX"));
806+
Assert.assertEquals(testVal1, userSetEnv.get(testKey1));
807+
Assert.assertEquals(testVal2, userSetEnv.get(testKey2));
808+
Assert.assertEquals(testVal3, userSetEnv.get(testKey3));
809+
}
810+
723811
@Test
724812
public void testErrorLogOnContainerExit() throws Exception {
725813
verifyTailErrorLogOnContainerExit(new Configuration(), "/stderr", false);

0 commit comments

Comments
 (0)