Repository: spark
Updated Branches:
  refs/heads/master 802b5b879 -> f3e177917


[SPARK-5754] [YARN] Spark/Yarn/Windows driver/executor escaping Fix

This is my retry to suggest a fix for using Spark on Yarn on Windows. The 
former request lacked coding style which I hope to have learned to do better, 
and wasn't a true solution as I didn't really understand where the problem came 
from. Albeit being still a bit obscure, I can name the "players" and have come 
up with a better explaination of why I am suggesting this fix.

I also used vanzin and srowen input to *try* to give a more elegant solution. I 
am not so sure if that worked out though.

I still hope that this PR is a lot more useful than the last. Also do I hope 
that this is a _solution_ to the problem that Spark doesn't work on Yarn on 
Windows. With these changes it works (and I can also explain why!).

I still believe that a Unit Test should be included, kind of like the one I 
committed the last time. But that was premature, as I want to get the principal 
'Go' from vanzin and srowen.

Thanks for your time both of you.

Author: Carsten Blank <bl...@cncengine.com>
Author: cbvoxel <bl...@cncengine.com>

Closes #8053 from cbvoxel/master.


Project: http://git-wip-us.apache.org/repos/asf/spark/repo
Commit: http://git-wip-us.apache.org/repos/asf/spark/commit/f3e17791
Tree: http://git-wip-us.apache.org/repos/asf/spark/tree/f3e17791
Diff: http://git-wip-us.apache.org/repos/asf/spark/diff/f3e17791

Branch: refs/heads/master
Commit: f3e177917dd093873dca286d2419704a561ba44b
Parents: 802b5b8
Author: Carsten Blank <bl...@cncengine.com>
Authored: Wed Aug 19 10:19:24 2015 -0700
Committer: Marcelo Vanzin <van...@cloudera.com>
Committed: Wed Aug 19 10:19:24 2015 -0700

----------------------------------------------------------------------
 .../org/apache/spark/deploy/yarn/Client.scala   |  2 +-
 .../spark/deploy/yarn/ExecutorRunnable.scala    |  2 +-
 .../spark/deploy/yarn/YarnSparkHadoopUtil.scala | 56 ++++++++++++++++----
 .../launcher/YarnCommandBuilderUtils.scala      | 27 ++++++++++
 4 files changed, 75 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/spark/blob/f3e17791/yarn/src/main/scala/org/apache/spark/deploy/yarn/Client.scala
----------------------------------------------------------------------
diff --git a/yarn/src/main/scala/org/apache/spark/deploy/yarn/Client.scala 
b/yarn/src/main/scala/org/apache/spark/deploy/yarn/Client.scala
index 8672ef6..262c6a8 100644
--- a/yarn/src/main/scala/org/apache/spark/deploy/yarn/Client.scala
+++ b/yarn/src/main/scala/org/apache/spark/deploy/yarn/Client.scala
@@ -431,7 +431,7 @@ private[spark] class Client(
     }
 
     // Distribute an archive with Hadoop and Spark configuration for the AM.
-    val (_, confLocalizedPath) = 
distribute(createConfArchive().getAbsolutePath(),
+    val (_, confLocalizedPath) = 
distribute(createConfArchive().toURI().getPath(),
       resType = LocalResourceType.ARCHIVE,
       destName = Some(LOCALIZED_CONF_DIR),
       appMasterOnly = true)

http://git-wip-us.apache.org/repos/asf/spark/blob/f3e17791/yarn/src/main/scala/org/apache/spark/deploy/yarn/ExecutorRunnable.scala
----------------------------------------------------------------------
diff --git 
a/yarn/src/main/scala/org/apache/spark/deploy/yarn/ExecutorRunnable.scala 
b/yarn/src/main/scala/org/apache/spark/deploy/yarn/ExecutorRunnable.scala
index 52580de..4cc5048 100644
--- a/yarn/src/main/scala/org/apache/spark/deploy/yarn/ExecutorRunnable.scala
+++ b/yarn/src/main/scala/org/apache/spark/deploy/yarn/ExecutorRunnable.scala
@@ -217,7 +217,7 @@ class ExecutorRunnable(
       // an inconsistent state.
       // TODO: If the OOM is not recoverable by rescheduling it on different 
node, then do
       // 'something' to fail job ... akin to blacklisting trackers in mapred ?
-      "-XX:OnOutOfMemoryError='kill %p'") ++
+      YarnSparkHadoopUtil.getOutOfMemoryErrorArgument) ++
       javaOpts ++
       Seq("org.apache.spark.executor.CoarseGrainedExecutorBackend",
         "--driver-url", masterAddress.toString,

http://git-wip-us.apache.org/repos/asf/spark/blob/f3e17791/yarn/src/main/scala/org/apache/spark/deploy/yarn/YarnSparkHadoopUtil.scala
----------------------------------------------------------------------
diff --git 
a/yarn/src/main/scala/org/apache/spark/deploy/yarn/YarnSparkHadoopUtil.scala 
b/yarn/src/main/scala/org/apache/spark/deploy/yarn/YarnSparkHadoopUtil.scala
index 68d01c1..445d3dc 100644
--- a/yarn/src/main/scala/org/apache/spark/deploy/yarn/YarnSparkHadoopUtil.scala
+++ b/yarn/src/main/scala/org/apache/spark/deploy/yarn/YarnSparkHadoopUtil.scala
@@ -37,6 +37,7 @@ import 
org.apache.hadoop.yarn.api.records.{ApplicationAccessType, ContainerId, P
 import org.apache.hadoop.yarn.util.ConverterUtils
 
 import org.apache.spark.deploy.SparkHadoopUtil
+import org.apache.spark.launcher.YarnCommandBuilderUtils
 import org.apache.spark.{SecurityManager, SparkConf, SparkException}
 import org.apache.spark.util.Utils
 
@@ -220,25 +221,60 @@ object YarnSparkHadoopUtil {
   }
 
   /**
+   * The handler if an OOM Exception is thrown by the JVM must be configured 
on Windows
+   * differently: the 'taskkill' command should be used, whereas Unix-based 
systems use 'kill'.
+   *
+   * As the JVM interprets both %p and %%p as the same, we can use either of 
them. However,
+   * some tests on Windows computers suggest, that the JVM only accepts '%%p'.
+   *
+   * Furthermore, the behavior of the character '%' on the Windows command 
line differs from
+   * the behavior of '%' in a .cmd file: it gets interpreted as an incomplete 
environment
+   * variable. Windows .cmd files escape a '%' by '%%'. Thus, the correct way 
of writing
+   * '%%p' in an escaped way is '%%%%p'.
+   *
+   * @return The correct OOM Error handler JVM option, platform dependent.
+   */
+  def getOutOfMemoryErrorArgument : String = {
+    if (Utils.isWindows) {
+      escapeForShell("-XX:OnOutOfMemoryError=taskkill /F /PID %%%%p")
+    } else {
+      "-XX:OnOutOfMemoryError='kill %p'"
+    }
+  }
+
+  /**
    * Escapes a string for inclusion in a command line executed by Yarn. Yarn 
executes commands
-   * using `bash -c "command arg1 arg2"` and that means plain quoting doesn't 
really work. The
-   * argument is enclosed in single quotes and some key characters are escaped.
+   * using either
+   *
+   * (Unix-based) `bash -c "command arg1 arg2"` and that means plain quoting 
doesn't really work.
+   * The argument is enclosed in single quotes and some key characters are 
escaped.
+   *
+   * (Windows-based) part of a .cmd file in which case windows escaping for 
each argument must be
+   * applied. Windows is quite lenient, however it is usually Java that causes 
trouble, needing to
+   * distinguish between arguments starting with '-' and class names. If 
arguments are surrounded
+   * by ' java takes the following string as is, hence an argument is 
mistakenly taken as a class
+   * name which happens to start with a '-'. The way to avoid this, is to 
surround nothing with
+   * a ', but instead with a ".
    *
    * @param arg A single argument.
    * @return Argument quoted for execution via Yarn's generated shell script.
    */
   def escapeForShell(arg: String): String = {
     if (arg != null) {
-      val escaped = new StringBuilder("'")
-      for (i <- 0 to arg.length() - 1) {
-        arg.charAt(i) match {
-          case '$' => escaped.append("\\$")
-          case '"' => escaped.append("\\\"")
-          case '\'' => escaped.append("'\\''")
-          case c => escaped.append(c)
+      if (Utils.isWindows) {
+        YarnCommandBuilderUtils.quoteForBatchScript(arg)
+      } else {
+        val escaped = new StringBuilder("'")
+        for (i <- 0 to arg.length() - 1) {
+          arg.charAt(i) match {
+            case '$' => escaped.append("\\$")
+            case '"' => escaped.append("\\\"")
+            case '\'' => escaped.append("'\\''")
+            case c => escaped.append(c)
+          }
         }
+        escaped.append("'").toString()
       }
-      escaped.append("'").toString()
     } else {
       arg
     }

http://git-wip-us.apache.org/repos/asf/spark/blob/f3e17791/yarn/src/main/scala/org/apache/spark/launcher/YarnCommandBuilderUtils.scala
----------------------------------------------------------------------
diff --git 
a/yarn/src/main/scala/org/apache/spark/launcher/YarnCommandBuilderUtils.scala 
b/yarn/src/main/scala/org/apache/spark/launcher/YarnCommandBuilderUtils.scala
new file mode 100644
index 0000000..3ac36ef
--- /dev/null
+++ 
b/yarn/src/main/scala/org/apache/spark/launcher/YarnCommandBuilderUtils.scala
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.spark.launcher
+
+/**
+ * Exposes needed methods
+ */
+private[spark] object YarnCommandBuilderUtils {
+  def quoteForBatchScript(arg: String) : String = {
+    CommandBuilderUtils.quoteForBatchScript(arg)
+  }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@spark.apache.org
For additional commands, e-mail: commits-h...@spark.apache.org

Reply via email to