Repository: mesos
Updated Branches:
  refs/heads/master f5f8289bf -> d503fbbfc


Agent: Add Windows support to the containerizer.

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


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

Branch: refs/heads/master
Commit: d503fbbfc6a4a036e5f30d268f9d1a1ecfbc6d70
Parents: f5f8289
Author: Alex Clemmer <clemmer.alexan...@gmail.com>
Authored: Mon May 30 21:40:30 2016 -0700
Committer: Joris Van Remoortere <joris.van.remoort...@gmail.com>
Committed: Mon May 30 22:29:25 2016 -0700

----------------------------------------------------------------------
 src/slave/containerizer/docker.cpp              |  9 ++-
 .../containerizer/external_containerizer.cpp    | 34 ++++++++++
 src/slave/containerizer/fetcher.cpp             |  7 +--
 src/slave/containerizer/mesos/containerizer.cpp | 66 +++++++++++++++-----
 src/slave/containerizer/mesos/launcher.cpp      |  6 ++
 src/slave/containerizer/mesos/launcher.hpp      | 16 ++++-
 6 files changed, 116 insertions(+), 22 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/d503fbbf/src/slave/containerizer/docker.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/docker.cpp 
b/src/slave/containerizer/docker.cpp
index 52caf9f..7453e52 100644
--- a/src/slave/containerizer/docker.cpp
+++ b/src/slave/containerizer/docker.cpp
@@ -37,6 +37,8 @@
 #include <stout/jsonify.hpp>
 #include <stout/os.hpp>
 
+#include <stout/os/killtree.hpp>
+
 #include "common/status_utils.hpp"
 
 #include "hook/manager.hpp"
@@ -257,6 +259,10 @@ DockerContainerizerProcess::Container::create(
     return Error("Failed to touch 'stderr': " + touch.error());
   }
 
+  // NOTE: `os::chown` has no meaningful interpretation on Windows. This is
+  // safe to `#ifdef` out because we don't compile the user flag on Windows, so
+  // this should always be `None`.
+#ifndef __WINDOWS__
   if (user.isSome()) {
     Try<Nothing> chown = os::chown(user.get(), directory);
 
@@ -264,6 +270,7 @@ DockerContainerizerProcess::Container::create(
       return Error("Failed to chown: " + chown.error());
     }
   }
+#endif // __WINDOWS__
 
   string dockerSymlinkPath = path::join(
       paths::getSlavePath(flags.work_dir, slaveId),
@@ -1350,7 +1357,7 @@ Future<pid_t> 
DockerContainerizerProcess::checkpointExecutor(
   // after we set Container::status.
   CHECK(containers_.contains(containerId));
 
-  Option<int> pid = dockerContainer.pid;
+  Option<pid_t> pid = dockerContainer.pid;
 
   if (!pid.isSome()) {
     return Failure("Unable to get executor pid after launch");

http://git-wip-us.apache.org/repos/asf/mesos/blob/d503fbbf/src/slave/containerizer/external_containerizer.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/external_containerizer.cpp 
b/src/slave/containerizer/external_containerizer.cpp
index cf4384c..9ee137a 100644
--- a/src/slave/containerizer/external_containerizer.cpp
+++ b/src/slave/containerizer/external_containerizer.cpp
@@ -15,7 +15,11 @@
 // limitations under the License.
 
 #include <errno.h>
+
+#ifndef __WINDOWS__
 #include <poll.h>
+#endif // __WINDOWS__
+
 #include <signal.h>
 #include <stdio.h>
 
@@ -41,6 +45,8 @@
 #include <stout/strings.hpp>
 #include <stout/uuid.hpp>
 
+#include <stout/os/killtree.hpp>
+
 #include "common/status_utils.hpp"
 
 #include "slave/paths.hpp"
@@ -343,6 +349,11 @@ Future<Nothing> ExternalContainerizerProcess::__recover(
                   << "' of framework " << framework.id;
 
         Option<string> user = None();
+
+        // NOTE: `chown` has no meaningful interpretation on Windows. This is
+        // safe to `#ifdef` out because we don't compile the user flag on
+        // Windows, so this should always be `None`.
+#ifndef __WINDOWS__
         if (flags.switch_user) {
           // The command (either in form of task or executor command)
           // can define a specific user to run as. If present, this
@@ -354,6 +365,7 @@ Future<Nothing> ExternalContainerizerProcess::__recover(
             user = framework.info.get().user();
           }
         }
+#endif // __WINDOWS__
 
         // Re-create the sandbox for this container.
         const string directory = paths::createExecutorDirectory(
@@ -626,7 +638,11 @@ Future<containerizer::Termination> 
ExternalContainerizerProcess::_wait(
   // Invoke the protobuf::read asynchronously.
   // TODO(tillt): Consider moving protobuf::read into libprocess and
   // making it work fully asynchronously.
+#ifndef __WINDOWS__
   Result<containerizer::Termination>(*read)(int, bool, bool) =
+#else
+  Result<containerizer::Termination>(*read)(HANDLE, bool, bool) =
+#endif // __WINDOWS__
     &::protobuf::read<containerizer::Termination>;
 
   Future<Result<containerizer::Termination>> future = async(
@@ -812,7 +828,11 @@ Future<ResourceStatistics> 
ExternalContainerizerProcess::_usage(
                    "' failed: " + invoked.error());
   }
 
+#ifndef __WINDOWS__
   Result<ResourceStatistics>(*read)(int, bool, bool) =
+#else
+  Result<ResourceStatistics>(*read)(HANDLE, bool, bool) =
+#endif // __WINDOWS__
     &::protobuf::read<ResourceStatistics>;
 
   Future<Result<ResourceStatistics>> future = async(
@@ -950,7 +970,11 @@ Future<hashset<ContainerID>> 
ExternalContainerizerProcess::containers()
     return Failure("Containers failed: " + invoked.error());
   }
 
+#ifndef __WINDOWS__
   Result<containerizer::Containers>(*read)(int, bool, bool) =
+#else
+  Result<containerizer::Containers>(*read)(HANDLE, bool, bool) =
+#endif // __WINDOWS__
     &::protobuf::read<containerizer::Containers>;
 
   Future<Result<containerizer::Containers>> future = async(
@@ -1075,6 +1099,10 @@ Try<Subprocess> ExternalContainerizerProcess::invoke(
   VLOG_IF(2, sandbox.isSome() &&
       sandbox.get().user.isSome()) << "user: " << sandbox.get().user.get();
 
+  // NOTE: `chown` has no meaningful interpretation on Windows. This is safe to
+  // `#ifdef` out because we don't compile the user flag on Windows, so this
+  // should always be `None`.
+#ifndef __WINDOWS__
   // Re/establish the sandbox conditions for the containerizer.
   if (sandbox.isSome() && sandbox.get().user.isSome()) {
     Try<Nothing> chown = os::chown(
@@ -1084,6 +1112,7 @@ Try<Subprocess> ExternalContainerizerProcess::invoke(
       return Error("Failed to chown work directory: " + chown.error());
     }
   }
+#endif // __WINDOWS__
 
   // Fork exec of external process. Run a chdir and a setsid within
   // the child-context.
@@ -1130,6 +1159,10 @@ Try<Subprocess> ExternalContainerizerProcess::invoke(
         err.error());
   }
 
+  // NOTE: `chown` has no meaningful interpretation on Windows. This is safe to
+  // `#ifdef` out because we don't compile the user flag on Windows, so this
+  // should always be `None`.
+#ifndef __WINDOWS__
   if (sandbox.isSome() && sandbox.get().user.isSome()) {
     Try<Nothing> chown = os::chown(
         sandbox.get().user.get(),
@@ -1141,6 +1174,7 @@ Try<Subprocess> ExternalContainerizerProcess::invoke(
           chown.error());
     }
   }
+#endif // __WINDOWS__
 
   // TODO(tillt): Consider adding an overload to io::redirect
   // that accepts a file path as 'to' for further reducing code.

http://git-wip-us.apache.org/repos/asf/mesos/blob/d503fbbf/src/slave/containerizer/fetcher.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/fetcher.cpp 
b/src/slave/containerizer/fetcher.cpp
index 12f64ae..7b52cb1 100644
--- a/src/slave/containerizer/fetcher.cpp
+++ b/src/slave/containerizer/fetcher.cpp
@@ -211,12 +211,8 @@ Result<string> Fetcher::uriToLocalPath(
     fileUri = true;
   }
 
-  const bool isRelativePath =
 #ifndef __WINDOWS__
-    !strings::startsWith(path, "/");
-#else
-    static_cast<bool>(::PathIsRelative(path.c_str()));
-#endif // __WINDOWS__
+  const bool isRelativePath = !strings::startsWith(path, "/");
 
   if (isRelativePath) {
     if (fileUri) {
@@ -234,6 +230,7 @@ Result<string> Fetcher::uriToLocalPath(
                 << "making it: '" << path << "'";
     }
   }
+#endif // __WINDOWS__
 
   return path;
 }

http://git-wip-us.apache.org/repos/asf/mesos/blob/d503fbbf/src/slave/containerizer/mesos/containerizer.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/containerizer.cpp 
b/src/slave/containerizer/mesos/containerizer.cpp
index b154587..c7b9744 100644
--- a/src/slave/containerizer/mesos/containerizer.cpp
+++ b/src/slave/containerizer/mesos/containerizer.cpp
@@ -51,9 +51,12 @@
 #include "slave/containerizer/mesos/launcher.hpp"
 #ifdef __linux__
 #include "slave/containerizer/mesos/linux_launcher.hpp"
-#endif
+#endif // __linux__
 
 #include "slave/containerizer/mesos/isolators/posix.hpp"
+#ifdef __WINDOWS__
+#include "slave/containerizer/mesos/isolators/windows.hpp"
+#endif // __WINDOWS__
 
 #include "slave/containerizer/mesos/isolators/posix/disk.hpp"
 
@@ -66,29 +69,32 @@
 #include "slave/containerizer/mesos/isolators/cgroups/mem.hpp"
 #include "slave/containerizer/mesos/isolators/cgroups/net_cls.hpp"
 #include "slave/containerizer/mesos/isolators/cgroups/perf_event.hpp"
-#endif
+#endif // __linux__
 
 #ifdef ENABLE_NVIDIA_GPU_SUPPORT
 #ifdef __linux__
 #include "slave/containerizer/mesos/isolators/cgroups/devices/gpus/nvidia.hpp"
-#endif
+#endif // __linux__
 #endif
 
 #ifdef __linux__
 #include "slave/containerizer/mesos/isolators/docker/runtime.hpp"
-#endif
+#endif // __linux__
 
 #ifdef __linux__
 #include "slave/containerizer/mesos/isolators/docker/volume/isolator.hpp"
-#endif
+#endif // __linux__
 
 #ifdef __linux__
 #include "slave/containerizer/mesos/isolators/filesystem/linux.hpp"
-#endif
+#endif // __linux__
 #include "slave/containerizer/mesos/isolators/filesystem/posix.hpp"
+#ifdef __WINDOWS__
+#include "slave/containerizer/mesos/isolators/filesystem/windows.hpp"
+#endif // __WINDOWS__
 #ifdef __linux__
 #include "slave/containerizer/mesos/isolators/filesystem/shared.hpp"
-#endif
+#endif // __linux__
 
 #ifdef __linux__
 #include "slave/containerizer/mesos/isolators/namespaces/pid.hpp"
@@ -128,7 +134,11 @@ using state::FrameworkState;
 using state::ExecutorState;
 using state::RunState;
 
+#ifndef __WINDOWS__
 const char MESOS_CONTAINERIZER[] = "mesos-containerizer";
+#else
+const char MESOS_CONTAINERIZER[] = "mesos-containerizer.exe";
+#endif // __WINDOWS__
 
 Try<MesosContainerizer*> MesosContainerizer::create(
     const Flags& flags,
@@ -214,6 +224,15 @@ Try<MesosContainerizer*> MesosContainerizer::create(
     return LinuxLauncher::available()
       ? LinuxLauncher::create(flags_)
       : PosixLauncher::create(flags_);
+#elif __WINDOWS__
+    // NOTE: Because the most basic launcher historically has been "posix", we
+    // accept this flag on Windows, but map it to the `WindowsLauncher`.
+    if (flags_.launcher.isSome() && !(flags_.launcher.get() == "posix" ||
+        flags_.launcher.get() == "windows")) {
+      return Error("Unsupported launcher: " + flags_.launcher.get());
+    }
+
+    return WindowsLauncher::create(flags_);
 #else
     if (flags_.launcher.isSome() && flags_.launcher.get() != "posix") {
       return Error("Unsupported launcher: " + flags_.launcher.get());
@@ -236,15 +255,20 @@ Try<MesosContainerizer*> MesosContainerizer::create(
   const hashmap<string, lambda::function<Try<Isolator*>(const Flags&)>>
     creators = {
     // Filesystem isolators.
+#ifndef __WINDOWS__
     {"filesystem/posix", &PosixFilesystemIsolatorProcess::create},
+#else
+    {"filesystem/windows", &WindowsFilesystemIsolatorProcess::create},
+#endif // __WINDOWS__
 #ifdef __linux__
     {"filesystem/linux", &LinuxFilesystemIsolatorProcess::create},
 
     // TODO(jieyu): Deprecate this in favor of using filesystem/linux.
     {"filesystem/shared", &SharedFilesystemIsolatorProcess::create},
-#endif
+#endif // __linux__
 
     // Runtime isolators.
+#ifndef __WINDOWS__
     {"posix/cpu", &PosixCpuIsolatorProcess::create},
     {"posix/mem", &PosixMemIsolatorProcess::create},
 
@@ -255,6 +279,9 @@ Try<MesosContainerizer*> MesosContainerizer::create(
 #if ENABLE_XFS_DISK_ISOLATOR
     {"disk/xfs", &XfsDiskIsolatorProcess::create},
 #endif
+#else
+    {"windows/cpu", &WindowsCpuIsolatorProcess::create},
+#endif // __WINDOWS__
 #ifdef __linux__
     {"cgroups/cpu", &CgroupsCpushareIsolatorProcess::create},
     {"cgroups/mem", &CgroupsMemIsolatorProcess::create},
@@ -267,8 +294,9 @@ Try<MesosContainerizer*> MesosContainerizer::create(
     {"docker/volume", &DockerVolumeIsolatorProcess::create},
     {"namespaces/pid", &NamespacesPidIsolatorProcess::create},
     {"network/cni", &NetworkCniIsolatorProcess::create},
-#endif
-#ifdef WITH_NETWORK_ISOLATOR
+#endif // __linux__
+    // NOTE: Network isolation is currently not supported on Windows builds.
+#if !defined(__WINDOWS__) && defined(WITH_NETWORK_ISOLATOR)
     {"network/port_mapping", &PortMappingIsolatorProcess::create},
 #endif
   };
@@ -1155,9 +1183,9 @@ Future<bool> MesosContainerizerProcess::__launch(
     // Use a pipe to block the child until it's been isolated.
     int pipes[2];
 
-    // We assume this should not fail under reasonable conditions so
-    // we use CHECK.
-    CHECK(pipe(pipes) == 0);
+    // TODO(jmlvanre): consider returning failure if `pipe` gives an
+    // error. Currently we preserve the previous logic.
+    CHECK_SOME(os::pipe(pipes));
 
     // Prepare the flags to pass to the launch process.
     MesosContainerizerLaunch::Flags launchFlags;
@@ -1196,8 +1224,16 @@ Future<bool> MesosContainerizerProcess::__launch(
     launchFlags.rootfs = executorRootfs;
     launchFlags.user = user;
 #endif // __WINDOWS__
+
+#ifndef __WINDOWS__
     launchFlags.pipe_read = pipes[0];
     launchFlags.pipe_write = pipes[1];
+#else
+    // NOTE: On windows we need to pass `Handle`s between processes, as fds
+    // are not unique across processes.
+    launchFlags.pipe_read = os::fd_to_handle(pipes[0]);
+    launchFlags.pipe_write = os::fd_to_handle(pipes[1]);
+#endif // __WINDOWS
     launchFlags.commands = commands;
 
     VLOG(1) << "Launching '" << MESOS_CONTAINERIZER << "' with flags '"
@@ -1264,8 +1300,8 @@ Future<bool> MesosContainerizerProcess::__launch(
                   user,
                   slaveId))
       .then(defer(self(), &Self::exec, containerId, pipes[1]))
-      .onAny(lambda::bind(&os::close, pipes[0]))
-      .onAny(lambda::bind(&os::close, pipes[1]));
+      .onAny([pipes]() { os::close(pipes[0]); })
+      .onAny([pipes]() { os::close(pipes[1]); });
   }));
 }
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/d503fbbf/src/slave/containerizer/mesos/launcher.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/launcher.cpp 
b/src/slave/containerizer/mesos/launcher.cpp
index a5c8c31..b1b323b 100644
--- a/src/slave/containerizer/mesos/launcher.cpp
+++ b/src/slave/containerizer/mesos/launcher.cpp
@@ -164,6 +164,12 @@ Future<Nothing> _destroy(const Future<Option<int>>& future)
   }
 }
 
+
+Try<Launcher*> WindowsLauncher::create(const Flags& flags)
+{
+  return new WindowsLauncher();
+}
+
 } // namespace slave {
 } // namespace internal {
 } // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/d503fbbf/src/slave/containerizer/mesos/launcher.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/launcher.hpp 
b/src/slave/containerizer/mesos/launcher.hpp
index 5977c30..05320f4 100644
--- a/src/slave/containerizer/mesos/launcher.hpp
+++ b/src/slave/containerizer/mesos/launcher.hpp
@@ -107,7 +107,7 @@ public:
 
   virtual process::Future<Nothing> destroy(const ContainerID& containerId);
 
-private:
+protected:
   PosixLauncher() {}
 
   // The 'pid' is the process id of the first process and also the
@@ -115,6 +115,20 @@ private:
   hashmap<ContainerID, pid_t> pids;
 };
 
+
+// Minimal implementation of a `Launcher` for the Windows platform. Does not
+// take into account process groups (jobs) or sessions.
+class WindowsLauncher : public PosixLauncher
+{
+public:
+  static Try<Launcher*> create(const Flags& flags);
+
+  virtual ~WindowsLauncher() {}
+
+private:
+  WindowsLauncher() {}
+};
+
 } // namespace slave {
 } // namespace internal {
 } // namespace mesos {

Reply via email to