Repository: mesos Updated Branches: refs/heads/master bd144fcc7 -> 21c547f50
Create pre-launch hook before a docker container launches in slave. Review: https://reviews.apache.org/r/36185 Project: http://git-wip-us.apache.org/repos/asf/mesos/repo Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/21c547f5 Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/21c547f5 Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/21c547f5 Branch: refs/heads/master Commit: 21c547f502ad397cc8c967e78353b14c6d6cb0d7 Parents: bd144fc Author: haosdent huang <[email protected]> Authored: Thu Aug 20 09:02:03 2015 -0700 Committer: Timothy Chen <[email protected]> Committed: Thu Aug 20 09:02:03 2015 -0700 ---------------------------------------------------------------------- include/mesos/hook.hpp | 21 ++ src/examples/test_hook_module.cpp | 20 ++ src/hook/manager.cpp | 32 +++ src/hook/manager.hpp | 11 + src/slave/containerizer/docker.cpp | 33 ++- src/slave/containerizer/docker.hpp | 14 +- .../docker_containerizer_tests.cpp | 262 +----------------- src/tests/hook_tests.cpp | 115 ++++++++ src/tests/mesos.hpp | 263 +++++++++++++++++++ 9 files changed, 497 insertions(+), 274 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mesos/blob/21c547f5/include/mesos/hook.hpp ---------------------------------------------------------------------- diff --git a/include/mesos/hook.hpp b/include/mesos/hook.hpp index 6c3861b..d90bacc 100644 --- a/include/mesos/hook.hpp +++ b/include/mesos/hook.hpp @@ -19,7 +19,11 @@ #ifndef __MESOS_HOOK_HPP__ #define __MESOS_HOOK_HPP__ +#include <map> +#include <string> + #include <mesos/mesos.hpp> +#include <mesos/resources.hpp> #include <stout/none.hpp> #include <stout/nothing.hpp> @@ -71,6 +75,23 @@ public: return None(); } + // This hook is called from within slave before docker is launched. + // A typical module implementing the hook will perform some settings + // as required. + virtual Try<Nothing> slavePreLaunchDockerHook( + const ContainerInfo& containerInfo, + const CommandInfo& commandInfo, + const Option<TaskInfo>& taskInfo, + const ExecutorInfo& executorInfo, + const std::string& name, + const std::string& sandboxDirectory, + const std::string& mappedDirectory, + const Option<Resources>& resources, + const Option<std::map<std::string, std::string>>& env) + { + return Nothing(); + } + // This hook is called from within slave when an executor is being // removed. A typical module implementing the hook will perform some // cleanup as required. http://git-wip-us.apache.org/repos/asf/mesos/blob/21c547f5/src/examples/test_hook_module.cpp ---------------------------------------------------------------------- diff --git a/src/examples/test_hook_module.cpp b/src/examples/test_hook_module.cpp index 2f0a1a0..bc13a8a 100644 --- a/src/examples/test_hook_module.cpp +++ b/src/examples/test_hook_module.cpp @@ -34,6 +34,9 @@ using namespace mesos; +using std::map; +using std::string; + using process::Future; // Must be kept in sync with variables of the same name in @@ -129,6 +132,23 @@ public: return environment; } + + virtual Try<Nothing> slavePreLaunchDockerHook( + const ContainerInfo& containerInfo, + const CommandInfo& commandInfo, + const Option<TaskInfo>& taskInfo, + const ExecutorInfo& executorInfo, + const string& name, + const string& sandboxDirectory, + const string& mappedDirectory, + const Option<Resources>& resources, + const Option<map<string, string>>& env) + { + LOG(INFO) << "Executing 'slavePreLaunchDockerHook'"; + return os::touch(sandboxDirectory + "/foo"); + } + + // This hook locates the file created by environment decorator hook // and deletes it. virtual Try<Nothing> slaveRemoveExecutorHook( http://git-wip-us.apache.org/repos/asf/mesos/blob/21c547f5/src/hook/manager.cpp ---------------------------------------------------------------------- diff --git a/src/hook/manager.cpp b/src/hook/manager.cpp index 0b693d2..754c238 100644 --- a/src/hook/manager.cpp +++ b/src/hook/manager.cpp @@ -33,6 +33,7 @@ #include "hook/manager.hpp" #include "module/manager.hpp" +using std::map; using std::string; using std::vector; @@ -184,6 +185,37 @@ Environment HookManager::slaveExecutorEnvironmentDecorator( } +void HookManager::slavePreLaunchDockerHook( + const ContainerInfo& containerInfo, + const CommandInfo& commandInfo, + const Option<TaskInfo>& taskInfo, + const ExecutorInfo& executorInfo, + const string& name, + const string& sandboxDirectory, + const string& mappedDirectory, + const Option<Resources>& resources, + const Option<map<string, string>>& env) +{ + foreachpair (const string& name, Hook* hook, availableHooks) { + Try<Nothing> result = + hook->slavePreLaunchDockerHook( + containerInfo, + commandInfo, + taskInfo, + executorInfo, + name, + sandboxDirectory, + mappedDirectory, + resources, + env); + if (result.isError()) { + LOG(WARNING) << "Slave pre launch docker hook failed for module '" + << name << "': " << result.error(); + } + } +} + + void HookManager::slaveRemoveExecutorHook( const FrameworkInfo& frameworkInfo, const ExecutorInfo& executorInfo) http://git-wip-us.apache.org/repos/asf/mesos/blob/21c547f5/src/hook/manager.hpp ---------------------------------------------------------------------- diff --git a/src/hook/manager.hpp b/src/hook/manager.hpp index df32484..30d8321 100644 --- a/src/hook/manager.hpp +++ b/src/hook/manager.hpp @@ -54,6 +54,17 @@ public: static Environment slaveExecutorEnvironmentDecorator( ExecutorInfo executorInfo); + static void slavePreLaunchDockerHook( + const ContainerInfo& containerInfo, + const CommandInfo& commandInfo, + const Option<TaskInfo>& taskInfo, + const ExecutorInfo& executorInfo, + const std::string& name, + const std::string& sandboxDirectory, + const std::string& mappedDirectory, + const Option<Resources>& resources, + const Option<std::map<std::string, std::string>>& env); + static void slaveRemoveExecutorHook( const FrameworkInfo& frameworkInfo, const ExecutorInfo& executorInfo); http://git-wip-us.apache.org/repos/asf/mesos/blob/21c547f5/src/slave/containerizer/docker.cpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/docker.cpp b/src/slave/containerizer/docker.cpp index 8f5d302..a17e4f2 100644 --- a/src/slave/containerizer/docker.cpp +++ b/src/slave/containerizer/docker.cpp @@ -35,6 +35,8 @@ #include "common/status_utils.hpp" +#include "hook/manager.hpp" + #ifdef __linux__ #include "linux/cgroups.hpp" #endif // __linux__ @@ -773,6 +775,19 @@ Future<bool> DockerContainerizerProcess::launch( << "' and framework '" << executorInfo.framework_id() << "'"; } + if (HookManager::hooksAvailable()) { + HookManager::slavePreLaunchDockerHook( + container.get()->container, + container.get()->command, + taskInfo, + executorInfo, + container.get()->name(), + container.get()->directory, + flags.sandbox_directory, + container.get()->resources, + container.get()->environment); + } + if (taskInfo.isSome() && flags.docker_mesos_image.isNone()) { // Launching task by forking a subprocess to run docker executor. return container.get()->launch = fetch(containerId, slaveId) @@ -826,15 +841,15 @@ Future<Docker::Container> DockerContainerizerProcess::launchExecutorContainer( // This executor could either be a custom executor specified by an // ExecutorInfo, or the docker executor. Future<Nothing> run = docker->run( - container->container, - container->command, - containerName, - container->directory, - flags.sandbox_directory, - container->resources, - container->environment, - path::join(container->directory, "stdout"), - path::join(container->directory, "stderr")); + container->container, + container->command, + containerName, + container->directory, + flags.sandbox_directory, + container->resources, + container->environment, + path::join(container->directory, "stdout"), + path::join(container->directory, "stderr")); Owned<Promise<Docker::Container>> promise(new Promise<Docker::Container>()); // We like to propogate the run failure when run fails so slave can http://git-wip-us.apache.org/repos/asf/mesos/blob/21c547f5/src/slave/containerizer/docker.hpp ---------------------------------------------------------------------- diff --git a/src/slave/containerizer/docker.hpp b/src/slave/containerizer/docker.hpp index e7436d0..d03bb1b 100644 --- a/src/slave/containerizer/docker.hpp +++ b/src/slave/containerizer/docker.hpp @@ -145,7 +145,7 @@ public: virtual process::Future<ResourceStatistics> usage( const ContainerID& containerId); - virtual Future<containerizer::Termination> wait( + virtual process::Future<containerizer::Termination> wait( const ContainerID& containerId); virtual void destroy( @@ -202,12 +202,12 @@ private: void __destroy( const ContainerID& containerId, bool killed, - const Future<Nothing>& future); + const process::Future<Nothing>& future); void ___destroy( const ContainerID& containerId, bool killed, - const Future<Option<int>>& status); + const process::Future<Option<int>>& status); process::Future<Nothing> _update( const ContainerID& containerId, @@ -421,16 +421,16 @@ private: const Flags flags; // Promise for future returned from wait(). - Promise<containerizer::Termination> termination; + process::Promise<containerizer::Termination> termination; // Exit status of executor or container (depending on whether or // not we used the command executor). Represented as a promise so // that destroying can chain with it being set. - Promise<Future<Option<int>>> status; + process::Promise<process::Future<Option<int>>> status; // Future that tells us the return value of last launch stage (fetch, pull, // run, etc). - Future<bool> launch; + process::Future<bool> launch; // We keep track of the resources for each container so we can set // the ResourceStatistics limits in usage(). Note that this is @@ -440,7 +440,7 @@ private: // The docker pull future is stored so we can discard when // destroy is called while docker is pulling the image. - Future<Docker::Image> pull; + process::Future<Docker::Image> pull; // Once the container is running, this saves the pid of the // running container. http://git-wip-us.apache.org/repos/asf/mesos/blob/21c547f5/src/tests/containerizer/docker_containerizer_tests.cpp ---------------------------------------------------------------------- diff --git a/src/tests/containerizer/docker_containerizer_tests.cpp b/src/tests/containerizer/docker_containerizer_tests.cpp index c8c27a6..936f8c7 100644 --- a/src/tests/containerizer/docker_containerizer_tests.cpp +++ b/src/tests/containerizer/docker_containerizer_tests.cpp @@ -30,9 +30,6 @@ #include "messages/messages.hpp" -#include "tests/flags.hpp" -#include "tests/mesos.hpp" - #include "slave/containerizer/docker.hpp" #include "slave/containerizer/fetcher.hpp" @@ -40,6 +37,9 @@ #include "slave/slave.hpp" #include "slave/state.hpp" +#include "tests/flags.hpp" +#include "tests/mesos.hpp" + using namespace mesos::internal::slave::paths; using namespace mesos::internal::slave::state; @@ -57,9 +57,9 @@ using process::Message; using process::PID; using process::UPID; -using std::vector; using std::list; using std::string; +using std::vector; using testing::_; using testing::DoAll; @@ -73,105 +73,6 @@ namespace internal { namespace tests { -class MockDocker : public Docker -{ -public: - MockDocker(const string& path) : Docker(path) - { - EXPECT_CALL(*this, pull(_, _, _)) - .WillRepeatedly(Invoke(this, &MockDocker::_pull)); - - EXPECT_CALL(*this, stop(_, _, _)) - .WillRepeatedly(Invoke(this, &MockDocker::_stop)); - - EXPECT_CALL(*this, run(_, _, _, _, _, _, _, _, _)) - .WillRepeatedly(Invoke(this, &MockDocker::_run)); - - EXPECT_CALL(*this, inspect(_, _)) - .WillRepeatedly(Invoke(this, &MockDocker::_inspect)); - } - - MOCK_CONST_METHOD9( - run, - process::Future<Nothing>( - const mesos::ContainerInfo&, - const mesos::CommandInfo&, - const std::string&, - const std::string&, - const std::string&, - const Option<mesos::Resources>&, - const Option<std::map<std::string, std::string>>&, - const Option<std::string>&, - const Option<std::string>&)); - - MOCK_CONST_METHOD3( - pull, - process::Future<Docker::Image>( - const string&, - const string&, - bool)); - - MOCK_CONST_METHOD3( - stop, - process::Future<Nothing>( - const string&, - const Duration&, - bool)); - - MOCK_CONST_METHOD2( - inspect, - process::Future<Docker::Container>( - const string&, - const Option<Duration>&)); - - process::Future<Nothing> _run( - 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, - const Option<std::map<std::string, std::string>>& env, - const Option<std::string>& stdoutPath, - const Option<std::string>& stderrPath) const - { - return Docker::run( - containerInfo, - commandInfo, - name, - sandboxDirectory, - mappedDirectory, - resources, - env, - stdoutPath, - stderrPath); - } - - process::Future<Docker::Image> _pull( - const string& directory, - const string& image, - bool force) const - { - return Docker::pull(directory, image, force); - } - - process::Future<Nothing> _stop( - const string& containerName, - const Duration& timeout, - bool remove) const - { - return Docker::stop(containerName, timeout, remove); - } - - process::Future<Docker::Container> _inspect( - const string& containerName, - const Option<Duration>& retryInterval) - { - return Docker::inspect(containerName, retryInterval); - } -}; - - class DockerContainerizerTest : public MesosTest { public: @@ -257,160 +158,6 @@ public: }; -class MockDockerContainerizer : public DockerContainerizer { -public: - MockDockerContainerizer( - const slave::Flags& flags, - Fetcher* fetcher, - Shared<Docker> docker) - : DockerContainerizer(flags, fetcher, docker) - { - initialize(); - } - - MockDockerContainerizer(const Owned<DockerContainerizerProcess>& process) - : DockerContainerizer(process) - { - initialize(); - } - - void initialize() - { - // NOTE: See TestContainerizer::setup for why we use - // 'EXPECT_CALL' and 'WillRepeatedly' here instead of - // 'ON_CALL' and 'WillByDefault'. - EXPECT_CALL(*this, launch(_, _, _, _, _, _, _)) - .WillRepeatedly(Invoke(this, &MockDockerContainerizer::_launchExecutor)); - - EXPECT_CALL(*this, launch(_, _, _, _, _, _, _, _)) - .WillRepeatedly(Invoke(this, &MockDockerContainerizer::_launch)); - - EXPECT_CALL(*this, update(_, _)) - .WillRepeatedly(Invoke(this, &MockDockerContainerizer::_update)); - } - - MOCK_METHOD7( - launch, - process::Future<bool>( - const ContainerID&, - const ExecutorInfo&, - const std::string&, - const Option<std::string>&, - const SlaveID&, - const process::PID<slave::Slave>&, - bool checkpoint)); - - MOCK_METHOD8( - launch, - process::Future<bool>( - const ContainerID&, - const TaskInfo&, - const ExecutorInfo&, - const std::string&, - const Option<std::string>&, - const SlaveID&, - const process::PID<slave::Slave>&, - bool checkpoint)); - - MOCK_METHOD2( - update, - process::Future<Nothing>( - const ContainerID&, - const Resources&)); - - // Default 'launch' implementation (necessary because we can't just - // use &DockerContainerizer::launch with 'Invoke'). - process::Future<bool> _launch( - const ContainerID& containerId, - const TaskInfo& taskInfo, - const ExecutorInfo& executorInfo, - const string& directory, - const Option<string>& user, - const SlaveID& slaveId, - const PID<Slave>& slavePid, - bool checkpoint) - { - return DockerContainerizer::launch( - containerId, - taskInfo, - executorInfo, - directory, - user, - slaveId, - slavePid, - checkpoint); - } - - process::Future<bool> _launchExecutor( - const ContainerID& containerId, - const ExecutorInfo& executorInfo, - const string& directory, - const Option<string>& user, - const SlaveID& slaveId, - const PID<Slave>& slavePid, - bool checkpoint) - { - return DockerContainerizer::launch( - containerId, - executorInfo, - directory, - user, - slaveId, - slavePid, - checkpoint); - } - - process::Future<Nothing> _update( - const ContainerID& containerId, - const Resources& resources) - { - return DockerContainerizer::update( - containerId, - resources); - } -}; - - -class MockDockerContainerizerProcess : public DockerContainerizerProcess -{ -public: - MockDockerContainerizerProcess( - const slave::Flags& flags, - Fetcher* fetcher, - const Shared<Docker>& docker) - : DockerContainerizerProcess(flags, fetcher, docker) - { - EXPECT_CALL(*this, fetch(_, _)) - .WillRepeatedly(Invoke(this, &MockDockerContainerizerProcess::_fetch)); - - EXPECT_CALL(*this, pull(_)) - .WillRepeatedly(Invoke(this, &MockDockerContainerizerProcess::_pull)); - } - - MOCK_METHOD2( - fetch, - process::Future<Nothing>( - const ContainerID& containerId, - const SlaveID& slaveId)); - - MOCK_METHOD1( - pull, - process::Future<Nothing>(const ContainerID& containerId)); - - process::Future<Nothing> _fetch( - const ContainerID& containerId, - const SlaveID& slaveId) - { - return DockerContainerizerProcess::fetch(containerId, slaveId); - } - - process::Future<Nothing> _pull(const ContainerID& containerId) - { - return DockerContainerizerProcess::pull(containerId); - } -}; - - // Only enable executor launch on linux as other platforms // requires running linux VM and need special port forwarding // to get host networking to work. @@ -2950,7 +2697,6 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_DockerInspectDiscard) Shutdown(); } - } // namespace tests { } // namespace internal { } // namespace mesos { http://git-wip-us.apache.org/repos/asf/mesos/blob/21c547f5/src/tests/hook_tests.cpp ---------------------------------------------------------------------- diff --git a/src/tests/hook_tests.cpp b/src/tests/hook_tests.cpp index e9f1c77..cfde49e 100644 --- a/src/tests/hook_tests.cpp +++ b/src/tests/hook_tests.cpp @@ -40,6 +40,7 @@ #include "slave/flags.hpp" #include "slave/slave.hpp" +#include "slave/containerizer/docker.hpp" #include "slave/containerizer/fetcher.hpp" #include "slave/containerizer/mesos/containerizer.hpp" @@ -62,7 +63,9 @@ using mesos::internal::slave::Slave; using process::Future; using process::PID; +using process::Shared; +using std::list; using std::string; using std::vector; @@ -513,6 +516,118 @@ TEST_F(HookTest, VerifySlaveTaskStatusLabelDecorator) Shutdown(); // Must shutdown before 'containerizer' gets deallocated. } + +// Test that the prepare launch docker hook execute before launch +// a docker container. Test hook create a file "foo" in the sandbox +// directory. When the docker container launched, the sandbox directory +// is mounted to the docker container. We validate the hook by verifying +// the "foo" file exists in the docker container or not. +TEST_F(HookTest, ROOT_DOCKER_VerifySlavePreLaunchDockerHook) +{ + Try<PID<Master>> master = StartMaster(); + ASSERT_SOME(master); + + MockDocker* mockDocker = new MockDocker(tests::flags.docker); + Shared<Docker> docker(mockDocker); + + slave::Flags flags = CreateSlaveFlags(); + + Fetcher fetcher; + + MockDockerContainerizer dockerContainerizer(flags, &fetcher, docker); + + Try<PID<Slave>> slave = StartSlave(&dockerContainerizer, flags); + ASSERT_SOME(slave); + + MockScheduler sched; + MesosSchedulerDriver driver( + &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL); + + Future<FrameworkID> frameworkId; + EXPECT_CALL(sched, registered(&driver, _, _)) + .WillOnce(FutureArg<1>(&frameworkId)); + + Future<vector<Offer>> offers; + EXPECT_CALL(sched, resourceOffers(&driver, _)) + .WillOnce(FutureArg<1>(&offers)) + .WillRepeatedly(Return()); // Ignore subsequent offers. + + driver.start(); + + AWAIT_READY(frameworkId); + + AWAIT_READY(offers); + ASSERT_NE(0u, offers.get().size()); + + const Offer& offer = offers.get()[0]; + + SlaveID slaveId = offer.slave_id(); + + TaskInfo task; + task.set_name(""); + task.mutable_task_id()->set_value("1"); + task.mutable_slave_id()->CopyFrom(offer.slave_id()); + task.mutable_resources()->CopyFrom(offer.resources()); + + CommandInfo command; + command.set_value("test -f " + path::join(flags.sandbox_directory, "foo")); + + ContainerInfo containerInfo; + containerInfo.set_type(ContainerInfo::DOCKER); + + // TODO(tnachen): Use local image to test if possible. + 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); + + Future<ContainerID> containerId; + EXPECT_CALL(dockerContainerizer, launch(_, _, _, _, _, _, _, _)) + .WillOnce(DoAll(FutureArg<0>(&containerId), + Invoke(&dockerContainerizer, + &MockDockerContainerizer::_launch))); + + Future<TaskStatus> statusRunning; + Future<TaskStatus> statusFinished; + EXPECT_CALL(sched, statusUpdate(&driver, _)) + .WillOnce(FutureArg<1>(&statusRunning)) + .WillOnce(FutureArg<1>(&statusFinished)) + .WillRepeatedly(DoDefault()); + + driver.launchTasks(offers.get()[0].id(), tasks); + + AWAIT_READY_FOR(containerId, Seconds(60)); + AWAIT_READY_FOR(statusRunning, Seconds(60)); + EXPECT_EQ(TASK_RUNNING, statusRunning.get().state()); + AWAIT_READY_FOR(statusFinished, Seconds(60)); + EXPECT_EQ(TASK_FINISHED, statusFinished.get().state()); + + Future<containerizer::Termination> termination = + dockerContainerizer.wait(containerId.get()); + + driver.stop(); + driver.join(); + + AWAIT_READY(termination); + + Future<list<Docker::Container>> containers = + docker.get()->ps(true, slave::DOCKER_NAME_PREFIX); + + AWAIT_READY(containers); + + // Cleanup all mesos launched containers. + foreach (const Docker::Container& container, containers.get()) { + AWAIT_READY_FOR(docker.get()->rm(container.id, true), Seconds(30)); + } + + Shutdown(); +} + } // namespace tests { } // namespace internal { } // namespace mesos { http://git-wip-us.apache.org/repos/asf/mesos/blob/21c547f5/src/tests/mesos.hpp ---------------------------------------------------------------------- diff --git a/src/tests/mesos.hpp b/src/tests/mesos.hpp index 64987f0..637636a 100644 --- a/src/tests/mesos.hpp +++ b/src/tests/mesos.hpp @@ -25,6 +25,8 @@ #include <string> #include <vector> +#include <gmock/gmock.h> + #include <mesos/executor.hpp> #include <mesos/scheduler.hpp> @@ -67,6 +69,8 @@ #include "slave/slave.hpp" #include "slave/containerizer/containerizer.hpp" +#include "slave/containerizer/docker.hpp" +#include "slave/containerizer/fetcher.hpp" #include "slave/containerizer/mesos/containerizer.hpp" @@ -81,6 +85,7 @@ using ::testing::_; using ::testing::An; using ::testing::DoDefault; +using ::testing::Invoke; using ::testing::Return; namespace mesos { @@ -956,6 +961,264 @@ public: }; +// Definition of a mock Docker to be used in tests with gmock. +class MockDocker : public Docker +{ +public: + MockDocker(const std::string& path) : Docker(path) + { + EXPECT_CALL(*this, pull(_, _, _)) + .WillRepeatedly(Invoke(this, &MockDocker::_pull)); + + EXPECT_CALL(*this, stop(_, _, _)) + .WillRepeatedly(Invoke(this, &MockDocker::_stop)); + + EXPECT_CALL(*this, run(_, _, _, _, _, _, _, _, _)) + .WillRepeatedly(Invoke(this, &MockDocker::_run)); + + EXPECT_CALL(*this, inspect(_, _)) + .WillRepeatedly(Invoke(this, &MockDocker::_inspect)); + } + + MOCK_CONST_METHOD9( + run, + process::Future<Nothing>( + const mesos::ContainerInfo&, + const mesos::CommandInfo&, + const std::string&, + const std::string&, + const std::string&, + const Option<mesos::Resources>&, + const Option<std::map<std::string, std::string>>&, + const Option<std::string>&, + const Option<std::string>&)); + + MOCK_CONST_METHOD3( + pull, + process::Future<Docker::Image>( + const std::string&, + const std::string&, + bool)); + + MOCK_CONST_METHOD3( + stop, + process::Future<Nothing>( + const std::string&, + const Duration&, + bool)); + + MOCK_CONST_METHOD2( + inspect, + process::Future<Docker::Container>( + const std::string&, + const Option<Duration>&)); + + process::Future<Nothing> _run( + 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, + const Option<std::map<std::string, std::string>>& env, + const Option<std::string>& stdoutPath, + const Option<std::string>& stderrPath) const + { + return Docker::run( + containerInfo, + commandInfo, + name, + sandboxDirectory, + mappedDirectory, + resources, + env, + stdoutPath, + stderrPath); + } + + process::Future<Docker::Image> _pull( + const std::string& directory, + const std::string& image, + bool force) const + { + return Docker::pull(directory, image, force); + } + + process::Future<Nothing> _stop( + const std::string& containerName, + const Duration& timeout, + bool remove) const + { + return Docker::stop(containerName, timeout, remove); + } + + process::Future<Docker::Container> _inspect( + const std::string& containerName, + const Option<Duration>& retryInterval) + { + return Docker::inspect(containerName, retryInterval); + } +}; + + +// Definition of a mock DockerContainerizer to be used in tests with gmock. +class MockDockerContainerizer : public slave::DockerContainerizer { +public: + MockDockerContainerizer( + const slave::Flags& flags, + slave::Fetcher* fetcher, + process::Shared<Docker> docker) + : slave::DockerContainerizer(flags, fetcher, docker) + { + initialize(); + } + + MockDockerContainerizer( + const process::Owned<slave::DockerContainerizerProcess>& process) + : slave::DockerContainerizer(process) + { + initialize(); + } + + void initialize() + { + // NOTE: See TestContainerizer::setup for why we use + // 'EXPECT_CALL' and 'WillRepeatedly' here instead of + // 'ON_CALL' and 'WillByDefault'. + EXPECT_CALL(*this, launch(_, _, _, _, _, _, _)) + .WillRepeatedly(Invoke(this, &MockDockerContainerizer::_launchExecutor)); + + EXPECT_CALL(*this, launch(_, _, _, _, _, _, _, _)) + .WillRepeatedly(Invoke(this, &MockDockerContainerizer::_launch)); + + EXPECT_CALL(*this, update(_, _)) + .WillRepeatedly(Invoke(this, &MockDockerContainerizer::_update)); + } + + MOCK_METHOD7( + launch, + process::Future<bool>( + const ContainerID&, + const ExecutorInfo&, + const std::string&, + const Option<std::string>&, + const SlaveID&, + const process::PID<slave::Slave>&, + bool checkpoint)); + + MOCK_METHOD8( + launch, + process::Future<bool>( + const ContainerID&, + const TaskInfo&, + const ExecutorInfo&, + const std::string&, + const Option<std::string>&, + const SlaveID&, + const process::PID<slave::Slave>&, + bool checkpoint)); + + MOCK_METHOD2( + update, + process::Future<Nothing>( + const ContainerID&, + const Resources&)); + + // Default 'launch' implementation (necessary because we can't just + // use &slave::DockerContainerizer::launch with 'Invoke'). + process::Future<bool> _launch( + const ContainerID& containerId, + const TaskInfo& taskInfo, + const ExecutorInfo& executorInfo, + const std::string& directory, + const Option<std::string>& user, + const SlaveID& slaveId, + const slave::PID<slave::Slave>& slavePid, + bool checkpoint) + { + return slave::DockerContainerizer::launch( + containerId, + taskInfo, + executorInfo, + directory, + user, + slaveId, + slavePid, + checkpoint); + } + + process::Future<bool> _launchExecutor( + const ContainerID& containerId, + const ExecutorInfo& executorInfo, + const std::string& directory, + const Option<std::string>& user, + const SlaveID& slaveId, + const slave::PID<slave::Slave>& slavePid, + bool checkpoint) + { + return slave::DockerContainerizer::launch( + containerId, + executorInfo, + directory, + user, + slaveId, + slavePid, + checkpoint); + } + + process::Future<Nothing> _update( + const ContainerID& containerId, + const Resources& resources) + { + return slave::DockerContainerizer::update( + containerId, + resources); + } +}; + + +// Definition of a mock DockerContainerizerProcess to be used in tests +// with gmock. +class MockDockerContainerizerProcess : public slave::DockerContainerizerProcess +{ +public: + MockDockerContainerizerProcess( + const slave::Flags& flags, + slave::Fetcher* fetcher, + const process::Shared<Docker>& docker) + : slave::DockerContainerizerProcess(flags, fetcher, docker) + { + EXPECT_CALL(*this, fetch(_, _)) + .WillRepeatedly(Invoke(this, &MockDockerContainerizerProcess::_fetch)); + + EXPECT_CALL(*this, pull(_)) + .WillRepeatedly(Invoke(this, &MockDockerContainerizerProcess::_pull)); + } + + MOCK_METHOD2( + fetch, + process::Future<Nothing>( + const ContainerID& containerId, + const SlaveID& slaveId)); + + MOCK_METHOD1( + pull, + process::Future<Nothing>(const ContainerID& containerId)); + + process::Future<Nothing> _fetch( + const ContainerID& containerId, + const SlaveID& slaveId) + { + return slave::DockerContainerizerProcess::fetch(containerId, slaveId); + } + + process::Future<Nothing> _pull(const ContainerID& containerId) + { + return slave::DockerContainerizerProcess::pull(containerId); + } +}; + + // Definition of a MockAuthozier that can be used in tests with gmock. class MockAuthorizer : public Authorizer {
