You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by ji...@apache.org on 2016/02/04 23:56:20 UTC

[1/3] mesos git commit: Renamed 'directory' flag to 'sandbox' in mesos containerizer launch.

Repository: mesos
Updated Branches:
  refs/heads/master 020d9ec43 -> d467eeeb8


Renamed 'directory' flag to 'sandbox' in mesos containerizer launch.

Review: https://reviews.apache.org/r/43166/


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

Branch: refs/heads/master
Commit: d467eeeb89a94c2ddacfc9248e0b2aecbbff56c0
Parents: 9228d91
Author: Gilbert Song <so...@gmail.com>
Authored: Thu Feb 4 14:09:36 2016 -0800
Committer: Jie Yu <yu...@gmail.com>
Committed: Thu Feb 4 14:56:14 2016 -0800

----------------------------------------------------------------------
 src/slave/containerizer/mesos/containerizer.cpp |  2 +-
 src/slave/containerizer/mesos/launch.cpp        | 21 ++++++++++----------
 src/slave/containerizer/mesos/launch.hpp        |  2 +-
 src/tests/containerizer/launch_tests.cpp        |  2 +-
 src/tests/containerizer/port_mapping_tests.cpp  |  2 +-
 5 files changed, 14 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/d467eeeb/src/slave/containerizer/mesos/containerizer.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/containerizer.cpp b/src/slave/containerizer/mesos/containerizer.cpp
index 8039a10..dc0868e 100644
--- a/src/slave/containerizer/mesos/containerizer.cpp
+++ b/src/slave/containerizer/mesos/containerizer.cpp
@@ -1026,7 +1026,7 @@ Future<bool> MesosContainerizerProcess::__launch(
       ? JSON::protobuf(executorLaunchCommand.get())
       : JSON::protobuf(executorInfo.command());
 
-    launchFlags.directory = rootfs.isSome()
+    launchFlags.sandbox = rootfs.isSome()
       ? flags.sandbox_directory
       : directory;
     launchFlags.rootfs = rootfs;

http://git-wip-us.apache.org/repos/asf/mesos/blob/d467eeeb/src/slave/containerizer/mesos/launch.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/launch.cpp b/src/slave/containerizer/mesos/launch.cpp
index 0cfdc0c..23970a2 100644
--- a/src/slave/containerizer/mesos/launch.cpp
+++ b/src/slave/containerizer/mesos/launch.cpp
@@ -51,16 +51,15 @@ MesosContainerizerLaunch::Flags::Flags()
       "command",
       "The command to execute.");
 
-  // TODO(jieyu): Consider renaming it to 'sandbox'.
-  add(&directory,
-      "directory",
-      "The directory to chdir to. If rootfs is specified this must\n"
+  add(&sandbox,
+      "sandbox",
+      "The sandbox to chdir to. If rootfs is specified this must\n"
       "be relative to the new root.");
 
   add(&rootfs,
       "rootfs",
       "Absolute path to the container root filesystem.\n"
-      "The command and directory flags are interpreted relative\n"
+      "The command and sandbox flags are interpreted relative\n"
       "to rootfs\n"
       "Different platforms may implement 'chroot' differently.");
 
@@ -91,8 +90,8 @@ int MesosContainerizerLaunch::execute()
     return 1;
   }
 
-  if (flags.directory.isNone()) {
-    cerr << "Flag --directory is not specified" << endl;
+  if (flags.sandbox.isNone()) {
+    cerr << "Flag --sandbox is not specified" << endl;
     return 1;
   }
 
@@ -250,11 +249,11 @@ int MesosContainerizerLaunch::execute()
     }
   }
 
-  // Enter working directory, relative to the new root.
-  Try<Nothing> chdir = os::chdir(flags.directory.get());
+  // Enter sandbox directory, relative to the new root.
+  Try<Nothing> chdir = os::chdir(flags.sandbox.get());
   if (chdir.isError()) {
-    cerr << "Failed to chdir into work directory '"
-         << flags.directory.get() << "': " << chdir.error() << endl;
+    cerr << "Failed to chdir into sandbox directory '"
+         << flags.sandbox.get() << "': " << chdir.error() << endl;
     return 1;
   }
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/d467eeeb/src/slave/containerizer/mesos/launch.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/launch.hpp b/src/slave/containerizer/mesos/launch.hpp
index 7220319..78d6b9d 100644
--- a/src/slave/containerizer/mesos/launch.hpp
+++ b/src/slave/containerizer/mesos/launch.hpp
@@ -35,7 +35,7 @@ public:
     Flags();
 
     Option<JSON::Object> command;
-    Option<std::string> directory;
+    Option<std::string> sandbox;
     Option<std::string> rootfs;
     Option<std::string> user;
     Option<int> pipe_read;

http://git-wip-us.apache.org/repos/asf/mesos/blob/d467eeeb/src/tests/containerizer/launch_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/launch_tests.cpp b/src/tests/containerizer/launch_tests.cpp
index c7ebe26..fd84c13 100644
--- a/src/tests/containerizer/launch_tests.cpp
+++ b/src/tests/containerizer/launch_tests.cpp
@@ -62,7 +62,7 @@ public:
     command.set_value(_command);
 
     launchFlags.command = JSON::protobuf(command);
-    launchFlags.directory = "/tmp";
+    launchFlags.sandbox = "/tmp";
     launchFlags.pipe_read = open("/dev/zero", O_RDONLY);
     launchFlags.pipe_write = open("/dev/null", O_WRONLY);
     launchFlags.rootfs = rootfs;

http://git-wip-us.apache.org/repos/asf/mesos/blob/d467eeeb/src/tests/containerizer/port_mapping_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/port_mapping_tests.cpp b/src/tests/containerizer/port_mapping_tests.cpp
index fd6f063..26261b0 100644
--- a/src/tests/containerizer/port_mapping_tests.cpp
+++ b/src/tests/containerizer/port_mapping_tests.cpp
@@ -309,7 +309,7 @@ protected:
     MesosContainerizerLaunch::Flags launchFlags;
 
     launchFlags.command = JSON::protobuf(commandInfo);
-    launchFlags.directory = os::getcwd();
+    launchFlags.sandbox = os::getcwd();
 
     CHECK_SOME(os::user());
     launchFlags.user = os::user().get();


[3/3] mesos git commit: Supported entrypoint and cmd in docker runtime isolator.

Posted by ji...@apache.org.
Supported entrypoint and cmd in docker runtime isolator.

Review: https://reviews.apache.org/r/43081/


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

Branch: refs/heads/master
Commit: 21224a75349871624db3753adb45de7e8ea2e863
Parents: 020d9ec
Author: Gilbert Song <so...@gmail.com>
Authored: Thu Feb 4 13:27:24 2016 -0800
Committer: Jie Yu <yu...@gmail.com>
Committed: Thu Feb 4 14:56:14 2016 -0800

----------------------------------------------------------------------
 src/slave/containerizer/mesos/containerizer.cpp |  18 +-
 .../mesos/isolators/docker/runtime.cpp          | 176 +++++++++++++++++++
 .../mesos/isolators/docker/runtime.hpp          |   4 +
 3 files changed, 197 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/21224a75/src/slave/containerizer/mesos/containerizer.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/containerizer.cpp b/src/slave/containerizer/mesos/containerizer.cpp
index ed8a545..8039a10 100644
--- a/src/slave/containerizer/mesos/containerizer.cpp
+++ b/src/slave/containerizer/mesos/containerizer.cpp
@@ -924,6 +924,12 @@ Future<bool> MesosContainerizerProcess::__launch(
   // Determine the root filesystem for the container. Only one
   // isolator should return the container root filesystem.
   Option<string> rootfs;
+
+  // Determine the executor launch command for the container.
+  // At most one command can be returned from docker runtime
+  // isolator if a docker image is specifed.
+  Option<CommandInfo> executorLaunchCommand;
+
   foreach (const Option<ContainerLaunchInfo>& launchInfo, launchInfos) {
     if (launchInfo.isSome() && launchInfo->has_rootfs()) {
       if (rootfs.isSome()) {
@@ -950,6 +956,14 @@ Future<bool> MesosContainerizerProcess::__launch(
         environment[name] = value;
       }
     }
+
+    if (launchInfo.isSome() && launchInfo->has_command()) {
+      if (executorLaunchCommand.isSome()) {
+        return Failure("At most one command can be returned from isolators");
+      } else {
+        executorLaunchCommand = launchInfo->command();
+      }
+    }
   }
 
   // TODO(jieyu): Consider moving this to 'executorEnvironment' and
@@ -1008,7 +1022,9 @@ Future<bool> MesosContainerizerProcess::__launch(
     // Prepare the flags to pass to the launch process.
     MesosContainerizerLaunch::Flags launchFlags;
 
-    launchFlags.command = JSON::protobuf(executorInfo.command());
+    launchFlags.command = executorLaunchCommand.isSome()
+      ? JSON::protobuf(executorLaunchCommand.get())
+      : JSON::protobuf(executorInfo.command());
 
     launchFlags.directory = rootfs.isSome()
       ? flags.sandbox_directory

http://git-wip-us.apache.org/repos/asf/mesos/blob/21224a75/src/slave/containerizer/mesos/isolators/docker/runtime.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/isolators/docker/runtime.cpp b/src/slave/containerizer/mesos/isolators/docker/runtime.cpp
index e60defd..c8a9372 100644
--- a/src/slave/containerizer/mesos/isolators/docker/runtime.cpp
+++ b/src/slave/containerizer/mesos/isolators/docker/runtime.cpp
@@ -19,8 +19,12 @@
 
 #include <glog/logging.h>
 
+#include <mesos/docker/v1.hpp>
+
+#include <stout/error.hpp>
 #include <stout/foreach.hpp>
 #include <stout/stringify.hpp>
+#include <stout/strings.hpp>
 
 #include "slave/flags.hpp"
 
@@ -100,6 +104,17 @@ Future<Option<ContainerLaunchInfo>> DockerRuntimeIsolatorProcess::prepare(
     launchInfo.mutable_environment()->CopyFrom(environment.get());
   }
 
+  Try<CommandInfo> command = getExecutorLaunchCommand(
+      containerId,
+      containerConfig);
+
+  if (command.isError()) {
+    return Failure("Failed to determine the executor launch command: " +
+                   command.error());
+  }
+
+  launchInfo.mutable_command()->CopyFrom(command.get());
+
   return launchInfo;
 }
 
@@ -144,6 +159,167 @@ Option<Environment> DockerRuntimeIsolatorProcess::getLaunchEnvironment(
 }
 
 
+// This method reads the CommandInfo form ExecutorInfo and optional
+// TaskInfo, and merge them with docker image default Entrypoint and
+// Cmd. It returns a merged CommandInfo which will be used to launch
+// the executor.
+Try<CommandInfo> DockerRuntimeIsolatorProcess::getExecutorLaunchCommand(
+    const ContainerID& containerId,
+    const ContainerConfig& containerConfig)
+{
+  CHECK(containerConfig.docker().manifest().has_config());
+
+  // We may or may not mutate the CommandInfo for executor depending
+  // on the logic table below. For custom executor case, we make
+  // changes to the command directly. For command task case, if no
+  // need to change the launch command for the user task, we do not do
+  // anything and return the CommandInfo from ExecutorInfo. We only
+  // add a flag `--task_command` to carry command as a JSON object if
+  // it is neccessary to mutate.
+  CommandInfo command;
+
+  if (!containerConfig.has_task_info()) {
+    // Custom executor case.
+    CHECK(containerConfig.executor_info().has_command());
+    command = containerConfig.executor_info().command();
+  } else {
+    // Command task case.
+    CHECK(containerConfig.task_info().has_command());
+    command = containerConfig.task_info().command();
+  }
+
+  // We merge the CommandInfo following the logic:
+  // 1. If 'shell' is true, we will ignore Entrypoint and Cmd from
+  //    the docker image (row 5-8).
+  // 2. If 'shell' is false and 'value' is set, we will ignore the
+  //    Entrypoint and Cmd from the docker image as well (row 3-4).
+  // 3. If 'shell' is false and 'value' is not set, use the docker
+  //    image specified runtime configuration.
+  //    i. If 'Entrypoint' is set, it is treated as executable,
+  //       then 'Cmd' get appended as arguments.
+  //    ii.If 'Entrypoint' is not set, use the first 'Cmd' as
+  //       executable and the others as arguments.
+  // +---------+--------------+--------------+--------------+--------------+
+  // |         | Entrypoint=0 | Entrypoint=0 | Entrypoint=1 | Entrypoint=1 |
+  // |         |     Cmd=0    |     Cmd=1    |     Cmd=0    |     Cmd=1    |
+  // +---------+--------------+--------------+--------------+--------------+
+  // |   sh=0  |     Error    |   ./Cmd[0]   | ./Entrypt[0] | ./Entrypt[0] |
+  // | value=0 |              |   Cmd[1]..   | Entrypt[1].. | Entrypt[1].. |
+  // |  argv=0 |              |              |              |     Cmd..    |
+  // +---------+--------------+--------------+--------------+--------------+
+  // |   sh=0  |     Error    |   ./Cmd[0]   | ./Entrypt[0] | ./Entrypt[0] |
+  // | value=0 |              |     argv     | Entrypt[1].. | Entrypt[1].. |
+  // |  argv=1 |              |              |     argv     |     argv     |
+  // +---------+--------------+--------------+--------------+--------------+
+  // |   sh=0  |    ./value   |    ./value   |    ./value   |    ./value   |
+  // | value=1 |              |              |              |              |
+  // |  argv=0 |              |              |              |              |
+  // +---------+--------------+--------------+--------------+--------------+
+  // |   sh=0  |    ./value   |    ./value   |    ./value   |    ./value   |
+  // | value=1 |     argv     |     argv     |     argv     |     argv     |
+  // |  argv=1 |              |              |              |              |
+  // +---------+--------------+--------------+--------------+--------------+
+  // |   sh=1  |     Error    |     Error    |     Error    |     Error    |
+  // | value=0 |              |              |              |              |
+  // |  argv=0 |              |              |              |              |
+  // +---------+--------------+--------------+--------------+--------------+
+  // |   sh=1  |     Error    |     Error    |     Error    |     Error    |
+  // | value=0 |              |              |              |              |
+  // |  argv=1 |              |              |              |              |
+  // +---------+--------------+--------------+--------------+--------------+
+  // |   sh=1  |  /bin/sh -c  |  /bin/sh -c  |  /bin/sh -c  |  /bin/sh -c  |
+  // | value=1 |     value    |     value    |     value    |     value    |
+  // |  argv=0 |              |              |              |              |
+  // +---------+--------------+--------------+--------------+--------------+
+  // |   sh=1  |  /bin/sh -c  |  /bin/sh -c  |  /bin/sh -c  |  /bin/sh -c  |
+  // | value=1 |     value    |     value    |     value    |     value    |
+  // |  argv=1 |              |              |              |              |
+  // +---------+--------------+--------------+--------------+--------------+
+  if (command.shell()) {
+    if (!command.has_value()) {
+      return Error("Shell specified but no command value provided");
+    }
+  } else {
+    if (!command.has_value()) {
+      // TODO(gilbert): Deprecate 'override' flag option in command
+      // task. We do not exclude override case here.
+
+      // We keep the arguments of commandInfo while it does not have a
+      // value, so that arguments can be specified by user, which is
+      // running with default image Entrypoint.
+      const docker::spec::v1::ImageManifest::Config& config =
+        containerConfig.docker().manifest().config();
+
+      // Filter out executable for commandInfo value.
+      if (config.entrypoint_size() > 0) {
+        command.set_value(config.entrypoint(0));
+
+        // Put user defined argv after default entrypoint argv
+        // in sequence.
+        command.clear_arguments();
+
+        for (int i = 1; i < config.entrypoint_size(); i++) {
+          command.add_arguments(config.entrypoint(i));
+        }
+
+        // Append all possible user argv after entrypoint arguments.
+        if (!containerConfig.has_task_info()) {
+          // Custom executor case.
+          command.mutable_arguments()->MergeFrom(
+              containerConfig.executor_info().command().arguments());
+        } else {
+          // Command task case.
+          command.mutable_arguments()->MergeFrom(
+              containerConfig.task_info().command().arguments());
+        }
+
+        // Overwrite default cmd arguments if CommandInfo arguments
+        // are set by user. The logic below is the case that no
+        // argument is set by user.
+        if (command.arguments_size() == config.entrypoint_size() - 1) {
+          foreach (const string& cmd, config.cmd()) {
+            command.add_arguments(cmd);
+          }
+        }
+      } else if (config.cmd_size() > 0) {
+        command.set_value(config.cmd(0));
+
+        // Overwrite default cmd arguments if CommandInfo arguments
+        // are set by user.
+        if (command.arguments_size() == 0) {
+          for (int i = 1; i < config.cmd_size(); i++) {
+            command.add_arguments(config.cmd(i));
+          }
+        }
+      } else {
+        return Error("No executable is found for container: '" +
+                     containerId.value() + "'");
+      }
+    }
+  }
+
+  if (containerConfig.has_task_info()) {
+    // For command executor, with command value as 'mesos-executor'.
+    CommandInfo executorCommand = containerConfig.executor_info().command();
+
+    // Only pass the mutated command to command executor as a flag if
+    // image default config is included (see table above: row 1-2).
+    if (!containerConfig.task_info().command().shell() &&
+        !containerConfig.task_info().command().has_value()) {
+      JSON::Object object = JSON::protobuf(command);
+
+      // Pass task command as a flag, which will be loaded by
+      // command executor.
+      executorCommand.add_arguments("--task_command=" + stringify(object));
+    }
+
+    return executorCommand;
+  }
+
+  return command;
+}
+
+
 Future<Nothing> DockerRuntimeIsolatorProcess::isolate(
     const ContainerID& containerId,
     pid_t pid)

http://git-wip-us.apache.org/repos/asf/mesos/blob/21224a75/src/slave/containerizer/mesos/isolators/docker/runtime.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/isolators/docker/runtime.hpp b/src/slave/containerizer/mesos/isolators/docker/runtime.hpp
index e419b6b..6e6ec86 100644
--- a/src/slave/containerizer/mesos/isolators/docker/runtime.hpp
+++ b/src/slave/containerizer/mesos/isolators/docker/runtime.hpp
@@ -65,6 +65,10 @@ private:
       const ContainerID& containerId,
       const mesos::slave::ContainerConfig& containerConfig);
 
+  Try<CommandInfo> getExecutorLaunchCommand(
+      const ContainerID& containerId,
+      const mesos::slave::ContainerConfig& containerConfig);
+
   const Flags flags;
 };
 


[2/3] mesos git commit: Added a new flag to command executor for overriding the task command.

Posted by ji...@apache.org.
Added a new flag to command executor for overriding the task command.

Review: https://reviews.apache.org/r/43082/


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

Branch: refs/heads/master
Commit: 9228d91d2da0d928f373bec205e0e31deeadfd37
Parents: 21224a7
Author: Gilbert Song <so...@gmail.com>
Authored: Thu Feb 4 13:48:37 2016 -0800
Committer: Jie Yu <yu...@gmail.com>
Committed: Thu Feb 4 14:56:14 2016 -0800

----------------------------------------------------------------------
 src/launcher/executor.cpp | 115 ++++++++++++++++++++++++++++-------------
 1 file changed, 78 insertions(+), 37 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/9228d91d/src/launcher/executor.cpp
----------------------------------------------------------------------
diff --git a/src/launcher/executor.cpp b/src/launcher/executor.cpp
index 356d311..b214a3f 100644
--- a/src/launcher/executor.cpp
+++ b/src/launcher/executor.cpp
@@ -39,6 +39,7 @@
 
 #include <stout/duration.hpp>
 #include <stout/flags.hpp>
+#include <stout/json.hpp>
 #include <stout/lambda.hpp>
 #include <stout/option.hpp>
 #include <stout/os.hpp>
@@ -81,7 +82,8 @@ public:
       const Option<char**>& override,
       const string& _healthCheckDir,
       const Option<string>& _sandboxDirectory,
-      const Option<string>& _user)
+      const Option<string>& _user,
+      const Option<string>& _taskCommand)
     : state(REGISTERING),
       launched(false),
       killed(false),
@@ -93,7 +95,8 @@ public:
       healthCheckDir(_healthCheckDir),
       override(override),
       sandboxDirectory(_sandboxDirectory),
-      user(_user) {}
+      user(_user),
+      taskCommand(_taskCommand) {}
 
   virtual ~CommandExecutorProcess() {}
 
@@ -137,26 +140,46 @@ public:
       return;
     }
 
-    // Skip sanity checks for TaskInfo if override is provided since
-    // the executor will be running the override command.
-    if (override.isNone()) {
-      // Sanity checks.
-      CHECK(task.has_command()) << "Expecting task " << task.task_id()
-                                << " to have a command!";
+    // Determine the command to launch the task.
+    CommandInfo command;
+
+    if (taskCommand.isSome()) {
+      // Get CommandInfo from a JSON string.
+      Try<JSON::Object> object = JSON::parse<JSON::Object>(taskCommand.get());
+      if (object.isError()) {
+        cerr << "Failed to parse JSON: " << object.error() << endl;
+        abort();
+      }
+
+      Try<CommandInfo> parse = protobuf::parse<CommandInfo>(object.get());
+      if (parse.isError()) {
+        cerr << "Failed to parse protobuf: " << parse.error() << endl;
+        abort();
+      }
+
+      command = parse.get();
+    } else if (task.has_command()) {
+      command = task.command();
+    } else {
+      CHECK_SOME(override)
+        << "Expecting task '" << task.task_id()
+        << "' to have a command!";
+    }
 
+    if (override.isNone()) {
       // TODO(jieyu): For now, we just fail the executor if the task's
       // CommandInfo is not valid. The framework will receive
       // TASK_FAILED for the task, and will most likely find out the
       // cause with some debugging. This is a temporary solution. A more
       // correct solution is to perform this validation at master side.
-      if (task.command().shell()) {
-        CHECK(task.command().has_value())
-          << "Shell command of task " << task.task_id()
-          << " is not specified!";
+      if (command.shell()) {
+        CHECK(command.has_value())
+          << "Shell command of task '" << task.task_id()
+          << "' is not specified!";
       } else {
-        CHECK(task.command().has_value())
-          << "Executable of task " << task.task_id()
-          << " is not specified!";
+        CHECK(command.has_value())
+          << "Executable of task '" << task.task_id()
+          << "' is not specified!";
       }
     }
 
@@ -241,31 +264,31 @@ public:
     }
 
     // Prepare the argv before fork as it's not async signal safe.
-    char **argv = new char*[task.command().arguments().size() + 1];
-    for (int i = 0; i < task.command().arguments().size(); i++) {
-      argv[i] = (char*) task.command().arguments(i).c_str();
+    char **argv = new char*[command.arguments().size() + 1];
+    for (int i = 0; i < command.arguments().size(); i++) {
+      argv[i] = (char*) command.arguments(i).c_str();
     }
-    argv[task.command().arguments().size()] = NULL;
+    argv[command.arguments().size()] = NULL;
 
     // Prepare the command log message.
-    string command;
+    string commandString;
     if (override.isSome()) {
       char** argv = override.get();
       // argv is guaranteed to be NULL terminated and we rely on
       // that fact to print command to be executed.
       for (int i = 0; argv[i] != NULL; i++) {
-        command += string(argv[i]) + " ";
+        commandString += string(argv[i]) + " ";
       }
-    } else if (task.command().shell()) {
-      command = "sh -c '" + task.command().value() + "'";
+    } else if (command.shell()) {
+      commandString = "sh -c '" + command.value() + "'";
     } else {
-      command =
-        "[" + task.command().value() + ", " +
-        strings::join(", ", task.command().arguments()) + "]";
+      commandString =
+        "[" + command.value() + ", " +
+        strings::join(", ", command.arguments()) + "]";
     }
 
     if ((pid = fork()) == -1) {
-      cerr << "Failed to fork to run " << command << ": "
+      cerr << "Failed to fork to run " << commandString << ": "
            << os::strerror(errno) << endl;
       abort();
     }
@@ -344,19 +367,19 @@ public:
       }
 
 
-      cout << command << endl;
+      cout << commandString << endl;
 
       // The child has successfully setsid, now run the command.
       if (override.isNone()) {
-        if (task.command().shell()) {
+        if (command.shell()) {
           execlp(
               "sh",
               "sh",
               "-c",
-              task.command().value().c_str(),
+              command.value().c_str(),
               (char*) NULL);
         } else {
-          execvp(task.command().value().c_str(), argv);
+          execvp(command.value().c_str(), argv);
         }
       } else {
         char** argv = override.get();
@@ -624,6 +647,7 @@ private:
   Option<char**> override;
   Option<string> sandboxDirectory;
   Option<string> user;
+  Option<string> taskCommand;
 };
 
 
@@ -634,10 +658,11 @@ public:
       const Option<char**>& override,
       const string& healthCheckDir,
       const Option<string>& sandboxDirectory,
-      const Option<string>& user)
+      const Option<string>& user,
+      const Option<string>& taskCommand)
   {
     process = new CommandExecutorProcess(
-        override, healthCheckDir, sandboxDirectory, user);
+        override, healthCheckDir, sandboxDirectory, user, taskCommand);
     spawn(process);
   }
 
@@ -715,6 +740,8 @@ class Flags : public flags::FlagsBase
 public:
   Flags()
   {
+    // TODO(gilbert): Deprecate the 'override' flag since no one is
+    // using it, and it may cause confusing with 'task_command' flag.
     add(&override,
         "override",
         "Whether to override the command the executor should run when the\n"
@@ -734,6 +761,11 @@ public:
         "user",
         "The user that the task should be running as.");
 
+    add(&task_command,
+        "task_command",
+        "If specified, this is the overrided command for launching the\n"
+        "task (instead of the command from TaskInfo).");
+
     // TODO(nnielsen): Add 'prefix' option to enable replacing
     // 'sh -c' with user specified wrapper.
   }
@@ -741,6 +773,7 @@ public:
   bool override;
   Option<string> sandbox_directory;
   Option<string> user;
+  Option<string> task_command;
 };
 
 
@@ -774,11 +807,19 @@ int main(int argc, char** argv)
   }
 
   const Option<string> envPath = os::getenv("MESOS_LAUNCHER_DIR");
-  string path =
-    envPath.isSome() ? envPath.get()
-                     : os::realpath(Path(argv[0]).dirname()).get();
+
+  string path = envPath.isSome()
+    ? envPath.get()
+    : os::realpath(Path(argv[0]).dirname()).get();
+
   mesos::internal::CommandExecutor executor(
-      override, path, flags.sandbox_directory, flags.user);
+      override,
+      path,
+      flags.sandbox_directory,
+      flags.user,
+      flags.task_command);
+
   mesos::MesosExecutorDriver driver(&executor);
+
   return driver.run() == mesos::DRIVER_STOPPED ? EXIT_SUCCESS : EXIT_FAILURE;
 }