1 /** 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 package org.apache.hadoop.yarn.server.nodemanager.containermanager.launcher; 20 21 import static org.junit.Assert.assertEquals; 22 import static org.junit.Assert.assertThat; 23 import static org.junit.Assert.fail; 24 import static org.junit.matchers.JUnitMatchers.containsString; 25 import static org.mockito.Mockito.mock; 26 import static org.mockito.Mockito.when; 27 28 import java.io.BufferedReader; 29 import java.io.File; 30 import java.io.FileOutputStream; 31 import java.io.FileReader; 32 import java.io.IOException; 33 import java.io.PrintStream; 34 import java.io.PrintWriter; 35 import java.nio.ByteBuffer; 36 import java.util.ArrayList; 37 import java.util.Arrays; 38 import java.util.Collections; 39 import java.util.HashMap; 40 import java.util.List; 41 import java.util.Map; 42 import java.util.StringTokenizer; 43 import java.util.jar.JarFile; 44 import java.util.jar.Manifest; 45 46 import org.apache.commons.codec.binary.Base64; 47 import org.apache.hadoop.conf.Configuration; 48 import org.apache.hadoop.fs.FileUtil; 49 import org.apache.hadoop.fs.Path; 50 import org.apache.hadoop.fs.UnsupportedFileSystemException; 51 import org.apache.hadoop.security.token.SecretManager.InvalidToken; 52 import org.apache.hadoop.util.Shell; 53 import org.apache.hadoop.util.Shell.ExitCodeException; 54 import org.apache.hadoop.util.StringUtils; 55 import org.apache.hadoop.yarn.api.ApplicationConstants; 56 import org.apache.hadoop.yarn.api.ApplicationConstants.Environment; 57 import org.apache.hadoop.yarn.api.protocolrecords.GetContainerStatusesRequest; 58 import org.apache.hadoop.yarn.api.protocolrecords.StartContainerRequest; 59 import org.apache.hadoop.yarn.api.protocolrecords.StartContainersRequest; 60 import org.apache.hadoop.yarn.api.protocolrecords.StopContainersRequest; 61 import org.apache.hadoop.yarn.api.records.ApplicationAttemptId; 62 import org.apache.hadoop.yarn.api.records.ApplicationId; 63 import org.apache.hadoop.yarn.api.records.ContainerExitStatus; 64 import org.apache.hadoop.yarn.api.records.ContainerId; 65 import org.apache.hadoop.yarn.api.records.ContainerLaunchContext; 66 import org.apache.hadoop.yarn.api.records.ContainerState; 67 import org.apache.hadoop.yarn.api.records.ContainerStatus; 68 import org.apache.hadoop.yarn.api.records.LocalResource; 69 import org.apache.hadoop.yarn.api.records.LocalResourceType; 70 import org.apache.hadoop.yarn.api.records.LocalResourceVisibility; 71 import org.apache.hadoop.yarn.api.records.NodeId; 72 import org.apache.hadoop.yarn.api.records.Priority; 73 import org.apache.hadoop.yarn.api.records.Resource; 74 import org.apache.hadoop.yarn.api.records.Token; 75 import org.apache.hadoop.yarn.api.records.URL; 76 import org.apache.hadoop.yarn.conf.YarnConfiguration; 77 import org.apache.hadoop.yarn.event.Dispatcher; 78 import org.apache.hadoop.yarn.event.Event; 79 import org.apache.hadoop.yarn.event.EventHandler; 80 import org.apache.hadoop.yarn.security.ContainerTokenIdentifier; 81 import org.apache.hadoop.yarn.server.api.protocolrecords.NMContainerStatus; 82 import org.apache.hadoop.yarn.server.nodemanager.ContainerExecutor; 83 import org.apache.hadoop.yarn.server.nodemanager.ContainerExecutor.ExitCode; 84 import org.apache.hadoop.yarn.server.nodemanager.DefaultContainerExecutor; 85 import org.apache.hadoop.yarn.server.nodemanager.Context; 86 import org.apache.hadoop.yarn.server.nodemanager.NodeManager.NMContext; 87 import org.apache.hadoop.yarn.server.nodemanager.containermanager.BaseContainerManagerTest; 88 import org.apache.hadoop.yarn.server.nodemanager.containermanager.container.Container; 89 import org.apache.hadoop.yarn.server.nodemanager.containermanager.container.ContainerEventType; 90 import org.apache.hadoop.yarn.server.nodemanager.containermanager.container.ContainerExitEvent; 91 import org.apache.hadoop.yarn.server.nodemanager.containermanager.launcher.ContainerLaunch.ShellScriptBuilder; 92 import org.apache.hadoop.yarn.server.nodemanager.containermanager.localizer.ContainerLocalizer; 93 import org.apache.hadoop.yarn.server.nodemanager.security.NMContainerTokenSecretManager; 94 import org.apache.hadoop.yarn.server.nodemanager.security.NMTokenSecretManagerInNM; 95 import org.apache.hadoop.yarn.server.security.ApplicationACLsManager; 96 import org.apache.hadoop.yarn.server.nodemanager.recovery.NMNullStateStoreService; 97 import org.apache.hadoop.yarn.server.utils.BuilderUtils; 98 import org.apache.hadoop.yarn.util.Apps; 99 import org.apache.hadoop.yarn.util.AuxiliaryServiceHelper; 100 import org.apache.hadoop.yarn.util.ConverterUtils; 101 import org.apache.hadoop.yarn.util.LinuxResourceCalculatorPlugin; 102 import org.apache.hadoop.yarn.util.ResourceCalculatorPlugin; 103 import org.junit.Assert; 104 import org.junit.Assume; 105 import org.junit.Before; 106 import org.junit.Test; 107 108 public class TestContainerLaunch extends BaseContainerManagerTest { 109 110 protected Context distContext = new NMContext(new NMContainerTokenSecretManager( 111 conf), new NMTokenSecretManagerInNM(), null, 112 new ApplicationACLsManager(conf), new NMNullStateStoreService()) { 113 public int getHttpPort() { 114 return HTTP_PORT; 115 }; 116 public NodeId getNodeId() { 117 return NodeId.newInstance("ahost", 1234); 118 }; 119 }; 120 TestContainerLaunch()121 public TestContainerLaunch() throws UnsupportedFileSystemException { 122 super(); 123 } 124 125 @Before setup()126 public void setup() throws IOException { 127 conf.setClass( 128 YarnConfiguration.NM_CONTAINER_MON_RESOURCE_CALCULATOR, 129 LinuxResourceCalculatorPlugin.class, ResourceCalculatorPlugin.class); 130 super.setup(); 131 } 132 133 @Test testSpecialCharSymlinks()134 public void testSpecialCharSymlinks() throws IOException { 135 136 File shellFile = null; 137 File tempFile = null; 138 String badSymlink = Shell.WINDOWS ? "foo@zz_#!-+bar.cmd" : 139 "foo@zz%_#*&!-+= bar()"; 140 File symLinkFile = null; 141 142 try { 143 shellFile = Shell.appendScriptExtension(tmpDir, "hello"); 144 tempFile = Shell.appendScriptExtension(tmpDir, "temp"); 145 String timeoutCommand = Shell.WINDOWS ? "@echo \"hello\"" : 146 "echo \"hello\""; 147 PrintWriter writer = new PrintWriter(new FileOutputStream(shellFile)); 148 FileUtil.setExecutable(shellFile, true); 149 writer.println(timeoutCommand); 150 writer.close(); 151 152 Map<Path, List<String>> resources = 153 new HashMap<Path, List<String>>(); 154 Path path = new Path(shellFile.getAbsolutePath()); 155 resources.put(path, Arrays.asList(badSymlink)); 156 157 FileOutputStream fos = new FileOutputStream(tempFile); 158 159 Map<String, String> env = new HashMap<String, String>(); 160 List<String> commands = new ArrayList<String>(); 161 if (Shell.WINDOWS) { 162 commands.add("cmd"); 163 commands.add("/c"); 164 commands.add("\"" + badSymlink + "\""); 165 } else { 166 commands.add("/bin/sh ./\\\"" + badSymlink + "\\\""); 167 } 168 169 new DefaultContainerExecutor().writeLaunchEnv(fos, env, resources, commands); 170 fos.flush(); 171 fos.close(); 172 FileUtil.setExecutable(tempFile, true); 173 174 Shell.ShellCommandExecutor shexc 175 = new Shell.ShellCommandExecutor(new String[]{tempFile.getAbsolutePath()}, tmpDir); 176 177 shexc.execute(); 178 assertEquals(shexc.getExitCode(), 0); 179 assert(shexc.getOutput().contains("hello")); 180 181 symLinkFile = new File(tmpDir, badSymlink); 182 } 183 finally { 184 // cleanup 185 if (shellFile != null 186 && shellFile.exists()) { 187 shellFile.delete(); 188 } 189 if (tempFile != null 190 && tempFile.exists()) { 191 tempFile.delete(); 192 } 193 if (symLinkFile != null 194 && symLinkFile.exists()) { 195 symLinkFile.delete(); 196 } 197 } 198 } 199 200 // test the diagnostics are generated 201 @Test (timeout = 20000) testInvalidSymlinkDiagnostics()202 public void testInvalidSymlinkDiagnostics() throws IOException { 203 204 File shellFile = null; 205 File tempFile = null; 206 String symLink = Shell.WINDOWS ? "test.cmd" : 207 "test"; 208 File symLinkFile = null; 209 210 try { 211 shellFile = Shell.appendScriptExtension(tmpDir, "hello"); 212 tempFile = Shell.appendScriptExtension(tmpDir, "temp"); 213 String timeoutCommand = Shell.WINDOWS ? "@echo \"hello\"" : 214 "echo \"hello\""; 215 PrintWriter writer = new PrintWriter(new FileOutputStream(shellFile)); 216 FileUtil.setExecutable(shellFile, true); 217 writer.println(timeoutCommand); 218 writer.close(); 219 220 Map<Path, List<String>> resources = 221 new HashMap<Path, List<String>>(); 222 //This is an invalid path and should throw exception because of No such file. 223 Path invalidPath = new Path(shellFile.getAbsolutePath()+"randomPath"); 224 resources.put(invalidPath, Arrays.asList(symLink)); 225 FileOutputStream fos = new FileOutputStream(tempFile); 226 227 Map<String, String> env = new HashMap<String, String>(); 228 List<String> commands = new ArrayList<String>(); 229 if (Shell.WINDOWS) { 230 commands.add("cmd"); 231 commands.add("/c"); 232 commands.add("\"" + symLink + "\""); 233 } else { 234 commands.add("/bin/sh ./\\\"" + symLink + "\\\""); 235 } 236 new DefaultContainerExecutor().writeLaunchEnv(fos, env, resources, commands); 237 fos.flush(); 238 fos.close(); 239 FileUtil.setExecutable(tempFile, true); 240 241 Shell.ShellCommandExecutor shexc 242 = new Shell.ShellCommandExecutor(new String[]{tempFile.getAbsolutePath()}, tmpDir); 243 String diagnostics = null; 244 try { 245 shexc.execute(); 246 Assert.fail("Should catch exception"); 247 } catch(ExitCodeException e){ 248 diagnostics = e.getMessage(); 249 } 250 Assert.assertNotNull(diagnostics); 251 Assert.assertTrue(shexc.getExitCode() != 0); 252 symLinkFile = new File(tmpDir, symLink); 253 } 254 finally { 255 // cleanup 256 if (shellFile != null 257 && shellFile.exists()) { 258 shellFile.delete(); 259 } 260 if (tempFile != null 261 && tempFile.exists()) { 262 tempFile.delete(); 263 } 264 if (symLinkFile != null 265 && symLinkFile.exists()) { 266 symLinkFile.delete(); 267 } 268 } 269 } 270 271 @Test (timeout = 20000) testInvalidEnvSyntaxDiagnostics()272 public void testInvalidEnvSyntaxDiagnostics() throws IOException { 273 274 File shellFile = null; 275 try { 276 shellFile = Shell.appendScriptExtension(tmpDir, "hello"); 277 Map<Path, List<String>> resources = 278 new HashMap<Path, List<String>>(); 279 FileOutputStream fos = new FileOutputStream(shellFile); 280 FileUtil.setExecutable(shellFile, true); 281 282 Map<String, String> env = new HashMap<String, String>(); 283 // invalid env 284 env.put( 285 "APPLICATION_WORKFLOW_CONTEXT", "{\"workflowId\":\"609f91c5cd83\"," + 286 "\"workflowName\":\"\n\ninsert table " + 287 "\npartition (cd_education_status)\nselect cd_demo_sk, cd_gender, " ); 288 List<String> commands = new ArrayList<String>(); 289 new DefaultContainerExecutor().writeLaunchEnv(fos, env, resources, commands); 290 fos.flush(); 291 fos.close(); 292 293 // It is supposed that LANG is set as C. 294 Map<String, String> cmdEnv = new HashMap<String, String>(); 295 cmdEnv.put("LANG", "C"); 296 Shell.ShellCommandExecutor shexc 297 = new Shell.ShellCommandExecutor(new String[]{shellFile.getAbsolutePath()}, 298 tmpDir, cmdEnv); 299 String diagnostics = null; 300 try { 301 shexc.execute(); 302 Assert.fail("Should catch exception"); 303 } catch(ExitCodeException e){ 304 diagnostics = e.getMessage(); 305 } 306 Assert.assertTrue(diagnostics.contains(Shell.WINDOWS ? 307 "is not recognized as an internal or external command" : 308 "command not found")); 309 Assert.assertTrue(shexc.getExitCode() != 0); 310 } 311 finally { 312 // cleanup 313 if (shellFile != null 314 && shellFile.exists()) { 315 shellFile.delete(); 316 } 317 } 318 } 319 320 @Test(timeout = 10000) testEnvExpansion()321 public void testEnvExpansion() throws IOException { 322 Path logPath = new Path("/nm/container/logs"); 323 String input = 324 Apps.crossPlatformify("HADOOP_HOME") + "/share/hadoop/common/*" 325 + ApplicationConstants.CLASS_PATH_SEPARATOR 326 + Apps.crossPlatformify("HADOOP_HOME") + "/share/hadoop/common/lib/*" 327 + ApplicationConstants.CLASS_PATH_SEPARATOR 328 + Apps.crossPlatformify("HADOOP_LOG_HOME") 329 + ApplicationConstants.LOG_DIR_EXPANSION_VAR; 330 331 String res = ContainerLaunch.expandEnvironment(input, logPath); 332 333 if (Shell.WINDOWS) { 334 Assert.assertEquals("%HADOOP_HOME%/share/hadoop/common/*;" 335 + "%HADOOP_HOME%/share/hadoop/common/lib/*;" 336 + "%HADOOP_LOG_HOME%/nm/container/logs", res); 337 } else { 338 Assert.assertEquals("$HADOOP_HOME/share/hadoop/common/*:" 339 + "$HADOOP_HOME/share/hadoop/common/lib/*:" 340 + "$HADOOP_LOG_HOME/nm/container/logs", res); 341 } 342 System.out.println(res); 343 } 344 345 @Test (timeout = 20000) testContainerLaunchStdoutAndStderrDiagnostics()346 public void testContainerLaunchStdoutAndStderrDiagnostics() throws IOException { 347 348 File shellFile = null; 349 try { 350 shellFile = Shell.appendScriptExtension(tmpDir, "hello"); 351 // echo "hello" to stdout and "error" to stderr and exit code with 2; 352 String command = Shell.WINDOWS ? 353 "@echo \"hello\" & @echo \"error\" 1>&2 & exit /b 2" : 354 "echo \"hello\"; echo \"error\" 1>&2; exit 2;"; 355 PrintWriter writer = new PrintWriter(new FileOutputStream(shellFile)); 356 FileUtil.setExecutable(shellFile, true); 357 writer.println(command); 358 writer.close(); 359 Map<Path, List<String>> resources = 360 new HashMap<Path, List<String>>(); 361 FileOutputStream fos = new FileOutputStream(shellFile, true); 362 363 Map<String, String> env = new HashMap<String, String>(); 364 List<String> commands = new ArrayList<String>(); 365 commands.add(command); 366 ContainerExecutor exec = new DefaultContainerExecutor(); 367 exec.writeLaunchEnv(fos, env, resources, commands); 368 fos.flush(); 369 fos.close(); 370 371 Shell.ShellCommandExecutor shexc 372 = new Shell.ShellCommandExecutor(new String[]{shellFile.getAbsolutePath()}, tmpDir); 373 String diagnostics = null; 374 try { 375 shexc.execute(); 376 Assert.fail("Should catch exception"); 377 } catch(ExitCodeException e){ 378 diagnostics = e.getMessage(); 379 } 380 // test stderr 381 Assert.assertTrue(diagnostics.contains("error")); 382 // test stdout 383 Assert.assertTrue(shexc.getOutput().contains("hello")); 384 Assert.assertTrue(shexc.getExitCode() == 2); 385 } 386 finally { 387 // cleanup 388 if (shellFile != null 389 && shellFile.exists()) { 390 shellFile.delete(); 391 } 392 } 393 } 394 395 @Test testPrependDistcache()396 public void testPrependDistcache() throws Exception { 397 398 // Test is only relevant on Windows 399 Assume.assumeTrue(Shell.WINDOWS); 400 401 ContainerLaunchContext containerLaunchContext = 402 recordFactory.newRecordInstance(ContainerLaunchContext.class); 403 404 ApplicationId appId = ApplicationId.newInstance(0, 0); 405 ApplicationAttemptId appAttemptId = 406 ApplicationAttemptId.newInstance(appId, 1); 407 408 ContainerId cId = ContainerId.newContainerId(appAttemptId, 0); 409 Map<String, String> userSetEnv = new HashMap<String, String>(); 410 userSetEnv.put(Environment.CONTAINER_ID.name(), "user_set_container_id"); 411 userSetEnv.put(Environment.NM_HOST.name(), "user_set_NM_HOST"); 412 userSetEnv.put(Environment.NM_PORT.name(), "user_set_NM_PORT"); 413 userSetEnv.put(Environment.NM_HTTP_PORT.name(), "user_set_NM_HTTP_PORT"); 414 userSetEnv.put(Environment.LOCAL_DIRS.name(), "user_set_LOCAL_DIR"); 415 userSetEnv.put(Environment.USER.key(), "user_set_" + 416 Environment.USER.key()); 417 userSetEnv.put(Environment.LOGNAME.name(), "user_set_LOGNAME"); 418 userSetEnv.put(Environment.PWD.name(), "user_set_PWD"); 419 userSetEnv.put(Environment.HOME.name(), "user_set_HOME"); 420 userSetEnv.put(Environment.CLASSPATH.name(), "APATH"); 421 containerLaunchContext.setEnvironment(userSetEnv); 422 Container container = mock(Container.class); 423 when(container.getContainerId()).thenReturn(cId); 424 when(container.getLaunchContext()).thenReturn(containerLaunchContext); 425 when(container.getLocalizedResources()).thenReturn(null); 426 Dispatcher dispatcher = mock(Dispatcher.class); 427 EventHandler eventHandler = new EventHandler() { 428 public void handle(Event event) { 429 Assert.assertTrue(event instanceof ContainerExitEvent); 430 ContainerExitEvent exitEvent = (ContainerExitEvent) event; 431 Assert.assertEquals(ContainerEventType.CONTAINER_EXITED_WITH_FAILURE, 432 exitEvent.getType()); 433 } 434 }; 435 when(dispatcher.getEventHandler()).thenReturn(eventHandler); 436 437 Configuration conf = new Configuration(); 438 439 ContainerLaunch launch = new ContainerLaunch(distContext, conf, 440 dispatcher, exec, null, container, dirsHandler, containerManager); 441 442 String testDir = System.getProperty("test.build.data", 443 "target/test-dir"); 444 Path pwd = new Path(testDir); 445 List<Path> appDirs = new ArrayList<Path>(); 446 List<String> containerLogs = new ArrayList<String>(); 447 448 Map<Path, List<String>> resources = new HashMap<Path, List<String>>(); 449 Path userjar = new Path("user.jar"); 450 List<String> lpaths = new ArrayList<String>(); 451 lpaths.add("userjarlink.jar"); 452 resources.put(userjar, lpaths); 453 454 Path nmp = new Path(testDir); 455 456 launch.sanitizeEnv( 457 userSetEnv, pwd, appDirs, containerLogs, resources, nmp); 458 459 List<String> result = 460 getJarManifestClasspath(userSetEnv.get(Environment.CLASSPATH.name())); 461 462 Assert.assertTrue(result.size() > 1); 463 Assert.assertTrue( 464 result.get(result.size() - 1).endsWith("userjarlink.jar")); 465 466 //Then, with user classpath first 467 userSetEnv.put(Environment.CLASSPATH_PREPEND_DISTCACHE.name(), "true"); 468 469 cId = ContainerId.newContainerId(appAttemptId, 1); 470 when(container.getContainerId()).thenReturn(cId); 471 472 launch = new ContainerLaunch(distContext, conf, 473 dispatcher, exec, null, container, dirsHandler, containerManager); 474 475 launch.sanitizeEnv( 476 userSetEnv, pwd, appDirs, containerLogs, resources, nmp); 477 478 result = 479 getJarManifestClasspath(userSetEnv.get(Environment.CLASSPATH.name())); 480 481 Assert.assertTrue(result.size() > 1); 482 Assert.assertTrue( 483 result.get(0).endsWith("userjarlink.jar")); 484 485 } 486 getJarManifestClasspath(String path)487 private static List<String> getJarManifestClasspath(String path) 488 throws Exception { 489 List<String> classpath = new ArrayList<String>(); 490 JarFile jarFile = new JarFile(path); 491 Manifest manifest = jarFile.getManifest(); 492 String cps = manifest.getMainAttributes().getValue("Class-Path"); 493 StringTokenizer cptok = new StringTokenizer(cps); 494 while (cptok.hasMoreTokens()) { 495 String cpentry = cptok.nextToken(); 496 classpath.add(cpentry); 497 } 498 return classpath; 499 } 500 501 /** 502 * See if environment variable is forwarded using sanitizeEnv. 503 * @throws Exception 504 */ 505 @Test (timeout = 60000) testContainerEnvVariables()506 public void testContainerEnvVariables() throws Exception { 507 containerManager.start(); 508 509 ContainerLaunchContext containerLaunchContext = 510 recordFactory.newRecordInstance(ContainerLaunchContext.class); 511 512 // ////// Construct the Container-id 513 ApplicationId appId = ApplicationId.newInstance(0, 0); 514 ApplicationAttemptId appAttemptId = 515 ApplicationAttemptId.newInstance(appId, 1); 516 517 ContainerId cId = ContainerId.newContainerId(appAttemptId, 0); 518 Map<String, String> userSetEnv = new HashMap<String, String>(); 519 userSetEnv.put(Environment.CONTAINER_ID.name(), "user_set_container_id"); 520 userSetEnv.put(Environment.NM_HOST.name(), "user_set_NM_HOST"); 521 userSetEnv.put(Environment.NM_PORT.name(), "user_set_NM_PORT"); 522 userSetEnv.put(Environment.NM_HTTP_PORT.name(), "user_set_NM_HTTP_PORT"); 523 userSetEnv.put(Environment.LOCAL_DIRS.name(), "user_set_LOCAL_DIR"); 524 userSetEnv.put(Environment.USER.key(), "user_set_" + 525 Environment.USER.key()); 526 userSetEnv.put(Environment.LOGNAME.name(), "user_set_LOGNAME"); 527 userSetEnv.put(Environment.PWD.name(), "user_set_PWD"); 528 userSetEnv.put(Environment.HOME.name(), "user_set_HOME"); 529 containerLaunchContext.setEnvironment(userSetEnv); 530 531 File scriptFile = Shell.appendScriptExtension(tmpDir, "scriptFile"); 532 PrintWriter fileWriter = new PrintWriter(scriptFile); 533 File processStartFile = 534 new File(tmpDir, "env_vars.txt").getAbsoluteFile(); 535 if (Shell.WINDOWS) { 536 fileWriter.println("@echo " + Environment.CONTAINER_ID.$() + "> " 537 + processStartFile); 538 fileWriter.println("@echo " + Environment.NM_HOST.$() + ">> " 539 + processStartFile); 540 fileWriter.println("@echo " + Environment.NM_PORT.$() + ">> " 541 + processStartFile); 542 fileWriter.println("@echo " + Environment.NM_HTTP_PORT.$() + ">> " 543 + processStartFile); 544 fileWriter.println("@echo " + Environment.LOCAL_DIRS.$() + ">> " 545 + processStartFile); 546 fileWriter.println("@echo " + Environment.USER.$() + ">> " 547 + processStartFile); 548 fileWriter.println("@echo " + Environment.LOGNAME.$() + ">> " 549 + processStartFile); 550 fileWriter.println("@echo " + Environment.PWD.$() + ">> " 551 + processStartFile); 552 fileWriter.println("@echo " + Environment.HOME.$() + ">> " 553 + processStartFile); 554 for (String serviceName : containerManager.getAuxServiceMetaData() 555 .keySet()) { 556 fileWriter.println("@echo %" + AuxiliaryServiceHelper.NM_AUX_SERVICE 557 + serviceName + "%>> " 558 + processStartFile); 559 } 560 fileWriter.println("@echo " + cId + ">> " + processStartFile); 561 fileWriter.println("@ping -n 100 127.0.0.1 >nul"); 562 } else { 563 fileWriter.write("\numask 0"); // So that start file is readable by the test 564 fileWriter.write("\necho $" + Environment.CONTAINER_ID.name() + " > " 565 + processStartFile); 566 fileWriter.write("\necho $" + Environment.NM_HOST.name() + " >> " 567 + processStartFile); 568 fileWriter.write("\necho $" + Environment.NM_PORT.name() + " >> " 569 + processStartFile); 570 fileWriter.write("\necho $" + Environment.NM_HTTP_PORT.name() + " >> " 571 + processStartFile); 572 fileWriter.write("\necho $" + Environment.LOCAL_DIRS.name() + " >> " 573 + processStartFile); 574 fileWriter.write("\necho $" + Environment.USER.name() + " >> " 575 + processStartFile); 576 fileWriter.write("\necho $" + Environment.LOGNAME.name() + " >> " 577 + processStartFile); 578 fileWriter.write("\necho $" + Environment.PWD.name() + " >> " 579 + processStartFile); 580 fileWriter.write("\necho $" + Environment.HOME.name() + " >> " 581 + processStartFile); 582 for (String serviceName : containerManager.getAuxServiceMetaData() 583 .keySet()) { 584 fileWriter.write("\necho $" + AuxiliaryServiceHelper.NM_AUX_SERVICE 585 + serviceName + " >> " 586 + processStartFile); 587 } 588 fileWriter.write("\necho $$ >> " + processStartFile); 589 fileWriter.write("\nexec sleep 100"); 590 } 591 fileWriter.close(); 592 593 // upload the script file so that the container can run it 594 URL resource_alpha = 595 ConverterUtils.getYarnUrlFromPath(localFS 596 .makeQualified(new Path(scriptFile.getAbsolutePath()))); 597 LocalResource rsrc_alpha = 598 recordFactory.newRecordInstance(LocalResource.class); 599 rsrc_alpha.setResource(resource_alpha); 600 rsrc_alpha.setSize(-1); 601 rsrc_alpha.setVisibility(LocalResourceVisibility.APPLICATION); 602 rsrc_alpha.setType(LocalResourceType.FILE); 603 rsrc_alpha.setTimestamp(scriptFile.lastModified()); 604 String destinationFile = "dest_file"; 605 Map<String, LocalResource> localResources = 606 new HashMap<String, LocalResource>(); 607 localResources.put(destinationFile, rsrc_alpha); 608 containerLaunchContext.setLocalResources(localResources); 609 610 // set up the rest of the container 611 List<String> commands = Arrays.asList(Shell.getRunScriptCommand(scriptFile)); 612 containerLaunchContext.setCommands(commands); 613 StartContainerRequest scRequest = 614 StartContainerRequest.newInstance(containerLaunchContext, 615 createContainerToken(cId, Priority.newInstance(0), 0)); 616 List<StartContainerRequest> list = new ArrayList<StartContainerRequest>(); 617 list.add(scRequest); 618 StartContainersRequest allRequests = 619 StartContainersRequest.newInstance(list); 620 containerManager.startContainers(allRequests); 621 622 int timeoutSecs = 0; 623 while (!processStartFile.exists() && timeoutSecs++ < 20) { 624 Thread.sleep(1000); 625 LOG.info("Waiting for process start-file to be created"); 626 } 627 Assert.assertTrue("ProcessStartFile doesn't exist!", 628 processStartFile.exists()); 629 630 // Now verify the contents of the file 631 List<String> localDirs = dirsHandler.getLocalDirs(); 632 List<String> logDirs = dirsHandler.getLogDirs(); 633 634 List<Path> appDirs = new ArrayList<Path>(localDirs.size()); 635 for (String localDir : localDirs) { 636 Path usersdir = new Path(localDir, ContainerLocalizer.USERCACHE); 637 Path userdir = new Path(usersdir, user); 638 Path appsdir = new Path(userdir, ContainerLocalizer.APPCACHE); 639 appDirs.add(new Path(appsdir, appId.toString())); 640 } 641 List<String> containerLogDirs = new ArrayList<String>(); 642 String relativeContainerLogDir = ContainerLaunch 643 .getRelativeContainerLogDir(appId.toString(), cId.toString()); 644 for(String logDir : logDirs){ 645 containerLogDirs.add(logDir + Path.SEPARATOR + relativeContainerLogDir); 646 } 647 BufferedReader reader = 648 new BufferedReader(new FileReader(processStartFile)); 649 Assert.assertEquals(cId.toString(), reader.readLine()); 650 Assert.assertEquals(context.getNodeId().getHost(), reader.readLine()); 651 Assert.assertEquals(String.valueOf(context.getNodeId().getPort()), 652 reader.readLine()); 653 Assert.assertEquals(String.valueOf(HTTP_PORT), reader.readLine()); 654 Assert.assertEquals(StringUtils.join(",", appDirs), reader.readLine()); 655 Assert.assertEquals(user, reader.readLine()); 656 Assert.assertEquals(user, reader.readLine()); 657 String obtainedPWD = reader.readLine(); 658 boolean found = false; 659 for (Path localDir : appDirs) { 660 if (new Path(localDir, cId.toString()).toString().equals(obtainedPWD)) { 661 found = true; 662 break; 663 } 664 } 665 Assert.assertTrue("Wrong local-dir found : " + obtainedPWD, found); 666 Assert.assertEquals( 667 conf.get( 668 YarnConfiguration.NM_USER_HOME_DIR, 669 YarnConfiguration.DEFAULT_NM_USER_HOME_DIR), 670 reader.readLine()); 671 672 for (String serviceName : containerManager.getAuxServiceMetaData().keySet()) { 673 Assert.assertEquals( 674 containerManager.getAuxServiceMetaData().get(serviceName), 675 ByteBuffer.wrap(Base64.decodeBase64(reader.readLine().getBytes()))); 676 } 677 678 Assert.assertEquals(cId.toString(), containerLaunchContext 679 .getEnvironment().get(Environment.CONTAINER_ID.name())); 680 Assert.assertEquals(context.getNodeId().getHost(), containerLaunchContext 681 .getEnvironment().get(Environment.NM_HOST.name())); 682 Assert.assertEquals(String.valueOf(context.getNodeId().getPort()), 683 containerLaunchContext.getEnvironment().get(Environment.NM_PORT.name())); 684 Assert.assertEquals(String.valueOf(HTTP_PORT), containerLaunchContext 685 .getEnvironment().get(Environment.NM_HTTP_PORT.name())); 686 Assert.assertEquals(StringUtils.join(",", appDirs), containerLaunchContext 687 .getEnvironment().get(Environment.LOCAL_DIRS.name())); 688 Assert.assertEquals(StringUtils.join(",", containerLogDirs), 689 containerLaunchContext.getEnvironment().get(Environment.LOG_DIRS.name())); 690 Assert.assertEquals(user, containerLaunchContext.getEnvironment() 691 .get(Environment.USER.name())); 692 Assert.assertEquals(user, containerLaunchContext.getEnvironment() 693 .get(Environment.LOGNAME.name())); 694 found = false; 695 obtainedPWD = 696 containerLaunchContext.getEnvironment().get(Environment.PWD.name()); 697 for (Path localDir : appDirs) { 698 if (new Path(localDir, cId.toString()).toString().equals(obtainedPWD)) { 699 found = true; 700 break; 701 } 702 } 703 Assert.assertTrue("Wrong local-dir found : " + obtainedPWD, found); 704 Assert.assertEquals( 705 conf.get( 706 YarnConfiguration.NM_USER_HOME_DIR, 707 YarnConfiguration.DEFAULT_NM_USER_HOME_DIR), 708 containerLaunchContext.getEnvironment() 709 .get(Environment.HOME.name())); 710 711 // Get the pid of the process 712 String pid = reader.readLine().trim(); 713 // No more lines 714 Assert.assertEquals(null, reader.readLine()); 715 716 // Now test the stop functionality. 717 718 // Assert that the process is alive 719 Assert.assertTrue("Process is not alive!", 720 DefaultContainerExecutor.containerIsAlive(pid)); 721 // Once more 722 Assert.assertTrue("Process is not alive!", 723 DefaultContainerExecutor.containerIsAlive(pid)); 724 725 // Now test the stop functionality. 726 List<ContainerId> containerIds = new ArrayList<ContainerId>(); 727 containerIds.add(cId); 728 StopContainersRequest stopRequest = 729 StopContainersRequest.newInstance(containerIds); 730 containerManager.stopContainers(stopRequest); 731 732 BaseContainerManagerTest.waitForContainerState(containerManager, cId, 733 ContainerState.COMPLETE); 734 735 GetContainerStatusesRequest gcsRequest = 736 GetContainerStatusesRequest.newInstance(containerIds); 737 ContainerStatus containerStatus = 738 containerManager.getContainerStatuses(gcsRequest).getContainerStatuses().get(0); 739 int expectedExitCode = ContainerExitStatus.KILLED_BY_APPMASTER; 740 Assert.assertEquals(expectedExitCode, containerStatus.getExitStatus()); 741 742 // Assert that the process is not alive anymore 743 Assert.assertFalse("Process is still alive!", 744 DefaultContainerExecutor.containerIsAlive(pid)); 745 } 746 747 @Test (timeout = 5000) testAuxiliaryServiceHelper()748 public void testAuxiliaryServiceHelper() throws Exception { 749 Map<String, String> env = new HashMap<String, String>(); 750 String serviceName = "testAuxiliaryService"; 751 ByteBuffer bb = ByteBuffer.wrap("testAuxiliaryService".getBytes()); 752 AuxiliaryServiceHelper.setServiceDataIntoEnv(serviceName, bb, env); 753 Assert.assertEquals(bb, 754 AuxiliaryServiceHelper.getServiceDataFromEnv(serviceName, env)); 755 } 756 internalKillTest(boolean delayed)757 private void internalKillTest(boolean delayed) throws Exception { 758 conf.setLong(YarnConfiguration.NM_SLEEP_DELAY_BEFORE_SIGKILL_MS, 759 delayed ? 1000 : 0); 760 containerManager.start(); 761 762 // ////// Construct the Container-id 763 ApplicationId appId = ApplicationId.newInstance(1, 1); 764 ApplicationAttemptId appAttemptId = 765 ApplicationAttemptId.newInstance(appId, 1); 766 ContainerId cId = ContainerId.newContainerId(appAttemptId, 0); 767 File processStartFile = 768 new File(tmpDir, "pid.txt").getAbsoluteFile(); 769 770 // setup a script that can handle sigterm gracefully 771 File scriptFile = Shell.appendScriptExtension(tmpDir, "testscript"); 772 PrintWriter writer = new PrintWriter(new FileOutputStream(scriptFile)); 773 if (Shell.WINDOWS) { 774 writer.println("@echo \"Running testscript for delayed kill\""); 775 writer.println("@echo \"Writing pid to start file\""); 776 writer.println("@echo " + cId + "> " + processStartFile); 777 writer.println("@ping -n 100 127.0.0.1 >nul"); 778 } else { 779 writer.println("#!/bin/bash\n\n"); 780 writer.println("echo \"Running testscript for delayed kill\""); 781 writer.println("hello=\"Got SIGTERM\""); 782 writer.println("umask 0"); 783 writer.println("trap \"echo $hello >> " + processStartFile + "\" SIGTERM"); 784 writer.println("echo \"Writing pid to start file\""); 785 writer.println("echo $$ >> " + processStartFile); 786 writer.println("while true; do\nsleep 1s;\ndone"); 787 } 788 writer.close(); 789 FileUtil.setExecutable(scriptFile, true); 790 791 ContainerLaunchContext containerLaunchContext = 792 recordFactory.newRecordInstance(ContainerLaunchContext.class); 793 794 // upload the script file so that the container can run it 795 URL resource_alpha = 796 ConverterUtils.getYarnUrlFromPath(localFS 797 .makeQualified(new Path(scriptFile.getAbsolutePath()))); 798 LocalResource rsrc_alpha = 799 recordFactory.newRecordInstance(LocalResource.class); 800 rsrc_alpha.setResource(resource_alpha); 801 rsrc_alpha.setSize(-1); 802 rsrc_alpha.setVisibility(LocalResourceVisibility.APPLICATION); 803 rsrc_alpha.setType(LocalResourceType.FILE); 804 rsrc_alpha.setTimestamp(scriptFile.lastModified()); 805 String destinationFile = "dest_file.sh"; 806 Map<String, LocalResource> localResources = 807 new HashMap<String, LocalResource>(); 808 localResources.put(destinationFile, rsrc_alpha); 809 containerLaunchContext.setLocalResources(localResources); 810 811 // set up the rest of the container 812 List<String> commands = Arrays.asList(Shell.getRunScriptCommand(scriptFile)); 813 containerLaunchContext.setCommands(commands); 814 Priority priority = Priority.newInstance(10); 815 long createTime = 1234; 816 Token containerToken = createContainerToken(cId, priority, createTime); 817 818 StartContainerRequest scRequest = 819 StartContainerRequest.newInstance(containerLaunchContext, 820 containerToken); 821 List<StartContainerRequest> list = new ArrayList<StartContainerRequest>(); 822 list.add(scRequest); 823 StartContainersRequest allRequests = 824 StartContainersRequest.newInstance(list); 825 containerManager.startContainers(allRequests); 826 827 int timeoutSecs = 0; 828 while (!processStartFile.exists() && timeoutSecs++ < 20) { 829 Thread.sleep(1000); 830 LOG.info("Waiting for process start-file to be created"); 831 } 832 Assert.assertTrue("ProcessStartFile doesn't exist!", 833 processStartFile.exists()); 834 835 NMContainerStatus nmContainerStatus = 836 containerManager.getContext().getContainers().get(cId) 837 .getNMContainerStatus(); 838 Assert.assertEquals(priority, nmContainerStatus.getPriority()); 839 840 // Now test the stop functionality. 841 List<ContainerId> containerIds = new ArrayList<ContainerId>(); 842 containerIds.add(cId); 843 StopContainersRequest stopRequest = 844 StopContainersRequest.newInstance(containerIds); 845 containerManager.stopContainers(stopRequest); 846 847 BaseContainerManagerTest.waitForContainerState(containerManager, cId, 848 ContainerState.COMPLETE); 849 850 // if delayed container stop sends a sigterm followed by a sigkill 851 // otherwise sigkill is sent immediately 852 GetContainerStatusesRequest gcsRequest = 853 GetContainerStatusesRequest.newInstance(containerIds); 854 855 ContainerStatus containerStatus = 856 containerManager.getContainerStatuses(gcsRequest) 857 .getContainerStatuses().get(0); 858 Assert.assertEquals(ContainerExitStatus.KILLED_BY_APPMASTER, 859 containerStatus.getExitStatus()); 860 861 // Now verify the contents of the file. Script generates a message when it 862 // receives a sigterm so we look for that. We cannot perform this check on 863 // Windows, because the process is not notified when killed by winutils. 864 // There is no way for the process to trap and respond. Instead, we can 865 // verify that the job object with ID matching container ID no longer exists. 866 if (Shell.WINDOWS || !delayed) { 867 Assert.assertFalse("Process is still alive!", 868 DefaultContainerExecutor.containerIsAlive(cId.toString())); 869 } else { 870 BufferedReader reader = 871 new BufferedReader(new FileReader(processStartFile)); 872 873 boolean foundSigTermMessage = false; 874 while (true) { 875 String line = reader.readLine(); 876 if (line == null) { 877 break; 878 } 879 if (line.contains("SIGTERM")) { 880 foundSigTermMessage = true; 881 break; 882 } 883 } 884 Assert.assertTrue("Did not find sigterm message", foundSigTermMessage); 885 reader.close(); 886 } 887 } 888 889 @Test (timeout = 30000) testDelayedKill()890 public void testDelayedKill() throws Exception { 891 internalKillTest(true); 892 } 893 894 @Test (timeout = 30000) testImmediateKill()895 public void testImmediateKill() throws Exception { 896 internalKillTest(false); 897 } 898 899 @SuppressWarnings("rawtypes") 900 @Test (timeout = 10000) testCallFailureWithNullLocalizedResources()901 public void testCallFailureWithNullLocalizedResources() { 902 Container container = mock(Container.class); 903 when(container.getContainerId()).thenReturn(ContainerId.newContainerId( 904 ApplicationAttemptId.newInstance(ApplicationId.newInstance( 905 System.currentTimeMillis(), 1), 1), 1)); 906 ContainerLaunchContext clc = mock(ContainerLaunchContext.class); 907 when(clc.getCommands()).thenReturn(Collections.<String>emptyList()); 908 when(container.getLaunchContext()).thenReturn(clc); 909 when(container.getLocalizedResources()).thenReturn(null); 910 Dispatcher dispatcher = mock(Dispatcher.class); 911 EventHandler eventHandler = new EventHandler() { 912 public void handle(Event event) { 913 Assert.assertTrue(event instanceof ContainerExitEvent); 914 ContainerExitEvent exitEvent = (ContainerExitEvent) event; 915 Assert.assertEquals(ContainerEventType.CONTAINER_EXITED_WITH_FAILURE, 916 exitEvent.getType()); 917 } 918 }; 919 when(dispatcher.getEventHandler()).thenReturn(eventHandler); 920 ContainerLaunch launch = new ContainerLaunch(context, new Configuration(), 921 dispatcher, exec, null, container, dirsHandler, containerManager); 922 launch.call(); 923 } 924 createContainerToken(ContainerId cId, Priority priority, long createTime)925 protected Token createContainerToken(ContainerId cId, Priority priority, 926 long createTime) throws InvalidToken { 927 Resource r = BuilderUtils.newResource(1024, 1); 928 ContainerTokenIdentifier containerTokenIdentifier = 929 new ContainerTokenIdentifier(cId, context.getNodeId().toString(), user, 930 r, System.currentTimeMillis() + 10000L, 123, DUMMY_RM_IDENTIFIER, 931 priority, createTime); 932 Token containerToken = 933 BuilderUtils.newContainerToken( 934 context.getNodeId(), 935 context.getContainerTokenSecretManager().retrievePassword( 936 containerTokenIdentifier), containerTokenIdentifier); 937 return containerToken; 938 } 939 940 /** 941 * Test that script exists with non-zero exit code when command fails. 942 * @throws IOException 943 */ 944 @Test (timeout = 10000) testShellScriptBuilderNonZeroExitCode()945 public void testShellScriptBuilderNonZeroExitCode() throws IOException { 946 ShellScriptBuilder builder = ShellScriptBuilder.create(); 947 builder.command(Arrays.asList(new String[] {"unknownCommand"})); 948 File shellFile = Shell.appendScriptExtension(tmpDir, "testShellScriptBuilderError"); 949 PrintStream writer = new PrintStream(new FileOutputStream(shellFile)); 950 builder.write(writer); 951 writer.close(); 952 try { 953 FileUtil.setExecutable(shellFile, true); 954 955 Shell.ShellCommandExecutor shexc = new Shell.ShellCommandExecutor( 956 new String[]{shellFile.getAbsolutePath()}, tmpDir); 957 try { 958 shexc.execute(); 959 fail("builder shell command was expected to throw"); 960 } 961 catch(IOException e) { 962 // expected 963 System.out.println("Received an expected exception: " + e.getMessage()); 964 } 965 } 966 finally { 967 FileUtil.fullyDelete(shellFile); 968 } 969 } 970 971 private static final String expectedMessage = "The command line has a length of"; 972 973 @Test (timeout = 10000) testWindowsShellScriptBuilderCommand()974 public void testWindowsShellScriptBuilderCommand() throws IOException { 975 String callCmd = "@call "; 976 977 // Test is only relevant on Windows 978 Assume.assumeTrue(Shell.WINDOWS); 979 980 // The tests are built on assuming 8191 max command line length 981 assertEquals(8191, Shell.WINDOWS_MAX_SHELL_LENGHT); 982 983 ShellScriptBuilder builder = ShellScriptBuilder.create(); 984 985 // Basic tests: less length, exact length, max+1 length 986 builder.command(Arrays.asList( 987 org.apache.commons.lang.StringUtils.repeat("A", 1024))); 988 builder.command(Arrays.asList( 989 org.apache.commons.lang.StringUtils.repeat( 990 "E", Shell.WINDOWS_MAX_SHELL_LENGHT - callCmd.length()))); 991 try { 992 builder.command(Arrays.asList( 993 org.apache.commons.lang.StringUtils.repeat( 994 "X", Shell.WINDOWS_MAX_SHELL_LENGHT -callCmd.length() + 1))); 995 fail("longCommand was expected to throw"); 996 } catch(IOException e) { 997 assertThat(e.getMessage(), containsString(expectedMessage)); 998 } 999 1000 // Composite tests, from parts: less, exact and + 1001 builder.command(Arrays.asList( 1002 org.apache.commons.lang.StringUtils.repeat("A", 1024), 1003 org.apache.commons.lang.StringUtils.repeat("A", 1024), 1004 org.apache.commons.lang.StringUtils.repeat("A", 1024))); 1005 1006 // buildr.command joins the command parts with an extra space 1007 builder.command(Arrays.asList( 1008 org.apache.commons.lang.StringUtils.repeat("E", 4095), 1009 org.apache.commons.lang.StringUtils.repeat("E", 2047), 1010 org.apache.commons.lang.StringUtils.repeat("E", 2047 - callCmd.length()))); 1011 1012 try { 1013 builder.command(Arrays.asList( 1014 org.apache.commons.lang.StringUtils.repeat("X", 4095), 1015 org.apache.commons.lang.StringUtils.repeat("X", 2047), 1016 org.apache.commons.lang.StringUtils.repeat("X", 2048 - callCmd.length()))); 1017 fail("long commands was expected to throw"); 1018 } catch(IOException e) { 1019 assertThat(e.getMessage(), containsString(expectedMessage)); 1020 } 1021 } 1022 1023 @Test (timeout = 10000) testWindowsShellScriptBuilderEnv()1024 public void testWindowsShellScriptBuilderEnv() throws IOException { 1025 // Test is only relevant on Windows 1026 Assume.assumeTrue(Shell.WINDOWS); 1027 1028 // The tests are built on assuming 8191 max command line length 1029 assertEquals(8191, Shell.WINDOWS_MAX_SHELL_LENGHT); 1030 1031 ShellScriptBuilder builder = ShellScriptBuilder.create(); 1032 1033 // test env 1034 builder.env("somekey", org.apache.commons.lang.StringUtils.repeat("A", 1024)); 1035 builder.env("somekey", org.apache.commons.lang.StringUtils.repeat( 1036 "A", Shell.WINDOWS_MAX_SHELL_LENGHT - ("@set somekey=").length())); 1037 try { 1038 builder.env("somekey", org.apache.commons.lang.StringUtils.repeat( 1039 "A", Shell.WINDOWS_MAX_SHELL_LENGHT - ("@set somekey=").length()) + 1); 1040 fail("long env was expected to throw"); 1041 } catch(IOException e) { 1042 assertThat(e.getMessage(), containsString(expectedMessage)); 1043 } 1044 } 1045 1046 @Test (timeout = 10000) testWindowsShellScriptBuilderMkdir()1047 public void testWindowsShellScriptBuilderMkdir() throws IOException { 1048 String mkDirCmd = "@if not exist \"\" mkdir \"\""; 1049 1050 // Test is only relevant on Windows 1051 Assume.assumeTrue(Shell.WINDOWS); 1052 1053 // The tests are built on assuming 8191 max command line length 1054 assertEquals(8191, Shell.WINDOWS_MAX_SHELL_LENGHT); 1055 1056 ShellScriptBuilder builder = ShellScriptBuilder.create(); 1057 1058 // test mkdir 1059 builder.mkdir(new Path(org.apache.commons.lang.StringUtils.repeat("A", 1024))); 1060 builder.mkdir(new Path(org.apache.commons.lang.StringUtils.repeat( 1061 "E", (Shell.WINDOWS_MAX_SHELL_LENGHT - mkDirCmd.length())/2))); 1062 try { 1063 builder.mkdir(new Path(org.apache.commons.lang.StringUtils.repeat( 1064 "X", (Shell.WINDOWS_MAX_SHELL_LENGHT - mkDirCmd.length())/2 +1))); 1065 fail("long mkdir was expected to throw"); 1066 } catch(IOException e) { 1067 assertThat(e.getMessage(), containsString(expectedMessage)); 1068 } 1069 } 1070 1071 @Test (timeout = 10000) testWindowsShellScriptBuilderLink()1072 public void testWindowsShellScriptBuilderLink() throws IOException { 1073 // Test is only relevant on Windows 1074 Assume.assumeTrue(Shell.WINDOWS); 1075 1076 String linkCmd = "@" +Shell.WINUTILS + " symlink \"\" \"\""; 1077 1078 // The tests are built on assuming 8191 max command line length 1079 assertEquals(8191, Shell.WINDOWS_MAX_SHELL_LENGHT); 1080 1081 ShellScriptBuilder builder = ShellScriptBuilder.create(); 1082 1083 // test link 1084 builder.link(new Path(org.apache.commons.lang.StringUtils.repeat("A", 1024)), 1085 new Path(org.apache.commons.lang.StringUtils.repeat("B", 1024))); 1086 builder.link( 1087 new Path(org.apache.commons.lang.StringUtils.repeat( 1088 "E", (Shell.WINDOWS_MAX_SHELL_LENGHT - linkCmd.length())/2)), 1089 new Path(org.apache.commons.lang.StringUtils.repeat( 1090 "F", (Shell.WINDOWS_MAX_SHELL_LENGHT - linkCmd.length())/2))); 1091 try { 1092 builder.link( 1093 new Path(org.apache.commons.lang.StringUtils.repeat( 1094 "X", (Shell.WINDOWS_MAX_SHELL_LENGHT - linkCmd.length())/2 + 1)), 1095 new Path(org.apache.commons.lang.StringUtils.repeat( 1096 "Y", (Shell.WINDOWS_MAX_SHELL_LENGHT - linkCmd.length())/2) + 1)); 1097 fail("long link was expected to throw"); 1098 } catch(IOException e) { 1099 assertThat(e.getMessage(), containsString(expectedMessage)); 1100 } 1101 } 1102 1103 @Test testKillProcessGroup()1104 public void testKillProcessGroup() throws Exception { 1105 Assume.assumeTrue(Shell.isSetsidAvailable); 1106 containerManager.start(); 1107 1108 // Construct the Container-id 1109 ApplicationId appId = ApplicationId.newInstance(2, 2); 1110 ApplicationAttemptId appAttemptId = 1111 ApplicationAttemptId.newInstance(appId, 1); 1112 ContainerId cId = ContainerId.newContainerId(appAttemptId, 0); 1113 File processStartFile = 1114 new File(tmpDir, "pid.txt").getAbsoluteFile(); 1115 File childProcessStartFile = 1116 new File(tmpDir, "child_pid.txt").getAbsoluteFile(); 1117 1118 // setup a script that can handle sigterm gracefully 1119 File scriptFile = Shell.appendScriptExtension(tmpDir, "testscript"); 1120 PrintWriter writer = new PrintWriter(new FileOutputStream(scriptFile)); 1121 writer.println("#!/bin/bash\n\n"); 1122 writer.println("echo \"Running testscript for forked process\""); 1123 writer.println("umask 0"); 1124 writer.println("echo $$ >> " + processStartFile); 1125 writer.println("while true;\ndo sleep 1s;\ndone > /dev/null 2>&1 &"); 1126 writer.println("echo $! >> " + childProcessStartFile); 1127 writer.println("while true;\ndo sleep 1s;\ndone"); 1128 writer.close(); 1129 FileUtil.setExecutable(scriptFile, true); 1130 1131 ContainerLaunchContext containerLaunchContext = 1132 recordFactory.newRecordInstance(ContainerLaunchContext.class); 1133 1134 // upload the script file so that the container can run it 1135 URL resource_alpha = 1136 ConverterUtils.getYarnUrlFromPath(localFS 1137 .makeQualified(new Path(scriptFile.getAbsolutePath()))); 1138 LocalResource rsrc_alpha = 1139 recordFactory.newRecordInstance(LocalResource.class); 1140 rsrc_alpha.setResource(resource_alpha); 1141 rsrc_alpha.setSize(-1); 1142 rsrc_alpha.setVisibility(LocalResourceVisibility.APPLICATION); 1143 rsrc_alpha.setType(LocalResourceType.FILE); 1144 rsrc_alpha.setTimestamp(scriptFile.lastModified()); 1145 String destinationFile = "dest_file.sh"; 1146 Map<String, LocalResource> localResources = 1147 new HashMap<String, LocalResource>(); 1148 localResources.put(destinationFile, rsrc_alpha); 1149 containerLaunchContext.setLocalResources(localResources); 1150 1151 // set up the rest of the container 1152 List<String> commands = Arrays.asList(Shell.getRunScriptCommand(scriptFile)); 1153 containerLaunchContext.setCommands(commands); 1154 Priority priority = Priority.newInstance(10); 1155 long createTime = 1234; 1156 Token containerToken = createContainerToken(cId, priority, createTime); 1157 1158 StartContainerRequest scRequest = 1159 StartContainerRequest.newInstance(containerLaunchContext, 1160 containerToken); 1161 List<StartContainerRequest> list = new ArrayList<StartContainerRequest>(); 1162 list.add(scRequest); 1163 StartContainersRequest allRequests = 1164 StartContainersRequest.newInstance(list); 1165 containerManager.startContainers(allRequests); 1166 1167 int timeoutSecs = 0; 1168 while (!processStartFile.exists() && timeoutSecs++ < 20) { 1169 Thread.sleep(1000); 1170 LOG.info("Waiting for process start-file to be created"); 1171 } 1172 Assert.assertTrue("ProcessStartFile doesn't exist!", 1173 processStartFile.exists()); 1174 1175 BufferedReader reader = 1176 new BufferedReader(new FileReader(processStartFile)); 1177 // Get the pid of the process 1178 String pid = reader.readLine().trim(); 1179 // No more lines 1180 Assert.assertEquals(null, reader.readLine()); 1181 reader.close(); 1182 1183 reader = 1184 new BufferedReader(new FileReader(childProcessStartFile)); 1185 // Get the pid of the child process 1186 String child = reader.readLine().trim(); 1187 // No more lines 1188 Assert.assertEquals(null, reader.readLine()); 1189 reader.close(); 1190 1191 LOG.info("Manually killing pid " + pid + ", but not child pid " + child); 1192 Shell.execCommand(new String[]{"kill", "-9", pid}); 1193 1194 BaseContainerManagerTest.waitForContainerState(containerManager, cId, 1195 ContainerState.COMPLETE); 1196 1197 Assert.assertFalse("Process is still alive!", 1198 DefaultContainerExecutor.containerIsAlive(pid)); 1199 1200 List<ContainerId> containerIds = new ArrayList<ContainerId>(); 1201 containerIds.add(cId); 1202 1203 GetContainerStatusesRequest gcsRequest = 1204 GetContainerStatusesRequest.newInstance(containerIds); 1205 1206 ContainerStatus containerStatus = 1207 containerManager.getContainerStatuses(gcsRequest) 1208 .getContainerStatuses().get(0); 1209 Assert.assertEquals(ExitCode.FORCE_KILLED.getExitCode(), 1210 containerStatus.getExitStatus()); 1211 } 1212 } 1213