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:22 UTC

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

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;
 };