Repository: mesos Updated Branches: refs/heads/master f105ed772 -> 5165a4a54
Added explicit DockerInfo within ContainerInfo. Added new DockerInfo to explicitly capture Docker options, and allow command URIs to be fetched and mapped into sandbox, which gets bind-mounted into the container. Review: https://reviews.apache.org/r/24475 Project: http://git-wip-us.apache.org/repos/asf/mesos/repo Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/2057e3fa Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/2057e3fa Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/2057e3fa Branch: refs/heads/master Commit: 2057e3fa37f880b52d766feb5ed33a0209f218bc Parents: f105ed7 Author: Timothy Chen <[email protected]> Authored: Thu Aug 14 09:58:11 2014 -0700 Committer: Benjamin Hindman <[email protected]> Committed: Thu Aug 14 09:58:11 2014 -0700 ---------------------------------------------------------------------- include/mesos/mesos.proto | 14 + src/docker/docker.cpp | 73 ++++- src/docker/docker.hpp | 6 +- src/slave/containerizer/containerizer.cpp | 36 +++ src/slave/containerizer/containerizer.hpp | 8 + src/slave/containerizer/docker.cpp | 308 ++++++++++++++----- src/slave/containerizer/mesos/containerizer.cpp | 35 --- src/slave/flags.hpp | 7 + src/tests/docker_containerizer_tests.cpp | 80 ++++- src/tests/docker_tests.cpp | 33 +- 10 files changed, 469 insertions(+), 131 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mesos/blob/2057e3fa/include/mesos/mesos.proto ---------------------------------------------------------------------- diff --git a/include/mesos/mesos.proto b/include/mesos/mesos.proto index adc8fab..dea51f9 100644 --- a/include/mesos/mesos.proto +++ b/include/mesos/mesos.proto @@ -263,6 +263,10 @@ message ExecutorInfo { required ExecutorID executor_id = 1; optional FrameworkID framework_id = 8; // TODO(benh): Make this required. required CommandInfo command = 7; + // Executor provided with a container will launch the container + // with the executor's CommandInfo and we expect the container to + // act as a Mesos executor. + optional ContainerInfo container = 11; repeated Resource resources = 5; optional string name = 9; @@ -578,6 +582,9 @@ message TaskInfo { repeated Resource resources = 4; optional ExecutorInfo executor = 5; optional CommandInfo command = 7; + // Task provided with a container will launch the container as part + // of this task paired with the task's CommandInfo. + optional ContainerInfo container = 9; optional bytes data = 6; // A health check for the task (currently in *alpha* and initial // support will only be for TaskInfo's that have a CommandInfo). @@ -845,6 +852,13 @@ message ContainerInfo { DOCKER = 1; } + message DockerInfo { + // The docker image that is going to be passed to the registry. + required string image = 1; + } + required Type type = 1; repeated Volume volumes = 2; + + optional DockerInfo docker = 3; } http://git-wip-us.apache.org/repos/asf/mesos/blob/2057e3fa/src/docker/docker.cpp ---------------------------------------------------------------------- diff --git a/src/docker/docker.cpp b/src/docker/docker.cpp index 1cba381..71dbb13 100644 --- a/src/docker/docker.cpp +++ b/src/docker/docker.cpp @@ -39,6 +39,8 @@ #include "slave/containerizer/isolators/cgroups/cpushare.hpp" #include "slave/containerizer/isolators/cgroups/mem.hpp" +using namespace mesos; + using namespace mesos::internal::slave; using namespace process; @@ -209,12 +211,20 @@ Try<Docker::Container> Docker::Container::create(const JSON::Object& json) Future<Nothing> Docker::run( - const string& image, - const string& command, + const ContainerInfo& containerInfo, + const CommandInfo& commandInfo, const string& name, - const Option<mesos::Resources>& resources, + const string& sandboxDirectory, + const string& mappedDirectory, + const Option<Resources>& resources, const Option<map<string, string> >& env) const { + if (!containerInfo.has_docker()) { + return Failure("No docker info found in container info"); + } + + const ContainerInfo::DockerInfo& dockerInfo = containerInfo.docker(); + string cmd = path + " run -d"; if (resources.isSome()) { @@ -243,15 +253,66 @@ Future<Nothing> Docker::run( } } - cmd += " --net=host --name=" + name + " " + image + " " + command; + foreach (const Environment::Variable& variable, + commandInfo.environment().variables()) { + // TODO(tnachen): Use subprocess with args instead once we can + // handle splitting command string into args. + string key = strings::replace(variable.name(), "\"", "\\\""); + string value = strings::replace(variable.value(), "\"", "\\\""); + cmd += " -e \"" + key + "=" + value + "\""; + } + + cmd += " -e \"MESOS_SANDBOX=" + mappedDirectory + "\""; + + foreach (const Volume& volume, containerInfo.volumes()) { + string volumeConfig = volume.container_path(); + if (volume.has_host_path()) { + volumeConfig = volume.host_path() + ":" + volumeConfig; + if (volume.has_mode()) { + switch (volume.mode()) { + case Volume::RW: volumeConfig += ":rw"; break; + case Volume::RO: volumeConfig += ":ro"; break; + default: return Failure("Unsupported volume mode"); + } + } + } else if (volume.has_mode() && !volume.has_host_path()) { + return Failure("Host path is required with mode"); + } + + cmd += " -v=" + volumeConfig; + } + + // Mapping sandbox directory into the contianer mapped directory. + cmd += " -v=" + sandboxDirectory + ":" + mappedDirectory; + + const string& image = dockerInfo.image(); + + // TODO(tnachen): Support more network options other than host + // networking that docker provides (ie: BRIDGE). We currently + // require host networking since if the docker container is + // expected to be an executor it needs to be able to communicate + // with the slave by the slave's PID. There can be more future work + // to allow a bridge to connect but this is not yet implemented. + cmd += " --net=host --name=" + name + " " + image + " " + + commandInfo.value(); VLOG(1) << "Running " << cmd; + map<string, string> environment; + + // Currently the Docker CLI picks up dockerconfig by looking for + // the config file in the $HOME directory. If one of the URIs + // provided is a docker config file we want docker to be able to + // pick it up from the sandbox directory where we store all the + // URI downloads. + environment["HOME"] = sandboxDirectory; + Try<Subprocess> s = subprocess( cmd, Subprocess::PATH("/dev/null"), - Subprocess::PATH("/dev/null"), - Subprocess::PIPE()); + Subprocess::PIPE(), + Subprocess::PIPE(), + environment); if (s.isError()) { return Failure(s.error()); http://git-wip-us.apache.org/repos/asf/mesos/blob/2057e3fa/src/docker/docker.hpp ---------------------------------------------------------------------- diff --git a/src/docker/docker.hpp b/src/docker/docker.hpp index 98b2d60..3270c91 100644 --- a/src/docker/docker.hpp +++ b/src/docker/docker.hpp @@ -65,9 +65,11 @@ public: // Performs 'docker run IMAGE'. process::Future<Nothing> run( - const std::string& image, - const std::string& command, + const mesos::ContainerInfo& containerInfo, + const mesos::CommandInfo& commandInfo, const std::string& name, + const std::string& sandboxDirectory, + const std::string& mappedDirectory, const Option<mesos::Resources>& resources = None(), const Option<std::map<std::string, std::string> >& env = None()) const; http://git-wip-us.apache.org/repos/asf/mesos/blob/2057e3fa/src/slave/containerizer/containerizer.cpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/containerizer.cpp b/src/slave/containerizer/containerizer.cpp index c91ba38..0a262ee 100644 --- a/src/slave/containerizer/containerizer.cpp +++ b/src/slave/containerizer/containerizer.cpp @@ -269,6 +269,42 @@ map<string, string> executorEnvironment( return env; } + +// Helper method to build the environment map used to launch fetcher. +map<string, string> fetcherEnvironment( + const CommandInfo& commandInfo, + const std::string& directory, + const Option<std::string>& user, + const Flags& flags) +{ + // Prepare the environment variables to pass to mesos-fetcher. + string uris = ""; + foreach (const CommandInfo::URI& uri, commandInfo.uris()) { + uris += uri.value() + "+" + + (uri.has_executable() && uri.executable() ? "1" : "0") + + (uri.extract() ? "X" : "N"); + uris += " "; + } + // Remove extra space at the end. + uris = strings::trim(uris); + + map<string, string> environment; + environment["MESOS_EXECUTOR_URIS"] = uris; + environment["MESOS_WORK_DIRECTORY"] = directory; + if (user.isSome()) { + environment["MESOS_USER"] = user.get(); + } + if (!flags.frameworks_home.empty()) { + environment["MESOS_FRAMEWORKS_HOME"] = flags.frameworks_home; + } + if (!flags.hadoop_home.empty()) { + environment["HADOOP_HOME"] = flags.hadoop_home; + } + + return environment; +} + + } // namespace slave { } // namespace internal { } // namespace mesos { http://git-wip-us.apache.org/repos/asf/mesos/blob/2057e3fa/src/slave/containerizer/containerizer.hpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/containerizer.hpp b/src/slave/containerizer/containerizer.hpp index 02754cd..8a66412 100644 --- a/src/slave/containerizer/containerizer.hpp +++ b/src/slave/containerizer/containerizer.hpp @@ -134,6 +134,14 @@ std::map<std::string, std::string> executorEnvironment( bool checkpoint, const Duration& recoveryTimeout); + +std::map<std::string, std::string> fetcherEnvironment( + const CommandInfo& commandInfo, + const std::string& directory, + const Option<std::string>& user, + const Flags& flags); + + } // namespace slave { } // namespace internal { } // namespace mesos { http://git-wip-us.apache.org/repos/asf/mesos/blob/2057e3fa/src/slave/containerizer/docker.cpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/docker.cpp b/src/slave/containerizer/docker.cpp index 904cdd3..c18023c 100644 --- a/src/slave/containerizer/docker.cpp +++ b/src/slave/containerizer/docker.cpp @@ -21,6 +21,7 @@ #include <string> #include <process/defer.hpp> +#include <process/io.hpp> #include <process/reap.hpp> #include <process/subprocess.hpp> @@ -28,6 +29,8 @@ #include <stout/hashset.hpp> #include <stout/os.hpp> +#include "common/status_utils.hpp" + #include "docker/docker.hpp" #ifdef __linux__ @@ -117,6 +120,19 @@ public: virtual process::Future<hashset<ContainerID> > containers(); private: + process::Future<Nothing> fetch( + const ContainerID& containerId, + const CommandInfo& commandInfo, + const std::string& directory); + + process::Future<Nothing> _fetch( + const ContainerID& containerId, + const Option<int>& status); + + process::Future<Nothing> _fetchFailed( + const ContainerID& containerId, + const std::string& failure); + // Continuations and helpers. process::Future<Nothing> _recover( const std::list<Docker::Container>& containers); @@ -133,12 +149,29 @@ private: process::Future<bool> _launch( const ContainerID& containerId, const ExecutorInfo& executorInfo, + const string& directory, const SlaveID& slaveId, const PID<Slave>& slavePid, bool checkpoint); process::Future<bool> __launch( const ContainerID& containerId, + const TaskInfo& taskInfo, + const ExecutorInfo& executorInfo, + const std::string& directory, + const SlaveID& slaveId, + const PID<Slave>& slavePid, + bool checkpoint); + + process::Future<bool> __launch( + const ContainerID& containerId, + const ExecutorInfo& executorInfo, + const SlaveID& slaveId, + const PID<Slave>& slavePid, + bool checkpoint); + + process::Future<bool> ___launch( + const ContainerID& containerId, const ExecutorInfo& executorInfo, const SlaveID& slaveId, const PID<Slave>& slavePid, @@ -195,8 +228,7 @@ private: // Parse the ContainerID from a Docker container and return None if // the container was not launched from Mesos. -Option<ContainerID> parse( - const Docker::Container& container) +Option<ContainerID> parse(const Docker::Container& container) { Option<string> name = None(); @@ -218,8 +250,7 @@ Option<ContainerID> parse( } -Try<DockerContainerizer*> DockerContainerizer::create( - const Flags& flags) +Try<DockerContainerizer*> DockerContainerizer::create(const Flags& flags) { Try<Docker> docker = Docker::create(flags.docker); if (docker.isError()) { @@ -247,6 +278,85 @@ DockerContainerizer::~DockerContainerizer() } +Future<Nothing> DockerContainerizerProcess::fetch( + const ContainerID& containerId, + const CommandInfo& commandInfo, + const string& directory) +{ + if (commandInfo.uris().size() == 0) { + return Nothing(); + } + + Result<string> realpath = os::realpath( + path::join(flags.launcher_dir, "mesos-fetcher")); + if (!realpath.isSome()) { + LOG(ERROR) << "Failed to determine the canonical path " + << "for the mesos-fetcher '" + << path::join(flags.launcher_dir, "mesos-fetcher") + << "': " + << (realpath.isError() ? realpath.error() : + "No such file or directory"); + return Failure("Could not fetch URIs: failed to find mesos-fetcher"); + } + + map<string, string> fetcherEnv = fetcherEnvironment( + commandInfo, + directory, + None(), + flags); + + VLOG(1) << "Starting to fetch URIs for container: " << containerId + << ", directory: " << directory; + + Try<Subprocess> fetcher = subprocess( + realpath.get(), + Subprocess::PATH("/dev/null"), + Subprocess::PATH(path::join(directory, "stdout")), + Subprocess::PATH(path::join(directory, "stderr")), + fetcherEnv); + + if (fetcher.isError()) { + return Failure("Failed to execute mesos-fetcher: " + fetcher.error()); + } + + const Future<Option<int> >& status = fetcher.get().status(); + + return status + .onFailed(defer(self(), &Self::_fetchFailed, containerId, lambda::_1)) + .then(defer(self(), &Self::_fetch, containerId, lambda::_1)); +} + + +Future<Nothing> DockerContainerizerProcess::_fetchFailed( + const ContainerID& containerId, + const string& failure) +{ + containerizer::Termination termination; + termination.set_message("Fetch failed for container: " + failure); + promises[containerId]->set(termination); + promises.erase(containerId); + return Nothing(); +} + + +Future<Nothing> DockerContainerizerProcess::_fetch( + const ContainerID& containerId, + const Option<int>& status) +{ + if (!status.isSome()) { + return _fetchFailed( + containerId, + "No status available from fetch"); + } else if (status.get() != 0) { + return _fetchFailed( + containerId, + "Fetch failed with status " + WSTRINGIFY(status.get())); + } + + return Nothing(); +} + + Future<Nothing> DockerContainerizer::recover( const Option<SlaveState>& state) { @@ -509,63 +619,36 @@ Future<bool> DockerContainerizerProcess::launch( return Failure("Container already started"); } - CommandInfo command = executorInfo.command(); - - if (!command.has_container()) { + if (!executorInfo.has_container()) { LOG(INFO) << "No container info found, skipping launch"; return false; } - string image = command.container().image(); + ContainerInfo containerInfo = executorInfo.container(); - // Check if we should try and launch this command. - if (!strings::startsWith(image, "docker:///")) { - LOG(INFO) << "No docker image found, skipping launch"; + if (containerInfo.type() != ContainerInfo::DOCKER) { + LOG(INFO) << "Skipping non-docker container"; return false; } - Owned<Promise<containerizer::Termination> > promise( - new Promise<containerizer::Termination>()); - - promises.put(containerId, promise); - LOG(INFO) << "Starting container '" << containerId << "' for executor '" << executorInfo.executor_id() << "' and framework '" << executorInfo.framework_id() << "'"; - // Extract the Docker image. - image = strings::remove(image, "docker:///", strings::PREFIX); - - // Construct the Docker container name. - string name = containerName(containerId); - - map<string, string> env = executorEnvironment( - executorInfo, - directory, - slaveId, - slavePid, - checkpoint, - flags.recovery_timeout); - - // Include any environment variables from CommandInfo. - foreach (const Environment::Variable& variable, - command.environment().variables()) { - env[variable.name()] = variable.value(); - } + Owned<Promise<containerizer::Termination> > promise( + new Promise<containerizer::Termination>()); - Resources resources = executorInfo.resources(); + promises.put(containerId, promise); - // Start a docker container then launch the executor (but destroy - // the Docker container if launching the executor failed). - return docker.run(image, command.value(), name, resources, env) + return fetch(containerId, executorInfo.command(), directory) .then(defer(self(), - &Self::_launch, - containerId, - executorInfo, - slaveId, - slavePid, - checkpoint)) - .onFailed(defer(self(), &Self::destroy, containerId, false)); + &Self::_launch, + containerId, + executorInfo, + directory, + slaveId, + slavePid, + checkpoint)); } @@ -583,46 +666,78 @@ Future<bool> DockerContainerizerProcess::launch( return Failure("Container already started"); } - if (!taskInfo.has_command()) { - LOG(WARNING) << "Not expecting call without command info"; + if (!taskInfo.has_container()) { + LOG(INFO) << "No container info found, skipping launch"; return false; } - const CommandInfo& command = taskInfo.command(); + ContainerInfo containerInfo = executorInfo.container(); - // Check if we should try and launch this command. - if (!command.has_container() || - !strings::startsWith(command.container().image(), "docker:///")) { - LOG(INFO) << "No container info or container image is not docker image, " - << "skipping launch"; + if (containerInfo.type() != ContainerInfo::DOCKER) { + LOG(INFO) << "Skipping non-docker container"; return false; } + LOG(INFO) << "Starting container '" << containerId + << "' for task '" << taskInfo.task_id() + << "' (and executor '" << executorInfo.executor_id() + << "') of framework '" << executorInfo.framework_id() << "'"; + Owned<Promise<containerizer::Termination> > promise( - new Promise<containerizer::Termination>()); + new Promise<containerizer::Termination>()); promises.put(containerId, promise); + const CommandInfo& command = taskInfo.command(); + + return fetch(containerId, command, directory) + .then(defer(self(), + &Self::_launch, + containerId, + taskInfo, + executorInfo, + directory, + slaveId, + slavePid, + checkpoint)); +} + + +Future<bool> DockerContainerizerProcess::_launch( + const ContainerID& containerId, + const TaskInfo& taskInfo, + const ExecutorInfo& executorInfo, + const string& directory, + const SlaveID& slaveId, + const PID<Slave>& slavePid, + bool checkpoint) +{ // Store the resources for usage(). resources.put(containerId, taskInfo.resources()); - LOG(INFO) << "Starting container '" << containerId - << "' for task '" << taskInfo.task_id() - << "' (and executor '" << executorInfo.executor_id() - << "') of framework '" << executorInfo.framework_id() << "'"; + const ContainerInfo& container = taskInfo.container(); // Extract the Docker image. - string image = command.container().image(); - image = strings::remove(image, "docker:///", strings::PREFIX); + string image = container.docker().image(); // Construct the Docker container name. string name = containerName(containerId); - // Start a docker container then launch the executor (but destroy - // the Docker container if launching the executor failed). - return docker.run(image, command.value(), name, taskInfo.resources()) + const CommandInfo& command = taskInfo.command(); + + // Start a docker container and docker logs then launch the executor + // (but destroy the Docker container if launching the executor + // failed). Docker logs will automatically exit once the container + // is stopped. + return docker.run( + container, + command, + name, + directory, + flags.docker_sandbox_directory, + taskInfo.resources()) .then(defer(self(), - &Self::_launch, + &Self::__launch, containerId, taskInfo, executorInfo, @@ -634,7 +749,7 @@ Future<bool> DockerContainerizerProcess::launch( } -Future<bool> DockerContainerizerProcess::_launch( +Future<bool> DockerContainerizerProcess::__launch( const ContainerID& containerId, const TaskInfo& taskInfo, const ExecutorInfo& executorInfo, @@ -652,7 +767,7 @@ Future<bool> DockerContainerizerProcess::_launch( checkpoint, flags.recovery_timeout); - // Include any enviroment variables from CommandInfo. + // Include any enviroment variables from ExecutorInfo. foreach (const Environment::Variable& variable, executorInfo.command().environment().variables()) { env[variable.name()] = variable.value(); @@ -733,23 +848,78 @@ Future<bool> DockerContainerizerProcess::_launch( Future<bool> DockerContainerizerProcess::_launch( const ContainerID& containerId, const ExecutorInfo& executorInfo, + const string& directory, const SlaveID& slaveId, const PID<Slave>& slavePid, bool checkpoint) { - return docker.inspect(containerName(containerId)) + Owned<Promise<containerizer::Termination> > promise( + new Promise<containerizer::Termination>()); + + promises.put(containerId, promise); + + // Construct the Docker container name. + string name = containerName(containerId); + + map<string, string> env = executorEnvironment( + executorInfo, + directory, + slaveId, + slavePid, + checkpoint, + flags.recovery_timeout); + + // Include any environment variables from CommandInfo. + foreach (const Environment::Variable& variable, + executorInfo.command().environment().variables()) { + env[variable.name()] = variable.value(); + } + + const ContainerInfo& container = executorInfo.container(); + const CommandInfo& command = executorInfo.command(); + + // Start a docker container which is an executor and docker logs. + // Docker logs will automatically exit once the container is + // stopped. + return docker.run( + container, + command, + name, + directory, + flags.docker_sandbox_directory, + None(), + env) .then(defer(self(), &Self::__launch, containerId, executorInfo, slaveId, slavePid, + checkpoint)) + .onFailed(defer(self(), &Self::destroy, containerId, false)); +} + + +Future<bool> DockerContainerizerProcess::__launch( + const ContainerID& containerId, + const ExecutorInfo& executorInfo, + const SlaveID& slaveId, + const PID<Slave>& slavePid, + bool checkpoint) +{ + return docker.inspect(containerName(containerId)) + .then(defer(self(), + &Self::___launch, + containerId, + executorInfo, + slaveId, + slavePid, checkpoint, lambda::_1)); } -Future<bool> DockerContainerizerProcess::__launch( +Future<bool> DockerContainerizerProcess::___launch( const ContainerID& containerId, const ExecutorInfo& executorInfo, const SlaveID& slaveId, http://git-wip-us.apache.org/repos/asf/mesos/blob/2057e3fa/src/slave/containerizer/mesos/containerizer.cpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/mesos/containerizer.cpp b/src/slave/containerizer/mesos/containerizer.cpp index 694c9d1..d0676c5 100644 --- a/src/slave/containerizer/mesos/containerizer.cpp +++ b/src/slave/containerizer/mesos/containerizer.cpp @@ -67,41 +67,6 @@ using state::RunState; Future<Nothing> _nothing() { return Nothing(); } -// Helper method to build the environment map used to launch fetcher. -map<string, string> fetcherEnvironment( - const CommandInfo& commandInfo, - const std::string& directory, - const Option<std::string>& user, - const Flags& flags) -{ - // Prepare the environment variables to pass to mesos-fetcher. - string uris = ""; - foreach (const CommandInfo::URI& uri, commandInfo.uris()) { - uris += uri.value() + "+" + - (uri.has_executable() && uri.executable() ? "1" : "0") + - (uri.extract() ? "X" : "N"); - uris += " "; - } - // Remove extra space at the end. - uris = strings::trim(uris); - - map<string, string> environment; - environment["MESOS_EXECUTOR_URIS"] = uris; - environment["MESOS_WORK_DIRECTORY"] = directory; - if (user.isSome()) { - environment["MESOS_USER"] = user.get(); - } - if (!flags.frameworks_home.empty()) { - environment["MESOS_FRAMEWORKS_HOME"] = flags.frameworks_home; - } - if (!flags.hadoop_home.empty()) { - environment["HADOOP_HOME"] = flags.hadoop_home; - } - - return environment; -} - - Try<MesosContainerizer*> MesosContainerizer::create( const Flags& flags, bool local) http://git-wip-us.apache.org/repos/asf/mesos/blob/2057e3fa/src/slave/flags.hpp ---------------------------------------------------------------------- diff --git a/src/slave/flags.hpp b/src/slave/flags.hpp index 1e36c51..44ad632 100644 --- a/src/slave/flags.hpp +++ b/src/slave/flags.hpp @@ -289,6 +289,12 @@ public: "The absolute path to the docker executable for docker containerizer.", "docker"); + add(&Flags::docker_sandbox_directory, + "docker_sandbox_directory", + "The absolute path for the directory in the container where the sandbox\n" + "is mapped to", + "/mnt/mesos/sandbox"); + #ifdef WITH_NETWORK_ISOLATOR add(&Flags::ephemeral_ports_per_container, "ephemeral_ports_per_container", @@ -347,6 +353,7 @@ public: std::string containerizers; Option<std::string> default_container_image; std::string docker; + std::string docker_sandbox_directory; #ifdef WITH_NETWORK_ISOLATOR uint16_t ephemeral_ports_per_container; Option<std::string> eth0_name; http://git-wip-us.apache.org/repos/asf/mesos/blob/2057e3fa/src/tests/docker_containerizer_tests.cpp ---------------------------------------------------------------------- diff --git a/src/tests/docker_containerizer_tests.cpp b/src/tests/docker_containerizer_tests.cpp index a559836..3099063 100644 --- a/src/tests/docker_containerizer_tests.cpp +++ b/src/tests/docker_containerizer_tests.cpp @@ -226,11 +226,20 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_Launch_Executor) ExecutorID executorId; executorId.set_value("e1"); executorInfo.mutable_executor_id()->CopyFrom(executorId); + CommandInfo command; command.set_value("test-executor"); - command.mutable_container()->set_image("docker:///mesosphere/test-executor"); executorInfo.mutable_command()->CopyFrom(command); + ContainerInfo containerInfo; + containerInfo.set_type(ContainerInfo::DOCKER); + + ContainerInfo::DockerInfo dockerInfo; + dockerInfo.set_image("mesosphere/test-executor"); + + containerInfo.mutable_docker()->CopyFrom(dockerInfo); + executorInfo.mutable_container()->CopyFrom(containerInfo); + task.mutable_executor()->CopyFrom(executorInfo); vector<TaskInfo> tasks; @@ -324,11 +333,17 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_Launch) task.mutable_resources()->CopyFrom(offer.resources()); CommandInfo command; - CommandInfo::ContainerInfo* containerInfo = command.mutable_container(); - containerInfo->set_image("docker:///busybox"); - command.set_value("sleep 120"); + command.set_value("sleep 1000"); + + ContainerInfo containerInfo; + containerInfo.set_type(ContainerInfo::DOCKER); + + ContainerInfo::DockerInfo dockerInfo; + dockerInfo.set_image("busybox"); + containerInfo.mutable_docker()->CopyFrom(dockerInfo); task.mutable_command()->CopyFrom(command); + task.mutable_container()->CopyFrom(containerInfo); vector<TaskInfo> tasks; tasks.push_back(task); @@ -411,11 +426,17 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_Kill) task.mutable_resources()->CopyFrom(offer.resources()); CommandInfo command; - CommandInfo::ContainerInfo* containerInfo = command.mutable_container(); - containerInfo->set_image("docker:///busybox"); - command.set_value("sleep 120"); + command.set_value("sleep 1000"); + + ContainerInfo containerInfo; + containerInfo.set_type(ContainerInfo::DOCKER); + + ContainerInfo::DockerInfo dockerInfo; + dockerInfo.set_image("busybox"); + containerInfo.mutable_docker()->CopyFrom(dockerInfo); task.mutable_command()->CopyFrom(command); + task.mutable_container()->CopyFrom(containerInfo); vector<TaskInfo> tasks; tasks.push_back(task); @@ -509,13 +530,18 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_Usage) task.mutable_resources()->CopyFrom(offer.resources()); CommandInfo command; - CommandInfo::ContainerInfo* containerInfo = command.mutable_container(); - containerInfo->set_image("docker:///busybox"); - // Run a CPU intensive command, so we can measure utime and stime later. command.set_value("dd if=/dev/zero of=/dev/null"); + ContainerInfo containerInfo; + containerInfo.set_type(ContainerInfo::DOCKER); + + ContainerInfo::DockerInfo dockerInfo; + dockerInfo.set_image("busybox"); + containerInfo.mutable_docker()->CopyFrom(dockerInfo); + task.mutable_command()->CopyFrom(command); + task.mutable_container()->CopyFrom(containerInfo); vector<TaskInfo> tasks; tasks.push_back(task); @@ -628,11 +654,17 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_Update) task.mutable_resources()->CopyFrom(offer.resources()); CommandInfo command; - CommandInfo::ContainerInfo* containerInfo = command.mutable_container(); - containerInfo->set_image("docker:///busybox"); - command.set_value("sleep 180"); + command.set_value("sleep 1000"); + + ContainerInfo containerInfo; + containerInfo.set_type(ContainerInfo::DOCKER); + + ContainerInfo::DockerInfo dockerInfo; + dockerInfo.set_image("busybox"); + containerInfo.mutable_docker()->CopyFrom(dockerInfo); task.mutable_command()->CopyFrom(command); + task.mutable_container()->CopyFrom(containerInfo); vector<TaskInfo> tasks; tasks.push_back(task); @@ -736,18 +768,32 @@ TEST_F(DockerContainerizerTest, DISABLED_ROOT_DOCKER_Recover) Resources resources = Resources::parse("cpus:1;mem:512").get(); + ContainerInfo containerInfo; + containerInfo.set_type(ContainerInfo::DOCKER); + + ContainerInfo::DockerInfo dockerInfo; + dockerInfo.set_image("busybox"); + containerInfo.mutable_docker()->CopyFrom(dockerInfo); + + CommandInfo commandInfo; + commandInfo.set_value("sleep 1000"); + Future<Nothing> d1 = docker.run( - "busybox", - "sleep 360", + containerInfo, + commandInfo, slave::DOCKER_NAME_PREFIX + stringify(containerId), + flags.work_dir, + flags.docker_sandbox_directory, resources); Future<Nothing> d2 = docker.run( - "busybox", - "sleep 360", + containerInfo, + commandInfo, slave::DOCKER_NAME_PREFIX + stringify(reapedContainerId), + flags.work_dir, + flags.docker_sandbox_directory, resources); AWAIT_READY(d1); http://git-wip-us.apache.org/repos/asf/mesos/blob/2057e3fa/src/tests/docker_tests.cpp ---------------------------------------------------------------------- diff --git a/src/tests/docker_tests.cpp b/src/tests/docker_tests.cpp index 4ef1df4..3d02702 100644 --- a/src/tests/docker_tests.cpp +++ b/src/tests/docker_tests.cpp @@ -28,10 +28,12 @@ #include "mesos/resources.hpp" +#include "tests/environment.hpp" #include "tests/flags.hpp" using namespace mesos; using namespace mesos::internal; +using namespace mesos::internal::tests; using namespace process; @@ -57,8 +59,28 @@ TEST(DockerTest, ROOT_DOCKER_interface) EXPECT_NE("/" + containerName, container.name); } + Try<string> directory = environment->mkdtemp(); + CHECK_SOME(directory) << "Failed to create temporary directory"; + + ContainerInfo containerInfo; + containerInfo.set_type(ContainerInfo::DOCKER); + + ContainerInfo::DockerInfo dockerInfo; + dockerInfo.set_image("busybox"); + containerInfo.mutable_docker()->CopyFrom(dockerInfo); + + CommandInfo commandInfo; + commandInfo.set_value("sleep 120"); + // Start the container. - status = docker.run("busybox", "sleep 120", containerName, resources); + status = docker.run( + containerInfo, + commandInfo, + containerName, + directory.get(), + "/mnt/mesos/sandbox", + resources); + AWAIT_READY(status); // Should be able to see the container now. @@ -132,7 +154,14 @@ TEST(DockerTest, ROOT_DOCKER_interface) // Start the container again, this time we will do a "rm -f" // directly, instead of killing and rm. - status = docker.run("busybox", "sleep 120", containerName, resources); + status = docker.run( + containerInfo, + commandInfo, + containerName, + directory.get(), + "/mnt/mesos/sandbox", + resources); + AWAIT_READY(status); // Verify that the container is there.
