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
 {

Reply via email to