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 {