You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by be...@apache.org on 2014/08/05 00:09:22 UTC

[01/43] git commit: Added usage() for the docker containerizer.

Repository: mesos
Updated Branches:
  refs/heads/master dcc87a160 -> 0ba6b89b7


Added usage() for the docker containerizer.

Added one unit test for this.


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

Branch: refs/heads/master
Commit: 1f994fd1c4e28987d022c815200a42e854a4aa69
Parents: 1bb4bd7
Author: Yifan Gu <gu...@gmail.com>
Authored: Thu Jun 26 23:56:58 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:15 2014 -0700

----------------------------------------------------------------------
 src/slave/containerizer/docker.cpp          | 39 ++++++++++-
 src/slave/containerizer/isolators/posix.hpp | 14 +++-
 src/tests/docker_containerizer_tests.cpp    | 86 ++++++++++++++++++++++++
 src/usage/usage.cpp                         |  4 +-
 src/usage/usage.hpp                         |  2 +-
 5 files changed, 137 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/1f994fd1/src/slave/containerizer/docker.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/docker.cpp b/src/slave/containerizer/docker.cpp
index 3c7f810..5968916 100644
--- a/src/slave/containerizer/docker.cpp
+++ b/src/slave/containerizer/docker.cpp
@@ -36,6 +36,9 @@
 #include "slave/containerizer/containerizer.hpp"
 #include "slave/containerizer/docker.hpp"
 
+#include "usage/usage.hpp"
+
+
 using std::list;
 using std::map;
 using std::string;
@@ -130,6 +133,10 @@ private:
       const bool& killed,
       const Future<Option<int > >& status);
 
+  Future<ResourceStatistics> _usage(
+    const ContainerID& containerId,
+    const Future<Docker::Container> container);
+
   // Call back for when the executor exits. This will trigger
   // container destroy.
   void reaped(const ContainerID& containerId);
@@ -623,9 +630,35 @@ Future<Nothing> DockerContainerizerProcess::update(
 Future<ResourceStatistics> DockerContainerizerProcess::usage(
     const ContainerID& containerId)
 {
-  // TODO(benh): Implement! Look up Docker container ID then read
-  // cgroups files ala ./isolators/cgroups/cpushare.cpp.
-  return ResourceStatistics();
+#ifndef __linux__
+  return Failure("Does not support usage() on non-linux platform");
+#endif // __linux__
+
+  if (!promises.contains(containerId)) {
+    return Failure("Unknown container: " + stringify(containerId));
+  }
+
+  // Construct the Docker container name.
+  string name = DOCKER_NAME_PREFIX + stringify(containerId);
+  return docker.inspect(name)
+    .then(defer(self(), &Self::_usage, containerId, lambda::_1));
+}
+
+
+Future<ResourceStatistics> DockerContainerizerProcess::_usage(
+    const ContainerID& containerId,
+    const Future<Docker::Container> container)
+{
+  pid_t pid = container.get().pid();
+  if (pid == 0) {
+    return Failure("Container is not running");
+  }
+  Try<ResourceStatistics> usage =
+    mesos::internal::usage(pid, true, true);
+  if (usage.isError()) {
+    return Failure(usage.error());
+  }
+  return usage.get();
 }
 
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/1f994fd1/src/slave/containerizer/isolators/posix.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/isolators/posix.hpp b/src/slave/containerizer/isolators/posix.hpp
index 17bbd10..f120aaf 100644
--- a/src/slave/containerizer/isolators/posix.hpp
+++ b/src/slave/containerizer/isolators/posix.hpp
@@ -160,7 +160,12 @@ public:
     }
 
     // Use 'mesos-usage' but only request 'cpus_' values.
-    return mesos::internal::usage(pids.get(containerId).get(), false, true);
+    Try<ResourceStatistics> usage =
+      mesos::internal::usage(pids.get(containerId).get(), false, true);
+    if (usage.isError()) {
+      return Failure(usage.error());
+    }
+    return usage.get();
   }
 
 private:
@@ -188,7 +193,12 @@ public:
     }
 
     // Use 'mesos-usage' but only request 'mem_' values.
-    return mesos::internal::usage(pids.get(containerId).get(), true, false);
+    Try<ResourceStatistics> usage =
+      mesos::internal::usage(pids.get(containerId).get(), true, false);
+    if (usage.isError()) {
+      return Failure(usage.error());
+    }
+    return usage.get();
   }
 
 private:

http://git-wip-us.apache.org/repos/asf/mesos/blob/1f994fd1/src/tests/docker_containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/docker_containerizer_tests.cpp b/src/tests/docker_containerizer_tests.cpp
index a6ba9da..3941d5a 100644
--- a/src/tests/docker_containerizer_tests.cpp
+++ b/src/tests/docker_containerizer_tests.cpp
@@ -172,3 +172,89 @@ TEST_F(DockerContainerizerTest, DOCKER_Launch)
 
   Shutdown();
 }
+
+// This test tests DockerContainerizer::usage()
+TEST_F(DockerContainerizerTest, DOCKER_Usage)
+{
+  Try<PID<Master> > master = StartMaster();
+  ASSERT_SOME(master);
+
+  slave::Flags flags = CreateSlaveFlags();
+
+  Docker docker("docker");
+
+  MockDockerContainerizer dockerContainer(flags, true, docker);
+
+  Try<PID<Slave> > slave = StartSlave((slave::Containerizer*) &dockerContainer);
+  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);
+  EXPECT_NE(0u, offers.get().size());
+
+  const Offer& offer = offers.get()[0];
+
+  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;
+  CommandInfo::ContainerInfo* containerInfo = command.mutable_container();
+  containerInfo->set_image("docker://busybox");
+  command.set_value("sleep 120");
+
+  task.mutable_command()->CopyFrom(command);
+
+  Future<TaskStatus> statusRunning;
+
+  vector<TaskInfo> tasks;
+  tasks.push_back(task);
+
+  EXPECT_CALL(sched, statusUpdate(&driver, _))
+    .WillOnce(FutureArg<1>(&statusRunning))
+    .WillRepeatedly(DoDefault());
+
+  // Usage() should fail since the container is not launched.
+  Future<ResourceStatistics> usage =
+    dockerContainer.usage(dockerContainer.lastContainerId);
+
+  AWAIT_FAILED(usage);
+
+  driver.launchTasks(offers.get()[0].id(), tasks);
+
+  AWAIT_READY_FOR(statusRunning, Seconds(60));
+  EXPECT_EQ(TASK_RUNNING, statusRunning.get().state());
+
+  usage = dockerContainer.usage(dockerContainer.lastContainerId);
+  AWAIT_READY(usage);
+  // TODO(yifan): Verify the usage.
+
+  dockerContainer.destroy(dockerContainer.lastContainerId);
+
+  // Usage() should fail again since the container is destroyed
+  usage = dockerContainer.usage(dockerContainer.lastContainerId);
+  AWAIT_FAILED(usage);
+
+  driver.stop();
+  driver.join();
+
+  Shutdown();
+}

http://git-wip-us.apache.org/repos/asf/mesos/blob/1f994fd1/src/usage/usage.cpp
----------------------------------------------------------------------
diff --git a/src/usage/usage.cpp b/src/usage/usage.cpp
index 29014d1..bb6c0ea 100644
--- a/src/usage/usage.cpp
+++ b/src/usage/usage.cpp
@@ -30,12 +30,12 @@
 namespace mesos {
 namespace internal {
 
-ResourceStatistics usage(pid_t pid, bool mem, bool cpus)
+Try<ResourceStatistics> usage(pid_t pid, bool mem, bool cpus)
 {
   Try<os::ProcessTree> pstree = os::pstree(pid);
 
   if (pstree.isError()) {
-    return ResourceStatistics();
+    return Error("Failed to get usage: " + pstree.error());
   }
 
   ResourceStatistics statistics;

http://git-wip-us.apache.org/repos/asf/mesos/blob/1f994fd1/src/usage/usage.hpp
----------------------------------------------------------------------
diff --git a/src/usage/usage.hpp b/src/usage/usage.hpp
index 5a76746..e0ad2e2 100644
--- a/src/usage/usage.hpp
+++ b/src/usage/usage.hpp
@@ -29,7 +29,7 @@ namespace internal {
 // Collects resource usage of a process tree rooted at 'pid'. Only
 // collects the 'mem_*' values if 'mem' is true and the 'cpus_*'
 // values if 'cpus' is true.
-ResourceStatistics usage(pid_t pid, bool mem = true, bool cpus = true);
+Try<ResourceStatistics> usage(pid_t pid, bool mem = true, bool cpus = true);
 
 } // namespace internal {
 } // namespace mesos {


[03/43] git commit: Integrated DockerContainerizer.

Posted by be...@apache.org.
Integrated DockerContainerizer.


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

Branch: refs/heads/master
Commit: 1ecab99a039253d474702470397ed15e075a0b3a
Parents: fa400fe
Author: Benjamin Hindman <be...@gmail.com>
Authored: Mon Jun 23 09:33:25 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:15 2014 -0700

----------------------------------------------------------------------
 src/slave/containerizer/containerizer.cpp | 11 +++++++
 src/slave/containerizer/docker.cpp        | 42 +++++++++++++++++++-------
 2 files changed, 42 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/1ecab99a/src/slave/containerizer/containerizer.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/containerizer.cpp b/src/slave/containerizer/containerizer.cpp
index d62d25d..0978dc2 100644
--- a/src/slave/containerizer/containerizer.cpp
+++ b/src/slave/containerizer/containerizer.cpp
@@ -36,6 +36,7 @@
 #endif // __linux__
 #include "slave/containerizer/composing.hpp"
 #include "slave/containerizer/containerizer.hpp"
+#include "slave/containerizer/docker.hpp"
 #include "slave/containerizer/isolator.hpp"
 #include "slave/containerizer/launcher.hpp"
 #include "slave/containerizer/external_containerizer.hpp"
@@ -170,6 +171,16 @@ Try<Containerizer*> Containerizer::create(const Flags& flags, bool local)
       } else {
         containerizers.push_back(containerizer.get());
       }
+    }  else if (type == "docker") {
+      Docker docker("docker");
+      Try<DockerContainerizer*> containerizer =
+        DockerContainerizer::create(flags, local, docker);
+      if (containerizer.isError()) {
+        return Error("Could not create DockerContainerizer: " +
+                     containerizer.error());
+      } else {
+        containerizers.push_back(containerizer.get());
+      }
     } else if (type == "external") {
       Try<Containerizer*> containerizer =
         ExternalContainerizer::create(flags, local);

http://git-wip-us.apache.org/repos/asf/mesos/blob/1ecab99a/src/slave/containerizer/docker.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/docker.cpp b/src/slave/containerizer/docker.cpp
index 42bbd4c..3d62390 100644
--- a/src/slave/containerizer/docker.cpp
+++ b/src/slave/containerizer/docker.cpp
@@ -403,6 +403,9 @@ Future<Nothing> DockerContainerizerProcess::_recover(
     const list<Docker::Container>& containers)
 {
   foreach (const Docker::Container& container, containers) {
+    VLOG(1) << "Checking if Docker container named '"
+            << container.name() << "' was started by Mesos";
+
     Option<ContainerID> id = parse(container);
 
     // Ignore containers that Mesos didn't start.
@@ -410,12 +413,15 @@ Future<Nothing> DockerContainerizerProcess::_recover(
       continue;
     }
 
+    VLOG(1) << "Checking if Mesos container with ID '"
+            << stringify(id.get()) << "' has been orphaned";
+
     // Check if we're watching an executor for this container ID and
     // if not, kill the Docker container.
     if (!statuses.keys().contains(id.get())) {
       // TODO(benh): Retry 'docker kill' if it failed but the container
       // still exists (asynchronously).
-      docker.kill(container.name());
+      docker.kill(container.id());
     }
   }
 
@@ -478,10 +484,18 @@ Future<bool> DockerContainerizerProcess::launch(
             << "' (and executor '" << executorInfo.executor_id()
             << "') of framework '" << executorInfo.framework_id() << "'";
 
+  // Extract the Docker image.
+  string image = command.container().image();
+  image = strings::remove(image, "docker://", strings::PREFIX);
+
+  // Construct the Docker container name.
+  string name = DOCKER_NAME_PREFIX + stringify(containerId);
+
   // Start a docker container then launch the executor (but destroy
   // the Docker container if launching the executor failed).
-  return docker.run(command.container().image())
-    .then(defer(self(), &Self::_launch,
+  return docker.run(image, command.value(), name)
+    .then(defer(self(),
+                &Self::_launch,
                 containerId,
                 taskInfo,
                 executorInfo,
@@ -674,17 +688,23 @@ void DockerContainerizerProcess::reaped(const ContainerID& containerId)
 Option<ContainerID> DockerContainerizerProcess::parse(
     const Docker::Container& container)
 {
-  if (!strings::startsWith(container.name(), DOCKER_NAME_PREFIX)) {
-    return None();
+  Option<string> name = None();
+
+  if (strings::startsWith(container.name(), DOCKER_NAME_PREFIX)) {
+    name = strings::remove(
+        container.name(), DOCKER_NAME_PREFIX, strings::PREFIX);
+  } else if (strings::startsWith(container.name(), "/" + DOCKER_NAME_PREFIX)) {
+    name = strings::remove(
+        container.name(), "/" + DOCKER_NAME_PREFIX, strings::PREFIX);
   }
 
-  string name = strings::remove(
-      container.name(), DOCKER_NAME_PREFIX, strings::PREFIX);
-
-  ContainerID id;
-  id.set_value(name);
+  if (name.isSome()) {
+    ContainerID id;
+    id.set_value(name.get());
+    return id;
+  }
 
-  return id;
+  return None();
 }
 
 


[42/43] git commit: Refactored some Docker tests to be more generic.

Posted by be...@apache.org.
Refactored some Docker tests to be more generic.


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

Branch: refs/heads/master
Commit: b5727ce526f2e87fc82d3dacce6a56a1e744369c
Parents: 2f2cf5b
Author: Benjamin Hindman <be...@gmail.com>
Authored: Wed Jul 9 13:53:05 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:17 2014 -0700

----------------------------------------------------------------------
 src/tests/docker_containerizer_tests.cpp | 34 +++++++++++++++++++--------
 1 file changed, 24 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/b5727ce5/src/tests/docker_containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/docker_containerizer_tests.cpp b/src/tests/docker_containerizer_tests.cpp
index 3d02e62..c3eee4d 100644
--- a/src/tests/docker_containerizer_tests.cpp
+++ b/src/tests/docker_containerizer_tests.cpp
@@ -405,19 +405,33 @@ TEST_F(DockerContainerizerTest, DOCKER_Update)
 
   AWAIT_READY(update);
 
-  string id = path::join("docker", container.get().id());
+  Result<string> cpuHierarchy = cgroups::hierarchy("cpu");
+  Result<string> memoryHierarchy = cgroups::hierarchy("memory");
 
-  Try<Bytes> mem =
-    cgroups::memory::soft_limit_in_bytes(
-        path::join(flags.cgroups_hierarchy, "memory"), id);
-  ASSERT_SOME(mem);
+  ASSERT_SOME(cpuHierarchy);
+  ASSERT_SOME(memoryHierarchy);
+
+  Option<pid_t> pid = container.get().pid();
+  ASSERT_SOME(pid);
+
+  Result<string> cpuCgroup = cgroups::cpu::cgroup(pid.get());
+  ASSERT_SOME(cpuCgroup);
+
+  Result<string> memoryCgroup = cgroups::memory::cgroup(pid.get());
+  ASSERT_SOME(memoryCgroup);
 
-  Try<uint64_t> cpu =
-    cgroups::cpu::shares(
-        path::join(flags.cgroups_hierarchy, "cpu"), id);
+  Try<uint64_t> cpu = cgroups::cpu::shares(
+      cpuHierarchy.get(),
+      cpuCgroup.get());
 
   ASSERT_SOME(cpu);
 
+  Try<Bytes> mem = cgroups::memory::soft_limit_in_bytes(
+      memoryHierarchy.get(),
+      memoryCgroup.get());
+
+  ASSERT_SOME(mem);
+
   EXPECT_EQ(1024u, cpu.get());
   EXPECT_EQ(128u, mem.get().megabytes());
 
@@ -481,7 +495,7 @@ TEST_F(DockerContainerizerTest, DOCKER_Recover)
 
   Try<process::Subprocess> wait =
     process::subprocess(
-        "docker wait " +
+        tests::flags.docker + " wait " +
         slave::DOCKER_NAME_PREFIX +
         stringify(containerId));
 
@@ -489,7 +503,7 @@ TEST_F(DockerContainerizerTest, DOCKER_Recover)
 
   Try<process::Subprocess> reaped =
     process::subprocess(
-        "docker wait " +
+        tests::flags.docker + " wait " +
         slave::DOCKER_NAME_PREFIX +
         stringify(reapedContainerId));
 


[05/43] git commit: Added a Docker containerizer.

Posted by be...@apache.org.
Added a Docker containerizer.


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

Branch: refs/heads/master
Commit: 86a9769485309f506b4cb9f91eaaeba52464247b
Parents: ab6db9b
Author: Benjamin Hindman <be...@gmail.com>
Authored: Sun Jun 22 17:56:31 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:15 2014 -0700

----------------------------------------------------------------------
 src/Makefile.am                    |   2 +
 src/slave/containerizer/docker.cpp | 693 ++++++++++++++++++++++++++++++++
 src/slave/containerizer/docker.hpp |  94 +++++
 3 files changed, 789 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/86a97694/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index ef99788..7507d72 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -265,6 +265,8 @@ libmesos_no_3rdparty_la_SOURCES =					\
 	slave/containerizer/containerizer.cpp				\
 	slave/containerizer/composing.cpp				\
 	slave/containerizer/composing.hpp				\
+	slave/containerizer/docker.cpp					\
+	slave/containerizer/docker.hpp					\
 	slave/containerizer/external_containerizer.cpp			\
 	slave/containerizer/isolator.cpp				\
 	slave/containerizer/launcher.cpp				\

http://git-wip-us.apache.org/repos/asf/mesos/blob/86a97694/src/slave/containerizer/docker.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/docker.cpp b/src/slave/containerizer/docker.cpp
new file mode 100644
index 0000000..42bbd4c
--- /dev/null
+++ b/src/slave/containerizer/docker.cpp
@@ -0,0 +1,693 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <list>
+#include <map>
+#include <string>
+
+#include <process/defer.hpp>
+#include <process/reap.hpp>
+#include <process/subprocess.hpp>
+
+#include <stout/hashmap.hpp>
+#include <stout/hashset.hpp>
+#include <stout/os.hpp>
+
+#include "docker/docker.hpp"
+
+#include "slave/paths.hpp"
+#include "slave/slave.hpp"
+
+#include "slave/containerizer/containerizer.hpp"
+#include "slave/containerizer/docker.hpp"
+
+using std::list;
+using std::map;
+using std::string;
+
+using namespace process;
+
+namespace mesos {
+namespace internal {
+namespace slave {
+
+using state::SlaveState;
+using state::FrameworkState;
+using state::ExecutorState;
+using state::RunState;
+
+
+// Prefix included in every Docker container created by Mesos to
+// distinguish from Docker containers created manually.
+static string DOCKER_NAME_PREFIX = "mesos-";
+
+
+class DockerContainerizerProcess
+  : public process::Process<DockerContainerizerProcess>
+{
+public:
+  DockerContainerizerProcess(
+      const Flags& flags,
+      bool local,
+      const Docker& docker)
+    : flags(flags),
+      docker(docker) {}
+
+  virtual process::Future<Nothing> recover(
+      const Option<state::SlaveState>& state);
+
+  virtual process::Future<bool> launch(
+      const ContainerID& containerId,
+      const ExecutorInfo& executorInfo,
+      const std::string& directory,
+      const Option<string>& user,
+      const SlaveID& slaveId,
+      const PID<Slave>& slavePid,
+      bool checkpoint);
+
+  virtual process::Future<bool> launch(
+      const ContainerID& containerId,
+      const TaskInfo& taskInfo,
+      const ExecutorInfo& executorInfo,
+      const std::string& directory,
+      const Option<string>& user,
+      const SlaveID& slaveId,
+      const PID<Slave>& slavePid,
+      bool checkpoint);
+
+  virtual process::Future<Nothing> update(
+      const ContainerID& containerId,
+      const Resources& resources);
+
+  virtual process::Future<ResourceStatistics> usage(
+      const ContainerID& containerId);
+
+  virtual Future<containerizer::Termination> wait(
+      const ContainerID& containerId);
+
+  virtual void destroy(const ContainerID& containerId);
+
+  virtual process::Future<hashset<ContainerID> > containers();
+
+private:
+  // Continuations and helpers.
+  process::Future<Nothing> _recover(
+      const std::list<Docker::Container>& containers);
+
+  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);
+
+  // Call back for when the executor exits. This will trigger
+  // container destroy.
+  void reaped(const ContainerID& containerId);
+
+  // 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);
+
+  const Flags flags;
+
+  Docker docker;
+
+  // TODO(idownes): Consider putting these per-container variables into a
+  // struct.
+  // Promises for futures returned from wait().
+  hashmap<ContainerID,
+    process::Owned<process::Promise<containerizer::Termination> > > promises;
+
+  // We need to keep track of the future exit status for each executor because
+  // we'll only get a single notification when the executor exits.
+  hashmap<ContainerID, process::Future<Option<int> > > statuses;
+
+  // We keep track of the resources for each container so we can set the
+  // ResourceStatistics limits in usage().
+  hashmap<ContainerID, Resources> resources;
+
+  // Set of containers that are in process of being destroyed.
+  hashset<ContainerID> destroying;
+};
+
+
+Try<DockerContainerizer*> DockerContainerizer::create(
+    const Flags& flags,
+    bool local,
+    const Docker& docker)
+{
+  // Try out the Docker command so that
+  Future<list<Docker::Container> > containers = docker.ps();
+
+  if (!containers.await(Seconds(3))) {
+    return Error("Failed to use Docker: Timed out");
+  } else if (containers.isFailed()) {
+    return Error("Failed to use Docker: " + containers.failure());
+  }
+
+  return new DockerContainerizer(flags, local, docker);
+}
+
+
+DockerContainerizer::DockerContainerizer(
+    const Flags& flags,
+    bool local,
+    const Docker& docker)
+{
+  process = new DockerContainerizerProcess(flags, local, docker);
+  spawn(process);
+}
+
+
+DockerContainerizer::~DockerContainerizer()
+{
+  terminate(process);
+  process::wait(process);
+  delete process;
+}
+
+
+Future<Nothing> DockerContainerizer::recover(
+    const Option<SlaveState>& state)
+{
+  return dispatch(process, &DockerContainerizerProcess::recover, state);
+}
+
+
+Future<bool> DockerContainerizer::launch(
+    const ContainerID& containerId,
+    const ExecutorInfo& executorInfo,
+    const string& directory,
+    const Option<string>& user,
+    const SlaveID& slaveId,
+    const PID<Slave>& slavePid,
+    bool checkpoint)
+{
+  return dispatch(process,
+                  &DockerContainerizerProcess::launch,
+                  containerId,
+                  executorInfo,
+                  directory,
+                  user,
+                  slaveId,
+                  slavePid,
+                  checkpoint);
+}
+
+
+Future<bool> DockerContainerizer::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 dispatch(process,
+                  &DockerContainerizerProcess::launch,
+                  containerId,
+                  taskInfo,
+                  executorInfo,
+                  directory,
+                  user,
+                  slaveId,
+                  slavePid,
+                  checkpoint);
+}
+
+
+Future<Nothing> DockerContainerizer::update(
+    const ContainerID& containerId,
+    const Resources& resources)
+{
+  return dispatch(process,
+                  &DockerContainerizerProcess::update,
+                  containerId,
+                  resources);
+}
+
+
+Future<ResourceStatistics> DockerContainerizer::usage(
+    const ContainerID& containerId)
+{
+  return dispatch(process, &DockerContainerizerProcess::usage, containerId);
+}
+
+
+Future<containerizer::Termination> DockerContainerizer::wait(
+    const ContainerID& containerId)
+{
+  return dispatch(process, &DockerContainerizerProcess::wait, containerId);
+}
+
+
+void DockerContainerizer::destroy(const ContainerID& containerId)
+{
+  dispatch(process, &DockerContainerizerProcess::destroy, containerId);
+}
+
+
+Future<hashset<ContainerID> > DockerContainerizer::containers()
+{
+  return dispatch(process, &DockerContainerizerProcess::containers);
+}
+
+
+// A Subprocess async-safe "setup" helper used by
+// DockerContainerizerProcess when launching the mesos-executor that
+// does a 'setsid' and then synchronizes with the parent.
+static int setup(const string& directory)
+{
+  // Put child into its own process session to prevent slave suicide
+  // on child process SIGKILL/SIGTERM.
+  if (::setsid() == -1) {
+    return errno;
+  }
+
+  // Run the process in the specified directory.
+  if (!directory.empty()) {
+    if (::chdir(directory.c_str()) == -1) {
+      return errno;
+    }
+  }
+
+  // Synchronize with parent process by reading a byte from stdin.
+  char c;
+  ssize_t length;
+  while ((length = read(STDIN_FILENO, &c, sizeof(c))) == -1 && errno == EINTR);
+
+  if (length != sizeof(c)) {
+    // This will occur if the slave terminates during executor launch.
+    // There's a reasonable probability this will occur during slave
+    // restarts across a large/busy cluster.
+    ABORT("Failed to synchronize with slave (it has probably exited)");
+  }
+
+  return 0;
+}
+
+
+Future<Nothing> DockerContainerizerProcess::recover(
+    const Option<SlaveState>& state)
+{
+  LOG(INFO) << "Recovering Docker containers";
+
+  if (state.isSome()) {
+    // Collection of pids that we've started reaping in order to
+    // detect very unlikely duplicate scenario (see below).
+    hashmap<ContainerID, pid_t> pids;
+
+    foreachvalue (const FrameworkState& framework, state.get().frameworks) {
+      foreachvalue (const ExecutorState& executor, framework.executors) {
+        if (executor.info.isNone()) {
+          LOG(WARNING) << "Skipping recovery of executor '" << executor.id
+                       << "' of framework " << framework.id
+                       << " because its info could not be recovered";
+          continue;
+        }
+
+        if (executor.latest.isNone()) {
+          LOG(WARNING) << "Skipping recovery of executor '" << executor.id
+                       << "' of framework " << framework.id
+                       << " because its latest run could not be recovered";
+          continue;
+        }
+
+        // We are only interested in the latest run of the executor!
+        const ContainerID& containerId = executor.latest.get();
+        Option<RunState> run = executor.runs.get(containerId);
+        CHECK_SOME(run);
+        CHECK_SOME(run.get().id);
+        CHECK_EQ(containerId, run.get().id.get());
+
+        // We need the pid so the reaper can monitor the executor so
+        // skip this executor if it's not present. This is not an
+        // error because the slave will try to wait on the container
+        // which will return a failed Termination and everything will
+        // get cleaned up.
+        if (!run.get().forkedPid.isSome()) {
+          continue;
+        }
+
+        if (run.get().completed) {
+          VLOG(1) << "Skipping recovery of executor '" << executor.id
+                  << "' of framework " << framework.id
+                  << " because its latest run "
+                  << containerId << " is completed";
+          continue;
+        }
+
+        LOG(INFO) << "Recovering container '" << containerId
+                  << "' for executor '" << executor.id
+                  << "' of framework " << framework.id;
+
+        // Save a termination promise.
+        Owned<Promise<containerizer::Termination> > promise(
+            new Promise<containerizer::Termination>());
+
+        promises.put(containerId, promise);
+
+        CHECK_SOME(run.get().forkedPid);
+        pid_t pid = run.get().forkedPid.get();
+
+        Future<Option<int > > status = process::reap(pid);
+
+        statuses[containerId] = status;
+        status.onAny(defer(self(), &Self::reaped, containerId));
+
+        if (pids.containsValue(pid)) {
+          // This should (almost) never occur. There is the
+          // possibility that a new executor is launched with the same
+          // pid as one that just exited (highly unlikely) and the
+          // slave dies after the new executor is launched but before
+          // it hears about the termination of the earlier executor
+          // (also unlikely). Regardless, the launcher can't do
+          // anything sensible so this is considered an error.
+          return Failure(
+              "Detected duplicate pid " + stringify(pid) +
+              " for container " + stringify(containerId));
+        }
+
+        pids.put(containerId, pid);
+      }
+    }
+  }
+
+  // Get the list of Docker containers in order to Remove any orphans.
+  return docker.ps()
+    .then(defer(self(), &Self::_recover, lambda::_1));
+}
+
+
+Future<Nothing> DockerContainerizerProcess::_recover(
+    const list<Docker::Container>& containers)
+{
+  foreach (const Docker::Container& container, containers) {
+    Option<ContainerID> id = parse(container);
+
+    // Ignore containers that Mesos didn't start.
+    if (id.isNone()) {
+      continue;
+    }
+
+    // Check if we're watching an executor for this container ID and
+    // if not, kill the Docker container.
+    if (!statuses.keys().contains(id.get())) {
+      // TODO(benh): Retry 'docker kill' if it failed but the container
+      // still exists (asynchronously).
+      docker.kill(container.name());
+    }
+  }
+
+  return Nothing();
+}
+
+
+Future<bool> DockerContainerizerProcess::launch(
+    const ContainerID& containerId,
+    const ExecutorInfo& executorInfo,
+    const string& directory,
+    const Option<string>& user,
+    const SlaveID& slaveId,
+    const PID<Slave>& slavePid,
+    bool checkpoint)
+{
+  // TODO(benh): Implement support for launching an ExecutorInfo.
+  return false;
+}
+
+
+Future<bool> DockerContainerizerProcess::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)
+{
+  if (promises.contains(containerId)) {
+    LOG(ERROR) << "Cannot start already running container '"
+               << containerId << "'";
+    return Failure("Container already started");
+  }
+
+  if (!taskInfo.has_command()) {
+    return false;
+  }
+
+  const CommandInfo& command = taskInfo.command();
+
+  // Check if we should try and launch this command.
+  if (!command.has_container() ||
+      !strings::startsWith(command.container().image(), "docker")) {
+    return false;
+  }
+
+  Owned<Promise<containerizer::Termination> > promise(
+      new Promise<containerizer::Termination>());
+
+  promises.put(containerId, promise);
+
+  // 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() << "'";
+
+  // Start a docker container then launch the executor (but destroy
+  // the Docker container if launching the executor failed).
+  return docker.run(command.container().image())
+    .then(defer(self(), &Self::_launch,
+                containerId,
+                taskInfo,
+                executorInfo,
+                directory,
+                slaveId,
+                slavePid,
+                checkpoint))
+    .onFailed(defer(self(), &Self::destroy, containerId));
+}
+
+
+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)
+{
+  // Prepare environment variables for the executor.
+  map<string, string> env = executorEnvironment(
+      executorInfo,
+      directory,
+      slaveId,
+      slavePid,
+      checkpoint,
+      flags.recovery_timeout);
+
+  // Include any enviroment variables from CommandInfo.
+  foreach (const Environment::Variable& variable,
+           executorInfo.command().environment().variables()) {
+    env[variable.name()] = variable.value();
+  }
+
+  // Construct the mesos-executor "override" to do a 'docker wait'
+  // using the "name" we gave the container (to distinguish it from
+  // Docker containers not created by Mesos).
+  // TODO(benh): Get full path to 'docker'.
+  string override =
+    "docker wait " + DOCKER_NAME_PREFIX + stringify(containerId);
+
+  Try<Subprocess> s = subprocess(
+      executorInfo.command().value() + "--override " + override,
+      Subprocess::PIPE(),
+      Subprocess::PATH(path::join(directory, "stdout")),
+      Subprocess::PATH(path::join(directory, "stderr")),
+      None(),
+      env,
+      lambda::bind(&setup, directory));
+
+  if (s.isError()) {
+    return Failure("Failed to fork executor: " + s.error());
+  }
+
+  // Checkpoint the executor's pid if requested.
+  if (checkpoint) {
+    const string& path = slave::paths::getForkedPidPath(
+        slave::paths::getMetaRootDir(flags.work_dir),
+        slaveId,
+        executorInfo.framework_id(),
+        executorInfo.executor_id(),
+        containerId);
+
+    LOG(INFO) << "Checkpointing executor's forked pid "
+              << s.get().pid() << " to '" << path <<  "'";
+
+    Try<Nothing> checkpointed =
+      slave::state::checkpoint(path, stringify(s.get().pid()));
+
+    if (checkpointed.isError()) {
+      LOG(ERROR) << "Failed to checkpoint executor's forked pid to '"
+                 << path << "': " << checkpointed.error();
+
+      // Close the subprocess's stdin so that it aborts.
+      CHECK_SOME(s.get().in());
+      os::close(s.get().in().get());
+
+      return Failure("Could not checkpoint executor's pid");
+    }
+  }
+
+  // Checkpoing complete, now synchronize with the process so that it
+  // can continue to execute.
+  CHECK_SOME(s.get().in());
+  char c;
+  ssize_t length;
+  while ((length = write(s.get().in().get(), &c, sizeof(c))) == -1 &&
+         errno == EINTR);
+
+  if (length != sizeof(c)) {
+    os::close(s.get().in().get());
+    return Failure("Failed to synchronize with child process: " +
+                   string(strerror(errno)));
+  }
+
+  // And finally watch for when the executor gets reaped.
+  Future<Option<int> > status = process::reap(s.get().pid());
+
+  statuses.put(containerId, status);
+  status.onAny(defer(self(), &Self::reaped, containerId));
+
+  return true;
+}
+
+
+Future<Nothing> DockerContainerizerProcess::update(
+    const ContainerID& containerId,
+    const Resources& resources)
+{
+  // TODO(benh): Right now we're only launching tasks so we don't
+  // expect the containers to be resized. This will need to get
+  // implemented to support executors.
+  return Nothing();
+}
+
+
+Future<ResourceStatistics> DockerContainerizerProcess::usage(
+    const ContainerID& containerId)
+{
+  // TODO(benh): Implement! Look up Docker container ID then read
+  // cgroups files ala ./isolators/cgroups/cpushare.cpp.
+  return ResourceStatistics();
+}
+
+
+Future<containerizer::Termination> DockerContainerizerProcess::wait(
+    const ContainerID& containerId)
+{
+  if (!promises.contains(containerId)) {
+    return Failure("Unknown container: " + stringify(containerId));
+  }
+
+  return promises[containerId]->future();
+}
+
+
+void DockerContainerizerProcess::destroy(const ContainerID& containerId)
+{
+  if (!promises.contains(containerId)) {
+    LOG(WARNING) << "Ignoring destroy of unknown container: " << containerId;
+    return;
+  }
+
+  if (destroying.contains(containerId)) {
+    // Destroy has already been initiated.
+    return;
+  }
+
+  destroying.insert(containerId);
+
+  LOG(INFO) << "Destroying container '" << containerId << "'";
+
+  // Do a 'docker kill' which we'll then find out about in '_wait'
+  // after the mesos-executor exits because it's doing a 'docker wait'
+  // (via --override).
+  //
+  // NOTE: We might not actually have a mesos-executor running (which
+  // we could check by looking if 'containerId' is a key in
+  // 'statuses') but if that is the case then we're doing a destroy
+  // because we failed to launch the mesos-executor (see defer at
+  // bottom of 'launch') so no need to do anything after a successful
+  // 'docker kill'.
+
+  // TODO(benh): Retry 'docker kill' if it failed but the container
+  // still exists (asynchronously).
+  docker.kill(DOCKER_NAME_PREFIX + stringify(containerId));
+}
+
+
+Future<hashset<ContainerID> > DockerContainerizerProcess::containers()
+{
+  return promises.keys();
+}
+
+
+void DockerContainerizerProcess::reaped(const ContainerID& containerId)
+{
+  if (!promises.contains(containerId)) {
+    return;
+  }
+
+  LOG(INFO) << "Executor for container '" << containerId << "' has exited";
+
+  // The executor has exited so destroy the container.
+  destroy(containerId);
+}
+
+
+Option<ContainerID> DockerContainerizerProcess::parse(
+    const Docker::Container& container)
+{
+  if (!strings::startsWith(container.name(), DOCKER_NAME_PREFIX)) {
+    return None();
+  }
+
+  string name = strings::remove(
+      container.name(), DOCKER_NAME_PREFIX, strings::PREFIX);
+
+  ContainerID id;
+  id.set_value(name);
+
+  return id;
+}
+
+
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/86a97694/src/slave/containerizer/docker.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/docker.hpp b/src/slave/containerizer/docker.hpp
new file mode 100644
index 0000000..0ecbc88
--- /dev/null
+++ b/src/slave/containerizer/docker.hpp
@@ -0,0 +1,94 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __DOCKER_CONTAINERIZER_HPP__
+#define __DOCKER_CONTAINERIZER_HPP__
+
+#include <stout/hashset.hpp>
+
+#include "docker/docker.hpp"
+
+#include "slave/containerizer/containerizer.hpp"
+
+namespace mesos {
+namespace internal {
+namespace slave {
+
+// Forward declaration.
+class DockerContainerizerProcess;
+
+class DockerContainerizer : public Containerizer
+{
+public:
+  static Try<DockerContainerizer*> create(
+      const Flags& flags,
+      bool local,
+      const Docker& docker);
+
+  DockerContainerizer(
+      const Flags& flags,
+      bool local,
+      const Docker& docker);
+
+  virtual ~DockerContainerizer();
+
+  virtual process::Future<Nothing> recover(
+      const Option<state::SlaveState>& state);
+
+  virtual process::Future<bool> launch(
+      const ContainerID& containerId,
+      const ExecutorInfo& executorInfo,
+      const std::string& directory,
+      const Option<std::string>& user,
+      const SlaveID& slaveId,
+      const process::PID<Slave>& slavePid,
+      bool checkpoint);
+
+  virtual 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 process::PID<Slave>& slavePid,
+      bool checkpoint);
+
+  virtual process::Future<Nothing> update(
+      const ContainerID& containerId,
+      const Resources& resources);
+
+  virtual process::Future<ResourceStatistics> usage(
+      const ContainerID& containerId);
+
+  virtual process::Future<containerizer::Termination> wait(
+      const ContainerID& containerId);
+
+  virtual void destroy(const ContainerID& containerId);
+
+  virtual process::Future<hashset<ContainerID> > containers();
+
+private:
+  DockerContainerizerProcess* process;
+};
+
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {
+
+#endif // __DOCKER_CONTAINERIZER_HPP__


[39/43] git commit: Added an example Docker framework for testing.

Posted by be...@apache.org.
Added an example Docker framework for testing.


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

Branch: refs/heads/master
Commit: e66eab2cbb34d86fd2111b935388689c775ef02e
Parents: a5d683b
Author: Benjamin Hindman <be...@gmail.com>
Authored: Tue Jul 8 14:45:42 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:17 2014 -0700

----------------------------------------------------------------------
 src/Makefile.am                               |   5 +
 src/examples/docker_no_executor_framework.cpp | 222 +++++++++++++++++++++
 2 files changed, 227 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/e66eab2c/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index e5b26df..d0b3285 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1020,6 +1020,11 @@ no_executor_framework_SOURCES = examples/no_executor_framework.cpp
 no_executor_framework_CPPFLAGS = $(MESOS_CPPFLAGS)
 no_executor_framework_LDADD = libmesos.la
 
+check_PROGRAMS += docker-no-executor-framework
+docker_no_executor_framework_SOURCES = examples/docker_no_executor_framework.cpp
+docker_no_executor_framework_CPPFLAGS = $(MESOS_CPPFLAGS)
+docker_no_executor_framework_LDADD = libmesos.la
+
 check_PROGRAMS += balloon-framework
 balloon_framework_SOURCES = examples/balloon_framework.cpp
 balloon_framework_CPPFLAGS = $(MESOS_CPPFLAGS)

http://git-wip-us.apache.org/repos/asf/mesos/blob/e66eab2c/src/examples/docker_no_executor_framework.cpp
----------------------------------------------------------------------
diff --git a/src/examples/docker_no_executor_framework.cpp b/src/examples/docker_no_executor_framework.cpp
new file mode 100644
index 0000000..d64c23b
--- /dev/null
+++ b/src/examples/docker_no_executor_framework.cpp
@@ -0,0 +1,222 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <libgen.h>
+
+#include <iostream>
+#include <string>
+
+#include <boost/lexical_cast.hpp>
+
+#include <mesos/scheduler.hpp>
+
+#include <stout/exit.hpp>
+#include <stout/os.hpp>
+
+using namespace mesos;
+
+using boost::lexical_cast;
+
+using std::cout;
+using std::cerr;
+using std::endl;
+using std::flush;
+using std::string;
+using std::vector;
+
+const int32_t CPUS_PER_TASK = 1;
+const int32_t MEM_PER_TASK = 32;
+
+class DockerNoExecutorScheduler : public Scheduler
+{
+public:
+  DockerNoExecutorScheduler()
+    : tasksLaunched(0), tasksFinished(0), totalTasks(5) {}
+
+  virtual ~DockerNoExecutorScheduler() {}
+
+  virtual void registered(SchedulerDriver*,
+                          const FrameworkID&,
+                          const MasterInfo&)
+  {
+    cout << "Registered!" << endl;
+  }
+
+  virtual void reregistered(SchedulerDriver*, const MasterInfo& masterInfo) {}
+
+  virtual void disconnected(SchedulerDriver* driver) {}
+
+  virtual void resourceOffers(SchedulerDriver* driver,
+                              const vector<Offer>& offers)
+  {
+    cout << "." << flush;
+    for (size_t i = 0; i < offers.size(); i++) {
+      const Offer& offer = offers[i];
+
+      // Lookup resources we care about.
+      // TODO(benh): It would be nice to ultimately have some helper
+      // functions for looking up resources.
+      double cpus = 0;
+      double mem = 0;
+
+      for (int i = 0; i < offer.resources_size(); i++) {
+        const Resource& resource = offer.resources(i);
+        if (resource.name() == "cpus" &&
+            resource.type() == Value::SCALAR) {
+          cpus = resource.scalar().value();
+        } else if (resource.name() == "mem" &&
+                   resource.type() == Value::SCALAR) {
+          mem = resource.scalar().value();
+        }
+      }
+
+      // Launch tasks.
+      vector<TaskInfo> tasks;
+      while (tasksLaunched < totalTasks &&
+             cpus >= CPUS_PER_TASK &&
+             mem >= MEM_PER_TASK) {
+        int taskId = tasksLaunched++;
+
+        cout << "Starting task " << taskId << " on "
+             << offer.hostname() << endl;
+
+        TaskInfo task;
+        task.set_name("Task " + lexical_cast<string>(taskId));
+        task.mutable_task_id()->set_value(lexical_cast<string>(taskId));
+        task.mutable_slave_id()->MergeFrom(offer.slave_id());
+        task.mutable_command()->set_value("echo hello");
+
+        // Use Docker to run the task.
+        CommandInfo::ContainerInfo* container =
+          task.mutable_command()->mutable_container();
+        container->set_image("docker://ubuntu");
+
+        Resource* resource;
+
+        resource = task.add_resources();
+        resource->set_name("cpus");
+        resource->set_type(Value::SCALAR);
+        resource->mutable_scalar()->set_value(CPUS_PER_TASK);
+
+        resource = task.add_resources();
+        resource->set_name("mem");
+        resource->set_type(Value::SCALAR);
+        resource->mutable_scalar()->set_value(MEM_PER_TASK);
+
+        tasks.push_back(task);
+
+        cpus -= CPUS_PER_TASK;
+        mem -= MEM_PER_TASK;
+      }
+
+      driver->launchTasks(offer.id(), tasks);
+    }
+  }
+
+  virtual void offerRescinded(SchedulerDriver* driver,
+                              const OfferID& offerId) {}
+
+  virtual void statusUpdate(SchedulerDriver* driver, const TaskStatus& status)
+  {
+    int taskId = lexical_cast<int>(status.task_id().value());
+
+    cout << "Task " << taskId << " is in state " << status.state() << endl;
+
+    if (status.state() == TASK_FINISHED)
+      tasksFinished++;
+
+    if (tasksFinished == totalTasks)
+      driver->stop();
+  }
+
+  virtual void frameworkMessage(SchedulerDriver* driver,
+                                const ExecutorID& executorId,
+                                const SlaveID& slaveId,
+                                const string& data) {}
+
+  virtual void slaveLost(SchedulerDriver* driver, const SlaveID& slaveId) {}
+
+  virtual void executorLost(SchedulerDriver* driver,
+                            const ExecutorID& executorId,
+                            const SlaveID& slaveId,
+                            int status) {}
+
+  virtual void error(SchedulerDriver* driver, const string& message) {}
+
+private:
+  int tasksLaunched;
+  int tasksFinished;
+  int totalTasks;
+};
+
+
+int main(int argc, char** argv)
+{
+  if (argc != 2) {
+    cerr << "Usage: " << argv[0] << " <master>" << endl;
+    return -1;
+  }
+
+  DockerNoExecutorScheduler scheduler;
+
+  FrameworkInfo framework;
+  framework.set_user(""); // Have Mesos fill in the current user.
+  framework.set_name("No Executor Framework (C++)");
+
+  // TODO(vinod): Make checkpointing the default when it is default
+  // on the slave.
+  if (os::hasenv("MESOS_CHECKPOINT")) {
+    cout << "Enabling checkpoint for the framework" << endl;
+    framework.set_checkpoint(true);
+  }
+
+  MesosSchedulerDriver* driver;
+  if (os::hasenv("MESOS_AUTHENTICATE")) {
+    cout << "Enabling authentication for the framework" << endl;
+
+    if (!os::hasenv("DEFAULT_PRINCIPAL")) {
+      EXIT(1) << "Expecting authentication principal in the environment";
+    }
+
+    if (!os::hasenv("DEFAULT_SECRET")) {
+      EXIT(1) << "Expecting authentication secret in the environment";
+    }
+
+    Credential credential;
+    credential.set_principal(getenv("DEFAULT_PRINCIPAL"));
+    credential.set_secret(getenv("DEFAULT_SECRET"));
+
+    framework.set_principal(getenv("DEFAULT_PRINCIPAL"));
+
+    driver = new MesosSchedulerDriver(
+        &scheduler, framework, argv[1], credential);
+  } else {
+    framework.set_principal("no-executor-framework-cpp");
+
+    driver = new MesosSchedulerDriver(
+        &scheduler, framework, argv[1]);
+  }
+
+  int status = driver->run() == DRIVER_STOPPED ? 0 : 1;
+
+  // Ensure that the driver process terminates.
+  driver->stop();
+
+  delete driver;
+  return status;
+}


[31/43] git commit: Used unsigned constants for older compilers.

Posted by be...@apache.org.
Used unsigned constants for older compilers.


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

Branch: refs/heads/master
Commit: 283665b0c1965d6e113ec633a5fd065b81f3e64b
Parents: e66eab2
Author: Benjamin Hindman <be...@gmail.com>
Authored: Wed Jul 9 11:05:32 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:17 2014 -0700

----------------------------------------------------------------------
 src/tests/docker_containerizer_tests.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/283665b0/src/tests/docker_containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/docker_containerizer_tests.cpp b/src/tests/docker_containerizer_tests.cpp
index cd1c88c..f772eb0 100644
--- a/src/tests/docker_containerizer_tests.cpp
+++ b/src/tests/docker_containerizer_tests.cpp
@@ -419,8 +419,8 @@ TEST_F(DockerContainerizerTest, DOCKER_Update)
 
   ASSERT_SOME(cpu);
 
-  EXPECT_EQ(1024, cpu.get());
-  EXPECT_EQ(128, mem.get().megabytes());
+  EXPECT_EQ(1024u, cpu.get());
+  EXPECT_EQ(128u, mem.get().megabytes());
 
   Future<containerizer::Termination> termination =
     dockerContainerizer.wait(containerId.get());


[23/43] git commit: Moved cpu/mem constants from cpushare.cpp/mem.cpp to cpushare.hpp/mem.hpp

Posted by be...@apache.org.
Moved cpu/mem constants from cpushare.cpp/mem.cpp to cpushare.hpp/mem.hpp


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

Branch: refs/heads/master
Commit: 9c94cceb654e87e8c487ea19b15206cdeef006c8
Parents: d5f9c58
Author: Yifan Gu <gu...@gmail.com>
Authored: Mon Jul 7 12:09:57 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:16 2014 -0700

----------------------------------------------------------------------
 src/docker/docker.cpp                                  | 5 +++++
 src/slave/containerizer/isolators/cgroups/cpushare.cpp | 6 ------
 src/slave/containerizer/isolators/cgroups/cpushare.hpp | 6 ++++++
 src/slave/containerizer/isolators/cgroups/mem.cpp      | 3 ---
 src/slave/containerizer/isolators/cgroups/mem.hpp      | 3 +++
 5 files changed, 14 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/9c94cceb/src/docker/docker.cpp
----------------------------------------------------------------------
diff --git a/src/docker/docker.cpp b/src/docker/docker.cpp
index c46df07..fb7f0e9 100644
--- a/src/docker/docker.cpp
+++ b/src/docker/docker.cpp
@@ -31,6 +31,11 @@
 
 #include "docker/docker.hpp"
 
+#include "slave/containerizer/isolators/cgroups/cpushare.hpp"
+#include "slave/containerizer/isolators/cgroups/mem.hpp"
+
+using namespace mesos::internal::slave;
+
 using namespace process;
 
 using std::list;

http://git-wip-us.apache.org/repos/asf/mesos/blob/9c94cceb/src/slave/containerizer/isolators/cgroups/cpushare.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/isolators/cgroups/cpushare.cpp b/src/slave/containerizer/isolators/cgroups/cpushare.cpp
index 3265a80..b1cad47 100644
--- a/src/slave/containerizer/isolators/cgroups/cpushare.cpp
+++ b/src/slave/containerizer/isolators/cgroups/cpushare.cpp
@@ -55,12 +55,6 @@ namespace mesos {
 namespace internal {
 namespace slave {
 
-// CPU subsystem constants.
-const uint64_t CPU_SHARES_PER_CPU = 1024;
-const uint64_t MIN_CPU_SHARES = 10;
-const Duration CPU_CFS_PERIOD = Milliseconds(100); // Linux default.
-const Duration MIN_CPU_CFS_QUOTA = Milliseconds(1);
-
 
 template<class T>
 static Future<Option<T> > none() { return None(); }

http://git-wip-us.apache.org/repos/asf/mesos/blob/9c94cceb/src/slave/containerizer/isolators/cgroups/cpushare.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/isolators/cgroups/cpushare.hpp b/src/slave/containerizer/isolators/cgroups/cpushare.hpp
index 780037b..19dde35 100644
--- a/src/slave/containerizer/isolators/cgroups/cpushare.hpp
+++ b/src/slave/containerizer/isolators/cgroups/cpushare.hpp
@@ -34,6 +34,12 @@ namespace mesos {
 namespace internal {
 namespace slave {
 
+// CPU subsystem constants.
+const uint64_t CPU_SHARES_PER_CPU = 1024;
+const uint64_t MIN_CPU_SHARES = 10;
+const Duration CPU_CFS_PERIOD = Milliseconds(100); // Linux default.
+const Duration MIN_CPU_CFS_QUOTA = Milliseconds(1);
+
 
 // Use the Linux cpu cgroup controller for cpu isolation which uses the
 // Completely Fair Scheduler (CFS).

http://git-wip-us.apache.org/repos/asf/mesos/blob/9c94cceb/src/slave/containerizer/isolators/cgroups/mem.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/isolators/cgroups/mem.cpp b/src/slave/containerizer/isolators/cgroups/mem.cpp
index e8d1e35..e8160ef 100644
--- a/src/slave/containerizer/isolators/cgroups/mem.cpp
+++ b/src/slave/containerizer/isolators/cgroups/mem.cpp
@@ -55,9 +55,6 @@ namespace mesos {
 namespace internal {
 namespace slave {
 
-// Memory subsystem constants.
-const Bytes MIN_MEMORY = Megabytes(32);
-
 
 template<class T>
 static Future<Option<T> > none() { return None(); }

http://git-wip-us.apache.org/repos/asf/mesos/blob/9c94cceb/src/slave/containerizer/isolators/cgroups/mem.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/isolators/cgroups/mem.hpp b/src/slave/containerizer/isolators/cgroups/mem.hpp
index 8c476c7..6869ed4 100644
--- a/src/slave/containerizer/isolators/cgroups/mem.hpp
+++ b/src/slave/containerizer/isolators/cgroups/mem.hpp
@@ -38,6 +38,9 @@ namespace mesos {
 namespace internal {
 namespace slave {
 
+// Memory subsystem constants.
+const Bytes MIN_MEMORY = Megabytes(32);
+
 
 class CgroupsMemIsolatorProcess : public IsolatorProcess
 {


[10/43] git commit: Add environment test for Docker.

Posted by be...@apache.org.
Add environment test for Docker.


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

Branch: refs/heads/master
Commit: 4406fb546b3f154bcf2beca49508b29656a3c3a5
Parents: 1ecab99
Author: Timothy Chen <tn...@gmail.com>
Authored: Tue Jun 24 11:28:34 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:15 2014 -0700

----------------------------------------------------------------------
 src/Makefile.am                          |   1 +
 src/docker/docker.cpp                    |  14 +++
 src/docker/docker.hpp                    |   4 +
 src/slave/containerizer/docker.cpp       |  10 +--
 src/tests/docker_containerizer_tests.cpp | 121 ++++++++++++++++++++++++++
 src/tests/environment.cpp                |  10 ++-
 6 files changed, 152 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/4406fb54/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index 7507d72..aad5440 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1045,6 +1045,7 @@ mesos_tests_SOURCES =				\
   tests/containerizer.cpp			\
   tests/containerizer_tests.cpp			\
   tests/credentials_tests.cpp			\
+  tests/docker_containerizer_tests.cpp          \
   tests/environment.cpp				\
   tests/examples_tests.cpp			\
   tests/exception_tests.cpp			\

http://git-wip-us.apache.org/repos/asf/mesos/blob/4406fb54/src/docker/docker.cpp
----------------------------------------------------------------------
diff --git a/src/docker/docker.cpp b/src/docker/docker.cpp
index 976a660..53c0805 100644
--- a/src/docker/docker.cpp
+++ b/src/docker/docker.cpp
@@ -21,6 +21,20 @@ using std::string;
 using std::vector;
 
 
+Try<Nothing> Docker::validateDocker(const Docker &docker)
+{
+  Future<list<Docker::Container> > containers = docker.ps();
+
+  if (!containers.await(Seconds(3))) {
+    return Error("Failed to use Docker: Timed out");
+  } else if (containers.isFailed()) {
+    return Error("Failed to use Docker: " + containers.failure());
+  }
+
+  return Nothing();
+}
+
+
 string Docker::Container::id() const
 {
   map<string, JSON::Value>::const_iterator entry =

http://git-wip-us.apache.org/repos/asf/mesos/blob/4406fb54/src/docker/docker.hpp
----------------------------------------------------------------------
diff --git a/src/docker/docker.hpp b/src/docker/docker.hpp
index 3bed71d..a43fa58 100644
--- a/src/docker/docker.hpp
+++ b/src/docker/docker.hpp
@@ -26,12 +26,16 @@
 #include <process/subprocess.hpp>
 
 #include <stout/json.hpp>
+#include <stout/nothing.hpp>
 #include <stout/option.hpp>
 
 // Abstraction for working with Docker (modeled on CLI).
 class Docker
 {
 public:
+  // Validate Docker support
+  static Try<Nothing> validateDocker(const Docker& docker);
+
   class Container
   {
   public:

http://git-wip-us.apache.org/repos/asf/mesos/blob/4406fb54/src/slave/containerizer/docker.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/docker.cpp b/src/slave/containerizer/docker.cpp
index 3d62390..f9cfa9f 100644
--- a/src/slave/containerizer/docker.cpp
+++ b/src/slave/containerizer/docker.cpp
@@ -154,13 +154,9 @@ Try<DockerContainerizer*> DockerContainerizer::create(
     bool local,
     const Docker& docker)
 {
-  // Try out the Docker command so that
-  Future<list<Docker::Container> > containers = docker.ps();
-
-  if (!containers.await(Seconds(3))) {
-    return Error("Failed to use Docker: Timed out");
-  } else if (containers.isFailed()) {
-    return Error("Failed to use Docker: " + containers.failure());
+  Try<Nothing> validation = Docker::validateDocker(docker);
+  if (validation.isError()) {
+    return Error(validation.error());
   }
 
   return new DockerContainerizer(flags, local, docker);

http://git-wip-us.apache.org/repos/asf/mesos/blob/4406fb54/src/tests/docker_containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/docker_containerizer_tests.cpp b/src/tests/docker_containerizer_tests.cpp
new file mode 100644
index 0000000..8cbde0b
--- /dev/null
+++ b/src/tests/docker_containerizer_tests.cpp
@@ -0,0 +1,121 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <process/future.hpp>
+
+#include "tests/mesos.hpp"
+
+#include "slave/slave.hpp"
+#include "slave/containerizer/docker.hpp"
+
+using namespace mesos;
+using namespace mesos::internal;
+using namespace mesos::internal::tests;
+
+using mesos::internal::master::Master;
+
+using mesos::internal::slave::Slave;
+
+using process::Future;
+using process::PID;
+
+using std::vector;
+using std::list;
+
+using testing::_;
+using testing::Eq;
+using testing::Return;
+
+class DockerContainerizerTest : public MesosTest {};
+
+TEST_F(DockerContainerizerTest, DOCKER_Launch) {
+  Try<PID<Master> > master = StartMaster();
+  ASSERT_SOME(master);
+
+  slave::Flags flags = CreateSlaveFlags();
+  flags.isolation.clear();
+  flags.containerizers = "docker";
+
+  Try<PID<Slave> > slave = StartSlave(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(offers);
+  EXPECT_NE(0u, offers.get().size());
+
+  const Offer& offer = offers.get()[0];
+
+  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;
+
+  CommandInfo::ContainerInfo* containerInfo =
+    task.mutable_command()->mutable_container();
+
+  containerInfo->set_image("docker://busybox");
+
+  command.set_value("sleep 30");
+
+  task.mutable_command()->CopyFrom(command);
+
+  Future<TaskStatus> statusRunning;
+
+  vector<TaskInfo> tasks;
+  tasks.push_back(task);
+
+  EXPECT_CALL(sched, statusUpdate(&driver, _))
+    .WillOnce(FutureArg<1>(&statusRunning));
+
+  driver.launchTasks(offers.get()[0].id(), tasks);
+
+  AWAIT_READY(statusRunning);
+  ASSERT_EQ(TASK_RUNNING, statusRunning.get().state());
+
+  Docker docker("docker");
+  Future<list<Docker::Container> > containers = docker.ps();
+
+  AWAIT_READY(containers);
+
+  ASSERT_TRUE(containers.get().size() > 0);
+
+  driver.stop();
+  driver.join();
+
+  Shutdown();
+}

http://git-wip-us.apache.org/repos/asf/mesos/blob/4406fb54/src/tests/environment.cpp
----------------------------------------------------------------------
diff --git a/src/tests/environment.cpp b/src/tests/environment.cpp
index 551698f..5c14cca 100644
--- a/src/tests/environment.cpp
+++ b/src/tests/environment.cpp
@@ -25,6 +25,8 @@
 #include <list>
 #include <string>
 
+#include "docker/docker.hpp"
+
 #include <process/gmock.hpp>
 #include <process/gtest.hpp>
 
@@ -70,6 +72,7 @@ Environment* environment;
 //   'CGROUPS_' : Disable test if cgroups support isn't present.
 //   'NOHIERARCHY_' : Disable test if there is already a cgroups
 //       hierarchy mounted.
+//   'DOCKER_': Disable test if Docker is not supported.
 //
 // These flags can be composed in any order, but must come after
 // 'DISABLED_'. In addition, we disable tests that attempt to use the
@@ -127,6 +130,12 @@ static bool enable(const ::testing::TestInfo& test)
     }
 #endif
 
+    if (strings::contains(name, "DOCKER_")) {
+      Docker docker("docker");
+      Try<Nothing> validate = Docker::validateDocker(docker);
+      return !validate.isError();
+    }
+
     // Filter out benchmark tests when we run 'make check'.
     if (strings::contains(name, "BENCHMARK_") && !flags.benchmark) {
       return false;
@@ -332,4 +341,3 @@ Try<string> Environment::mkdtemp()
 } // namespace tests {
 } // namespace internal {
 } // namespace mesos {
-


[06/43] git commit: Added a 'Docker' abstraction.

Posted by be...@apache.org.
Added a 'Docker' abstraction.


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

Branch: refs/heads/master
Commit: ab6db9b5adab7d08e24e7004b8774f71c9014c2c
Parents: dcc87a1
Author: Benjamin Hindman <be...@gmail.com>
Authored: Sun Jun 22 17:56:05 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:15 2014 -0700

----------------------------------------------------------------------
 src/Makefile.am       |   2 +
 src/docker/docker.cpp | 170 +++++++++++++++++++++++++++++++++++++++++++++
 src/docker/docker.hpp |  72 +++++++++++++++++++
 3 files changed, 244 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/ab6db9b5/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index 411cff4..ef99788 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -234,6 +234,8 @@ libmesos_no_3rdparty_la_SOURCES =					\
 	common/thread.cpp						\
 	common/type_utils.cpp						\
 	common/values.cpp						\
+	docker/docker.hpp					        \
+	docker/docker.cpp					        \
 	exec/exec.cpp							\
 	files/files.cpp							\
 	local/local.cpp							\

http://git-wip-us.apache.org/repos/asf/mesos/blob/ab6db9b5/src/docker/docker.cpp
----------------------------------------------------------------------
diff --git a/src/docker/docker.cpp b/src/docker/docker.cpp
new file mode 100644
index 0000000..db31ba3
--- /dev/null
+++ b/src/docker/docker.cpp
@@ -0,0 +1,170 @@
+#include <map>
+#include <vector>
+
+#include <stout/lambda.hpp>
+#include <stout/strings.hpp>
+
+#include <process/check.hpp>
+#include <process/collect.hpp>
+#include <process/io.hpp>
+
+#include "docker/docker.hpp"
+
+using namespace process;
+
+using std::list;
+using std::map;
+using std::string;
+using std::vector;
+
+
+string Docker::Container::name() const
+{
+  map<string, JSON::Value>::const_iterator entry =
+    json.values.find("Name");
+  CHECK(entry != json.values.end());
+  JSON::Value value = entry->second;
+  CHECK(value.is<JSON::String>());
+  return value.as<JSON::String>().value;
+}
+
+
+Future<Option<int> > Docker::run(const string& image) const
+{
+  Try<Subprocess> s = subprocess(
+      path + " run " + image,
+      Subprocess::PIPE(),
+      Subprocess::PIPE(),
+      Subprocess::PIPE());
+
+  if (s.isError()) {
+    return Failure(s.error());
+  }
+
+  return s.get().status();
+}
+
+
+Future<Option<int> > Docker::kill(const string& container) const
+{
+  Try<Subprocess> s = subprocess(
+      path + " kill " + container,
+      Subprocess::PIPE(),
+      Subprocess::PIPE(),
+      Subprocess::PIPE());
+
+  if (s.isError()) {
+    return Failure(s.error());
+  }
+
+  return s.get().status();
+}
+
+
+Future<Docker::Container> Docker::inspect(const string& container) const
+{
+  Try<Subprocess> s = subprocess(
+      path + " inspect " + container,
+      Subprocess::PIPE(),
+      Subprocess::PIPE(),
+      Subprocess::PIPE());
+
+  if (s.isError()) {
+    // TODO(benh): Include stdout and stderr in error message.
+    return Failure(s.error());
+  }
+
+  return s.get().status()
+    .then(lambda::bind(&Docker::_inspect, s.get()));
+}
+
+
+Future<Docker::Container> Docker::_inspect(const Subprocess& s)
+{
+  // Check the exit status of 'docker ps'.
+  CHECK_READY(s.status());
+
+  Option<int> status = s.status().get();
+
+  if (status.isSome() && status.get() != 0) {
+    // TODO(benh): Include stdout and stderr in error message.
+    return Failure("Failed to do 'docker ps'");
+  }
+
+  // Read to EOF.
+  // TODO(benh): Read output asynchronously.
+  CHECK_SOME(s.out());
+  string output = io::read(s.out().get()).get();
+
+  Try<JSON::Array> parse = JSON::parse<JSON::Array>(output);
+
+  if (parse.isError()) {
+    return Failure("Failed to parse JSON: " + parse.error());
+  }
+
+  JSON::Array array = parse.get();
+
+  // Skip the container if it no longer exists.
+  if (array.values.size() == 1) {
+    CHECK(array.values.front().is<JSON::Object>());
+    return Docker::Container(array.values.front().as<JSON::Object>());
+  }
+
+  // TODO(benh): Handle the case where the short container ID was
+  // not sufficiently unique and 'array.values.size() > 1'.
+
+  return Failure("Failed to find container");
+}
+
+
+Future<list<Docker::Container> > Docker::ps() const
+{
+  Try<Subprocess> s = subprocess(
+      path + " ps",
+      Subprocess::PIPE(),
+      Subprocess::PIPE(),
+      Subprocess::PIPE());
+
+  if (s.isError()) {
+    return Failure(s.error());
+  }
+
+  return s.get().status()
+    .then(lambda::bind(&Docker::_ps, Docker(path), s.get()));
+}
+
+
+Future<list<Docker::Container> > Docker::_ps(
+    const Docker& docker,
+    const Subprocess& s)
+{
+  // Check the exit status of 'docker ps'.
+  CHECK_READY(s.status());
+
+  Option<int> status = s.status().get();
+
+  if (status.isSome() && status.get() != 0) {
+    // TODO(benh): Include stdout and stderr in error message.
+    return Failure("Failed to do 'docker ps'");
+  }
+
+  // Read to EOF.
+  // TODO(benh): Read output asynchronously.
+  CHECK_SOME(s.out());
+  string output = io::read(s.out().get()).get();
+
+  vector<string> lines = strings::split(output, "\n");
+
+  // Skip the header.
+  CHECK_NE(0, lines.size());
+  lines.erase(lines.begin());
+
+  list<Future<Docker::Container> > futures;
+
+  foreach (const string& line, lines) {
+    // Inspect the container.
+    futures.push_back(docker.inspect(strings::split(line, "\n")[0]));
+  }
+
+  return collect(futures);
+}

http://git-wip-us.apache.org/repos/asf/mesos/blob/ab6db9b5/src/docker/docker.hpp
----------------------------------------------------------------------
diff --git a/src/docker/docker.hpp b/src/docker/docker.hpp
new file mode 100644
index 0000000..26d6ec3
--- /dev/null
+++ b/src/docker/docker.hpp
@@ -0,0 +1,72 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __DOCKER_HPP__
+#define __DOCKER_HPP__
+
+#include <list>
+#include <string>
+
+#include <process/future.hpp>
+#include <process/subprocess.hpp>
+
+#include <stout/json.hpp>
+#include <stout/option.hpp>
+
+// Abstraction for working with Docker (modeled on CLI).
+class Docker
+{
+public:
+  class Container
+  {
+  public:
+    Container(const JSON::Object& json) : json(json) {}
+
+    // Returns the name of the container.
+    std::string name() const;
+
+  private:
+    JSON::Object json; // JSON returned from 'docker inspect'.
+  };
+
+  // Uses the specified path to the Docker CLI tool.
+  Docker(const std::string& path) : path(path) {}
+
+  // Performs 'docker run IMAGE'.
+  process::Future<Option<int> > run(const std::string& image) const;
+
+  // Performs 'docker kill CONTAINER'.
+  process::Future<Option<int> > kill(const std::string& container) const;
+
+  // Performs 'docker inspect CONTAINER'.
+  process::Future<Container> inspect(const std::string& container) const;
+
+  // Performs 'docker ps'.
+  process::Future<std::list<Container> > ps() const;
+
+private:
+  // Continuations.
+  static process::Future<Container> _inspect(const process::Subprocess& s);
+  static process::Future<std::list<Container> > _ps(
+      const Docker& docker,
+      const process::Subprocess& s);
+
+  const std::string path;
+};
+
+#endif // __DOCKER_HPP__


[33/43] git commit: Modified Docker example framework to use busybox.

Posted by be...@apache.org.
Modified Docker example framework to use busybox.


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

Branch: refs/heads/master
Commit: c530a5bbc3b010b21ac27e18bfdcb5f686717dcd
Parents: 4792825
Author: Benjamin Hindman <be...@gmail.com>
Authored: Wed Jul 9 13:13:01 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:17 2014 -0700

----------------------------------------------------------------------
 src/examples/docker_no_executor_framework.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/c530a5bb/src/examples/docker_no_executor_framework.cpp
----------------------------------------------------------------------
diff --git a/src/examples/docker_no_executor_framework.cpp b/src/examples/docker_no_executor_framework.cpp
index d64c23b..0d59928 100644
--- a/src/examples/docker_no_executor_framework.cpp
+++ b/src/examples/docker_no_executor_framework.cpp
@@ -104,7 +104,7 @@ public:
         // Use Docker to run the task.
         CommandInfo::ContainerInfo* container =
           task.mutable_command()->mutable_container();
-        container->set_image("docker://ubuntu");
+        container->set_image("docker://busybox");
 
         Resource* resource;
 


[09/43] git commit: Added pid() in docker/docker.cpp to get the pid of the container.

Posted by be...@apache.org.
Added pid() in docker/docker.cpp to get the pid of the container.


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

Branch: refs/heads/master
Commit: bd8f063715cac1547b35bfee20d3a117667d3753
Parents: 4406fb5
Author: Yifan Gu <gu...@gmail.com>
Authored: Mon Jun 23 22:52:52 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:15 2014 -0700

----------------------------------------------------------------------
 src/docker/docker.cpp | 15 +++++++++++++++
 src/docker/docker.hpp |  4 ++++
 2 files changed, 19 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/bd8f0637/src/docker/docker.cpp
----------------------------------------------------------------------
diff --git a/src/docker/docker.cpp b/src/docker/docker.cpp
index 53c0805..1e4f62f 100644
--- a/src/docker/docker.cpp
+++ b/src/docker/docker.cpp
@@ -55,6 +55,21 @@ string Docker::Container::name() const
   return value.as<JSON::String>().value;
 }
 
+pid_t Docker::Container::pid() const
+{
+  map<string, JSON::Value>::const_iterator state =
+    json.values.find("State");
+  CHECK(state != json.values.end());
+  JSON::Value value = state->second;
+  CHECK(value.is<JSON::Object>());
+
+  map<string, JSON::Value>::const_iterator entry =
+    value.as<JSON::Object>().values.find("Pid");
+  CHECK(entry != json.values.end());
+  JSON::Value pid = entry->second;
+  CHECK(pid.is<JSON::Number>());
+  return pid_t(pid.as<JSON::Number>().value);
+}
 
 Future<Option<int> > Docker::run(
     const string& image,

http://git-wip-us.apache.org/repos/asf/mesos/blob/bd8f0637/src/docker/docker.hpp
----------------------------------------------------------------------
diff --git a/src/docker/docker.hpp b/src/docker/docker.hpp
index a43fa58..264dc79 100644
--- a/src/docker/docker.hpp
+++ b/src/docker/docker.hpp
@@ -47,6 +47,10 @@ public:
     // Returns the name of the container.
     std::string name() const;
 
+    // Returns the Pid of the container.
+    // Note: If it returns 0, it means the container is not running.
+    pid_t pid() const;
+
   private:
     JSON::Object json; // JSON returned from 'docker inspect'.
   };


[15/43] git commit: Exposed and used the Docker container name prefix.

Posted by be...@apache.org.
Exposed and used the Docker container name prefix.


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

Branch: refs/heads/master
Commit: 57c1ec2d45a01ca391ae234cf2cbb74fd37bd9cf
Parents: 109296f
Author: Benjamin Hindman <be...@gmail.com>
Authored: Sun Jun 29 13:42:02 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:16 2014 -0700

----------------------------------------------------------------------
 src/slave/containerizer/docker.cpp       | 7 ++++---
 src/slave/containerizer/docker.hpp       | 6 ++++++
 src/tests/docker_containerizer_tests.cpp | 3 ++-
 3 files changed, 12 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/57c1ec2d/src/slave/containerizer/docker.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/docker.cpp b/src/slave/containerizer/docker.cpp
index 5968916..87510fa 100644
--- a/src/slave/containerizer/docker.cpp
+++ b/src/slave/containerizer/docker.cpp
@@ -55,9 +55,10 @@ using state::ExecutorState;
 using state::RunState;
 
 
-// Prefix included in every Docker container created by Mesos to
-// distinguish from Docker containers created manually.
-static string DOCKER_NAME_PREFIX = "mesos-";
+// Declared in header, see explanation there.
+// TODO(benh): At some point to run multiple slaves we'll need to make
+// the Docker container name creation include the slave ID.
+string DOCKER_NAME_PREFIX = "mesos-";
 
 
 class DockerContainerizerProcess

http://git-wip-us.apache.org/repos/asf/mesos/blob/57c1ec2d/src/slave/containerizer/docker.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/docker.hpp b/src/slave/containerizer/docker.hpp
index a6411e8..1a5d1c2 100644
--- a/src/slave/containerizer/docker.hpp
+++ b/src/slave/containerizer/docker.hpp
@@ -29,9 +29,15 @@ namespace mesos {
 namespace internal {
 namespace slave {
 
+// Prefix used to name Docker containers in order to distinguish those
+// created by Mesos from those created manually.
+extern std::string DOCKER_NAME_PREFIX;
+
+
 // Forward declaration.
 class DockerContainerizerProcess;
 
+
 class DockerContainerizer : public Containerizer
 {
 public:

http://git-wip-us.apache.org/repos/asf/mesos/blob/57c1ec2d/src/tests/docker_containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/docker_containerizer_tests.cpp b/src/tests/docker_containerizer_tests.cpp
index cdf925d..17c4b1c 100644
--- a/src/tests/docker_containerizer_tests.cpp
+++ b/src/tests/docker_containerizer_tests.cpp
@@ -154,7 +154,8 @@ TEST_F(DockerContainerizerTest, DOCKER_Launch)
   ASSERT_TRUE(containers.get().size() > 0);
 
   bool foundContainer = false;
-  string expectedName = "mesos-" + dockerContainer.lastContainerId.value();
+  string expectedName =
+    slave::DOCKER_NAME_PREFIX + dockerContainer.lastContainerId.value();
 
   foreach (const Docker::Container& container, containers.get()) {
     // Docker inspect name contains an extra slash in the beginning.


[20/43] git commit: Made Docker::ps/rm default arguments match CLI defaults.

Posted by be...@apache.org.
Made Docker::ps/rm default arguments match CLI defaults.


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

Branch: refs/heads/master
Commit: a54dda8a49d49265255580241a80edb68b4d76f4
Parents: 38e4752
Author: Benjamin Hindman <be...@gmail.com>
Authored: Sun Jun 29 17:57:28 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:16 2014 -0700

----------------------------------------------------------------------
 src/docker/docker.hpp                    | 4 ++--
 src/slave/containerizer/docker.cpp       | 9 +++++----
 src/tests/docker_containerizer_tests.cpp | 2 +-
 3 files changed, 8 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/a54dda8a/src/docker/docker.hpp
----------------------------------------------------------------------
diff --git a/src/docker/docker.hpp b/src/docker/docker.hpp
index 6643036..2957e92 100644
--- a/src/docker/docker.hpp
+++ b/src/docker/docker.hpp
@@ -71,7 +71,7 @@ public:
   // Performs 'docker rm (-f) CONTAINER'.
   process::Future<Option<int> > rm(
       const std::string& container,
-      const bool force = true) const;
+      const bool force = false) const;
 
   // Performs 'docker inspect CONTAINER'.
   process::Future<Container> inspect(
@@ -79,7 +79,7 @@ public:
 
   // Performs 'docker ps (-a)'.
   process::Future<std::list<Container> > ps(
-      const bool all = true) const;
+      const bool all = false) const;
 
   process::Future<std::string> info() const;
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/a54dda8a/src/slave/containerizer/docker.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/docker.cpp b/src/slave/containerizer/docker.cpp
index 24255e6..5026082 100644
--- a/src/slave/containerizer/docker.cpp
+++ b/src/slave/containerizer/docker.cpp
@@ -409,8 +409,9 @@ Future<Nothing> DockerContainerizerProcess::recover(
     }
   }
 
-  // Get the list of Docker containers in order to Remove any orphans.
-  return docker.ps()
+  // Get the list of all Docker containers (running and exited) in
+  // order to remove any orphans.
+  return docker.ps(true)
     .then(defer(self(), &Self::_recover, lambda::_1));
 }
 
@@ -437,7 +438,7 @@ Future<Nothing> DockerContainerizerProcess::_recover(
     if (!statuses.keys().contains(id.get())) {
       // TODO(benh): Retry 'docker rm -f' if it failed but the container
       // still exists (asynchronously).
-      docker.rm(container.id());
+      docker.rm(container.id(), true);
     }
   }
 
@@ -705,7 +706,7 @@ void DockerContainerizerProcess::destroy(
 
   // TODO(benh): Retry 'docker rm -f' if it failed but the container
   // still exists (asynchronously).
-  docker.rm(DOCKER_NAME_PREFIX + stringify(containerId))
+  docker.rm(DOCKER_NAME_PREFIX + stringify(containerId), true)
     .onAny(defer(self(), &Self::_destroy, containerId, killed, lambda::_1));
 }
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/a54dda8a/src/tests/docker_containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/docker_containerizer_tests.cpp b/src/tests/docker_containerizer_tests.cpp
index b716de4..274eee7 100644
--- a/src/tests/docker_containerizer_tests.cpp
+++ b/src/tests/docker_containerizer_tests.cpp
@@ -147,7 +147,7 @@ TEST_F(DockerContainerizerTest, DOCKER_Launch)
   AWAIT_READY_FOR(statusRunning, Seconds(60));
   EXPECT_EQ(TASK_RUNNING, statusRunning.get().state());
 
-  Future<list<Docker::Container> > containers = docker.ps();
+  Future<list<Docker::Container> > containers = docker.ps(true);
 
   AWAIT_READY(containers);
 


[12/43] git commit: Modify validate Docker to call 'info' instead.

Posted by be...@apache.org.
Modify validate Docker to call 'info' instead.


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

Branch: refs/heads/master
Commit: 9bd535e81b1bf8526f916cdcdd3ac1d1e7164a8f
Parents: d60cc83
Author: Timothy Chen <tn...@gmail.com>
Authored: Wed Jun 25 06:56:57 2014 +0000
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:15 2014 -0700

----------------------------------------------------------------------
 src/docker/docker.cpp     | 37 +++++++++++++++++++++++++++++++++----
 src/docker/docker.hpp     |  2 ++
 src/tests/environment.cpp |  8 ++++++++
 3 files changed, 43 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/9bd535e8/src/docker/docker.cpp
----------------------------------------------------------------------
diff --git a/src/docker/docker.cpp b/src/docker/docker.cpp
index 500517c..1e3ed71 100644
--- a/src/docker/docker.cpp
+++ b/src/docker/docker.cpp
@@ -23,12 +23,12 @@ using std::vector;
 
 Try<Nothing> Docker::validateDocker(const Docker &docker)
 {
-  Future<list<Docker::Container> > containers = docker.ps();
+  Future<std::string> info = docker.info();
 
-  if (!containers.await(Seconds(3))) {
+  if (!info.await(Seconds(3))) {
     return Error("Failed to use Docker: Timed out");
-  } else if (containers.isFailed()) {
-    return Error("Failed to use Docker: " + containers.failure());
+  } else if (info.isFailed()) {
+    return Error("Failed to use Docker: " + info.failure());
   }
 
   return Nothing();
@@ -300,3 +300,32 @@ Future<list<Docker::Container> > Docker::_ps(
 
   return collect(futures);
 }
+
+Future<std::string> Docker::info() const
+{
+  std::string cmd = path + " info";
+
+  VLOG(1) << "Running " << cmd;
+
+  Try<Subprocess> s = subprocess(
+      cmd,
+      Subprocess::PIPE(),
+      Subprocess::PIPE(),
+      Subprocess::PIPE());
+
+  if (s.isError()) {
+    return Failure(s.error());
+  }
+
+  Result<string> output = os::read(s.get().out().get());
+
+  if (output.isError()) {
+    // TODO(benh): Include stderr in error message.
+    return Failure("Failed to read output: " + output.error());
+  } else if (output.isNone()) {
+    // TODO(benh): Include stderr in error message.
+    return Failure("No output available");
+  }
+
+  return output.get();
+}

http://git-wip-us.apache.org/repos/asf/mesos/blob/9bd535e8/src/docker/docker.hpp
----------------------------------------------------------------------
diff --git a/src/docker/docker.hpp b/src/docker/docker.hpp
index b0d0a3a..080e341 100644
--- a/src/docker/docker.hpp
+++ b/src/docker/docker.hpp
@@ -81,6 +81,8 @@ public:
   process::Future<std::list<Container> > ps(
       const bool all = true) const;
 
+  process::Future<std::string> info() const;
+
 private:
   // Continuations.
   static process::Future<Container> _inspect(

http://git-wip-us.apache.org/repos/asf/mesos/blob/9bd535e8/src/tests/environment.cpp
----------------------------------------------------------------------
diff --git a/src/tests/environment.cpp b/src/tests/environment.cpp
index 5c14cca..11a08e3 100644
--- a/src/tests/environment.cpp
+++ b/src/tests/environment.cpp
@@ -133,6 +133,14 @@ static bool enable(const ::testing::TestInfo& test)
     if (strings::contains(name, "DOCKER_")) {
       Docker docker("docker");
       Try<Nothing> validate = Docker::validateDocker(docker);
+      if (validate.isError()) {
+	std::cerr
+          << "-------------------------------------------------------------\n"
+          << "Skipping Docker tests because validation failed\n"
+	  << "[Error] " + validate.error() + "\n"
+          << "-------------------------------------------------------------"
+          << std::endl;
+      }
       return !validate.isError();
     }
 


[30/43] git commit: Implemented DockerContainerizer::update.

Posted by be...@apache.org.
Implemented DockerContainerizer::update.


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

Branch: refs/heads/master
Commit: a5d683b6c5d4a7c8cc9608f6e57cb9cf7f172ba4
Parents: 81782c0
Author: Timothy Chen <tn...@gmail.com>
Authored: Mon Jun 30 15:33:00 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:17 2014 -0700

----------------------------------------------------------------------
 src/linux/cgroups.cpp                    |  19 ++++
 src/linux/cgroups.hpp                    |   5 +
 src/slave/containerizer/docker.cpp       | 144 +++++++++++++++++++++++++-
 src/slave/containerizer/docker.hpp       |   2 +
 src/tests/docker_containerizer_tests.cpp | 120 +++++++++++++++++++++
 5 files changed, 286 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/a5d683b6/src/linux/cgroups.cpp
----------------------------------------------------------------------
diff --git a/src/linux/cgroups.cpp b/src/linux/cgroups.cpp
index ccb86cf..39a4874 100644
--- a/src/linux/cgroups.cpp
+++ b/src/linux/cgroups.cpp
@@ -1846,6 +1846,25 @@ Try<Nothing> shares(
 }
 
 
+Try<uint64_t> shares(
+    const string& hierarchy,
+    const string& cgroup)
+{
+  Try<string> read = cgroups::read(hierarchy, cgroup, "cpu.shares");
+
+  if (read.isError()) {
+    return Error(read.error());
+  }
+
+  uint64_t shares;
+  std::istringstream ss(read.get());
+
+  ss >> shares;
+
+  return shares;
+}
+
+
 Try<Nothing> cfs_period_us(
     const string& hierarchy,
     const string& cgroup,

http://git-wip-us.apache.org/repos/asf/mesos/blob/a5d683b6/src/linux/cgroups.hpp
----------------------------------------------------------------------
diff --git a/src/linux/cgroups.hpp b/src/linux/cgroups.hpp
index c571e91..9dfba6e 100644
--- a/src/linux/cgroups.hpp
+++ b/src/linux/cgroups.hpp
@@ -392,6 +392,11 @@ Try<Nothing> shares(
     const std::string& cgroup,
     uint64_t shares);
 
+// Returns the cpu shares from cpu.shares.
+Try<uint64_t> shares(
+    const std::string& hierarchy,
+    const std::string& cgroup);
+
 
 // Returns the cpu shares from cpu.shares.
 Try<uint64_t> shares(

http://git-wip-us.apache.org/repos/asf/mesos/blob/a5d683b6/src/slave/containerizer/docker.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/docker.cpp b/src/slave/containerizer/docker.cpp
index 7cd2d7d..5a68d94 100644
--- a/src/slave/containerizer/docker.cpp
+++ b/src/slave/containerizer/docker.cpp
@@ -30,12 +30,19 @@
 
 #include "docker/docker.hpp"
 
+#ifdef __linux__
+#include "linux/cgroups.hpp"
+#endif // __linux__
+
 #include "slave/paths.hpp"
 #include "slave/slave.hpp"
 
 #include "slave/containerizer/containerizer.hpp"
 #include "slave/containerizer/docker.hpp"
 
+#include "slave/containerizer/isolators/cgroups/cpushare.hpp"
+#include "slave/containerizer/isolators/cgroups/mem.hpp"
+
 #include "usage/usage.hpp"
 
 
@@ -134,6 +141,11 @@ private:
       const bool& killed,
       const Future<Option<int > >& status);
 
+  process::Future<Nothing> _update(
+      const ContainerID& containerId,
+      const Resources& resources,
+      const Future<Docker::Container>& future);
+
   Future<ResourceStatistics> _usage(
     const ContainerID& containerId,
     const Docker::Container& container);
@@ -169,6 +181,30 @@ private:
 };
 
 
+Try<Nothing> DockerContainerizer::prepareCgroups(const Flags& flags)
+{
+#ifdef __linux__
+  std::vector<string> subsystems;
+  subsystems.push_back("cpu");
+  subsystems.push_back("cpuacct");
+  subsystems.push_back("memory");
+
+  foreach (const string& subsystem, subsystems) {
+    // We're assuming docker is under cgroup directory "docker".
+    Try<string> hierarchy =
+      cgroups::prepare(flags.cgroups_hierarchy, subsystem, "docker");
+
+    if (hierarchy.isError()) {
+      return Error(
+          "Failed to prepare cgroup hierarchy " + flags.cgroups_hierarchy +
+          " subsystem '" + subsystem + "' for Docker: " + hierarchy.error());
+    }
+  }
+#endif // __linux__
+  return Nothing();
+}
+
+
 Try<DockerContainerizer*> DockerContainerizer::create(
     const Flags& flags,
     bool local)
@@ -179,6 +215,11 @@ Try<DockerContainerizer*> DockerContainerizer::create(
     return Error(validation.error());
   }
 
+  Try<Nothing> prepare = prepareCgroups(flags);
+  if (prepare.isError()) {
+    return Error(prepare.error());
+  }
+
   return new DockerContainerizer(flags, local, docker);
 }
 
@@ -620,11 +661,106 @@ Future<bool> DockerContainerizerProcess::_launch(
 
 Future<Nothing> DockerContainerizerProcess::update(
     const ContainerID& containerId,
-    const Resources& resources)
+    const Resources& _resources)
+{
+  if (!promises.contains(containerId)) {
+    LOG(WARNING)
+      << "Ignoring updating unknown container: "
+      << containerId.value();
+    return Nothing();
+  }
+
+#ifdef __linux__
+  if (!_resources.cpus().isSome() && !_resources.mem().isSome()) {
+    LOG(WARNING) << "Ignoring update as no supported resources are present";
+    return Nothing();
+  }
+
+  // Store the resources for usage()
+  resources.put(containerId, _resources);
+
+  return docker.inspect(DOCKER_NAME_PREFIX + stringify(containerId))
+    .then(defer(self(), &Self::_update, containerId, _resources, lambda::_1));
+#else
+  return Nothing();
+#endif // __linux__
+}
+
+
+Future<Nothing> DockerContainerizerProcess::_update(
+    const ContainerID& containerId,
+    const Resources& _resources,
+    const Future<Docker::Container>& future)
 {
-  // TODO(benh): Right now we're only launching tasks so we don't
-  // expect the containers to be resized. This will need to get
-  // implemented to support executors.
+#ifdef __linux__
+  const string& id = path::join("docker", future.get().id());
+
+  // Update CPU shares.
+  if (_resources.cpus().isSome()) {
+    double cpuShares = _resources.cpus().get();
+
+    uint64_t shares =
+      std::max((uint64_t) (CPU_SHARES_PER_CPU * cpuShares), MIN_CPU_SHARES);
+
+    Try<Nothing> write =
+      cgroups::cpu::shares(
+          path::join(flags.cgroups_hierarchy, "cpu"), id, shares);
+
+    if (write.isError()) {
+      return Failure("Failed to update 'cpu.shares': " + write.error());
+    }
+
+    LOG(INFO)
+      << "Updated 'cpu.shares' to " << shares
+      << " for container " << containerId;
+  }
+
+  // Update Memory.
+  if (_resources.mem().isSome()) {
+    Bytes mem = _resources.mem().get();
+    Bytes limit = std::max(mem, MIN_MEMORY);
+
+    std::string memHierarchy =
+      path::join(flags.cgroups_hierarchy, "memory");
+
+    // Always set the soft limit.
+    Try<Nothing> write =
+      cgroups::memory::soft_limit_in_bytes(memHierarchy, id, limit);
+
+    if (write.isError()) {
+      return Failure("Failed to set 'memory.soft_limit_in_bytes': " +
+          write.error());
+    }
+
+    LOG(INFO)
+      << "Updated 'memory.soft_limit_in_bytes' to " << limit
+      << " for container " << containerId;
+
+    // Read the existing limit.
+    Try<Bytes> currentLimit =
+      cgroups::memory::limit_in_bytes(memHierarchy, id);
+
+    if (currentLimit.isError()) {
+      return Failure("Failed to read 'memory.limit_in_bytes': " +
+          currentLimit.error());
+    }
+
+    // Only update if new limit is higher.
+    if (limit > currentLimit.get()) {
+      write = cgroups::memory::limit_in_bytes(memHierarchy, id, limit);
+
+      if (write.isError()) {
+        return Failure("Failed to set 'memory.limit_in_bytes': " +
+            write.error());
+      }
+
+      LOG(INFO)
+        << "Updated 'memory.limit_in_bytes' to " << limit
+        << " for container " << containerId;
+    }
+  }
+#endif // __linux__
+
   return Nothing();
 }
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/a5d683b6/src/slave/containerizer/docker.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/docker.hpp b/src/slave/containerizer/docker.hpp
index 1a5d1c2..f4eb0ff 100644
--- a/src/slave/containerizer/docker.hpp
+++ b/src/slave/containerizer/docker.hpp
@@ -45,6 +45,8 @@ public:
       const Flags& flags,
       bool local);
 
+  static Try<Nothing> prepareCgroups(const Flags& flags);
+
   DockerContainerizer(
       const Flags& flags,
       bool local,

http://git-wip-us.apache.org/repos/asf/mesos/blob/a5d683b6/src/tests/docker_containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/docker_containerizer_tests.cpp b/src/tests/docker_containerizer_tests.cpp
index b0b8b39..cd1c88c 100644
--- a/src/tests/docker_containerizer_tests.cpp
+++ b/src/tests/docker_containerizer_tests.cpp
@@ -22,6 +22,8 @@
 #include <process/future.hpp>
 #include <process/subprocess.hpp>
 
+#include "linux/cgroups.hpp"
+
 #include "tests/flags.hpp"
 #include "tests/mesos.hpp"
 
@@ -62,6 +64,7 @@ public:
       const Docker& docker)
     : DockerContainerizer(flags, local, docker)
   {
+    DockerContainerizer::prepareCgroups(flags);
     EXPECT_CALL(*this, launch(_, _, _, _, _, _, _, _))
       .WillRepeatedly(Invoke(this, &MockDockerContainerizer::_launch));
   }
@@ -317,6 +320,123 @@ TEST_F(DockerContainerizerTest, DOCKER_Usage)
 }
 
 
+#ifdef __linux__
+TEST_F(DockerContainerizerTest, DOCKER_Update)
+{
+  Try<PID<Master> > master = StartMaster();
+  ASSERT_SOME(master);
+
+  slave::Flags flags = CreateSlaveFlags();
+
+  Docker docker(tests::flags.docker);
+
+  MockDockerContainerizer dockerContainerizer(flags, true, docker);
+
+  Try<PID<Slave> > slave = StartSlave(&dockerContainerizer);
+  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);
+  EXPECT_NE(0u, offers.get().size());
+
+  const Offer& offer = offers.get()[0];
+
+  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;
+  CommandInfo::ContainerInfo* containerInfo = command.mutable_container();
+  containerInfo->set_image("docker://busybox");
+  command.set_value("sleep 180");
+
+  task.mutable_command()->CopyFrom(command);
+
+  Future<TaskStatus> statusRunning;
+
+  vector<TaskInfo> tasks;
+  tasks.push_back(task);
+
+  Future<ContainerID> containerId;
+  EXPECT_CALL(dockerContainerizer, launch(_, _, _, _, _, _, _, _))
+    .WillOnce(DoAll(FutureArg<0>(&containerId),
+         Invoke(&dockerContainerizer,
+                &MockDockerContainerizer::_launch)));
+
+  EXPECT_CALL(sched, statusUpdate(&driver, _))
+    .WillOnce(FutureArg<1>(&statusRunning))
+    .WillRepeatedly(DoDefault());
+
+  driver.launchTasks(offers.get()[0].id(), tasks);
+
+  AWAIT_READY(containerId);
+
+  AWAIT_READY_FOR(statusRunning, Seconds(60));
+  EXPECT_EQ(TASK_RUNNING, statusRunning.get().state());
+
+  string containerName = slave::DOCKER_NAME_PREFIX + containerId.get().value();
+  Future<Docker::Container> container = docker.inspect(containerName);
+
+  AWAIT_READY(container);
+
+  Try<Resources> newResources = Resources::parse("cpus:1;mem:128");
+
+  ASSERT_SOME(newResources);
+
+  Future<Nothing> update =
+    dockerContainerizer.update(containerId.get(), newResources.get());
+
+  AWAIT_READY(update);
+
+  string id = path::join("docker", container.get().id());
+
+  Try<Bytes> mem =
+    cgroups::memory::soft_limit_in_bytes(
+        path::join(flags.cgroups_hierarchy, "memory"), id);
+  ASSERT_SOME(mem);
+
+  Try<uint64_t> cpu =
+    cgroups::cpu::shares(
+        path::join(flags.cgroups_hierarchy, "cpu"), id);
+
+  ASSERT_SOME(cpu);
+
+  EXPECT_EQ(1024, cpu.get());
+  EXPECT_EQ(128, mem.get().megabytes());
+
+  Future<containerizer::Termination> termination =
+    dockerContainerizer.wait(containerId.get());
+
+  dockerContainerizer.destroy(containerId.get());
+
+  AWAIT_READY(termination);
+
+  driver.stop();
+  driver.join();
+
+  Shutdown();
+}
+#endif //__linux__
+
+
 TEST_F(DockerContainerizerTest, DOCKER_Recover)
 {
   slave::Flags flags = CreateSlaveFlags();


[24/43] git commit: Added a DockerContainerizer::recover test.

Posted by be...@apache.org.
Added a DockerContainerizer::recover test.


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

Branch: refs/heads/master
Commit: 81782c0a97e576bf7e2edf0e64c179d17a5d510d
Parents: ee998e4
Author: Timothy Chen <tn...@gmail.com>
Authored: Mon Jul 7 17:37:21 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:16 2014 -0700

----------------------------------------------------------------------
 src/tests/docker_containerizer_tests.cpp | 90 +++++++++++++++++++++++++++
 1 file changed, 90 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/81782c0a/src/tests/docker_containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/docker_containerizer_tests.cpp b/src/tests/docker_containerizer_tests.cpp
index a6cae24..b0b8b39 100644
--- a/src/tests/docker_containerizer_tests.cpp
+++ b/src/tests/docker_containerizer_tests.cpp
@@ -20,6 +20,7 @@
 #include <gtest/gtest.h>
 
 #include <process/future.hpp>
+#include <process/subprocess.hpp>
 
 #include "tests/flags.hpp"
 #include "tests/mesos.hpp"
@@ -31,6 +32,7 @@
 
 using namespace mesos;
 using namespace mesos::internal;
+using namespace mesos::internal::slave::state;
 using namespace mesos::internal::tests;
 
 using mesos::internal::master::Master;
@@ -313,3 +315,91 @@ TEST_F(DockerContainerizerTest, DOCKER_Usage)
 
   Shutdown();
 }
+
+
+TEST_F(DockerContainerizerTest, DOCKER_Recover)
+{
+  slave::Flags flags = CreateSlaveFlags();
+
+  Docker docker(tests::flags.docker);
+
+  MockDockerContainerizer dockerContainerizer(flags, true, docker);
+
+  ContainerID containerId;
+  containerId.set_value("c1");
+  ContainerID reapedContainerId;
+  reapedContainerId.set_value("c2");
+
+  Resources resources = Resources::parse("cpus:1;mem:512").get();
+
+  Future<Option<int> > d1 =
+    docker.run(
+        "busybox",
+        "sleep 360",
+        slave::DOCKER_NAME_PREFIX + stringify(containerId),
+        resources);
+
+  Future<Option<int> > d2 =
+    docker.run(
+        "busybox",
+        "sleep 360",
+        slave::DOCKER_NAME_PREFIX + stringify(reapedContainerId),
+        resources);
+
+  AWAIT_READY(d1);
+  AWAIT_READY(d2);
+
+  SlaveState slaveState;
+  FrameworkState frameworkState;
+
+  ExecutorID execId;
+  execId.set_value("e1");
+
+  ExecutorState execState;
+  ExecutorInfo execInfo;
+  execState.info = execInfo;
+  execState.latest = containerId;
+
+  Try<process::Subprocess> wait =
+    process::subprocess(
+        "docker wait " +
+        slave::DOCKER_NAME_PREFIX +
+        stringify(containerId));
+
+  ASSERT_SOME(wait);
+
+  Try<process::Subprocess> reaped =
+    process::subprocess(
+        "docker wait " +
+        slave::DOCKER_NAME_PREFIX +
+        stringify(reapedContainerId));
+
+  ASSERT_SOME(reaped);
+
+  FrameworkID frameworkId;
+
+  RunState runState;
+  runState.id = containerId;
+  runState.forkedPid = wait.get().pid();
+  execState.runs.put(containerId, runState);
+  frameworkState.executors.put(execId, execState);
+
+  slaveState.frameworks.put(frameworkId, frameworkState);
+
+  Future<Nothing> recover = dockerContainerizer.recover(slaveState);
+
+  AWAIT_READY(recover);
+
+  Future<containerizer::Termination> termination =
+    dockerContainerizer.wait(containerId);
+
+  ASSERT_FALSE(termination.isFailed());
+
+  AWAIT_FAILED(dockerContainerizer.wait(reapedContainerId));
+
+  dockerContainerizer.destroy(containerId);
+
+  AWAIT_READY(termination);
+
+  AWAIT_READY(reaped.get().status());
+}


[27/43] git commit: Await container termination in docker kill test

Posted by be...@apache.org.
Await container termination in docker kill test


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

Branch: refs/heads/master
Commit: c4b98ad3720392d3e949e0254fa23fd8279ce985
Parents: 0bf53b4
Author: Timothy Chen <tn...@gmail.com>
Authored: Wed Jul 9 22:48:02 2014 +0000
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:17 2014 -0700

----------------------------------------------------------------------
 src/tests/docker_containerizer_tests.cpp | 5 +++++
 1 file changed, 5 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/c4b98ad3/src/tests/docker_containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/docker_containerizer_tests.cpp b/src/tests/docker_containerizer_tests.cpp
index 9a780e7..d70518a 100644
--- a/src/tests/docker_containerizer_tests.cpp
+++ b/src/tests/docker_containerizer_tests.cpp
@@ -293,11 +293,16 @@ TEST_F(DockerContainerizerTest, DOCKER_Kill)
   EXPECT_CALL(sched, statusUpdate(&driver, _))
     .WillOnce(FutureArg<1>(&statusKilled));
 
+  Future<containerizer::Termination> termination =
+    dockerContainerizer.wait(containerId.get());
+
   driver.killTask(task.task_id());
 
   AWAIT_READY(statusKilled);
   EXPECT_EQ(TASK_KILLED, statusKilled.get().state());
 
+  AWAIT_READY(termination);
+
   Future<list<Docker::Container> > containers =
     docker.ps(true, slave::DOCKER_NAME_PREFIX);
 


[07/43] git commit: Added Docker unit test, Docker flag and fix issues found.

Posted by be...@apache.org.
Added Docker unit test, Docker flag and fix issues found.


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

Branch: refs/heads/master
Commit: 286e78bfb607a3ce712deaa74881e0ed3a4c155b
Parents: 9bd535e
Author: Timothy Chen <tn...@gmail.com>
Authored: Wed Jun 25 18:25:51 2014 +0000
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:15 2014 -0700

----------------------------------------------------------------------
 src/docker/docker.cpp                     |  6 +-
 src/docker/docker.hpp                     |  2 +-
 src/slave/containerizer/containerizer.cpp |  5 +-
 src/slave/containerizer/docker.cpp        | 75 ++++++++++++++++++++----
 src/slave/containerizer/docker.hpp        |  3 +-
 src/slave/flags.hpp                       |  6 ++
 src/tests/docker_containerizer_tests.cpp  | 81 +++++++++++++++++++++-----
 src/tests/environment.cpp                 | 13 +++--
 src/tests/flags.hpp                       |  7 +++
 9 files changed, 160 insertions(+), 38 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/286e78bf/src/docker/docker.cpp
----------------------------------------------------------------------
diff --git a/src/docker/docker.cpp b/src/docker/docker.cpp
index 1e3ed71..070279e 100644
--- a/src/docker/docker.cpp
+++ b/src/docker/docker.cpp
@@ -21,7 +21,7 @@ using std::string;
 using std::vector;
 
 
-Try<Nothing> Docker::validateDocker(const Docker &docker)
+Try<Nothing> Docker::validate(const Docker &docker)
 {
   Future<std::string> info = docker.info();
 
@@ -76,11 +76,11 @@ Future<Option<int> > Docker::run(
     const string& command,
     const string& name) const
 {
-  VLOG(1) << "Running " << path << " run --name=" << name << " "
+  VLOG(1) << "Running " << path << " run -d --name=" << name << " "
           << image << " " << command;
 
   Try<Subprocess> s = subprocess(
-      path + " run --name=" + name + " " + image + " " + command,
+      path + " run -d --name=" + name + " " + image + " " + command,
       Subprocess::PIPE(),
       Subprocess::PIPE(),
       Subprocess::PIPE());

http://git-wip-us.apache.org/repos/asf/mesos/blob/286e78bf/src/docker/docker.hpp
----------------------------------------------------------------------
diff --git a/src/docker/docker.hpp b/src/docker/docker.hpp
index 080e341..56b6c61 100644
--- a/src/docker/docker.hpp
+++ b/src/docker/docker.hpp
@@ -34,7 +34,7 @@ class Docker
 {
 public:
   // Validate Docker support
-  static Try<Nothing> validateDocker(const Docker& docker);
+  static Try<Nothing> validate(const Docker& docker);
 
   class Container
   {

http://git-wip-us.apache.org/repos/asf/mesos/blob/286e78bf/src/slave/containerizer/containerizer.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/containerizer.cpp b/src/slave/containerizer/containerizer.cpp
index 0978dc2..003775b 100644
--- a/src/slave/containerizer/containerizer.cpp
+++ b/src/slave/containerizer/containerizer.cpp
@@ -171,10 +171,9 @@ Try<Containerizer*> Containerizer::create(const Flags& flags, bool local)
       } else {
         containerizers.push_back(containerizer.get());
       }
-    }  else if (type == "docker") {
-      Docker docker("docker");
+    } else if (type == "docker") {
       Try<DockerContainerizer*> containerizer =
-        DockerContainerizer::create(flags, local, docker);
+        DockerContainerizer::create(flags, local);
       if (containerizer.isError()) {
         return Error("Could not create DockerContainerizer: " +
                      containerizer.error());

http://git-wip-us.apache.org/repos/asf/mesos/blob/286e78bf/src/slave/containerizer/docker.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/docker.cpp b/src/slave/containerizer/docker.cpp
index f9cfa9f..851b663 100644
--- a/src/slave/containerizer/docker.cpp
+++ b/src/slave/containerizer/docker.cpp
@@ -100,7 +100,9 @@ public:
   virtual Future<containerizer::Termination> wait(
       const ContainerID& containerId);
 
-  virtual void destroy(const ContainerID& containerId);
+  virtual void destroy(
+      const ContainerID& containerId,
+      const bool& killed = true);
 
   virtual process::Future<hashset<ContainerID> > containers();
 
@@ -118,6 +120,16 @@ private:
       const PID<Slave>& slavePid,
       bool checkpoint);
 
+  void _destroy(
+      const ContainerID& containerId,
+      const bool& killed,
+      const Future<Option<int> >& future);
+
+  void __destroy(
+      const ContainerID& containerId,
+      const bool& killed,
+      const Future<Option<int > >& status);
+
   // Call back for when the executor exits. This will trigger
   // container destroy.
   void reaped(const ContainerID& containerId);
@@ -151,10 +163,10 @@ private:
 
 Try<DockerContainerizer*> DockerContainerizer::create(
     const Flags& flags,
-    bool local,
-    const Docker& docker)
+    bool local)
 {
-  Try<Nothing> validation = Docker::validateDocker(docker);
+  Docker docker(flags.docker);
+  Try<Nothing> validation = Docker::validate(docker);
   if (validation.isError()) {
     return Error(validation.error());
   }
@@ -259,7 +271,7 @@ Future<containerizer::Termination> DockerContainerizer::wait(
 
 void DockerContainerizer::destroy(const ContainerID& containerId)
 {
-  dispatch(process, &DockerContainerizerProcess::destroy, containerId);
+  dispatch(process, &DockerContainerizerProcess::destroy, containerId, true);
 }
 
 
@@ -499,7 +511,7 @@ Future<bool> DockerContainerizerProcess::launch(
                 slaveId,
                 slavePid,
                 checkpoint))
-    .onFailed(defer(self(), &Self::destroy, containerId));
+    .onFailed(defer(self(), &Self::destroy, containerId, false));
 }
 
 
@@ -535,11 +547,10 @@ Future<bool> DockerContainerizerProcess::_launch(
     "docker wait " + DOCKER_NAME_PREFIX + stringify(containerId);
 
   Try<Subprocess> s = subprocess(
-      executorInfo.command().value() + "--override " + override,
+      executorInfo.command().value() + " --override " + override,
       Subprocess::PIPE(),
       Subprocess::PATH(path::join(directory, "stdout")),
       Subprocess::PATH(path::join(directory, "stderr")),
-      None(),
       env,
       lambda::bind(&setup, directory));
 
@@ -629,7 +640,9 @@ Future<containerizer::Termination> DockerContainerizerProcess::wait(
 }
 
 
-void DockerContainerizerProcess::destroy(const ContainerID& containerId)
+void DockerContainerizerProcess::destroy(
+    const ContainerID& containerId,
+    const bool& killed)
 {
   if (!promises.contains(containerId)) {
     LOG(WARNING) << "Ignoring destroy of unknown container: " << containerId;
@@ -658,7 +671,47 @@ void DockerContainerizerProcess::destroy(const ContainerID& containerId)
 
   // TODO(benh): Retry 'docker kill' if it failed but the container
   // still exists (asynchronously).
-  docker.kill(DOCKER_NAME_PREFIX + stringify(containerId));
+  docker.kill(DOCKER_NAME_PREFIX + stringify(containerId))
+    .onAny(defer(self(), &Self::_destroy, containerId, killed, lambda::_1));
+}
+
+
+void DockerContainerizerProcess::_destroy(
+    const ContainerID& containerId,
+    const bool& killed,
+    const Future<Option<int> >& future)
+{
+  if (!future.isReady()) {
+    promises[containerId]->fail(
+        "Failed to destroy container: " +
+        (future.isFailed() ? future.failure() : "discarded future"));
+
+    destroying.erase(containerId);
+
+    return;
+  }
+
+  statuses.get(containerId).get()
+    .onAny(defer(self(), &Self::__destroy, containerId, killed, lambda::_1));
+}
+
+
+void DockerContainerizerProcess::__destroy(
+    const ContainerID& containerId,
+    const bool& killed,
+    const Future<Option<int> >& status)
+{
+  containerizer::Termination termination;
+  termination.set_killed(killed);
+  if (status.isReady() && status.get().isSome()) {
+    termination.set_status(status.get().get());
+  }
+
+  promises[containerId]->set(termination);
+
+  destroying.erase(containerId);
+  promises.erase(containerId);
+  statuses.erase(containerId);
 }
 
 
@@ -677,7 +730,7 @@ void DockerContainerizerProcess::reaped(const ContainerID& containerId)
   LOG(INFO) << "Executor for container '" << containerId << "' has exited";
 
   // The executor has exited so destroy the container.
-  destroy(containerId);
+  destroy(containerId, false);
 }
 
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/286e78bf/src/slave/containerizer/docker.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/docker.hpp b/src/slave/containerizer/docker.hpp
index 0ecbc88..a6411e8 100644
--- a/src/slave/containerizer/docker.hpp
+++ b/src/slave/containerizer/docker.hpp
@@ -37,8 +37,7 @@ class DockerContainerizer : public Containerizer
 public:
   static Try<DockerContainerizer*> create(
       const Flags& flags,
-      bool local,
-      const Docker& docker);
+      bool local);
 
   DockerContainerizer(
       const Flags& flags,

http://git-wip-us.apache.org/repos/asf/mesos/blob/286e78bf/src/slave/flags.hpp
----------------------------------------------------------------------
diff --git a/src/slave/flags.hpp b/src/slave/flags.hpp
index 5ea2f38..66bba7c 100644
--- a/src/slave/flags.hpp
+++ b/src/slave/flags.hpp
@@ -277,6 +277,11 @@ public:
         "The default container image to use if not specified by a task,\n"
         "when using external containerizer");
 
+    add(&Flags::docker,
+        "docker",
+        "The path to the docker executable for docker containerizer.",
+        "docker");
+
 #ifdef WITH_NETWORK_ISOLATOR
     add(&Flags::ephemeral_ports_per_container,
         "ephemeral_ports_per_container",
@@ -342,6 +347,7 @@ public:
   Option<std::string> containerizer_path;
   std::string containerizers;
   Option<std::string> default_container_image;
+  std::string docker;
 #ifdef WITH_NETWORK_ISOLATOR
   uint16_t ephemeral_ports_per_container;
   Option<std::string> private_resources;

http://git-wip-us.apache.org/repos/asf/mesos/blob/286e78bf/src/tests/docker_containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/docker_containerizer_tests.cpp b/src/tests/docker_containerizer_tests.cpp
index 8cbde0b..636187f 100644
--- a/src/tests/docker_containerizer_tests.cpp
+++ b/src/tests/docker_containerizer_tests.cpp
@@ -33,28 +33,68 @@ using namespace mesos::internal::tests;
 using mesos::internal::master::Master;
 
 using mesos::internal::slave::Slave;
+using mesos::internal::slave::DockerContainerizer;
 
 using process::Future;
 using process::PID;
 
 using std::vector;
 using std::list;
+using std::string;
 
 using testing::_;
+using testing::DoDefault;
 using testing::Eq;
 using testing::Return;
 
 class DockerContainerizerTest : public MesosTest {};
 
-TEST_F(DockerContainerizerTest, DOCKER_Launch) {
+class MockDockerContainerizer : public slave::DockerContainerizer {
+public:
+  MockDockerContainerizer(
+    const slave::Flags& flags,
+    bool local,
+    const Docker& docker) : DockerContainerizer(flags, local, docker) {}
+
+  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 process::PID<Slave>& slavePid,
+    bool checkpoint)
+  {
+    // Keeping the last launched container id
+    lastContainerId = containerId;
+    return slave::DockerContainerizer::launch(
+             containerId,
+             taskInfo,
+             executorInfo,
+             directory,
+             user,
+             slaveId,
+             slavePid,
+             checkpoint);
+  }
+
+  ContainerID lastContainerId;
+};
+
+
+TEST_F(DockerContainerizerTest, DOCKER_Launch)
+{
   Try<PID<Master> > master = StartMaster();
   ASSERT_SOME(master);
 
   slave::Flags flags = CreateSlaveFlags();
-  flags.isolation.clear();
-  flags.containerizers = "docker";
 
-  Try<PID<Slave> > slave = StartSlave(flags);
+  Docker docker("docker");
+
+  MockDockerContainerizer dockerContainer(flags, true, docker);
+
+  Try<PID<Slave> > slave = StartSlave((slave::Containerizer*) &dockerContainer);
   ASSERT_SOME(slave);
 
   MockScheduler sched;
@@ -72,6 +112,8 @@ TEST_F(DockerContainerizerTest, DOCKER_Launch) {
 
   driver.start();
 
+  AWAIT_READY(frameworkId);
+
   AWAIT_READY(offers);
   EXPECT_NE(0u, offers.get().size());
 
@@ -84,13 +126,9 @@ TEST_F(DockerContainerizerTest, DOCKER_Launch) {
   task.mutable_resources()->CopyFrom(offer.resources());
 
   CommandInfo command;
-
-  CommandInfo::ContainerInfo* containerInfo =
-    task.mutable_command()->mutable_container();
-
+  CommandInfo::ContainerInfo* containerInfo = command.mutable_container();
   containerInfo->set_image("docker://busybox");
-
-  command.set_value("sleep 30");
+  command.set_value("sleep 120");
 
   task.mutable_command()->CopyFrom(command);
 
@@ -100,20 +138,35 @@ TEST_F(DockerContainerizerTest, DOCKER_Launch) {
   tasks.push_back(task);
 
   EXPECT_CALL(sched, statusUpdate(&driver, _))
-    .WillOnce(FutureArg<1>(&statusRunning));
+    .WillOnce(FutureArg<1>(&statusRunning))
+    .WillRepeatedly(DoDefault());
 
   driver.launchTasks(offers.get()[0].id(), tasks);
 
-  AWAIT_READY(statusRunning);
-  ASSERT_EQ(TASK_RUNNING, statusRunning.get().state());
+  AWAIT_READY_FOR(statusRunning, Seconds(60));
+  EXPECT_EQ(TASK_RUNNING, statusRunning.get().state());
 
-  Docker docker("docker");
   Future<list<Docker::Container> > containers = docker.ps();
 
   AWAIT_READY(containers);
 
   ASSERT_TRUE(containers.get().size() > 0);
 
+  bool foundContainer = false;
+  string expectedName = "mesos-" + dockerContainer.lastContainerId.value();
+
+  foreach(const Docker::Container& container, containers.get()) {
+    // Docker inspect name contains an extra slash in the beginning
+    if (strings::contains(container.name(), expectedName)) {
+      foundContainer = true;
+      break;
+    }
+  }
+
+  ASSERT_TRUE(foundContainer);
+
+  AWAIT_READY(docker.kill(expectedName));
+
   driver.stop();
   driver.join();
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/286e78bf/src/tests/environment.cpp
----------------------------------------------------------------------
diff --git a/src/tests/environment.cpp b/src/tests/environment.cpp
index 11a08e3..b75c485 100644
--- a/src/tests/environment.cpp
+++ b/src/tests/environment.cpp
@@ -131,17 +131,22 @@ static bool enable(const ::testing::TestInfo& test)
 #endif
 
     if (strings::contains(name, "DOCKER_")) {
-      Docker docker("docker");
-      Try<Nothing> validate = Docker::validateDocker(docker);
+      Docker docker(flags.docker);
+      Try<Nothing> validate = Docker::validate(docker);
       if (validate.isError()) {
-	std::cerr
+        std::cerr
           << "-------------------------------------------------------------\n"
           << "Skipping Docker tests because validation failed\n"
-	  << "[Error] " + validate.error() + "\n"
+          << "[Error] " + validate.error() + "\n"
           << "-------------------------------------------------------------"
           << std::endl;
       }
+
+#ifdef __linux__
+      return user.get() == "root" && !validate.isError();
+#else
       return !validate.isError();
+#endif
     }
 
     // Filter out benchmark tests when we run 'make check'.

http://git-wip-us.apache.org/repos/asf/mesos/blob/286e78bf/src/tests/flags.hpp
----------------------------------------------------------------------
diff --git a/src/tests/flags.hpp b/src/tests/flags.hpp
index a003e7f..189fad9 100644
--- a/src/tests/flags.hpp
+++ b/src/tests/flags.hpp
@@ -61,16 +61,23 @@ public:
 
     path = os::realpath(BUILD_DIR);
     CHECK_SOME(path);
+
     add(&Flags::build_dir,
         "build_dir",
         "Where to find the build directory",
         path.get());
+
+    add(&Flags::docker,
+        "docker",
+        "Where to find docker executable",
+        "docker");
   }
 
   bool verbose;
   bool benchmark;
   std::string source_dir;
   std::string build_dir;
+  std::string docker;
 };
 
 // Global flags for running the tests.


[25/43] git commit: Refactored docker::usage() and docker::run().

Posted by be...@apache.org.
Refactored docker::usage() and docker::run().

Support resources restriction in docker::run().
Support resources display in docker::usage().
Also fixed docker::usage() to check for destroying containers
before calling docker::inspect().


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

Branch: refs/heads/master
Commit: ee998e410b6b37f26aa0f871daf6c6ad571da18c
Parents: 9c94cce
Author: Yifan Gu <gu...@gmail.com>
Authored: Sat Jun 28 16:54:10 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:16 2014 -0700

----------------------------------------------------------------------
 src/docker/docker.cpp                    | 37 +++++++++++++----
 src/docker/docker.hpp                    |  6 ++-
 src/slave/containerizer/docker.cpp       | 42 ++++++++++++++-----
 src/tests/docker_containerizer_tests.cpp | 60 +++++++++++++++++++--------
 src/tests/docker_tests.cpp               |  4 +-
 5 files changed, 111 insertions(+), 38 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/ee998e41/src/docker/docker.cpp
----------------------------------------------------------------------
diff --git a/src/docker/docker.cpp b/src/docker/docker.cpp
index fb7f0e9..8985f55 100644
--- a/src/docker/docker.cpp
+++ b/src/docker/docker.cpp
@@ -89,9 +89,11 @@ Option<pid_t> Docker::Container::pid() const
   map<string, JSON::Value>::const_iterator entry =
     value.as<JSON::Object>().values.find("Pid");
   CHECK(entry != json.values.end());
-  value = entry->second;
-  CHECK(value.is<JSON::Number>());
-  pid_t pid = pid_t(value.as<JSON::Number>().value);
+  // TODO(yifan) reload operator '=' to reuse the value variable above.
+  JSON::Value pidValue = entry->second;
+  CHECK(pidValue.is<JSON::Number>());
+
+  pid_t pid = pid_t(pidValue.as<JSON::Number>().value);
   if (pid == 0) {
     return None();
   }
@@ -101,13 +103,33 @@ Option<pid_t> Docker::Container::pid() const
 Future<Option<int> > Docker::run(
     const string& image,
     const string& command,
-    const string& name) const
+    const string& name,
+    const mesos::Resources& resources) const
 {
-  VLOG(1) << "Running " << path << " run -d --name=" << name << " "
-          << image << " " << command;
+  CHECK(resources.size() != 0);
+
+  string cmd = " run -d";
+
+  // TODO(yifan): Support other resources (e.g. disk, ports).
+  Option<double> cpus = resources.cpus();
+  if (cpus.isSome()) {
+    uint64_t cpuShare =
+      std::max((uint64_t) (CPU_SHARES_PER_CPU * cpus.get()), MIN_CPU_SHARES);
+    cmd += " -c " + stringify(cpuShare);
+  }
+
+  Option<Bytes> mem = resources.mem();
+  if (mem.isSome()) {
+    Bytes memLimit = std::max(mem.get(), MIN_MEMORY);
+    cmd += " -m " + stringify(memLimit.bytes());
+  }
+
+  cmd += " --name=" + name + " " + image + " " + command;
+
+  VLOG(1) << "Running " << path << cmd;
 
   Try<Subprocess> s = subprocess(
-      path + " run -d --name=" + name + " " + image + " " + command,
+      path + cmd,
       Subprocess::PIPE(),
       Subprocess::PIPE(),
       Subprocess::PIPE());
@@ -115,7 +137,6 @@ Future<Option<int> > Docker::run(
   if (s.isError()) {
     return Failure(s.error());
   }
-
   return s.get().status();
 }
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/ee998e41/src/docker/docker.hpp
----------------------------------------------------------------------
diff --git a/src/docker/docker.hpp b/src/docker/docker.hpp
index 89840af..912859c 100644
--- a/src/docker/docker.hpp
+++ b/src/docker/docker.hpp
@@ -30,6 +30,9 @@
 #include <stout/nothing.hpp>
 #include <stout/option.hpp>
 
+#include "mesos/resources.hpp"
+
+
 // Abstraction for working with Docker (modeled on CLI).
 class Docker
 {
@@ -63,7 +66,8 @@ public:
   process::Future<Option<int> > run(
       const std::string& image,
       const std::string& command,
-      const std::string& name) const;
+      const std::string& name,
+      const mesos::Resources& resources) const;
 
   // Performs 'docker kill CONTAINER'.
   process::Future<Option<int> > kill(

http://git-wip-us.apache.org/repos/asf/mesos/blob/ee998e41/src/slave/containerizer/docker.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/docker.cpp b/src/slave/containerizer/docker.cpp
index 38b2a03..7cd2d7d 100644
--- a/src/slave/containerizer/docker.cpp
+++ b/src/slave/containerizer/docker.cpp
@@ -136,7 +136,7 @@ private:
 
   Future<ResourceStatistics> _usage(
     const ContainerID& containerId,
-    const Future<Docker::Container> container);
+    const Docker::Container& container);
 
   // Call back for when the executor exits. This will trigger
   // container destroy.
@@ -510,7 +510,7 @@ Future<bool> DockerContainerizerProcess::launch(
 
   // 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)
+  return docker.run(image, command.value(), name, taskInfo.resources())
     .then(defer(self(),
                 &Self::_launch,
                 containerId,
@@ -634,33 +634,55 @@ Future<ResourceStatistics> DockerContainerizerProcess::usage(
 {
 #ifndef __linux__
   return Failure("Does not support usage() on non-linux platform");
-#endif // __linux__
-
+#else
   if (!promises.contains(containerId)) {
     return Failure("Unknown container: " + stringify(containerId));
   }
 
+  if (destroying.contains(containerId)) {
+    return Failure("Container is being removed: " + stringify(containerId));
+  }
+
   // Construct the Docker container name.
   string name = DOCKER_NAME_PREFIX + stringify(containerId);
   return docker.inspect(name)
     .then(defer(self(), &Self::_usage, containerId, lambda::_1));
+#endif // __linux__
 }
 
 
 Future<ResourceStatistics> DockerContainerizerProcess::_usage(
     const ContainerID& containerId,
-    const Future<Docker::Container> container)
+    const Docker::Container& container)
 {
-  Option<pid_t> pid = container.get().pid();
+  Option<pid_t> pid = container.pid();
   if (pid.isNone()) {
     return Failure("Container is not running");
   }
-  Try<ResourceStatistics> usage =
+
+  // Note that here getting the root pid is enough because
+  // the root process acts as an 'init' process in the docker
+  // container, so no other child processes will escape it.
+  Try<ResourceStatistics> statistics =
     mesos::internal::usage(pid.get(), true, true);
-  if (usage.isError()) {
-    return Failure(usage.error());
+  if (statistics.isError()) {
+    return Failure(statistics.error());
+  }
+
+  ResourceStatistics result = statistics.get();
+
+  // Set the resource allocations.
+  Resources resource = resources[containerId];
+  Option<Bytes> mem = resource.mem();
+  if (mem.isSome()) {
+    result.set_mem_limit_bytes(mem.get().bytes());
+  }
+
+  Option<double> cpus = resource.cpus();
+  if (cpus.isSome()) {
+    result.set_cpus_limit(cpus.get());
   }
-  return usage.get();
+  return result;
 }
 
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/ee998e41/src/tests/docker_containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/docker_containerizer_tests.cpp b/src/tests/docker_containerizer_tests.cpp
index c0b915a..a6cae24 100644
--- a/src/tests/docker_containerizer_tests.cpp
+++ b/src/tests/docker_containerizer_tests.cpp
@@ -24,8 +24,10 @@
 #include "tests/flags.hpp"
 #include "tests/mesos.hpp"
 
-#include "slave/slave.hpp"
 #include "slave/containerizer/docker.hpp"
+#include "slave/slave.hpp"
+#include "slave/state.hpp"
+
 
 using namespace mesos;
 using namespace mesos::internal;
@@ -148,8 +150,6 @@ TEST_F(DockerContainerizerTest, DOCKER_Launch)
 
   task.mutable_command()->CopyFrom(command);
 
-  Future<TaskStatus> statusRunning;
-
   vector<TaskInfo> tasks;
   tasks.push_back(task);
 
@@ -159,14 +159,14 @@ TEST_F(DockerContainerizerTest, DOCKER_Launch)
                     Invoke(&dockerContainerizer,
                            &MockDockerContainerizer::_launch)));
 
+  Future<TaskStatus> statusRunning;
   EXPECT_CALL(sched, statusUpdate(&driver, _))
     .WillOnce(FutureArg<1>(&statusRunning))
     .WillRepeatedly(DoDefault());
 
   driver.launchTasks(offers.get()[0].id(), tasks);
 
-  AWAIT_READY(containerId);
-
+  AWAIT_READY_FOR(containerId, Seconds(60));
   AWAIT_READY_FOR(statusRunning, Seconds(60));
   EXPECT_EQ(TASK_RUNNING, statusRunning.get().state());
 
@@ -206,12 +206,13 @@ TEST_F(DockerContainerizerTest, DOCKER_Usage)
   ASSERT_SOME(master);
 
   slave::Flags flags = CreateSlaveFlags();
+  flags.resources = Option<string>("cpus:2;mem:1024");
 
   Docker docker(tests::flags.docker);
 
   MockDockerContainerizer dockerContainerizer(flags, true, docker);
 
-  Try<PID<Slave> > slave = StartSlave(&dockerContainerizer);
+  Try<PID<Slave> > slave = StartSlave(&dockerContainerizer, flags);
   ASSERT_SOME(slave);
 
   MockScheduler sched;
@@ -245,11 +246,11 @@ TEST_F(DockerContainerizerTest, DOCKER_Usage)
   CommandInfo command;
   CommandInfo::ContainerInfo* containerInfo = command.mutable_container();
   containerInfo->set_image("docker://busybox");
-  command.set_value("sleep 120");
 
-  task.mutable_command()->CopyFrom(command);
+  // Run a CPU intensive command, so we can measure utime and stime later.
+  command.set_value("dd if=/dev/zero of=/dev/null");
 
-  Future<TaskStatus> statusRunning;
+  task.mutable_command()->CopyFrom(command);
 
   vector<TaskInfo> tasks;
   tasks.push_back(task);
@@ -260,26 +261,51 @@ TEST_F(DockerContainerizerTest, DOCKER_Usage)
                     Invoke(&dockerContainerizer,
                            &MockDockerContainerizer::_launch)));
 
+  Future<TaskStatus> statusRunning;
   EXPECT_CALL(sched, statusUpdate(&driver, _))
     .WillOnce(FutureArg<1>(&statusRunning))
     .WillRepeatedly(DoDefault());
 
   driver.launchTasks(offers.get()[0].id(), tasks);
 
-  AWAIT_READY(containerId);
-
+  AWAIT_READY_FOR(containerId, Seconds(60));
   AWAIT_READY_FOR(statusRunning, Seconds(60));
   EXPECT_EQ(TASK_RUNNING, statusRunning.get().state());
 
-  Future<ResourceStatistics> usage =
-    dockerContainerizer.usage(containerId.get());
-  AWAIT_READY(usage);
-  // TODO(yifan): Verify the usage.
+  // Verify the usage.
+  ResourceStatistics statistics;
+  Duration waited = Duration::zero();
+  do {
+    Future<ResourceStatistics> usage =
+      dockerContainerizer.usage(containerId.get());
+    AWAIT_READY(usage);
+
+    statistics = usage.get();
+
+    if (statistics.cpus_user_time_secs() > 0 &&
+        statistics.cpus_system_time_secs() > 0) {
+      break;
+    }
+
+    os::sleep(Milliseconds(200));
+    waited += Milliseconds(200);
+  } while (waited < Seconds(3));
+
+  EXPECT_EQ(2, statistics.cpus_limit());
+  EXPECT_EQ(Gigabytes(1).bytes(), statistics.mem_limit_bytes());
+  EXPECT_LT(0, statistics.cpus_user_time_secs());
+  EXPECT_LT(0, statistics.cpus_system_time_secs());
+
+  Future<containerizer::Termination> termination =
+    dockerContainerizer.wait(containerId.get());
 
   dockerContainerizer.destroy(containerId.get());
 
-  // Usage() should fail again since the container is destroyed.
-  usage = dockerContainerizer.usage(containerId.get());
+  AWAIT_READY(termination);
+
+  // Usage() should fail again since the container is destroyed
+  Future<ResourceStatistics> usage =
+    dockerContainerizer.usage(containerId.get());
   AWAIT_FAILED(usage);
 
   driver.stop();

http://git-wip-us.apache.org/repos/asf/mesos/blob/ee998e41/src/tests/docker_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/docker_tests.cpp b/src/tests/docker_tests.cpp
index 62d5657..b7a7b6f 100644
--- a/src/tests/docker_tests.cpp
+++ b/src/tests/docker_tests.cpp
@@ -60,7 +60,7 @@ TEST(DockerTest, DOCKER_interface)
   }
 
   // Start the container.
-  status = docker.run("busybox", "sleep 120", containerName);
+  status = docker.run("busybox", "sleep 120", containerName, resources);
   AWAIT_READY(status);
   ASSERT_SOME(status.get());
 
@@ -139,7 +139,7 @@ TEST(DockerTest, DOCKER_interface)
   // directly, instead of killing and rm.
   //
   // First, Invoke docker.run()
-  status = docker.run("busybox", "sleep 120", containerName);
+  status = docker.run("busybox", "sleep 120", containerName, resources);
   AWAIT_READY(status);
   ASSERT_SOME(status.get());
 


[08/43] git commit: Added rm() in docker.hpp/cpp to enable users remove containers.

Posted by be...@apache.org.
Added rm() in docker.hpp/cpp to enable users remove containers.


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

Branch: refs/heads/master
Commit: d60cc8358000433bc14011f73d475830e113bbb2
Parents: cad1bbb
Author: Yifan Gu <gu...@gmail.com>
Authored: Tue Jun 24 16:52:48 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:15 2014 -0700

----------------------------------------------------------------------
 src/docker/docker.cpp | 20 ++++++++++++++++++++
 src/docker/docker.hpp |  5 +++++
 2 files changed, 25 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/d60cc835/src/docker/docker.cpp
----------------------------------------------------------------------
diff --git a/src/docker/docker.cpp b/src/docker/docker.cpp
index 7d72ea2..500517c 100644
--- a/src/docker/docker.cpp
+++ b/src/docker/docker.cpp
@@ -111,6 +111,26 @@ Future<Option<int> > Docker::kill(const string& container) const
 }
 
 
+Future<Option<int> > Docker::rm(const string& container, const bool force) const
+{
+  string cmd = force ? " rm -f " : " rm ";
+
+  VLOG(1) << "Running " << path << cmd << container;
+
+  Try<Subprocess> s = subprocess(
+      path + cmd + container,
+      Subprocess::PIPE(),
+      Subprocess::PIPE(),
+      Subprocess::PIPE());
+
+  if (s.isError()) {
+    return Failure(s.error());
+  }
+
+  return s.get().status();
+}
+
+
 Future<Docker::Container> Docker::inspect(const string& container) const
 {
   VLOG(1) << "Running " << path << " inspect " << container;

http://git-wip-us.apache.org/repos/asf/mesos/blob/d60cc835/src/docker/docker.hpp
----------------------------------------------------------------------
diff --git a/src/docker/docker.hpp b/src/docker/docker.hpp
index 6aa25b1..b0d0a3a 100644
--- a/src/docker/docker.hpp
+++ b/src/docker/docker.hpp
@@ -68,6 +68,11 @@ public:
   process::Future<Option<int> > kill(
       const std::string& container) const;
 
+  // Performs 'docker rm (-f) CONTAINER'.
+  process::Future<Option<int> > rm(
+      const std::string& container,
+      const bool force = true) const;
+
   // Performs 'docker inspect CONTAINER'.
   process::Future<Container> inspect(
       const std::string& container) const;


[35/43] git commit: Moved WSTRINGIFY out of a namespace.

Posted by be...@apache.org.
Moved WSTRINGIFY out of a namespace.


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

Branch: refs/heads/master
Commit: 4792825580250b6a5a1fa050e6a2496592bbd2a8
Parents: ddc775a
Author: Benjamin Hindman <be...@gmail.com>
Authored: Wed Jul 9 13:11:35 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:17 2014 -0700

----------------------------------------------------------------------
 src/common/status_utils.hpp                        | 8 --------
 src/health-check/main.cpp                          | 2 --
 src/launcher/executor.cpp                          | 3 ---
 src/master/master.cpp                              | 2 --
 src/slave/containerizer/external_containerizer.cpp | 3 ---
 src/slave/slave.cpp                                | 2 --
 src/tests/script.cpp                               | 2 --
 7 files changed, 22 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/47928255/src/common/status_utils.hpp
----------------------------------------------------------------------
diff --git a/src/common/status_utils.hpp b/src/common/status_utils.hpp
index c51a8c6..8ef012a 100644
--- a/src/common/status_utils.hpp
+++ b/src/common/status_utils.hpp
@@ -24,10 +24,6 @@
 #include <stout/option.hpp>
 #include <stout/stringify.hpp>
 
-namespace mesos {
-namespace internal {
-namespace status {
-
 inline std::string WSTRINGIFY(int status)
 {
   std::string message;
@@ -41,8 +37,4 @@ inline std::string WSTRINGIFY(int status)
   return message;
 }
 
-} // namespace status {
-} // namespace internal {
-} // namespace mesos {
-
 #endif // __STATUS_UTILS_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/47928255/src/health-check/main.cpp
----------------------------------------------------------------------
diff --git a/src/health-check/main.cpp b/src/health-check/main.cpp
index 10d57a0..472bffc 100644
--- a/src/health-check/main.cpp
+++ b/src/health-check/main.cpp
@@ -60,8 +60,6 @@ namespace internal {
 
 using namespace process;
 
-using namespace mesos::internal::status;
-
 class HealthCheckerProcess : public ProtobufProcess<HealthCheckerProcess>
 {
 public:

http://git-wip-us.apache.org/repos/asf/mesos/blob/47928255/src/launcher/executor.cpp
----------------------------------------------------------------------
diff --git a/src/launcher/executor.cpp b/src/launcher/executor.cpp
index 948673e..b096b68 100644
--- a/src/launcher/executor.cpp
+++ b/src/launcher/executor.cpp
@@ -69,9 +69,6 @@ namespace internal {
 
 using namespace process;
 
-using namespace mesos::internal::status;
-
-
 class CommandExecutorProcess : public ProtobufProcess<CommandExecutorProcess>
 {
 public:

http://git-wip-us.apache.org/repos/asf/mesos/blob/47928255/src/master/master.cpp
----------------------------------------------------------------------
diff --git a/src/master/master.cpp b/src/master/master.cpp
index 77f2536..5630680 100644
--- a/src/master/master.cpp
+++ b/src/master/master.cpp
@@ -93,8 +93,6 @@ using process::metrics::Counter;
 
 using memory::shared_ptr;
 
-using namespace mesos::internal::status;
-
 namespace mesos {
 namespace internal {
 namespace master {

http://git-wip-us.apache.org/repos/asf/mesos/blob/47928255/src/slave/containerizer/external_containerizer.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/external_containerizer.cpp b/src/slave/containerizer/external_containerizer.cpp
index 03de1fc..e5726e1 100644
--- a/src/slave/containerizer/external_containerizer.cpp
+++ b/src/slave/containerizer/external_containerizer.cpp
@@ -63,9 +63,6 @@ using tuples::tuple;
 
 using namespace process;
 
-using namespace mesos::internal::status;
-
-
 namespace mesos {
 namespace internal {
 namespace slave {

http://git-wip-us.apache.org/repos/asf/mesos/blob/47928255/src/slave/slave.cpp
----------------------------------------------------------------------
diff --git a/src/slave/slave.cpp b/src/slave/slave.cpp
index 2f39d61..ef921e1 100644
--- a/src/slave/slave.cpp
+++ b/src/slave/slave.cpp
@@ -100,8 +100,6 @@ namespace slave {
 
 using namespace state;
 
-using namespace mesos::internal::status;
-
 Slave::Slave(const slave::Flags& _flags,
              MasterDetector* _detector,
              Containerizer* _containerizer,

http://git-wip-us.apache.org/repos/asf/mesos/blob/47928255/src/tests/script.cpp
----------------------------------------------------------------------
diff --git a/src/tests/script.cpp b/src/tests/script.cpp
index 0d29c6d..3129479 100644
--- a/src/tests/script.cpp
+++ b/src/tests/script.cpp
@@ -42,8 +42,6 @@
 
 using std::string;
 
-using namespace mesos::internal::status;
-
 namespace mesos {
 namespace internal {
 namespace tests {


[17/43] git commit: Renamed variable to be more accurate.

Posted by be...@apache.org.
Renamed variable to be more accurate.

Since we actually have a Docker::Container abstraction I
s/dockerContainer/dockerContainerizer/.


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

Branch: refs/heads/master
Commit: 5d78d0c2cb72b1a4ca6afd583123b2b34b75717a
Parents: 57c1ec2
Author: Benjamin Hindman <be...@gmail.com>
Authored: Sun Jun 29 13:48:19 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:16 2014 -0700

----------------------------------------------------------------------
 src/tests/docker_containerizer_tests.cpp | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/5d78d0c2/src/tests/docker_containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/docker_containerizer_tests.cpp b/src/tests/docker_containerizer_tests.cpp
index 17c4b1c..b716de4 100644
--- a/src/tests/docker_containerizer_tests.cpp
+++ b/src/tests/docker_containerizer_tests.cpp
@@ -93,9 +93,9 @@ TEST_F(DockerContainerizerTest, DOCKER_Launch)
 
   Docker docker(tests::flags.docker);
 
-  MockDockerContainerizer dockerContainer(flags, true, docker);
+  MockDockerContainerizer dockerContainerizer(flags, true, docker);
 
-  Try<PID<Slave> > slave = StartSlave((slave::Containerizer*) &dockerContainer);
+  Try<PID<Slave> > slave = StartSlave(&dockerContainerizer);
   ASSERT_SOME(slave);
 
   MockScheduler sched;
@@ -155,7 +155,7 @@ TEST_F(DockerContainerizerTest, DOCKER_Launch)
 
   bool foundContainer = false;
   string expectedName =
-    slave::DOCKER_NAME_PREFIX + dockerContainer.lastContainerId.value();
+    slave::DOCKER_NAME_PREFIX + dockerContainerizer.lastContainerId.value();
 
   foreach (const Docker::Container& container, containers.get()) {
     // Docker inspect name contains an extra slash in the beginning.
@@ -167,7 +167,7 @@ TEST_F(DockerContainerizerTest, DOCKER_Launch)
 
   ASSERT_TRUE(foundContainer);
 
-  dockerContainer.destroy(dockerContainer.lastContainerId);
+  dockerContainerizer.destroy(dockerContainerizer.lastContainerId);
 
   driver.stop();
   driver.join();
@@ -186,9 +186,9 @@ TEST_F(DockerContainerizerTest, DOCKER_Usage)
 
   Docker docker(tests::flags.docker);
 
-  MockDockerContainerizer dockerContainer(flags, true, docker);
+  MockDockerContainerizer dockerContainerizer(flags, true, docker);
 
-  Try<PID<Slave> > slave = StartSlave((slave::Containerizer*) &dockerContainer);
+  Try<PID<Slave> > slave = StartSlave(&dockerContainerizer);
   ASSERT_SOME(slave);
 
   MockScheduler sched;
@@ -237,7 +237,7 @@ TEST_F(DockerContainerizerTest, DOCKER_Usage)
 
   // Usage() should fail since the container is not launched.
   Future<ResourceStatistics> usage =
-    dockerContainer.usage(dockerContainer.lastContainerId);
+    dockerContainerizer.usage(dockerContainerizer.lastContainerId);
 
   AWAIT_FAILED(usage);
 
@@ -246,14 +246,14 @@ TEST_F(DockerContainerizerTest, DOCKER_Usage)
   AWAIT_READY_FOR(statusRunning, Seconds(60));
   EXPECT_EQ(TASK_RUNNING, statusRunning.get().state());
 
-  usage = dockerContainer.usage(dockerContainer.lastContainerId);
+  usage = dockerContainerizer.usage(dockerContainerizer.lastContainerId);
   AWAIT_READY(usage);
   // TODO(yifan): Verify the usage.
 
-  dockerContainer.destroy(dockerContainer.lastContainerId);
+  dockerContainerizer.destroy(dockerContainerizer.lastContainerId);
 
   // Usage() should fail again since the container is destroyed.
-  usage = dockerContainer.usage(dockerContainer.lastContainerId);
+  usage = dockerContainerizer.usage(dockerContainerizer.lastContainerId);
   AWAIT_FAILED(usage);
 
   driver.stop();


[26/43] git commit: Used the new cgroups helpers when updating Docker container resources.

Posted by be...@apache.org.
Used the new cgroups helpers when updating Docker container resources.

Rather than "prepare" the cgroups for Docker, now we just introspect
and determine the necessary hierarchies and cgroups we need based on
how Docker (or an operator) set up the cgroups mounts.


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

Branch: refs/heads/master
Commit: ddc775aa373f3d25f47bf1973d76df146fc08f2a
Parents: 283665b
Author: Benjamin Hindman <be...@gmail.com>
Authored: Wed Jul 9 11:30:08 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:17 2014 -0700

----------------------------------------------------------------------
 src/slave/containerizer/docker.cpp       | 127 ++++++++++++++++----------
 src/tests/docker_containerizer_tests.cpp |   1 -
 2 files changed, 79 insertions(+), 49 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/ddc775aa/src/slave/containerizer/docker.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/docker.cpp b/src/slave/containerizer/docker.cpp
index 5a68d94..f7cc630 100644
--- a/src/slave/containerizer/docker.cpp
+++ b/src/slave/containerizer/docker.cpp
@@ -144,7 +144,7 @@ private:
   process::Future<Nothing> _update(
       const ContainerID& containerId,
       const Resources& resources,
-      const Future<Docker::Container>& future);
+      const Docker::Container& container);
 
   Future<ResourceStatistics> _usage(
     const ContainerID& containerId,
@@ -181,30 +181,6 @@ private:
 };
 
 
-Try<Nothing> DockerContainerizer::prepareCgroups(const Flags& flags)
-{
-#ifdef __linux__
-  std::vector<string> subsystems;
-  subsystems.push_back("cpu");
-  subsystems.push_back("cpuacct");
-  subsystems.push_back("memory");
-
-  foreach (const string& subsystem, subsystems) {
-    // We're assuming docker is under cgroup directory "docker".
-    Try<string> hierarchy =
-      cgroups::prepare(flags.cgroups_hierarchy, subsystem, "docker");
-
-    if (hierarchy.isError()) {
-      return Error(
-          "Failed to prepare cgroup hierarchy " + flags.cgroups_hierarchy +
-          " subsystem '" + subsystem + "' for Docker: " + hierarchy.error());
-    }
-  }
-#endif // __linux__
-  return Nothing();
-}
-
-
 Try<DockerContainerizer*> DockerContainerizer::create(
     const Flags& flags,
     bool local)
@@ -215,11 +191,6 @@ Try<DockerContainerizer*> DockerContainerizer::create(
     return Error(validation.error());
   }
 
-  Try<Nothing> prepare = prepareCgroups(flags);
-  if (prepare.isError()) {
-    return Error(prepare.error());
-  }
-
   return new DockerContainerizer(flags, local, docker);
 }
 
@@ -594,7 +565,7 @@ Future<bool> DockerContainerizerProcess::_launch(
   // Docker containers not created by Mesos).
   // TODO(benh): Get full path to 'docker'.
   string override =
-    "docker wait " + DOCKER_NAME_PREFIX + stringify(containerId);
+    flags.docker + " wait " + DOCKER_NAME_PREFIX + stringify(containerId);
 
   Try<Subprocess> s = subprocess(
       executorInfo.command().value() + " --override " + override,
@@ -690,21 +661,64 @@ Future<Nothing> DockerContainerizerProcess::update(
 Future<Nothing> DockerContainerizerProcess::_update(
     const ContainerID& containerId,
     const Resources& _resources,
-    const Future<Docker::Container>& future)
+    const Docker::Container& container)
 {
 #ifdef __linux__
-  const string& id = path::join("docker", future.get().id());
+  // Determine the the cgroups hierarchies where the 'cpu' and
+  // 'memory' subsystems are mounted (they may be the same). Note that
+  // we make these static so we can reuse the result for subsequent
+  // calls.
+  static Result<string> cpuHierarchy = cgroups::hierarchy("cpu");
+  static Result<string> memoryHierarchy = cgroups::hierarchy("memory");
+
+  if (cpuHierarchy.isError()) {
+    return Failure("Failed to determine the cgroup hierarchy "
+                   "where the 'cpu' subsystem is mounted: " +
+                   cpuHierarchy.error());
+  }
+
+  if (memoryHierarchy.isError()) {
+    return Failure("Failed to determine the cgroup hierarchy "
+                   "where the 'memory' subsystem is mounted: " +
+                   memoryHierarchy.error());
+  }
 
-  // Update CPU shares.
-  if (_resources.cpus().isSome()) {
+  // We need to find the cgroup(s) this container is currently running
+  // in for both the hierarchy with the 'cpu' subsystem attached and
+  // the hierarchy with the 'memory' subsystem attached so we can
+  // update the proper cgroup control files.
+
+  // First check that this container still appears to be running.
+  Option<pid_t> pid = container.pid();
+  if (pid.isNone()) {
+    return Nothing();
+  }
+
+  // Determine the cgroup for the 'cpu' subsystem (based on the
+  // container's pid).
+  Result<string> cpuCgroup = cgroups::cpu::cgroup(pid.get());
+
+  if (cpuCgroup.isError()) {
+    return Failure("Failed to determine cgroup for the 'cpu' subsystem: " +
+                   cpuCgroup.error());
+  } else if (cpuCgroup.isNone()) {
+    LOG(WARNING)
+      << "Container " << containerId
+      << " does not appear to be a member of a cgroup "
+      << "where the 'cpu' subsystem is mounted";
+  }
+
+  // And update the CPU shares (if applicable).
+  if (cpuHierarchy.isSome() &&
+      cpuCgroup.isSome() &&
+      _resources.cpus().isSome()) {
     double cpuShares = _resources.cpus().get();
 
     uint64_t shares =
       std::max((uint64_t) (CPU_SHARES_PER_CPU * cpuShares), MIN_CPU_SHARES);
 
     Try<Nothing> write =
-      cgroups::cpu::shares(
-          path::join(flags.cgroups_hierarchy, "cpu"), id, shares);
+      cgroups::cpu::shares(cpuHierarchy.get(), cpuCgroup.get(), shares);
 
     if (write.isError()) {
       return Failure("Failed to update 'cpu.shares': " + write.error());
@@ -712,24 +726,38 @@ Future<Nothing> DockerContainerizerProcess::_update(
 
     LOG(INFO)
       << "Updated 'cpu.shares' to " << shares
+      << " at " << path::join(cpuHierarchy.get(), cpuCgroup.get())
       << " for container " << containerId;
   }
 
-  // Update Memory.
-  if (_resources.mem().isSome()) {
+  // Now determine the cgroup for the 'memory' subsystem.
+  Result<string> memoryCgroup = cgroups::memory::cgroup(pid.get());
+
+  if (memoryCgroup.isError()) {
+    return Failure("Failed to determine cgroup for the 'memory' subsystem: " +
+                   memoryCgroup.error());
+  } else if (memoryCgroup.isNone()) {
+    LOG(WARNING)
+      << "Container " << containerId
+      << " does not appear to be a member of a cgroup "
+      << "where the 'memory' subsystem is mounted";
+  }
+
+  // And update the memory limits (if applicable).
+  if (memoryHierarchy.isSome() &&
+      memoryCgroup.isSome() &&
+      _resources.mem().isSome()) {
     Bytes mem = _resources.mem().get();
     Bytes limit = std::max(mem, MIN_MEMORY);
 
-    std::string memHierarchy =
-      path::join(flags.cgroups_hierarchy, "memory");
-
     // Always set the soft limit.
     Try<Nothing> write =
-      cgroups::memory::soft_limit_in_bytes(memHierarchy, id, limit);
+      cgroups::memory::soft_limit_in_bytes(
+          memoryHierarchy.get(), memoryCgroup.get(), limit);
 
     if (write.isError()) {
       return Failure("Failed to set 'memory.soft_limit_in_bytes': " +
-          write.error());
+                     write.error());
     }
 
     LOG(INFO)
@@ -738,24 +766,27 @@ Future<Nothing> DockerContainerizerProcess::_update(
 
     // Read the existing limit.
     Try<Bytes> currentLimit =
-      cgroups::memory::limit_in_bytes(memHierarchy, id);
+      cgroups::memory::limit_in_bytes(
+          memoryHierarchy.get(), memoryCgroup.get());
 
     if (currentLimit.isError()) {
       return Failure("Failed to read 'memory.limit_in_bytes': " +
-          currentLimit.error());
+                     currentLimit.error());
     }
 
     // Only update if new limit is higher.
     if (limit > currentLimit.get()) {
-      write = cgroups::memory::limit_in_bytes(memHierarchy, id, limit);
+      write = cgroups::memory::limit_in_bytes(
+          memoryHierarchy.get(), memoryCgroup.get(), limit);
 
       if (write.isError()) {
         return Failure("Failed to set 'memory.limit_in_bytes': " +
-            write.error());
+                       write.error());
       }
 
       LOG(INFO)
         << "Updated 'memory.limit_in_bytes' to " << limit
+        << " at " << path::join(memoryHierarchy.get(), memoryCgroup.get())
         << " for container " << containerId;
     }
   }

http://git-wip-us.apache.org/repos/asf/mesos/blob/ddc775aa/src/tests/docker_containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/docker_containerizer_tests.cpp b/src/tests/docker_containerizer_tests.cpp
index f772eb0..3d02e62 100644
--- a/src/tests/docker_containerizer_tests.cpp
+++ b/src/tests/docker_containerizer_tests.cpp
@@ -64,7 +64,6 @@ public:
       const Docker& docker)
     : DockerContainerizer(flags, local, docker)
   {
-    DockerContainerizer::prepareCgroups(flags);
     EXPECT_CALL(*this, launch(_, _, _, _, _, _, _, _))
       .WillRepeatedly(Invoke(this, &MockDockerContainerizer::_launch));
   }


[38/43] git commit: Checked for mounted 'cpu' cgroups subsystem in Docker::validate.

Posted by be...@apache.org.
Checked for mounted 'cpu' cgroups subsystem in Docker::validate.


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

Branch: refs/heads/master
Commit: 0bf53b48d22571311c55c699799f99f375c601d6
Parents: 19781ef
Author: Benjamin Hindman <be...@gmail.com>
Authored: Wed Jul 9 15:25:27 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:17 2014 -0700

----------------------------------------------------------------------
 src/docker/docker.cpp | 12 ++++++++++++
 1 file changed, 12 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/0bf53b48/src/docker/docker.cpp
----------------------------------------------------------------------
diff --git a/src/docker/docker.cpp b/src/docker/docker.cpp
index 8985f55..1142ef3 100644
--- a/src/docker/docker.cpp
+++ b/src/docker/docker.cpp
@@ -31,6 +31,8 @@
 
 #include "docker/docker.hpp"
 
+#include "linux/cgroups.hpp"
+
 #include "slave/containerizer/isolators/cgroups/cpushare.hpp"
 #include "slave/containerizer/isolators/cgroups/mem.hpp"
 
@@ -46,6 +48,16 @@ using std::vector;
 
 Try<Nothing> Docker::validate(const Docker &docker)
 {
+  // Make sure that cgroups are mounted, and at least the 'cpu'
+  // subsystem is attached.
+  Result<string> hierarchy = cgroups::hierarchy("cpu");
+
+  if (hierarchy.isNone()) {
+    return Error("Failed to find a mounted cgroups hierarchy "
+                 "for the 'cpu' subsystem, you probably need "
+                 "to mount cgroups manually!");
+  }
+
   Future<std::string> info = docker.info();
 
   if (!info.await(Seconds(3))) {


[21/43] git commit: Refactored docker::ps(), added 'prefix' option to inspect only interested containers.

Posted by be...@apache.org.
Refactored docker::ps(), added 'prefix' option to inspect only interested containers.


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

Branch: refs/heads/master
Commit: d5f9c58de7ed77162df518bb0a206a94b83a28c0
Parents: 78d8e40
Author: Yifan Gu <gu...@gmail.com>
Authored: Wed Jul 2 18:28:54 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:16 2014 -0700

----------------------------------------------------------------------
 src/docker/docker.cpp                    | 21 ++++++++++++++++-----
 src/docker/docker.hpp                    |  7 +++++--
 src/slave/containerizer/docker.cpp       |  2 +-
 src/tests/docker_containerizer_tests.cpp |  3 ++-
 4 files changed, 24 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/d5f9c58d/src/docker/docker.cpp
----------------------------------------------------------------------
diff --git a/src/docker/docker.cpp b/src/docker/docker.cpp
index 1a2d1d2..c46df07 100644
--- a/src/docker/docker.cpp
+++ b/src/docker/docker.cpp
@@ -281,7 +281,9 @@ Future<Docker::Container> Docker::_inspect(const Subprocess& s)
 }
 
 
-Future<list<Docker::Container> > Docker::ps(const bool all) const
+Future<list<Docker::Container> > Docker::ps(
+    const bool all,
+    const Option<string>& prefix) const
 {
   string cmd = all ? " ps -a" : " ps";
 
@@ -298,13 +300,14 @@ Future<list<Docker::Container> > Docker::ps(const bool all) const
   }
 
   return s.get().status()
-    .then(lambda::bind(&Docker::_ps, *this, s.get()));
+    .then(lambda::bind(&Docker::_ps, *this, s.get(), prefix));
 }
 
 
 Future<list<Docker::Container> > Docker::_ps(
     const Docker& docker,
-    const Subprocess& s)
+    const Subprocess& s,
+    const Option<string>& prefix)
 {
   // Check the exit status of 'docker ps'.
   CHECK_READY(s.status());
@@ -338,13 +341,21 @@ Future<list<Docker::Container> > Docker::_ps(
   list<Future<Docker::Container> > futures;
 
   foreach (const string& line, lines) {
-    // Inspect the container.
-    futures.push_back(docker.inspect(strings::split(line, " ")[0]));
+    // Inspect the containers that we are interested in depending on
+    // whether or not a 'prefix' was specified.
+    vector<string> columns = strings::split(strings::trim(line), " ");
+    string name = columns[columns.size() - 1];
+    if (prefix.isNone()) {
+      futures.push_back(docker.inspect(name));
+    } else if (strings::startsWith(name, prefix.get())) {
+      futures.push_back(docker.inspect(name));
+    }
   }
 
   return collect(futures);
 }
 
+
 Future<std::string> Docker::info() const
 {
   std::string cmd = path + " info";

http://git-wip-us.apache.org/repos/asf/mesos/blob/d5f9c58d/src/docker/docker.hpp
----------------------------------------------------------------------
diff --git a/src/docker/docker.hpp b/src/docker/docker.hpp
index 9d2205c..89840af 100644
--- a/src/docker/docker.hpp
+++ b/src/docker/docker.hpp
@@ -26,6 +26,7 @@
 #include <process/subprocess.hpp>
 
 #include <stout/json.hpp>
+#include <stout/none.hpp>
 #include <stout/nothing.hpp>
 #include <stout/option.hpp>
 
@@ -87,7 +88,8 @@ public:
 
   // Performs 'docker ps (-a)'.
   process::Future<std::list<Container> > ps(
-      const bool all = false) const;
+      const bool all = false,
+      const Option<std::string>& prefix = None()) const;
 
   process::Future<std::string> info() const;
 
@@ -97,7 +99,8 @@ private:
       const process::Subprocess& s);
   static process::Future<std::list<Container> > _ps(
       const Docker& docker,
-      const process::Subprocess& s);
+      const process::Subprocess& s,
+      const Option<std::string>& prefix);
   static process::Future<Option<int> > _killAndRm(
       const Docker& docker,
       const std::string& container,

http://git-wip-us.apache.org/repos/asf/mesos/blob/d5f9c58d/src/slave/containerizer/docker.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/docker.cpp b/src/slave/containerizer/docker.cpp
index 44d4d3d..38b2a03 100644
--- a/src/slave/containerizer/docker.cpp
+++ b/src/slave/containerizer/docker.cpp
@@ -411,7 +411,7 @@ Future<Nothing> DockerContainerizerProcess::recover(
 
   // Get the list of all Docker containers (running and exited) in
   // order to remove any orphans.
-  return docker.ps(true)
+  return docker.ps(true, DOCKER_NAME_PREFIX)
     .then(defer(self(), &Self::_recover, lambda::_1));
 }
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/d5f9c58d/src/tests/docker_containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/docker_containerizer_tests.cpp b/src/tests/docker_containerizer_tests.cpp
index 5e41529..c0b915a 100644
--- a/src/tests/docker_containerizer_tests.cpp
+++ b/src/tests/docker_containerizer_tests.cpp
@@ -170,7 +170,8 @@ TEST_F(DockerContainerizerTest, DOCKER_Launch)
   AWAIT_READY_FOR(statusRunning, Seconds(60));
   EXPECT_EQ(TASK_RUNNING, statusRunning.get().state());
 
-  Future<list<Docker::Container> > containers = docker.ps(true);
+  Future<list<Docker::Container> > containers =
+    docker.ps(true, slave::DOCKER_NAME_PREFIX);
 
   AWAIT_READY(containers);
 


[11/43] git commit: Added Docker::ps() "all" option.

Posted by be...@apache.org.
Added Docker::ps() "all" option.


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

Branch: refs/heads/master
Commit: cad1bbb9c2ee0c51ed193f08e0d874951ed881dd
Parents: bd8f063
Author: Yifan Gu <gu...@gmail.com>
Authored: Tue Jun 24 18:09:25 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:15 2014 -0700

----------------------------------------------------------------------
 src/docker/docker.cpp | 8 +++++---
 src/docker/docker.hpp | 5 +++--
 2 files changed, 8 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/cad1bbb9/src/docker/docker.cpp
----------------------------------------------------------------------
diff --git a/src/docker/docker.cpp b/src/docker/docker.cpp
index 1e4f62f..7d72ea2 100644
--- a/src/docker/docker.cpp
+++ b/src/docker/docker.cpp
@@ -217,12 +217,14 @@ Future<Docker::Container> Docker::_inspect(const Subprocess& s)
 }
 
 
-Future<list<Docker::Container> > Docker::ps() const
+Future<list<Docker::Container> > Docker::ps(const bool all) const
 {
-  VLOG(1) << "Running " << path << " ps";
+  string cmd = all ? " ps -a" : " ps";
+
+  VLOG(1) << "Running " << path << cmd;
 
   Try<Subprocess> s = subprocess(
-      path + " ps",
+      path + cmd,
       Subprocess::PIPE(),
       Subprocess::PIPE(),
       Subprocess::PIPE());

http://git-wip-us.apache.org/repos/asf/mesos/blob/cad1bbb9/src/docker/docker.hpp
----------------------------------------------------------------------
diff --git a/src/docker/docker.hpp b/src/docker/docker.hpp
index 264dc79..6aa25b1 100644
--- a/src/docker/docker.hpp
+++ b/src/docker/docker.hpp
@@ -72,8 +72,9 @@ public:
   process::Future<Container> inspect(
       const std::string& container) const;
 
-  // Performs 'docker ps'.
-  process::Future<std::list<Container> > ps() const;
+  // Performs 'docker ps (-a)'.
+  process::Future<std::list<Container> > ps(
+      const bool all = true) const;
 
 private:
   // Continuations.


[29/43] git commit: Improved failure handling of DockerContainerizer.

Posted by be...@apache.org.
Improved failure handling of DockerContainerizer.

Two improvements:

(1) Checked the exit status of 'docker.run()' in order to fail early.

(2) Improved the override on mesos-executor to return the exit status
of the Docker container rather than the exit status of doing 'docker
wait' (which will be 0 even if the container exited with -1 because
'docker wait' correctly "waited" and it prints the container's exit
status to stdout).


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

Branch: refs/heads/master
Commit: 2f2cf5b84ec2fb273345a4f2949bd0a714d50f56
Parents: c530a5b
Author: Benjamin Hindman <be...@gmail.com>
Authored: Wed Jul 9 13:13:32 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:17 2014 -0700

----------------------------------------------------------------------
 src/slave/containerizer/docker.cpp | 52 ++++++++++++++++++++++++---------
 1 file changed, 39 insertions(+), 13 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/2f2cf5b8/src/slave/containerizer/docker.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/docker.cpp b/src/slave/containerizer/docker.cpp
index f7cc630..2aceb35 100644
--- a/src/slave/containerizer/docker.cpp
+++ b/src/slave/containerizer/docker.cpp
@@ -28,6 +28,8 @@
 #include <stout/hashset.hpp>
 #include <stout/os.hpp>
 
+#include "common/status_utils.hpp"
+
 #include "docker/docker.hpp"
 
 #ifdef __linux__
@@ -129,7 +131,8 @@ private:
       const std::string& directory,
       const SlaveID& slaveId,
       const PID<Slave>& slavePid,
-      bool checkpoint);
+      bool checkpoint,
+      const Option<int>& status);
 
   void _destroy(
       const ContainerID& containerId,
@@ -398,10 +401,10 @@ Future<Nothing> DockerContainerizerProcess::recover(
         CHECK_SOME(run.get().forkedPid);
         pid_t pid = run.get().forkedPid.get();
 
-        Future<Option<int > > status = process::reap(pid);
+        statuses[containerId] = process::reap(pid);
 
-        statuses[containerId] = status;
-        status.onAny(defer(self(), &Self::reaped, containerId));
+        statuses[containerId]
+          .onAny(defer(self(), &Self::reaped, containerId));
 
         if (pids.containsValue(pid)) {
           // This should (almost) never occur. There is the
@@ -531,7 +534,8 @@ Future<bool> DockerContainerizerProcess::launch(
                 directory,
                 slaveId,
                 slavePid,
-                checkpoint))
+                checkpoint,
+                lambda::_1))
     .onFailed(defer(self(), &Self::destroy, containerId, false));
 }
 
@@ -543,8 +547,17 @@ Future<bool> DockerContainerizerProcess::_launch(
     const string& directory,
     const SlaveID& slaveId,
     const PID<Slave>& slavePid,
-    bool checkpoint)
+    bool checkpoint,
+    const Option<int>& status)
 {
+  // Try and see if the run failed.
+  if (status.isSome() && status.get() != 0) {
+    // Best effort kill and remove the container just in case.
+    docker.killAndRm(DOCKER_NAME_PREFIX + stringify(containerId));
+    return Failure("Failed to run the container (" +
+                   WSTRINGIFY(status.get()) + ")");
+  }
+
   // Prepare environment variables for the executor.
   map<string, string> env = executorEnvironment(
       executorInfo,
@@ -562,10 +575,13 @@ Future<bool> DockerContainerizerProcess::_launch(
 
   // Construct the mesos-executor "override" to do a 'docker wait'
   // using the "name" we gave the container (to distinguish it from
-  // Docker containers not created by Mesos).
-  // TODO(benh): Get full path to 'docker'.
+  // Docker containers not created by Mesos). Note, however, that we
+  // don't want the exit status from 'docker wait' but rather the exit
+  // status from the container, hence the use of /bin/bash.
   string override =
-    flags.docker + " wait " + DOCKER_NAME_PREFIX + stringify(containerId);
+    "/bin/bash -c 'exit `" +
+    flags.docker + " wait " + DOCKER_NAME_PREFIX + stringify(containerId) +
+    "`'";
 
   Try<Subprocess> s = subprocess(
       executorInfo.command().value() + " --override " + override,
@@ -621,10 +637,10 @@ Future<bool> DockerContainerizerProcess::_launch(
   }
 
   // And finally watch for when the executor gets reaped.
-  Future<Option<int> > status = process::reap(s.get().pid());
+  statuses[containerId] = process::reap(s.get().pid());
 
-  statuses.put(containerId, status);
-  status.onAny(defer(self(), &Self::reaped, containerId));
+  statuses[containerId]
+    .onAny(defer(self(), &Self::reaped, containerId));
 
   return true;
 }
@@ -915,7 +931,17 @@ void DockerContainerizerProcess::_destroy(
     return;
   }
 
-  statuses.get(containerId).get()
+  // It's possible we've been asked to destroy a container that we
+  // aren't actually reaping any status because we failed to start the
+  // container in the first place (e.g., because we returned a Failure
+  // in 'launch' or '_launch'). In this case, we just put a None
+  // status in place so that the rest of the destroy workflow
+  // completes.
+  if (!statuses.contains(containerId)) {
+    statuses[containerId] = None();
+  }
+
+  statuses[containerId]
     .onAny(defer(self(), &Self::__destroy, containerId, killed, lambda::_1));
 }
 


[04/43] git commit: Bug fixes and cleanups in Docker abstraction.

Posted by be...@apache.org.
Bug fixes and cleanups in Docker abstraction.


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

Branch: refs/heads/master
Commit: fa400fef337900a97ccc77906dbf80b7d0fbd967
Parents: 86a9769
Author: Benjamin Hindman <be...@gmail.com>
Authored: Mon Jun 23 09:32:57 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:15 2014 -0700

----------------------------------------------------------------------
 src/docker/docker.cpp | 107 +++++++++++++++++++++++++++++++++++++++------
 src/docker/docker.hpp |  17 +++++--
 2 files changed, 107 insertions(+), 17 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/fa400fef/src/docker/docker.cpp
----------------------------------------------------------------------
diff --git a/src/docker/docker.cpp b/src/docker/docker.cpp
index db31ba3..976a660 100644
--- a/src/docker/docker.cpp
+++ b/src/docker/docker.cpp
@@ -4,9 +4,12 @@
 #include <stout/lambda.hpp>
 #include <stout/strings.hpp>
 
+#include <stout/result.hpp>
+
+#include <stout/os/read.hpp>
+
 #include <process/check.hpp>
 #include <process/collect.hpp>
-#include <process/io.hpp>
 
 #include "docker/docker.hpp"
 
@@ -18,6 +21,16 @@ using std::string;
 using std::vector;
 
 
+string Docker::Container::id() const
+{
+  map<string, JSON::Value>::const_iterator entry =
+    json.values.find("Id");
+  CHECK(entry != json.values.end());
+  JSON::Value value = entry->second;
+  CHECK(value.is<JSON::String>());
+  return value.as<JSON::String>().value;
+}
+
 string Docker::Container::name() const
 {
   map<string, JSON::Value>::const_iterator entry =
@@ -29,10 +42,16 @@ string Docker::Container::name() const
 }
 
 
-Future<Option<int> > Docker::run(const string& image) const
+Future<Option<int> > Docker::run(
+    const string& image,
+    const string& command,
+    const string& name) const
 {
+  VLOG(1) << "Running " << path << " run --name=" << name << " "
+          << image << " " << command;
+
   Try<Subprocess> s = subprocess(
-      path + " run " + image,
+      path + " run --name=" + name + " " + image + " " + command,
       Subprocess::PIPE(),
       Subprocess::PIPE(),
       Subprocess::PIPE());
@@ -47,6 +66,8 @@ Future<Option<int> > Docker::run(const string& image) const
 
 Future<Option<int> > Docker::kill(const string& container) const
 {
+  VLOG(1) << "Running " << path << " kill " << container;
+
   Try<Subprocess> s = subprocess(
       path + " kill " + container,
       Subprocess::PIPE(),
@@ -63,6 +84,8 @@ Future<Option<int> > Docker::kill(const string& container) const
 
 Future<Docker::Container> Docker::inspect(const string& container) const
 {
+  VLOG(1) << "Running " << path << " inspect " << container;
+
   Try<Subprocess> s = subprocess(
       path + " inspect " + container,
       Subprocess::PIPE(),
@@ -70,7 +93,6 @@ Future<Docker::Container> Docker::inspect(const string& container) const
       Subprocess::PIPE());
 
   if (s.isError()) {
-    // TODO(benh): Include stdout and stderr in error message.
     return Failure(s.error());
   }
 
@@ -79,24 +101,73 @@ Future<Docker::Container> Docker::inspect(const string& container) const
 }
 
 
+namespace os {
+
+inline Result<std::string> read(
+    int fd,
+    Option<size_t> size = None(),
+    size_t chunk = 16 * 4096)
+{
+  std::string result;
+
+  while (size.isNone() || result.size() < size.get()) {
+    char buffer[chunk];
+    ssize_t length = ::read(fd, buffer, chunk);
+
+    if (length < 0) {
+      // TODO(bmahler): Handle a non-blocking fd? (EAGAIN, EWOULDBLOCK)
+      if (errno == EINTR) {
+        continue;
+      }
+      return ErrnoError();
+    } else if (length == 0) {
+      // Reached EOF before expected! Only return as much data as
+      // available or None if we haven't read anything yet.
+      if (result.size() > 0) {
+        return result;
+      }
+      return None();
+    }
+
+    result.append(buffer, length);
+  }
+
+  return result;
+}
+
+} // namespace os {
+
+
 Future<Docker::Container> Docker::_inspect(const Subprocess& s)
 {
-  // Check the exit status of 'docker ps'.
+  // Check the exit status of 'docker inspect'.
   CHECK_READY(s.status());
 
   Option<int> status = s.status().get();
 
   if (status.isSome() && status.get() != 0) {
-    // TODO(benh): Include stdout and stderr in error message.
-    return Failure("Failed to do 'docker ps'");
+    // TODO(benh): Include stderr in error message.
+    Result<string> read = os::read(s.err().get());
+    return Failure("Failed to do 'docker inspect': " +
+                   (read.isSome()
+                    ? read.get()
+                    : " exited with status " + stringify(status.get())));
   }
 
   // Read to EOF.
   // TODO(benh): Read output asynchronously.
   CHECK_SOME(s.out());
-  string output = io::read(s.out().get()).get();
+  Result<string> output = os::read(s.out().get());
+
+  if (output.isError()) {
+    // TODO(benh): Include stderr in error message.
+    return Failure("Failed to read output: " + output.error());
+  } else if (output.isNone()) {
+    // TODO(benh): Include stderr in error message.
+    return Failure("No output available");
+  }
 
-  Try<JSON::Array> parse = JSON::parse<JSON::Array>(output);
+  Try<JSON::Array> parse = JSON::parse<JSON::Array>(output.get());
 
   if (parse.isError()) {
     return Failure("Failed to parse JSON: " + parse.error());
@@ -119,6 +190,8 @@ Future<Docker::Container> Docker::_inspect(const Subprocess& s)
 
 Future<list<Docker::Container> > Docker::ps() const
 {
+  VLOG(1) << "Running " << path << " ps";
+
   Try<Subprocess> s = subprocess(
       path + " ps",
       Subprocess::PIPE(),
@@ -144,16 +217,24 @@ Future<list<Docker::Container> > Docker::_ps(
   Option<int> status = s.status().get();
 
   if (status.isSome() && status.get() != 0) {
-    // TODO(benh): Include stdout and stderr in error message.
+    // TODO(benh): Include stderr in error message.
     return Failure("Failed to do 'docker ps'");
   }
 
   // Read to EOF.
   // TODO(benh): Read output asynchronously.
   CHECK_SOME(s.out());
-  string output = io::read(s.out().get()).get();
+  Result<string> output = os::read(s.out().get());
+
+  if (output.isError()) {
+    // TODO(benh): Include stderr in error message.
+    return Failure("Failed to read output: " + output.error());
+  } else if (output.isNone()) {
+    // TODO(benh): Include stderr in error message.
+    return Failure("No output available");
+  }
 
-  vector<string> lines = strings::split(output, "\n");
+  vector<string> lines = strings::tokenize(output.get(), "\n");
 
   // Skip the header.
   CHECK_NE(0, lines.size());
@@ -163,7 +244,7 @@ Future<list<Docker::Container> > Docker::_ps(
 
   foreach (const string& line, lines) {
     // Inspect the container.
-    futures.push_back(docker.inspect(strings::split(line, "\n")[0]));
+    futures.push_back(docker.inspect(strings::split(line, " ")[0]));
   }
 
   return collect(futures);

http://git-wip-us.apache.org/repos/asf/mesos/blob/fa400fef/src/docker/docker.hpp
----------------------------------------------------------------------
diff --git a/src/docker/docker.hpp b/src/docker/docker.hpp
index 26d6ec3..3bed71d 100644
--- a/src/docker/docker.hpp
+++ b/src/docker/docker.hpp
@@ -37,6 +37,9 @@ public:
   public:
     Container(const JSON::Object& json) : json(json) {}
 
+    // Returns the ID of the container.
+    std::string id() const;
+
     // Returns the name of the container.
     std::string name() const;
 
@@ -48,20 +51,26 @@ public:
   Docker(const std::string& path) : path(path) {}
 
   // Performs 'docker run IMAGE'.
-  process::Future<Option<int> > run(const std::string& image) const;
+  process::Future<Option<int> > run(
+      const std::string& image,
+      const std::string& command,
+      const std::string& name) const;
 
   // Performs 'docker kill CONTAINER'.
-  process::Future<Option<int> > kill(const std::string& container) const;
+  process::Future<Option<int> > kill(
+      const std::string& container) const;
 
   // Performs 'docker inspect CONTAINER'.
-  process::Future<Container> inspect(const std::string& container) const;
+  process::Future<Container> inspect(
+      const std::string& container) const;
 
   // Performs 'docker ps'.
   process::Future<std::list<Container> > ps() const;
 
 private:
   // Continuations.
-  static process::Future<Container> _inspect(const process::Subprocess& s);
+  static process::Future<Container> _inspect(
+      const process::Subprocess& s);
   static process::Future<std::list<Container> > _ps(
       const Docker& docker,
       const process::Subprocess& s);


[41/43] git commit: Added DockerContainerizer kill task test.

Posted by be...@apache.org.
Added DockerContainerizer kill task test.


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

Branch: refs/heads/master
Commit: 19781efdaf04fda8ba5eb6cb6068ee74c010b771
Parents: f3d6d77
Author: Benjamin Hindman <be...@gmail.com>
Authored: Wed Jul 9 15:11:44 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:17 2014 -0700

----------------------------------------------------------------------
 src/tests/docker_containerizer_tests.cpp | 105 +++++++++++++++++++++++++-
 1 file changed, 103 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/19781efd/src/tests/docker_containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/docker_containerizer_tests.cpp b/src/tests/docker_containerizer_tests.cpp
index 2d1a287..9a780e7 100644
--- a/src/tests/docker_containerizer_tests.cpp
+++ b/src/tests/docker_containerizer_tests.cpp
@@ -221,6 +221,108 @@ TEST_F(DockerContainerizerTest, DOCKER_Launch)
 }
 
 
+TEST_F(DockerContainerizerTest, DOCKER_Kill)
+{
+  Try<PID<Master> > master = StartMaster();
+  ASSERT_SOME(master);
+
+  slave::Flags flags = CreateSlaveFlags();
+
+  Docker docker(tests::flags.docker);
+
+  MockDockerContainerizer dockerContainerizer(flags, true, docker);
+
+  Try<PID<Slave> > slave = StartSlave(&dockerContainerizer);
+  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);
+  EXPECT_NE(0u, offers.get().size());
+
+  const Offer& offer = offers.get()[0];
+
+  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;
+  CommandInfo::ContainerInfo* containerInfo = command.mutable_container();
+  containerInfo->set_image("docker://busybox");
+  command.set_value("sleep 120");
+
+  task.mutable_command()->CopyFrom(command);
+
+  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;
+  EXPECT_CALL(sched, statusUpdate(&driver, _))
+    .WillOnce(FutureArg<1>(&statusRunning));
+
+  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());
+
+  Future<TaskStatus> statusKilled;
+  EXPECT_CALL(sched, statusUpdate(&driver, _))
+    .WillOnce(FutureArg<1>(&statusKilled));
+
+  driver.killTask(task.task_id());
+
+  AWAIT_READY(statusKilled);
+  EXPECT_EQ(TASK_KILLED, statusKilled.get().state());
+
+  Future<list<Docker::Container> > containers =
+    docker.ps(true, slave::DOCKER_NAME_PREFIX);
+
+  AWAIT_READY(containers);
+
+  bool foundContainer = false;
+  string expectedName = slave::DOCKER_NAME_PREFIX + containerId.get().value();
+
+  foreach (const Docker::Container& container, containers.get()) {
+    // Docker inspect name contains an extra slash in the beginning.
+    if (strings::contains(container.name(), expectedName)) {
+      foundContainer = true;
+      break;
+    }
+  }
+
+  ASSERT_FALSE(foundContainer);
+
+  driver.stop();
+  driver.join();
+
+  Shutdown();
+}
+
+
 // This test tests DockerContainerizer::usage().
 TEST_F(DockerContainerizerTest, DOCKER_Usage)
 {
@@ -391,8 +493,6 @@ TEST_F(DockerContainerizerTest, DOCKER_Update)
 
   task.mutable_command()->CopyFrom(command);
 
-  Future<TaskStatus> statusRunning;
-
   vector<TaskInfo> tasks;
   tasks.push_back(task);
 
@@ -402,6 +502,7 @@ TEST_F(DockerContainerizerTest, DOCKER_Update)
          Invoke(&dockerContainerizer,
                 &MockDockerContainerizer::_launch)));
 
+  Future<TaskStatus> statusRunning;
   EXPECT_CALL(sched, statusUpdate(&driver, _))
     .WillOnce(FutureArg<1>(&statusRunning))
     .WillRepeatedly(DoDefault());


[32/43] git commit: Revised comments to reflect DockerContainerizer implementation.

Posted by be...@apache.org.
Revised comments to reflect DockerContainerizer implementation.


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

Branch: refs/heads/master
Commit: c7f77126e20dc0f1d5a26b0f0d6bcd361bda9640
Parents: 2caf7b9
Author: Benjamin Hindman <be...@gmail.com>
Authored: Mon Jul 14 23:22:21 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:17 2014 -0700

----------------------------------------------------------------------
 src/slave/containerizer/docker.cpp | 20 ++++++++++++--------
 1 file changed, 12 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/c7f77126/src/slave/containerizer/docker.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/docker.cpp b/src/slave/containerizer/docker.cpp
index 7d3549c..294b4c2 100644
--- a/src/slave/containerizer/docker.cpp
+++ b/src/slave/containerizer/docker.cpp
@@ -1055,15 +1055,19 @@ void DockerContainerizerProcess::destroy(
   LOG(INFO) << "Destroying container '" << containerId << "'";
 
   // Do a 'docker rm -f' which we'll then find out about in '_wait'
-  // after the mesos-executor exits because it's doing a 'docker wait'
-  // (via --override).
+  // after we've reaped either the container's root process (in the
+  // event that we had just launched a container for an executor) or
+  // the mesos-executor (in the case we launched a container for a
+  // task). As a reminder, the mesos-executor exits because it's doing
+  // a 'docker wait' on the container using the --override flag of
+  // mesos-executor.
   //
-  // NOTE: We might not actually have a mesos-executor running (which
-  // we could check by looking if 'containerId' is a key in
-  // 'statuses') but if that is the case then we're doing a destroy
-  // because we failed to launch the mesos-executor (see defer at
-  // bottom of 'launch') so no need to do anything after a successful
-  // 'docker rm -f'.
+  // NOTE: We might not actually have a container or mesos-executor
+  // running (which we could check by looking if 'containerId' is a
+  // key in 'statuses'). If that is the case then we're doing a
+  // destroy because we failed to launch (see defer at bottom of
+  // 'launch'). We try and destroy regardless for now, just to be
+  // safe.
 
   // TODO(benh): Retry 'docker rm -f' if it failed but the container
   // still exists (asynchronously).


[16/43] git commit: Used 'flags.docker' in tests.

Posted by be...@apache.org.
Used 'flags.docker' in tests.

And some other minor style cleanups.


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

Branch: refs/heads/master
Commit: 109296f1365eff9176af29c14ff90b9e206de4bd
Parents: 1f994fd
Author: Benjamin Hindman <be...@gmail.com>
Authored: Sun Jun 29 13:36:27 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:16 2014 -0700

----------------------------------------------------------------------
 src/tests/docker_containerizer_tests.cpp | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/109296f1/src/tests/docker_containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/docker_containerizer_tests.cpp b/src/tests/docker_containerizer_tests.cpp
index 3941d5a..cdf925d 100644
--- a/src/tests/docker_containerizer_tests.cpp
+++ b/src/tests/docker_containerizer_tests.cpp
@@ -21,6 +21,7 @@
 
 #include <process/future.hpp>
 
+#include "tests/flags.hpp"
 #include "tests/mesos.hpp"
 
 #include "slave/slave.hpp"
@@ -66,7 +67,7 @@ public:
     const process::PID<Slave>& slavePid,
     bool checkpoint)
   {
-    // Keeping the last launched container id
+    // Keeping the last launched container id.
     lastContainerId = containerId;
     return slave::DockerContainerizer::launch(
              containerId,
@@ -90,7 +91,7 @@ TEST_F(DockerContainerizerTest, DOCKER_Launch)
 
   slave::Flags flags = CreateSlaveFlags();
 
-  Docker docker("docker");
+  Docker docker(tests::flags.docker);
 
   MockDockerContainerizer dockerContainer(flags, true, docker);
 
@@ -155,8 +156,8 @@ TEST_F(DockerContainerizerTest, DOCKER_Launch)
   bool foundContainer = false;
   string expectedName = "mesos-" + dockerContainer.lastContainerId.value();
 
-  foreach(const Docker::Container& container, containers.get()) {
-    // Docker inspect name contains an extra slash in the beginning
+  foreach (const Docker::Container& container, containers.get()) {
+    // Docker inspect name contains an extra slash in the beginning.
     if (strings::contains(container.name(), expectedName)) {
       foundContainer = true;
       break;
@@ -173,7 +174,8 @@ TEST_F(DockerContainerizerTest, DOCKER_Launch)
   Shutdown();
 }
 
-// This test tests DockerContainerizer::usage()
+
+// This test tests DockerContainerizer::usage().
 TEST_F(DockerContainerizerTest, DOCKER_Usage)
 {
   Try<PID<Master> > master = StartMaster();
@@ -181,7 +183,7 @@ TEST_F(DockerContainerizerTest, DOCKER_Usage)
 
   slave::Flags flags = CreateSlaveFlags();
 
-  Docker docker("docker");
+  Docker docker(tests::flags.docker);
 
   MockDockerContainerizer dockerContainer(flags, true, docker);
 
@@ -249,7 +251,7 @@ TEST_F(DockerContainerizerTest, DOCKER_Usage)
 
   dockerContainer.destroy(dockerContainer.lastContainerId);
 
-  // Usage() should fail again since the container is destroyed
+  // Usage() should fail again since the container is destroyed.
   usage = dockerContainer.usage(dockerContainer.lastContainerId);
   AWAIT_FAILED(usage);
 


[22/43] git commit: Added docker::killAndRm() to performs 'docker kill && docker rm (-f)'

Posted by be...@apache.org.
Added docker::killAndRm() to performs 'docker kill && docker rm (-f)'

This will be faster than doing 'docker rm -f' on a running container.


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

Branch: refs/heads/master
Commit: 78d8e402c66d9a561e9e4ba75e26a94149dad8cc
Parents: ded3558
Author: Yifan Gu <gu...@gmail.com>
Authored: Thu Jul 3 12:06:40 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:16 2014 -0700

----------------------------------------------------------------------
 src/docker/docker.cpp              | 26 ++++++++++++++++++++++++--
 src/docker/docker.hpp              | 12 ++++++++++++
 src/slave/containerizer/docker.cpp |  4 ++--
 3 files changed, 38 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/78d8e402/src/docker/docker.cpp
----------------------------------------------------------------------
diff --git a/src/docker/docker.cpp b/src/docker/docker.cpp
index 0283b67..1a2d1d2 100644
--- a/src/docker/docker.cpp
+++ b/src/docker/docker.cpp
@@ -133,7 +133,9 @@ Future<Option<int> > Docker::kill(const string& container) const
 }
 
 
-Future<Option<int> > Docker::rm(const string& container, const bool force) const
+Future<Option<int> > Docker::rm(
+    const string& container,
+    const bool force) const
 {
   string cmd = force ? " rm -f " : " rm ";
 
@@ -153,6 +155,26 @@ Future<Option<int> > Docker::rm(const string& container, const bool force) const
 }
 
 
+Future<Option<int> > Docker::killAndRm(const string& container) const
+{
+  return kill(container)
+    .then(lambda::bind(Docker::_killAndRm, *this, container, lambda::_1));
+}
+
+
+Future<Option<int> > Docker::_killAndRm(
+    const Docker& docker,
+    const string& container,
+    const Option<int>& status)
+{
+  // If 'kill' fails, then do a 'rm -f'.
+  if (status.isNone()) {
+    return docker.rm(container, true);
+  }
+  return docker.rm(container);
+}
+
+
 Future<Docker::Container> Docker::inspect(const string& container) const
 {
   VLOG(1) << "Running " << path << " inspect " << container;
@@ -276,7 +298,7 @@ Future<list<Docker::Container> > Docker::ps(const bool all) const
   }
 
   return s.get().status()
-    .then(lambda::bind(&Docker::_ps, Docker(path), s.get()));
+    .then(lambda::bind(&Docker::_ps, *this, s.get()));
 }
 
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/78d8e402/src/docker/docker.hpp
----------------------------------------------------------------------
diff --git a/src/docker/docker.hpp b/src/docker/docker.hpp
index 2957e92..9d2205c 100644
--- a/src/docker/docker.hpp
+++ b/src/docker/docker.hpp
@@ -73,6 +73,14 @@ public:
       const std::string& container,
       const bool force = false) const;
 
+  // Performs 'docker kill && docker rm'
+  // if 'docker kill' fails, then will do a 'docker rm -f'.
+  //
+  // TODO(yifan): Depreciate this when the docker provides
+  // something like 'docker rm --kill'.
+  process::Future<Option<int> > killAndRm(
+      const std::string& container) const;
+
   // Performs 'docker inspect CONTAINER'.
   process::Future<Container> inspect(
       const std::string& container) const;
@@ -90,6 +98,10 @@ private:
   static process::Future<std::list<Container> > _ps(
       const Docker& docker,
       const process::Subprocess& s);
+  static process::Future<Option<int> > _killAndRm(
+      const Docker& docker,
+      const std::string& container,
+      const Option<int>& status);
 
   const std::string path;
 };

http://git-wip-us.apache.org/repos/asf/mesos/blob/78d8e402/src/slave/containerizer/docker.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/docker.cpp b/src/slave/containerizer/docker.cpp
index 5026082..44d4d3d 100644
--- a/src/slave/containerizer/docker.cpp
+++ b/src/slave/containerizer/docker.cpp
@@ -438,7 +438,7 @@ Future<Nothing> DockerContainerizerProcess::_recover(
     if (!statuses.keys().contains(id.get())) {
       // TODO(benh): Retry 'docker rm -f' if it failed but the container
       // still exists (asynchronously).
-      docker.rm(container.id(), true);
+      docker.killAndRm(container.id());
     }
   }
 
@@ -706,7 +706,7 @@ void DockerContainerizerProcess::destroy(
 
   // TODO(benh): Retry 'docker rm -f' if it failed but the container
   // still exists (asynchronously).
-  docker.rm(DOCKER_NAME_PREFIX + stringify(containerId), true)
+  docker.killAndRm(DOCKER_NAME_PREFIX + stringify(containerId))
     .onAny(defer(self(), &Self::_destroy, containerId, killed, lambda::_1));
 }
 


[14/43] git commit: Refactored Docker::Container::pid() to return an Option.

Posted by be...@apache.org.
Refactored Docker::Container::pid() to return an Option.

Safer to check for 'None' than 0. Also fixed a compilation bug
comparing signed vs unsigned values with CHECK_NE.


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

Branch: refs/heads/master
Commit: 38e4752af9a8900883f1650f98e15d9903aa17c8
Parents: 5d78d0c
Author: Benjamin Hindman <be...@gmail.com>
Authored: Sun Jun 29 17:51:38 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:16 2014 -0700

----------------------------------------------------------------------
 src/docker/docker.cpp              | 14 +++++++++-----
 src/docker/docker.hpp              |  6 +++---
 src/slave/containerizer/docker.cpp |  6 +++---
 3 files changed, 15 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/38e4752a/src/docker/docker.cpp
----------------------------------------------------------------------
diff --git a/src/docker/docker.cpp b/src/docker/docker.cpp
index 070279e..0652a53 100644
--- a/src/docker/docker.cpp
+++ b/src/docker/docker.cpp
@@ -55,7 +55,7 @@ string Docker::Container::name() const
   return value.as<JSON::String>().value;
 }
 
-pid_t Docker::Container::pid() const
+Option<pid_t> Docker::Container::pid() const
 {
   map<string, JSON::Value>::const_iterator state =
     json.values.find("State");
@@ -66,9 +66,13 @@ pid_t Docker::Container::pid() const
   map<string, JSON::Value>::const_iterator entry =
     value.as<JSON::Object>().values.find("Pid");
   CHECK(entry != json.values.end());
-  JSON::Value pid = entry->second;
-  CHECK(pid.is<JSON::Number>());
-  return pid_t(pid.as<JSON::Number>().value);
+  value = entry->second;
+  CHECK(value.is<JSON::Number>());
+  pid_t pid = pid_t(value.as<JSON::Number>().value);
+  if (pid == 0) {
+    return None();
+  }
+  return pid;
 }
 
 Future<Option<int> > Docker::run(
@@ -288,7 +292,7 @@ Future<list<Docker::Container> > Docker::_ps(
   vector<string> lines = strings::tokenize(output.get(), "\n");
 
   // Skip the header.
-  CHECK_NE(0, lines.size());
+  CHECK(!lines.empty());
   lines.erase(lines.begin());
 
   list<Future<Docker::Container> > futures;

http://git-wip-us.apache.org/repos/asf/mesos/blob/38e4752a/src/docker/docker.hpp
----------------------------------------------------------------------
diff --git a/src/docker/docker.hpp b/src/docker/docker.hpp
index 56b6c61..6643036 100644
--- a/src/docker/docker.hpp
+++ b/src/docker/docker.hpp
@@ -47,9 +47,9 @@ public:
     // Returns the name of the container.
     std::string name() const;
 
-    // Returns the Pid of the container.
-    // Note: If it returns 0, it means the container is not running.
-    pid_t pid() const;
+    // Returns the Pid of the container, or None if the container is
+    // not running.
+    Option<pid_t> pid() const;
 
   private:
     JSON::Object json; // JSON returned from 'docker inspect'.

http://git-wip-us.apache.org/repos/asf/mesos/blob/38e4752a/src/slave/containerizer/docker.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/docker.cpp b/src/slave/containerizer/docker.cpp
index 87510fa..24255e6 100644
--- a/src/slave/containerizer/docker.cpp
+++ b/src/slave/containerizer/docker.cpp
@@ -650,12 +650,12 @@ Future<ResourceStatistics> DockerContainerizerProcess::_usage(
     const ContainerID& containerId,
     const Future<Docker::Container> container)
 {
-  pid_t pid = container.get().pid();
-  if (pid == 0) {
+  Option<pid_t> pid = container.get().pid();
+  if (pid.isNone()) {
     return Failure("Container is not running");
   }
   Try<ResourceStatistics> usage =
-    mesos::internal::usage(pid, true, true);
+    mesos::internal::usage(pid.get(), true, true);
   if (usage.isError()) {
     return Failure(usage.error());
   }


[02/43] git commit: Changed docker.kill() to docker.rm().

Posted by be...@apache.org.
Changed docker.kill() to docker.rm().

Since we actually want to destroy the container, so we need
to delete the container after killing it. We can do this by
calling docker.rm(), which by default does a 'docker rm -f'.


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

Branch: refs/heads/master
Commit: 1bb4bd7c6c3aab4d5c698f411e48ceba986f1fce
Parents: 286e78b
Author: Yifan Gu <gu...@gmail.com>
Authored: Fri Jun 27 01:04:19 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:15 2014 -0700

----------------------------------------------------------------------
 src/slave/containerizer/docker.cpp       | 14 +++++++-------
 src/tests/docker_containerizer_tests.cpp |  2 +-
 2 files changed, 8 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/1bb4bd7c/src/slave/containerizer/docker.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/docker.cpp b/src/slave/containerizer/docker.cpp
index 851b663..3c7f810 100644
--- a/src/slave/containerizer/docker.cpp
+++ b/src/slave/containerizer/docker.cpp
@@ -425,11 +425,11 @@ Future<Nothing> DockerContainerizerProcess::_recover(
             << stringify(id.get()) << "' has been orphaned";
 
     // Check if we're watching an executor for this container ID and
-    // if not, kill the Docker container.
+    // if not, rm -f the Docker container.
     if (!statuses.keys().contains(id.get())) {
-      // TODO(benh): Retry 'docker kill' if it failed but the container
+      // TODO(benh): Retry 'docker rm -f' if it failed but the container
       // still exists (asynchronously).
-      docker.kill(container.id());
+      docker.rm(container.id());
     }
   }
 
@@ -658,7 +658,7 @@ void DockerContainerizerProcess::destroy(
 
   LOG(INFO) << "Destroying container '" << containerId << "'";
 
-  // Do a 'docker kill' which we'll then find out about in '_wait'
+  // Do a 'docker rm -f' which we'll then find out about in '_wait'
   // after the mesos-executor exits because it's doing a 'docker wait'
   // (via --override).
   //
@@ -667,11 +667,11 @@ void DockerContainerizerProcess::destroy(
   // 'statuses') but if that is the case then we're doing a destroy
   // because we failed to launch the mesos-executor (see defer at
   // bottom of 'launch') so no need to do anything after a successful
-  // 'docker kill'.
+  // 'docker rm -f'.
 
-  // TODO(benh): Retry 'docker kill' if it failed but the container
+  // TODO(benh): Retry 'docker rm -f' if it failed but the container
   // still exists (asynchronously).
-  docker.kill(DOCKER_NAME_PREFIX + stringify(containerId))
+  docker.rm(DOCKER_NAME_PREFIX + stringify(containerId))
     .onAny(defer(self(), &Self::_destroy, containerId, killed, lambda::_1));
 }
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/1bb4bd7c/src/tests/docker_containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/docker_containerizer_tests.cpp b/src/tests/docker_containerizer_tests.cpp
index 636187f..a6ba9da 100644
--- a/src/tests/docker_containerizer_tests.cpp
+++ b/src/tests/docker_containerizer_tests.cpp
@@ -165,7 +165,7 @@ TEST_F(DockerContainerizerTest, DOCKER_Launch)
 
   ASSERT_TRUE(foundContainer);
 
-  AWAIT_READY(docker.kill(expectedName));
+  dockerContainer.destroy(dockerContainer.lastContainerId);
 
   driver.stop();
   driver.join();


[43/43] git commit: Fixed tests for new launch/wait containerizer semantics.

Posted by be...@apache.org.
Fixed tests for new launch/wait containerizer semantics.


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

Branch: refs/heads/master
Commit: 0ba6b89b7421d426709af5bf89fac138cf0ca63e
Parents: 48e7d4a
Author: Benjamin Hindman <be...@gmail.com>
Authored: Mon Aug 4 15:05:09 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:18 2014 -0700

----------------------------------------------------------------------
 src/tests/slave_recovery_tests.cpp |  5 -----
 src/tests/slave_tests.cpp          | 13 +++++++------
 2 files changed, 7 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/0ba6b89b/src/tests/slave_recovery_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/slave_recovery_tests.cpp b/src/tests/slave_recovery_tests.cpp
index 9a14b4c..8d48aed 100644
--- a/src/tests/slave_recovery_tests.cpp
+++ b/src/tests/slave_recovery_tests.cpp
@@ -3211,11 +3211,6 @@ TYPED_TEST(SlaveRecoveryTest, RestartBeforeContainerizerLaunch)
     .WillOnce(DoAll(FutureSatisfy(&launch),
                     Return(Future<bool>())));
 
-  // Ensure that wait doesn't complete so that containerizer doesn't
-  // return a failure on 'wait' due to the pending launch.
-  EXPECT_CALL(*containerizer1, wait(_))
-    .WillOnce(Return(Future<containerizer::Termination>()));
-
   // No status update should be sent for now.
   EXPECT_CALL(sched, statusUpdate(_, _))
     .Times(0);

http://git-wip-us.apache.org/repos/asf/mesos/blob/0ba6b89b/src/tests/slave_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/slave_tests.cpp b/src/tests/slave_tests.cpp
index 069fbda..432d8a2 100644
--- a/src/tests/slave_tests.cpp
+++ b/src/tests/slave_tests.cpp
@@ -299,16 +299,15 @@ TEST_F(SlaveTest, MesosExecutorWithOverride)
   vector<TaskInfo> tasks;
   tasks.push_back(task);
 
-  // Expect the launch but don't do anything as we'll be launching the
-  // executor ourselves manually below.
+  // Expect the launch and just assume it was sucessful since we'll be
+  // launching the executor ourselves manually below.
   Future<Nothing> launch;
   EXPECT_CALL(containerizer, launch(_, _, _, _, _, _, _))
     .WillOnce(DoAll(FutureSatisfy(&launch),
-                    Return(Future<bool>())));
+                    Return(true)));
 
-  // Expect wait after launch is called. wait() will fail if not
-  // intercepted here as the container will never be registered within
-  // the TestContainerizer when launch() is intercepted above.
+  // Expect wait after launch is called but don't return anything
+  // until after we've finished everything below.
   Future<Nothing> wait;
   process::Promise<containerizer::Termination> promise;
   EXPECT_CALL(containerizer, wait(_))
@@ -364,6 +363,8 @@ TEST_F(SlaveTest, MesosExecutorWithOverride)
   AWAIT_READY(status2);
   ASSERT_EQ(TASK_FINISHED, status2.get().state());
 
+  AWAIT_READY(wait);
+
   containerizer::Termination termination;
   termination.set_killed(false);
   termination.set_message("Killed executor");


[36/43] git commit: Fix docker usage test

Posted by be...@apache.org.
Fix docker usage test


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

Branch: refs/heads/master
Commit: f3d6d77712c9153b1afbd334dd2a8083c52cfb9b
Parents: b5727ce
Author: Timothy Chen <tn...@gmail.com>
Authored: Wed Jul 9 01:38:34 2014 +0000
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:17 2014 -0700

----------------------------------------------------------------------
 src/tests/docker_containerizer_tests.cpp | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/f3d6d777/src/tests/docker_containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/docker_containerizer_tests.cpp b/src/tests/docker_containerizer_tests.cpp
index c3eee4d..2d1a287 100644
--- a/src/tests/docker_containerizer_tests.cpp
+++ b/src/tests/docker_containerizer_tests.cpp
@@ -66,6 +66,9 @@ public:
   {
     EXPECT_CALL(*this, launch(_, _, _, _, _, _, _, _))
       .WillRepeatedly(Invoke(this, &MockDockerContainerizer::_launch));
+
+    EXPECT_CALL(*this, update(_, _))
+      .WillRepeatedly(Invoke(this, &MockDockerContainerizer::_update));
   }
 
   MOCK_METHOD8(
@@ -80,6 +83,12 @@ public:
           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(
@@ -102,6 +111,15 @@ public:
         slavePid,
         checkpoint);
   }
+
+  process::Future<Nothing> _update(
+      const ContainerID& containerId,
+      const Resources& resources)
+  {
+    return DockerContainerizer::update(
+        containerId,
+        resources);
+  }
 };
 
 
@@ -265,6 +283,10 @@ TEST_F(DockerContainerizerTest, DOCKER_Usage)
                     Invoke(&dockerContainerizer,
                            &MockDockerContainerizer::_launch)));
 
+  // We ignore all update calls to prevent resizing cgroup limits.
+  EXPECT_CALL(dockerContainerizer, update(_, _))
+    .WillRepeatedly(Return(Nothing()));
+
   Future<TaskStatus> statusRunning;
   EXPECT_CALL(sched, statusUpdate(&driver, _))
     .WillOnce(FutureArg<1>(&statusRunning))


[19/43] git commit: Be more mock friendly in MockDockerContainerizer.

Posted by be...@apache.org.
Be more mock friendly in MockDockerContainerizer.

Rather than having the MockDockerContainerizer store local variables
when certain events occur we can make the tests store what ever things
they need by mocking out the methods.


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

Branch: refs/heads/master
Commit: 5e7c4a4d36d93caf0cecb0c5a55432978923dde7
Parents: a54dda8
Author: Benjamin Hindman <be...@gmail.com>
Authored: Tue Jul 1 07:45:53 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:16 2014 -0700

----------------------------------------------------------------------
 src/tests/docker_containerizer_tests.cpp | 103 ++++++++++++++++----------
 1 file changed, 64 insertions(+), 39 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/5e7c4a4d/src/tests/docker_containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/docker_containerizer_tests.cpp b/src/tests/docker_containerizer_tests.cpp
index 274eee7..5e41529 100644
--- a/src/tests/docker_containerizer_tests.cpp
+++ b/src/tests/docker_containerizer_tests.cpp
@@ -45,42 +45,57 @@ using std::string;
 
 using testing::_;
 using testing::DoDefault;
-using testing::Eq;
+using testing::Invoke;
 using testing::Return;
 
 class DockerContainerizerTest : public MesosTest {};
 
-class MockDockerContainerizer : public slave::DockerContainerizer {
+class MockDockerContainerizer : public DockerContainerizer {
 public:
   MockDockerContainerizer(
-    const slave::Flags& flags,
-    bool local,
-    const Docker& docker) : DockerContainerizer(flags, local, docker) {}
-
-  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 process::PID<Slave>& slavePid,
-    bool checkpoint)
+      const slave::Flags& flags,
+      bool local,
+      const Docker& docker)
+    : DockerContainerizer(flags, local, docker)
   {
-    // Keeping the last launched container id.
-    lastContainerId = containerId;
-    return slave::DockerContainerizer::launch(
-             containerId,
-             taskInfo,
-             executorInfo,
-             directory,
-             user,
-             slaveId,
-             slavePid,
-             checkpoint);
+    EXPECT_CALL(*this, launch(_, _, _, _, _, _, _, _))
+      .WillRepeatedly(Invoke(this, &MockDockerContainerizer::_launch));
   }
 
-  ContainerID lastContainerId;
+  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));
+
+  // 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);
+  }
 };
 
 
@@ -138,12 +153,20 @@ TEST_F(DockerContainerizerTest, DOCKER_Launch)
   vector<TaskInfo> tasks;
   tasks.push_back(task);
 
+  Future<ContainerID> containerId;
+  EXPECT_CALL(dockerContainerizer, launch(_, _, _, _, _, _, _, _))
+    .WillOnce(DoAll(FutureArg<0>(&containerId),
+                    Invoke(&dockerContainerizer,
+                           &MockDockerContainerizer::_launch)));
+
   EXPECT_CALL(sched, statusUpdate(&driver, _))
     .WillOnce(FutureArg<1>(&statusRunning))
     .WillRepeatedly(DoDefault());
 
   driver.launchTasks(offers.get()[0].id(), tasks);
 
+  AWAIT_READY(containerId);
+
   AWAIT_READY_FOR(statusRunning, Seconds(60));
   EXPECT_EQ(TASK_RUNNING, statusRunning.get().state());
 
@@ -154,8 +177,7 @@ TEST_F(DockerContainerizerTest, DOCKER_Launch)
   ASSERT_TRUE(containers.get().size() > 0);
 
   bool foundContainer = false;
-  string expectedName =
-    slave::DOCKER_NAME_PREFIX + dockerContainerizer.lastContainerId.value();
+  string expectedName = slave::DOCKER_NAME_PREFIX + containerId.get().value();
 
   foreach (const Docker::Container& container, containers.get()) {
     // Docker inspect name contains an extra slash in the beginning.
@@ -167,7 +189,7 @@ TEST_F(DockerContainerizerTest, DOCKER_Launch)
 
   ASSERT_TRUE(foundContainer);
 
-  dockerContainerizer.destroy(dockerContainerizer.lastContainerId);
+  dockerContainerizer.destroy(containerId.get());
 
   driver.stop();
   driver.join();
@@ -231,29 +253,32 @@ TEST_F(DockerContainerizerTest, DOCKER_Usage)
   vector<TaskInfo> tasks;
   tasks.push_back(task);
 
+  Future<ContainerID> containerId;
+  EXPECT_CALL(dockerContainerizer, launch(_, _, _, _, _, _, _, _))
+    .WillOnce(DoAll(FutureArg<0>(&containerId),
+                    Invoke(&dockerContainerizer,
+                           &MockDockerContainerizer::_launch)));
+
   EXPECT_CALL(sched, statusUpdate(&driver, _))
     .WillOnce(FutureArg<1>(&statusRunning))
     .WillRepeatedly(DoDefault());
 
-  // Usage() should fail since the container is not launched.
-  Future<ResourceStatistics> usage =
-    dockerContainerizer.usage(dockerContainerizer.lastContainerId);
-
-  AWAIT_FAILED(usage);
-
   driver.launchTasks(offers.get()[0].id(), tasks);
 
+  AWAIT_READY(containerId);
+
   AWAIT_READY_FOR(statusRunning, Seconds(60));
   EXPECT_EQ(TASK_RUNNING, statusRunning.get().state());
 
-  usage = dockerContainerizer.usage(dockerContainerizer.lastContainerId);
+  Future<ResourceStatistics> usage =
+    dockerContainerizer.usage(containerId.get());
   AWAIT_READY(usage);
   // TODO(yifan): Verify the usage.
 
-  dockerContainerizer.destroy(dockerContainerizer.lastContainerId);
+  dockerContainerizer.destroy(containerId.get());
 
   // Usage() should fail again since the container is destroyed.
-  usage = dockerContainerizer.usage(dockerContainerizer.lastContainerId);
+  usage = dockerContainerizer.usage(containerId.get());
   AWAIT_FAILED(usage);
 
   driver.stop();


[34/43] git commit: Replaced the docker test executor with a smaller one.

Posted by be...@apache.org.
Replaced the docker test executor with a smaller one.


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

Branch: refs/heads/master
Commit: 233e2d49376e5de3afacb38e6edf09347c869fb2
Parents: c7f7712
Author: Yifan Gu <gu...@gmail.com>
Authored: Wed Jul 23 13:51:00 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:17 2014 -0700

----------------------------------------------------------------------
 src/tests/docker_containerizer_tests.cpp        |  2 +-
 src/tests/environment.cpp                       | 33 +-------------------
 .../mesos_test_executor_docker_image/Dockerfile | 16 ----------
 .../mesos_test_executor_docker_image/install.sh |  6 ----
 4 files changed, 2 insertions(+), 55 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/233e2d49/src/tests/docker_containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/docker_containerizer_tests.cpp b/src/tests/docker_containerizer_tests.cpp
index 1f5bc60..84324b0 100644
--- a/src/tests/docker_containerizer_tests.cpp
+++ b/src/tests/docker_containerizer_tests.cpp
@@ -229,7 +229,7 @@ TEST_F(DockerContainerizerTest, DOCKER_Launch_Executor)
   executorInfo.mutable_executor_id()->CopyFrom(executorId);
   CommandInfo command;
   command.set_value("test-executor");
-  command.mutable_container()->set_image("docker:///mesos/test-executor");
+  command.mutable_container()->set_image("docker:///mesosphere/test-executor");
   executorInfo.mutable_command()->CopyFrom(command);
 
   task.mutable_executor()->CopyFrom(executorInfo);

http://git-wip-us.apache.org/repos/asf/mesos/blob/233e2d49/src/tests/environment.cpp
----------------------------------------------------------------------
diff --git a/src/tests/environment.cpp b/src/tests/environment.cpp
index b1c70e7..6c80fa3 100644
--- a/src/tests/environment.cpp
+++ b/src/tests/environment.cpp
@@ -144,38 +144,7 @@ static bool enable(const ::testing::TestInfo& test)
       }
 
 #ifdef __linux__
-      if (user.get() == "root" && !validate.isError()) {
-        // Install docker test executor image for testing launching
-        // executor in docker image.
-	Try<process::Subprocess> install = 
-          process::subprocess(
-	      path::join(
-                flags.source_dir,
-		"src",
-                "tests",
-                "mesos_test_executor_docker_image",
-                "install.sh"));
-
-        if (install.isError()) {
-	  std::cerr
-            << "Unable to launch test executor install script: "
-	    << install.error()
-            << std::endl;
-          return false;
-        }
-
-	process::Future<Option<int> > status = install.get().status();
-        status.await(Minutes(2));
-
-	if (!status.isReady() || !status.get().isSome() || status.get() != 0) {
-	  std::cerr << "Unable to install test executor";
-          return false;
-	}
-
-        return true;
-      }
-
-      return false;
+      return user.get() == "root" && !validate.isError();
 #else
       return !validate.isError();
 #endif

http://git-wip-us.apache.org/repos/asf/mesos/blob/233e2d49/src/tests/mesos_test_executor_docker_image/Dockerfile
----------------------------------------------------------------------
diff --git a/src/tests/mesos_test_executor_docker_image/Dockerfile b/src/tests/mesos_test_executor_docker_image/Dockerfile
deleted file mode 100644
index 8ecc374..0000000
--- a/src/tests/mesos_test_executor_docker_image/Dockerfile
+++ /dev/null
@@ -1,16 +0,0 @@
-FROM stackbrew/ubuntu:13.10
-MAINTAINER Timothy Chen <tn...@apache.org>
-
-ADD http://downloads.mesosphere.io/master/ubuntu/13.10/mesos-test-executor.deb /tmp/mesos.deb
-
-RUN ["env", "DEBIAN_FRONTEND=noninteractive", "apt-get", "update"]
-RUN ["env", "DEBIAN_FRONTEND=noninteractive", "apt-get", "install", "-y", "--fix-missing", "--force-yes", "libsasl2-2", "libcurl3"]
-RUN ["env", "DEBIAN_FRONTEND=noninteractive", "apt-get", "install", "-y", "--fix-missing", "--force-yes", "default-jre-headless"]
-
-RUN ["env", "DEBIAN_FRONTEND=noninteractive", "dpkg", "-i", "/tmp/mesos.deb"]
-RUN ["bash", "-c", "echo manual > /etc/init/mesos-master.override"]
-RUN ["bash", "-c", "echo manual > /etc/init/mesos-slave.override"]
-
-RUN ["rm", "-rf", "/tmp/mesos.deb"]
-
-CMD ["true"]

http://git-wip-us.apache.org/repos/asf/mesos/blob/233e2d49/src/tests/mesos_test_executor_docker_image/install.sh
----------------------------------------------------------------------
diff --git a/src/tests/mesos_test_executor_docker_image/install.sh b/src/tests/mesos_test_executor_docker_image/install.sh
deleted file mode 100755
index dcec4e0..0000000
--- a/src/tests/mesos_test_executor_docker_image/install.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/usr/bin/env bash
-
-docker images | cut -d" " -f1 | grep -q mesos/test-executor
-if [ $? -ne 0 ]; then
-    docker build -t mesos/test-executor `dirname $0`
-fi


[13/43] git commit: Added docker_tests to test the docker abstraction.

Posted by be...@apache.org.
Added docker_tests to test the docker abstraction.


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

Branch: refs/heads/master
Commit: ded355832eb12540740c6b16e758896855d99db6
Parents: 965e29a
Author: Yifan Gu <gu...@gmail.com>
Authored: Wed Jul 2 14:01:02 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:16 2014 -0700

----------------------------------------------------------------------
 src/Makefile.am            |   1 +
 src/tests/docker_tests.cpp | 175 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 176 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/ded35583/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index aad5440..e5b26df 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1045,6 +1045,7 @@ mesos_tests_SOURCES =				\
   tests/containerizer.cpp			\
   tests/containerizer_tests.cpp			\
   tests/credentials_tests.cpp			\
+  tests/docker_tests.cpp			\
   tests/docker_containerizer_tests.cpp          \
   tests/environment.cpp				\
   tests/examples_tests.cpp			\

http://git-wip-us.apache.org/repos/asf/mesos/blob/ded35583/src/tests/docker_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/docker_tests.cpp b/src/tests/docker_tests.cpp
new file mode 100644
index 0000000..62d5657
--- /dev/null
+++ b/src/tests/docker_tests.cpp
@@ -0,0 +1,175 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <process/future.hpp>
+#include <process/gtest.hpp>
+
+#include <stout/option.hpp>
+#include <stout/gtest.hpp>
+
+#include "docker/docker.hpp"
+
+#include "mesos/resources.hpp"
+
+#include "tests/flags.hpp"
+
+using namespace mesos;
+using namespace mesos::internal;
+
+using process::Future;
+
+using std::list;
+using std::string;
+
+
+// This test tests the functionality of the
+// docker's interfaces.
+TEST(DockerTest, DOCKER_interface)
+{
+  string containerName = "mesos-docker-test";
+  Resources resources = Resources::parse("cpus:1;mem:512").get();
+  Docker docker(tests::flags.docker);
+
+  // Cleaning up the container first.
+  Future<Option<int> > status = docker.rm(containerName, true);
+  AWAIT_READY(status);
+  ASSERT_SOME(status.get());
+
+  // Verify that we do not see the container.
+  Future<list<Docker::Container> > containers = docker.ps(true);
+  AWAIT_READY(containers);
+  foreach (const Docker::Container& container, containers.get()) {
+    EXPECT_NE("/" + containerName, container.name());
+  }
+
+  // Start the container.
+  status = docker.run("busybox", "sleep 120", containerName);
+  AWAIT_READY(status);
+  ASSERT_SOME(status.get());
+
+  // Should be able to see the container now.
+  containers = docker.ps();
+  AWAIT_READY(containers);
+  bool found = false;
+  foreach (const Docker::Container& container, containers.get()) {
+    if ("/" + containerName == container.name()) {
+      found = true;
+      break;
+    }
+  }
+  EXPECT_TRUE(found);
+
+  Future<Docker::Container> container = docker.inspect(containerName);
+  AWAIT_READY(container);
+
+  // Test some fields of the container.
+  EXPECT_NE("", container.get().id());
+  EXPECT_EQ("/" + containerName, container.get().name());
+  EXPECT_SOME(container.get().pid());
+
+  // Kill the container.
+  status = docker.kill(containerName);
+  AWAIT_READY(status);
+  ASSERT_SOME(status.get());
+
+  // Now, the container should not appear in the result of ps().
+  // But it should appear in the result of ps(true).
+  containers = docker.ps();
+  AWAIT_READY(containers);
+  foreach (const Docker::Container& container, containers.get()) {
+    EXPECT_NE("/" + containerName, container.name());
+  }
+
+  containers = docker.ps(true);
+  AWAIT_READY(containers);
+  found = false;
+  foreach (const Docker::Container& container, containers.get()) {
+    if ("/" + containerName == container.name()) {
+      found = true;
+      break;
+    }
+  }
+  EXPECT_TRUE(found);
+
+  // Check the container's info, both id and name should remain
+  // the same since we haven't removed it, but the pid should be none
+  // since it's not running.
+  container = docker.inspect(containerName);
+  AWAIT_READY(container);
+
+  EXPECT_NE("", container.get().id());
+  EXPECT_EQ("/" + containerName, container.get().name());
+  EXPECT_NONE(container.get().pid());
+
+  // Remove the container.
+  status = docker.rm(containerName);
+  AWAIT_READY(status);
+  ASSERT_SOME(status.get());
+
+  // Should not be able to inspect the container.
+  container = docker.inspect(containerName);
+  AWAIT_FAILED(container);
+
+  // Also, now we should not be able to see the container
+  // by invoking ps(true).
+  containers = docker.ps(true);
+  AWAIT_READY(containers);
+  foreach (const Docker::Container& container, containers.get()) {
+    EXPECT_NE("/" + containerName, container.name());
+  }
+
+  // Start the container again, this time we will do a "rm -f"
+  // directly, instead of killing and rm.
+  //
+  // First, Invoke docker.run()
+  status = docker.run("busybox", "sleep 120", containerName);
+  AWAIT_READY(status);
+  ASSERT_SOME(status.get());
+
+  // Verify that the container is there.
+  containers = docker.ps();
+  AWAIT_READY(containers);
+  found = false;
+  foreach (const Docker::Container& container, containers.get()) {
+    if ("/" + containerName == container.name()) {
+      found = true;
+      break;
+    }
+  }
+  EXPECT_TRUE(found);
+
+  // Then do a "rm -f".
+  status = docker.rm(containerName, true);
+  AWAIT_READY(status);
+  ASSERT_SOME(status.get());
+
+  // Verify that the container is totally removed,
+  // that is we can't find it by ps() or ps(true).
+  containers = docker.ps();
+  AWAIT_READY(containers);
+  foreach (const Docker::Container& container, containers.get()) {
+    EXPECT_NE("/" + containerName, container.name());
+  }
+  containers = docker.ps(true);
+  AWAIT_READY(containers);
+  foreach (const Docker::Container& container, containers.get()) {
+    EXPECT_NE("/" + containerName, container.name());
+  }
+}


[28/43] git commit: Update docker uri to expect 3 slashes in prefix

Posted by be...@apache.org.
Update docker uri to expect 3 slashes in prefix


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

Branch: refs/heads/master
Commit: fb270159cf409375c76a36a17dba2a0f4ee67382
Parents: c4b98ad
Author: Timothy Chen <tn...@gmail.com>
Authored: Thu Jul 10 00:05:24 2014 +0000
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:17 2014 -0700

----------------------------------------------------------------------
 src/examples/docker_no_executor_framework.cpp | 2 +-
 src/slave/containerizer/docker.cpp            | 4 ++--
 src/tests/docker_containerizer_tests.cpp      | 8 ++++----
 3 files changed, 7 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/fb270159/src/examples/docker_no_executor_framework.cpp
----------------------------------------------------------------------
diff --git a/src/examples/docker_no_executor_framework.cpp b/src/examples/docker_no_executor_framework.cpp
index 0d59928..3619405 100644
--- a/src/examples/docker_no_executor_framework.cpp
+++ b/src/examples/docker_no_executor_framework.cpp
@@ -104,7 +104,7 @@ public:
         // Use Docker to run the task.
         CommandInfo::ContainerInfo* container =
           task.mutable_command()->mutable_container();
-        container->set_image("docker://busybox");
+        container->set_image("docker:///busybox");
 
         Resource* resource;
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/fb270159/src/slave/containerizer/docker.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/docker.cpp b/src/slave/containerizer/docker.cpp
index 2aceb35..60ac262 100644
--- a/src/slave/containerizer/docker.cpp
+++ b/src/slave/containerizer/docker.cpp
@@ -499,7 +499,7 @@ Future<bool> DockerContainerizerProcess::launch(
 
   // Check if we should try and launch this command.
   if (!command.has_container() ||
-      !strings::startsWith(command.container().image(), "docker")) {
+      !strings::startsWith(command.container().image(), "docker:///")) {
     return false;
   }
 
@@ -518,7 +518,7 @@ Future<bool> DockerContainerizerProcess::launch(
 
   // Extract the Docker image.
   string image = command.container().image();
-  image = strings::remove(image, "docker://", strings::PREFIX);
+  image = strings::remove(image, "docker:///", strings::PREFIX);
 
   // Construct the Docker container name.
   string name = DOCKER_NAME_PREFIX + stringify(containerId);

http://git-wip-us.apache.org/repos/asf/mesos/blob/fb270159/src/tests/docker_containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/docker_containerizer_tests.cpp b/src/tests/docker_containerizer_tests.cpp
index d70518a..2fa18e0 100644
--- a/src/tests/docker_containerizer_tests.cpp
+++ b/src/tests/docker_containerizer_tests.cpp
@@ -167,7 +167,7 @@ TEST_F(DockerContainerizerTest, DOCKER_Launch)
 
   CommandInfo command;
   CommandInfo::ContainerInfo* containerInfo = command.mutable_container();
-  containerInfo->set_image("docker://busybox");
+  containerInfo->set_image("docker:///busybox");
   command.set_value("sleep 120");
 
   task.mutable_command()->CopyFrom(command);
@@ -265,7 +265,7 @@ TEST_F(DockerContainerizerTest, DOCKER_Kill)
 
   CommandInfo command;
   CommandInfo::ContainerInfo* containerInfo = command.mutable_container();
-  containerInfo->set_image("docker://busybox");
+  containerInfo->set_image("docker:///busybox");
   command.set_value("sleep 120");
 
   task.mutable_command()->CopyFrom(command);
@@ -374,7 +374,7 @@ TEST_F(DockerContainerizerTest, DOCKER_Usage)
 
   CommandInfo command;
   CommandInfo::ContainerInfo* containerInfo = command.mutable_container();
-  containerInfo->set_image("docker://busybox");
+  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");
@@ -493,7 +493,7 @@ TEST_F(DockerContainerizerTest, DOCKER_Update)
 
   CommandInfo command;
   CommandInfo::ContainerInfo* containerInfo = command.mutable_container();
-  containerInfo->set_image("docker://busybox");
+  containerInfo->set_image("docker:///busybox");
   command.set_value("sleep 180");
 
   task.mutable_command()->CopyFrom(command);


[40/43] git commit: Addressing Docker review comments

Posted by be...@apache.org.
Addressing Docker review comments


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

Branch: refs/heads/master
Commit: 48e7d4a4438331129d5604315788c0a421542093
Parents: 233e2d4
Author: Timothy Chen <tn...@gmail.com>
Authored: Thu Jul 24 10:57:34 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:17 2014 -0700

----------------------------------------------------------------------
 src/Makefile.am                               |   4 -
 src/docker/docker.cpp                         | 447 ++++++++++++---------
 src/docker/docker.hpp                         |  73 ++--
 src/examples/docker_no_executor_framework.cpp |   2 +-
 src/slave/containerizer/containerizer.cpp     |   2 +-
 src/slave/containerizer/docker.cpp            | 240 +++++------
 src/slave/containerizer/docker.hpp            |   8 +-
 src/slave/flags.hpp                           |   2 +-
 src/tests/cgroups_tests.cpp                   |  13 +
 src/tests/docker_containerizer_tests.cpp      |  56 +--
 src/tests/docker_tests.cpp                    |  69 ++--
 src/tests/environment.cpp                     |  30 +-
 12 files changed, 495 insertions(+), 451 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/48e7d4a4/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index 850fad3..04be4e0 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1154,10 +1154,6 @@ EXTRA_DIST += examples/python/test_containerizer.py			\
 	      examples/python/test_framework.py
 
 
-# Docker test executor image files.
-EXTRA_DIST += tests/mesos_test_executor_docker_image/Dockerfile         \
-              tests/mesos_test_executor_docker_image/install.sh
-
 dist_check_SCRIPTS +=							\
   tests/balloon_framework_test.sh					\
   tests/low_level_scheduler_libprocess_test.sh				\

http://git-wip-us.apache.org/repos/asf/mesos/blob/48e7d4a4/src/docker/docker.cpp
----------------------------------------------------------------------
diff --git a/src/docker/docker.cpp b/src/docker/docker.cpp
index 4842cee..ee9c882 100644
--- a/src/docker/docker.cpp
+++ b/src/docker/docker.cpp
@@ -20,14 +20,17 @@
 #include <vector>
 
 #include <stout/lambda.hpp>
-#include <stout/strings.hpp>
-
+#include <stout/os.hpp>
 #include <stout/result.hpp>
+#include <stout/strings.hpp>
 
 #include <stout/os/read.hpp>
 
 #include <process/check.hpp>
 #include <process/collect.hpp>
+#include <process/io.hpp>
+
+#include "common/status_utils.hpp"
 
 #include "docker/docker.hpp"
 
@@ -46,8 +49,66 @@ using std::string;
 using std::vector;
 
 
-Try<Nothing> Docker::validate(const Docker &docker)
+template<class T>
+static Future<T> failure(
+    const string& cmd,
+    int status,
+    const string& err)
+{
+  return Failure(
+      "Failed to '" + cmd + "': exit status = " +
+      WSTRINGIFY(status) + " stderr = " + err);
+}
+
+
+// Asynchronously read stderr from subprocess.
+static Future<string> err(const Subprocess& s)
+{
+  CHECK_SOME(s.err());
+
+  Try<Nothing> nonblock = os::nonblock(s.err().get());
+  if (nonblock.isError()) {
+    return Failure("Cannot set nonblock for stderr: " + nonblock.error());
+  }
+
+  // TODO(tnachen): Although unlikely, it's possible to not capture
+  // the caller's failure message if io::read stderr fails. Can
+  // chain a callback to at least log.
+  return io::read(s.err().get());
+}
+
+
+static Future<Nothing> _checkError(const string& cmd, const Subprocess& s)
+{
+  Option<int> status = s.status().get();
+  if (status.isNone()) {
+    return Failure("No status found for '" + cmd + "'");
+  }
+
+  if (status.get() != 0) {
+    // TODO(tnachen): Consider returning stdout as well.
+    return err(s).then(
+        lambda::bind(failure<Nothing>, cmd, status.get(), lambda::_1));
+  }
+
+  return Nothing();
+}
+
+
+// Returns a failure if no status or non-zero status returned from
+// subprocess.
+static Future<Nothing> checkError(const string& cmd, const Subprocess& s)
+{
+  return s.status().then(lambda::bind(_checkError, cmd, s));
+}
+
+
+Try<Docker> Docker::create(const string& path, bool validate)
 {
+  if (!validate) {
+    return Docker(path);
+  }
+
   // Make sure that cgroups are mounted, and at least the 'cpu'
   // subsystem is attached.
   Result<string> hierarchy = cgroups::hierarchy("cpu");
@@ -58,76 +119,109 @@ Try<Nothing> Docker::validate(const Docker &docker)
                  "to mount cgroups manually!");
   }
 
-  Future<std::string> info = docker.info();
+  std::string cmd = path + " info";
+
+  Try<Subprocess> s = subprocess(
+      cmd,
+      Subprocess::PATH("/dev/null"),
+      Subprocess::PIPE(),
+      Subprocess::PATH("/dev/null"));
 
-  if (!info.await(Seconds(3))) {
-    return Error("Failed to use Docker: Timed out");
-  } else if (info.isFailed()) {
-    return Error("Failed to use Docker: " + info.failure());
+  if (s.isError()) {
+    return Error(s.error());
   }
 
-  return Nothing();
+  Try<Nothing> nonblock = os::nonblock(s.get().out().get());
+  if (nonblock.isError()) {
+    return Error("Failed to accept nonblock stdout:" + nonblock.error());
+  }
+
+  Future<string> output = io::read(s.get().out().get());
+
+  if (!output.await(Seconds(5))) {
+    return Error("Docker info failed with time out");
+  } else if (output.isFailed()) {
+    return Error("Docker info failed: " + output.failure());
+  }
+
+  return Docker(path);
 }
 
 
-string Docker::Container::id() const
+Try<Docker::Container> Docker::Container::create(const JSON::Object& json)
 {
   map<string, JSON::Value>::const_iterator entry =
     json.values.find("Id");
-  CHECK(entry != json.values.end());
-  JSON::Value value = entry->second;
-  CHECK(value.is<JSON::String>());
-  return value.as<JSON::String>().value;
-}
+  if (entry == json.values.end()) {
+    return Error("Unable to find Id in container");
+  }
 
-string Docker::Container::name() const
-{
-  map<string, JSON::Value>::const_iterator entry =
-    json.values.find("Name");
-  CHECK(entry != json.values.end());
-  JSON::Value value = entry->second;
-  CHECK(value.is<JSON::String>());
-  return value.as<JSON::String>().value;
-}
+  JSON::Value idValue = entry->second;
+  if (!idValue.is<JSON::String>()) {
+    return Error("Id in container is not a string type");
+  }
 
-Option<pid_t> Docker::Container::pid() const
-{
-  map<string, JSON::Value>::const_iterator state =
-    json.values.find("State");
-  CHECK(state != json.values.end());
-  JSON::Value value = state->second;
-  CHECK(value.is<JSON::Object>());
+  string id = idValue.as<JSON::String>().value;
 
-  map<string, JSON::Value>::const_iterator entry =
-    value.as<JSON::Object>().values.find("Pid");
-  CHECK(entry != json.values.end());
-  // TODO(yifan) reload operator '=' to reuse the value variable above.
+  entry = json.values.find("Name");
+  if (entry == json.values.end()) {
+    return Error("Unable to find Name in container");
+  }
+
+  JSON::Value nameValue = entry->second;
+  if (!nameValue.is<JSON::String>()) {
+    return Error("Name in container is not string type");
+  }
+
+  string name = nameValue.as<JSON::String>().value;
+
+  entry = json.values.find("State");
+  if (entry == json.values.end()) {
+    return Error("Unable to find State in container");
+  }
+
+  JSON::Value stateValue = entry->second;
+  if (!stateValue.is<JSON::Object>()) {
+    return Error("State in container is not object type");
+  }
+
+  entry = stateValue.as<JSON::Object>().values.find("Pid");
+  if (entry == json.values.end()) {
+    return Error("Unable to find Pid in State");
+  }
+
+  // TODO(yifan): Reload operator '=' to reuse the value variable above.
   JSON::Value pidValue = entry->second;
-  CHECK(pidValue.is<JSON::Number>());
+  if (!pidValue.is<JSON::Number>()) {
+    return Error("Pid in State is not number type");
+  }
 
   pid_t pid = pid_t(pidValue.as<JSON::Number>().value);
-  if (pid == 0) {
-    return None();
+
+  Option<pid_t> optionalPid;
+  if (pid != 0) {
+    optionalPid = pid;
   }
-  return pid;
+
+  return Docker::Container(id, name, optionalPid);
 }
 
-Future<Option<int> > Docker::run(
+
+Future<Nothing> Docker::run(
     const string& image,
     const string& command,
     const string& name,
     const Option<mesos::Resources>& resources,
     const Option<map<string, string> >& env) const
 {
-
-  string cmd = " run -d";
+  string cmd = path + " run -d";
 
   if (resources.isSome()) {
     // TODO(yifan): Support other resources (e.g. disk, ports).
     Option<double> cpus = resources.get().cpus();
     if (cpus.isSome()) {
       uint64_t cpuShare =
-	std::max((uint64_t) (CPU_SHARES_PER_CPU * cpus.get()), MIN_CPU_SHARES);
+        std::max((uint64_t) (CPU_SHARES_PER_CPU * cpus.get()), MIN_CPU_SHARES);
       cmd += " -c " + stringify(cpuShare);
     }
 
@@ -139,6 +233,8 @@ Future<Option<int> > Docker::run(
   }
 
   if (env.isSome()) {
+    // TODO(tnachen): Use subprocess with args instead once we can
+    // handle splitting command string into args.
     foreachpair (string key, string value, env.get()) {
       key = strings::replace(key, "\"", "\\\"");
       value = strings::replace(value, "\"", "\\\"");
@@ -148,88 +244,96 @@ Future<Option<int> > Docker::run(
 
   cmd += " --net=host --name=" + name + " " + image + " " + command;
 
-  VLOG(1) << "Running " << path << cmd;
+  VLOG(1) << "Running " << cmd;
 
   Try<Subprocess> s = subprocess(
-      path + cmd,
-      Subprocess::PIPE(),
-      Subprocess::PIPE(),
+      cmd,
+      Subprocess::PATH("/dev/null"),
+      Subprocess::PATH("/dev/null"),
       Subprocess::PIPE());
 
   if (s.isError()) {
     return Failure(s.error());
   }
-  return s.get().status();
+
+  return checkError(cmd, s.get());
 }
 
 
-Future<Option<int> > Docker::kill(const string& container) const
+Future<Nothing> Docker::kill(const string& container, bool remove) const
 {
-  VLOG(1) << "Running " << path << " kill " << container;
+  const string cmd = path + " kill " + container;
+
+  VLOG(1) << "Running " << cmd;
 
   Try<Subprocess> s = subprocess(
-      path + " kill " + container,
-      Subprocess::PIPE(),
-      Subprocess::PIPE(),
+      cmd,
+      Subprocess::PATH("/dev/null"),
+      Subprocess::PATH("/dev/null"),
       Subprocess::PIPE());
 
   if (s.isError()) {
     return Failure(s.error());
   }
 
-  return s.get().status();
+  return s.get().status()
+    .then(lambda::bind(
+        &Docker::_kill,
+        *this,
+        container,
+        cmd,
+        s.get(),
+        remove));
 }
 
-
-Future<Option<int> > Docker::rm(
+Future<Nothing> Docker::_kill(
+    const Docker& docker,
     const string& container,
-    const bool force) const
+    const string& cmd,
+    const Subprocess& s,
+    bool remove)
 {
-  string cmd = force ? " rm -f " : " rm ";
-
-  VLOG(1) << "Running " << path << cmd << container;
-
-  Try<Subprocess> s = subprocess(
-      path + cmd + container,
-      Subprocess::PIPE(),
-      Subprocess::PIPE(),
-      Subprocess::PIPE());
+  Option<int> status = s.status().get();
 
-  if (s.isError()) {
-    return Failure(s.error());
+  if (remove) {
+    bool force = !status.isSome() || status.get() != 0;
+    return docker.rm(container, force);
   }
 
-  return s.get().status();
+  return checkError(cmd, s);
 }
 
 
-Future<Option<int> > Docker::killAndRm(const string& container) const
+Future<Nothing> Docker::rm(
+    const string& container,
+    bool force) const
 {
-  return kill(container)
-    .then(lambda::bind(Docker::_killAndRm, *this, container, lambda::_1));
-}
+  const string cmd = path + (force ? " rm -f " : " rm ") + container;
 
+  VLOG(1) << "Running " << cmd;
 
-Future<Option<int> > Docker::_killAndRm(
-    const Docker& docker,
-    const string& container,
-    const Option<int>& status)
-{
-  // If 'kill' fails, then do a 'rm -f'.
-  if (status.isNone()) {
-    return docker.rm(container, true);
+  Try<Subprocess> s = subprocess(
+      cmd,
+      Subprocess::PATH("/dev/null"),
+      Subprocess::PATH("/dev/null"),
+      Subprocess::PIPE());
+
+  if (s.isError()) {
+    return Failure(s.error());
   }
-  return docker.rm(container);
+
+  return checkError(cmd, s.get());
 }
 
 
 Future<Docker::Container> Docker::inspect(const string& container) const
 {
-  VLOG(1) << "Running " << path << " inspect " << container;
+  const string cmd =  path + " inspect " + container;
+  VLOG(1) << "Running " << cmd;
 
   Try<Subprocess> s = subprocess(
-      path + " inspect " + container,
-      Subprocess::PIPE(),
+      cmd,
+      Subprocess::PATH("/dev/null"),
       Subprocess::PIPE(),
       Subprocess::PIPE());
 
@@ -238,88 +342,61 @@ Future<Docker::Container> Docker::inspect(const string& container) const
   }
 
   return s.get().status()
-    .then(lambda::bind(&Docker::_inspect, s.get()));
+    .then(lambda::bind(&Docker::_inspect, cmd, s.get()));
 }
 
 
-namespace os {
-
-inline Result<std::string> read(
-    int fd,
-    Option<size_t> size = None(),
-    size_t chunk = 16 * 4096)
-{
-  std::string result;
-
-  while (size.isNone() || result.size() < size.get()) {
-    char buffer[chunk];
-    ssize_t length = ::read(fd, buffer, chunk);
-
-    if (length < 0) {
-      // TODO(bmahler): Handle a non-blocking fd? (EAGAIN, EWOULDBLOCK)
-      if (errno == EINTR) {
-        continue;
-      }
-      return ErrnoError();
-    } else if (length == 0) {
-      // Reached EOF before expected! Only return as much data as
-      // available or None if we haven't read anything yet.
-      if (result.size() > 0) {
-        return result;
-      }
-      return None();
-    }
-
-    result.append(buffer, length);
-  }
-
-  return result;
-}
-
-} // namespace os {
-
-
-Future<Docker::Container> Docker::_inspect(const Subprocess& s)
+Future<Docker::Container> Docker::_inspect(
+    const string& cmd,
+    const Subprocess& s)
 {
   // Check the exit status of 'docker inspect'.
   CHECK_READY(s.status());
 
   Option<int> status = s.status().get();
 
-  if (status.isSome() && status.get() != 0) {
-    // TODO(benh): Include stderr in error message.
-    Result<string> read = os::read(s.err().get());
-    return Failure("Failed to do 'docker inspect': " +
-                   (read.isSome()
-                    ? read.get()
-                    : " exited with status " + stringify(status.get())));
+  if (!status.isSome()) {
+    return Failure("No status found from '" + cmd + "'");
+  } else if (status.get() != 0) {
+    return err(s).then(
+        lambda::bind(
+            failure<Docker::Container>,
+            cmd,
+            status.get(),
+            lambda::_1));
   }
 
   // Read to EOF.
-  // TODO(benh): Read output asynchronously.
   CHECK_SOME(s.out());
-  Result<string> output = os::read(s.out().get());
-
-  if (output.isError()) {
-    // TODO(benh): Include stderr in error message.
-    return Failure("Failed to read output: " + output.error());
-  } else if (output.isNone()) {
-    // TODO(benh): Include stderr in error message.
-    return Failure("No output available");
+  Try<Nothing> nonblock = os::nonblock(s.out().get());
+  if (nonblock.isError()) {
+    return Failure("Failed to accept nonblock stdout:" + nonblock.error());
   }
+  Future<string> output = io::read(s.out().get());
+  return output.then(lambda::bind(&Docker::__inspect, lambda::_1));
+}
 
-  Try<JSON::Array> parse = JSON::parse<JSON::Array>(output.get());
+
+Future<Docker::Container> Docker::__inspect(const string& output)
+{
+  Try<JSON::Array> parse = JSON::parse<JSON::Array>(output);
 
   if (parse.isError()) {
     return Failure("Failed to parse JSON: " + parse.error());
   }
 
   JSON::Array array = parse.get();
-
-  // Skip the container if it no longer exists.
+  // Only return if only one container identified with name.
   if (array.values.size() == 1) {
     CHECK(array.values.front().is<JSON::Object>());
-    return Docker::Container(array.values.front().as<JSON::Object>());
+    Try<Docker::Container> container =
+      Docker::Container::create(array.values.front().as<JSON::Object>());
+
+    if (container.isError()) {
+      return Failure("Unable to create container: " + container.error());
+    }
+
+    return container.get();
   }
 
   // TODO(benh): Handle the case where the short container ID was
@@ -330,16 +407,16 @@ Future<Docker::Container> Docker::_inspect(const Subprocess& s)
 
 
 Future<list<Docker::Container> > Docker::ps(
-    const bool all,
+    bool all,
     const Option<string>& prefix) const
 {
-  string cmd = all ? " ps -a" : " ps";
+  string cmd = path + (all ? " ps -a" : " ps");
 
-  VLOG(1) << "Running " << path << cmd;
+  VLOG(1) << "Running " << cmd;
 
   Try<Subprocess> s = subprocess(
-      path + cmd,
-      Subprocess::PIPE(),
+      cmd,
+      Subprocess::PATH("/dev/null"),
       Subprocess::PIPE(),
       Subprocess::PIPE());
 
@@ -348,39 +425,46 @@ Future<list<Docker::Container> > Docker::ps(
   }
 
   return s.get().status()
-    .then(lambda::bind(&Docker::_ps, *this, s.get(), prefix));
+    .then(lambda::bind(&Docker::_ps, *this, cmd, s.get(), prefix));
 }
 
 
 Future<list<Docker::Container> > Docker::_ps(
     const Docker& docker,
+    const string& cmd,
     const Subprocess& s,
     const Option<string>& prefix)
 {
-  // Check the exit status of 'docker ps'.
-  CHECK_READY(s.status());
-
   Option<int> status = s.status().get();
 
-  if (status.isSome() && status.get() != 0) {
-    // TODO(benh): Include stderr in error message.
-    return Failure("Failed to do 'docker ps'");
+  if (!status.isSome()) {
+    return Failure("No status found from '" + cmd + "'");
+  } else if (status.get() != 0) {
+    return err(s).then(
+        lambda::bind(
+            failure<list<Docker::Container> >,
+            cmd,
+            status.get(),
+            lambda::_1));
   }
 
   // Read to EOF.
-  // TODO(benh): Read output asynchronously.
   CHECK_SOME(s.out());
-  Result<string> output = os::read(s.out().get());
-
-  if (output.isError()) {
-    // TODO(benh): Include stderr in error message.
-    return Failure("Failed to read output: " + output.error());
-  } else if (output.isNone()) {
-    // TODO(benh): Include stderr in error message.
-    return Failure("No output available");
+  Try<Nothing> nonblock = os::nonblock(s.out().get());
+  if (nonblock.isError()) {
+    return Failure("Failed to accept nonblock stdout:" + nonblock.error());
   }
+  Future<string> output = io::read(s.out().get());
+  return output.then(lambda::bind(&Docker::__ps, docker, prefix, lambda::_1));
+}
 
-  vector<string> lines = strings::tokenize(output.get(), "\n");
+
+Future<list<Docker::Container> > Docker::__ps(
+    const Docker& docker,
+    const Option<string>& prefix,
+    const string& output)
+{
+  vector<string> lines = strings::tokenize(output, "\n");
 
   // Skip the header.
   CHECK(!lines.empty());
@@ -392,6 +476,7 @@ Future<list<Docker::Container> > Docker::_ps(
     // Inspect the containers that we are interested in depending on
     // whether or not a 'prefix' was specified.
     vector<string> columns = strings::split(strings::trim(line), " ");
+    // We expect the name column to be the last column from ps.
     string name = columns[columns.size() - 1];
     if (prefix.isNone()) {
       futures.push_back(docker.inspect(name));
@@ -402,33 +487,3 @@ Future<list<Docker::Container> > Docker::_ps(
 
   return collect(futures);
 }
-
-
-Future<std::string> Docker::info() const
-{
-  std::string cmd = path + " info";
-
-  VLOG(1) << "Running " << cmd;
-
-  Try<Subprocess> s = subprocess(
-      cmd,
-      Subprocess::PIPE(),
-      Subprocess::PIPE(),
-      Subprocess::PIPE());
-
-  if (s.isError()) {
-    return Failure(s.error());
-  }
-
-  Result<string> output = os::read(s.get().out().get());
-
-  if (output.isError()) {
-    // TODO(benh): Include stderr in error message.
-    return Failure("Failed to read output: " + output.error());
-  } else if (output.isNone()) {
-    // TODO(benh): Include stderr in error message.
-    return Failure("No output available");
-  }
-
-  return output.get();
-}

http://git-wip-us.apache.org/repos/asf/mesos/blob/48e7d4a4/src/docker/docker.hpp
----------------------------------------------------------------------
diff --git a/src/docker/docker.hpp b/src/docker/docker.hpp
index c4724de..98b2d60 100644
--- a/src/docker/docker.hpp
+++ b/src/docker/docker.hpp
@@ -37,55 +37,50 @@
 class Docker
 {
 public:
-  // Validate Docker support
-  static Try<Nothing> validate(const Docker& docker);
+  // Create Docker abstraction and optionally validate docker.
+  static Try<Docker> create(const std::string& path, bool validate = true);
 
   class Container
   {
   public:
-    Container(const JSON::Object& json) : json(json) {}
+    static Try<Container> create(const JSON::Object& json);
 
     // Returns the ID of the container.
-    std::string id() const;
+    std::string id;
 
     // Returns the name of the container.
-    std::string name() const;
+    std::string name;
 
-    // Returns the Pid of the container, or None if the container is
+    // Returns the pid of the container, or None if the container is
     // not running.
-    Option<pid_t> pid() const;
+    Option<pid_t> pid;
 
   private:
-    JSON::Object json; // JSON returned from 'docker inspect'.
+    Container(
+        const std::string& _id,
+        const std::string& _name,
+        const Option<pid_t>& _pid)
+      : id(_id), name(_name), pid(_pid) {}
   };
 
-  // Uses the specified path to the Docker CLI tool.
-  Docker(const std::string& path) : path(path) {}
-
   // Performs 'docker run IMAGE'.
-  process::Future<Option<int> > run(
+  process::Future<Nothing> run(
       const std::string& image,
       const std::string& command,
       const std::string& name,
       const Option<mesos::Resources>& resources = None(),
       const Option<std::map<std::string, std::string> >& env = None()) const;
 
-  // Performs 'docker kill CONTAINER'.
-  process::Future<Option<int> > kill(
-      const std::string& container) const;
+  // Performs 'docker kill CONTAINER'. If remove is true then a rm -f
+  // will be called when kill failed, otherwise a failure is returned.
+  process::Future<Nothing> kill(
+      const std::string& container,
+      bool remove = false) const;
 
   // Performs 'docker rm (-f) CONTAINER'.
-  process::Future<Option<int> > rm(
+  process::Future<Nothing> rm(
       const std::string& container,
-      const bool force = false) const;
-
-  // Performs 'docker kill && docker rm'
-  // if 'docker kill' fails, then will do a 'docker rm -f'.
-  //
-  // TODO(yifan): Depreciate this when the docker provides
-  // something like 'docker rm --kill'.
-  process::Future<Option<int> > killAndRm(
-      const std::string& container) const;
+      bool force = false) const;
 
   // Performs 'docker inspect CONTAINER'.
   process::Future<Container> inspect(
@@ -93,23 +88,37 @@ public:
 
   // Performs 'docker ps (-a)'.
   process::Future<std::list<Container> > ps(
-      const bool all = false,
+      bool all = false,
       const Option<std::string>& prefix = None()) const;
 
-  process::Future<std::string> info() const;
-
 private:
-  // Continuations.
+  // Uses the specified path to the Docker CLI tool.
+  Docker(const std::string& _path) : path(_path) {};
+
+  static process::Future<Nothing> _kill(
+      const Docker& docker,
+      const std::string& container,
+      const std::string& cmd,
+      const process::Subprocess& s,
+      bool remove);
+
   static process::Future<Container> _inspect(
+      const std::string& cmd,
       const process::Subprocess& s);
+
+  static process::Future<Container> __inspect(
+      const std::string& output);
+
   static process::Future<std::list<Container> > _ps(
       const Docker& docker,
+      const std::string& cmd,
       const process::Subprocess& s,
       const Option<std::string>& prefix);
-  static process::Future<Option<int> > _killAndRm(
+
+  static process::Future<std::list<Container> > __ps(
       const Docker& docker,
-      const std::string& container,
-      const Option<int>& status);
+      const Option<std::string>& prefix,
+      const std::string& output);
 
   const std::string path;
 };

http://git-wip-us.apache.org/repos/asf/mesos/blob/48e7d4a4/src/examples/docker_no_executor_framework.cpp
----------------------------------------------------------------------
diff --git a/src/examples/docker_no_executor_framework.cpp b/src/examples/docker_no_executor_framework.cpp
index 3619405..d5385d9 100644
--- a/src/examples/docker_no_executor_framework.cpp
+++ b/src/examples/docker_no_executor_framework.cpp
@@ -176,7 +176,7 @@ int main(int argc, char** argv)
 
   FrameworkInfo framework;
   framework.set_user(""); // Have Mesos fill in the current user.
-  framework.set_name("No Executor Framework (C++)");
+  framework.set_name("Docker No Executor Framework (C++)");
 
   // TODO(vinod): Make checkpointing the default when it is default
   // on the slave.

http://git-wip-us.apache.org/repos/asf/mesos/blob/48e7d4a4/src/slave/containerizer/containerizer.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/containerizer.cpp b/src/slave/containerizer/containerizer.cpp
index 003775b..c91ba38 100644
--- a/src/slave/containerizer/containerizer.cpp
+++ b/src/slave/containerizer/containerizer.cpp
@@ -173,7 +173,7 @@ Try<Containerizer*> Containerizer::create(const Flags& flags, bool local)
       }
     } else if (type == "docker") {
       Try<DockerContainerizer*> containerizer =
-        DockerContainerizer::create(flags, local);
+        DockerContainerizer::create(flags);
       if (containerizer.isError()) {
         return Error("Could not create DockerContainerizer: " +
                      containerizer.error());

http://git-wip-us.apache.org/repos/asf/mesos/blob/48e7d4a4/src/slave/containerizer/docker.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/docker.cpp b/src/slave/containerizer/docker.cpp
index 294b4c2..904cdd3 100644
--- a/src/slave/containerizer/docker.cpp
+++ b/src/slave/containerizer/docker.cpp
@@ -28,8 +28,6 @@
 #include <stout/hashset.hpp>
 #include <stout/os.hpp>
 
-#include "common/status_utils.hpp"
-
 #include "docker/docker.hpp"
 
 #ifdef __linux__
@@ -75,11 +73,10 @@ class DockerContainerizerProcess
 {
 public:
   DockerContainerizerProcess(
-      const Flags& flags,
-      bool local,
-      const Docker& docker)
-    : flags(flags),
-      docker(docker) {}
+      const Flags& _flags,
+      const Docker& _docker)
+    : flags(_flags),
+      docker(_docker) {}
 
   virtual process::Future<Nothing> recover(
       const Option<state::SlaveState>& state);
@@ -115,7 +112,7 @@ public:
 
   virtual void destroy(
       const ContainerID& containerId,
-      const bool& killed = true);
+      bool killed = true); // process is either killed or reaped.
 
   virtual process::Future<hashset<ContainerID> > containers();
 
@@ -131,16 +128,14 @@ private:
       const std::string& directory,
       const SlaveID& slaveId,
       const PID<Slave>& slavePid,
-      bool checkpoint,
-      const Option<int>& status);
+      bool checkpoint);
 
   process::Future<bool> _launch(
       const ContainerID& containerId,
       const ExecutorInfo& executorInfo,
       const SlaveID& slaveId,
       const PID<Slave>& slavePid,
-      bool checkpoint,
-      const Option<int>& status);
+      bool checkpoint);
 
   process::Future<bool> __launch(
       const ContainerID& containerId,
@@ -150,16 +145,15 @@ private:
       bool checkpoint,
       const Docker::Container& container);
 
-
   void _destroy(
       const ContainerID& containerId,
-      const bool& killed,
-      const Future<Option<int> >& future);
+      bool killed,
+      const Future<Nothing>& future);
 
   void __destroy(
       const ContainerID& containerId,
-      const bool& killed,
-      const Future<Option<int > >& status);
+      bool killed,
+      const Future<Option<int> >& status);
 
   process::Future<Nothing> _update(
       const ContainerID& containerId,
@@ -174,9 +168,7 @@ private:
   // container destroy.
   void reaped(const ContainerID& containerId);
 
-  // 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);
+  static std::string containerName(const ContainerID& containerId);
 
   const Flags flags;
 
@@ -201,26 +193,48 @@ 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<string> name = None();
+
+  if (strings::startsWith(container.name, DOCKER_NAME_PREFIX)) {
+    name = strings::remove(
+        container.name, DOCKER_NAME_PREFIX, strings::PREFIX);
+  } else if (strings::startsWith(container.name, "/" + DOCKER_NAME_PREFIX)) {
+    name = strings::remove(
+        container.name, "/" + DOCKER_NAME_PREFIX, strings::PREFIX);
+  }
+
+  if (name.isSome()) {
+    ContainerID id;
+    id.set_value(name.get());
+    return id;
+  }
+
+  return None();
+}
+
+
 Try<DockerContainerizer*> DockerContainerizer::create(
-    const Flags& flags,
-    bool local)
+    const Flags& flags)
 {
-  Docker docker(flags.docker);
-  Try<Nothing> validation = Docker::validate(docker);
-  if (validation.isError()) {
-    return Error(validation.error());
+  Try<Docker> docker = Docker::create(flags.docker);
+  if (docker.isError()) {
+    return Error(docker.error());
   }
 
-  return new DockerContainerizer(flags, local, docker);
+  return new DockerContainerizer(flags, docker.get());
 }
 
 
 DockerContainerizer::DockerContainerizer(
     const Flags& flags,
-    bool local,
     const Docker& docker)
 {
-  process = new DockerContainerizerProcess(flags, local, docker);
+  process = new DockerContainerizerProcess(flags, docker);
   spawn(process);
 }
 
@@ -355,6 +369,12 @@ static int setup(const string& directory)
 }
 
 
+string DockerContainerizerProcess::containerName(const ContainerID& containerId)
+{
+  return DOCKER_NAME_PREFIX + stringify(containerId);
+}
+
+
 Future<Nothing> DockerContainerizerProcess::recover(
     const Option<SlaveState>& state)
 {
@@ -415,7 +435,6 @@ Future<Nothing> DockerContainerizerProcess::recover(
 
         promises.put(containerId, promise);
 
-        CHECK_SOME(run.get().forkedPid);
         pid_t pid = run.get().forkedPid.get();
 
         statuses[containerId] = process::reap(pid);
@@ -429,8 +448,7 @@ Future<Nothing> DockerContainerizerProcess::recover(
           // pid as one that just exited (highly unlikely) and the
           // slave dies after the new executor is launched but before
           // it hears about the termination of the earlier executor
-          // (also unlikely). Regardless, the launcher can't do
-          // anything sensible so this is considered an error.
+          // (also unlikely).
           return Failure(
               "Detected duplicate pid " + stringify(pid) +
               " for container " + stringify(containerId));
@@ -453,7 +471,7 @@ Future<Nothing> DockerContainerizerProcess::_recover(
 {
   foreach (const Docker::Container& container, containers) {
     VLOG(1) << "Checking if Docker container named '"
-            << container.name() << "' was started by Mesos";
+            << container.name << "' was started by Mesos";
 
     Option<ContainerID> id = parse(container);
 
@@ -470,7 +488,7 @@ Future<Nothing> DockerContainerizerProcess::_recover(
     if (!statuses.keys().contains(id.get())) {
       // TODO(benh): Retry 'docker rm -f' if it failed but the container
       // still exists (asynchronously).
-      docker.killAndRm(container.id());
+      docker.kill(container.id, true);
     }
   }
 
@@ -488,14 +506,13 @@ Future<bool> DockerContainerizerProcess::launch(
     bool checkpoint)
 {
   if (promises.contains(containerId)) {
-    LOG(ERROR) << "Cannot start already running container '"
-               << containerId << "'";
     return Failure("Container already started");
   }
 
   CommandInfo command = executorInfo.command();
 
   if (!command.has_container()) {
+    LOG(INFO) << "No container info found, skipping launch";
     return false;
   }
 
@@ -503,6 +520,7 @@ Future<bool> DockerContainerizerProcess::launch(
 
   // Check if we should try and launch this command.
   if (!strings::startsWith(image, "docker:///")) {
+    LOG(INFO) << "No docker image found, skipping launch";
     return false;
   }
 
@@ -519,7 +537,7 @@ Future<bool> DockerContainerizerProcess::launch(
   image = strings::remove(image, "docker:///", strings::PREFIX);
 
   // Construct the Docker container name.
-  string name = DOCKER_NAME_PREFIX + stringify(containerId);
+  string name = containerName(containerId);
 
   map<string, string> env = executorEnvironment(
       executorInfo,
@@ -546,8 +564,7 @@ Future<bool> DockerContainerizerProcess::launch(
                executorInfo,
                slaveId,
                slavePid,
-               checkpoint,
-               lambda::_1))
+               checkpoint))
     .onFailed(defer(self(), &Self::destroy, containerId, false));
 }
 
@@ -563,12 +580,11 @@ Future<bool> DockerContainerizerProcess::launch(
     bool checkpoint)
 {
   if (promises.contains(containerId)) {
-    LOG(ERROR) << "Cannot start already running container '"
-               << containerId << "'";
     return Failure("Container already started");
   }
 
   if (!taskInfo.has_command()) {
+    LOG(WARNING) << "Not expecting call without command info";
     return false;
   }
 
@@ -577,6 +593,8 @@ Future<bool> DockerContainerizerProcess::launch(
   // 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";
     return false;
   }
 
@@ -598,7 +616,7 @@ Future<bool> DockerContainerizerProcess::launch(
   image = strings::remove(image, "docker:///", strings::PREFIX);
 
   // Construct the Docker container name.
-  string name = DOCKER_NAME_PREFIX + stringify(containerId);
+  string name = containerName(containerId);
 
   // Start a docker container then launch the executor (but destroy
   // the Docker container if launching the executor failed).
@@ -611,8 +629,7 @@ Future<bool> DockerContainerizerProcess::launch(
                 directory,
                 slaveId,
                 slavePid,
-                checkpoint,
-                lambda::_1))
+                checkpoint))
     .onFailed(defer(self(), &Self::destroy, containerId, false));
 }
 
@@ -624,17 +641,8 @@ Future<bool> DockerContainerizerProcess::_launch(
     const string& directory,
     const SlaveID& slaveId,
     const PID<Slave>& slavePid,
-    bool checkpoint,
-    const Option<int>& status)
+    bool checkpoint)
 {
-  // Try and see if the run failed.
-  if (status.isSome() && status.get() != 0) {
-    // Best effort kill and remove the container just in case.
-    docker.killAndRm(DOCKER_NAME_PREFIX + stringify(containerId));
-    return Failure("Failed to run the container (" +
-                   WSTRINGIFY(status.get()) + ")");
-  }
-
   // Prepare environment variables for the executor.
   map<string, string> env = executorEnvironment(
       executorInfo,
@@ -656,9 +664,8 @@ Future<bool> DockerContainerizerProcess::_launch(
   // don't want the exit status from 'docker wait' but rather the exit
   // status from the container, hence the use of /bin/bash.
   string override =
-    "/bin/bash -c 'exit `" +
-    flags.docker + " wait " + DOCKER_NAME_PREFIX + stringify(containerId) +
-    "`'";
+    "/bin/sh -c 'exit `" +
+    flags.docker + " wait " + containerName(containerId) + "`'";
 
   Try<Subprocess> s = subprocess(
       executorInfo.command().value() + " --override " + override,
@@ -708,9 +715,9 @@ Future<bool> DockerContainerizerProcess::_launch(
          errno == EINTR);
 
   if (length != sizeof(c)) {
+    string error = string(strerror(errno));
     os::close(s.get().in().get());
-    return Failure("Failed to synchronize with child process: " +
-                   string(strerror(errno)));
+    return Failure("Failed to synchronize with child process: " + error);
   }
 
   // And finally watch for when the executor gets reaped.
@@ -728,17 +735,9 @@ Future<bool> DockerContainerizerProcess::_launch(
     const ExecutorInfo& executorInfo,
     const SlaveID& slaveId,
     const PID<Slave>& slavePid,
-    bool checkpoint,
-    const Option<int>& status)
+    bool checkpoint)
 {
-  if (status.isSome() && status.get() != 0) {
-    // Best effort kill and remove the container just in case.
-    docker.killAndRm(DOCKER_NAME_PREFIX + stringify(containerId));
-    return Failure("Failed to run the container (" +
-                   WSTRINGIFY(status.get()) + ")");
-  }
-
-  return docker.inspect(DOCKER_NAME_PREFIX + stringify(containerId))
+  return docker.inspect(containerName(containerId))
     .then(defer(self(),
                 &Self::__launch,
                 containerId,
@@ -758,18 +757,18 @@ Future<bool> DockerContainerizerProcess::__launch(
     bool checkpoint,
     const Docker::Container& container)
 {
-  Option<int> pid = container.pid();
+  Option<int> pid = container.pid;
 
   if (!pid.isSome()) {
     return Failure("Unable to get executor pid after launch");
   }
 
   if (checkpoint) {
-    // TODO(tnachen): We might not be able to checkpoint
-    // if the slave dies before it can checkpoint while
-    // the executor is still running. Optinally we can consider
-    // recording the slave id and executor id as part of the
-    // docker container name so we can recover from this.
+    // TODO(tnachen): We might not be able to checkpoint if the slave
+    // dies before it can checkpoint while the executor is still
+    // running. Optinally we can consider recording the slave id and
+    // executor id as part of the docker container name so we can
+    // recover from this.
     const string& path =
       slave::paths::getForkedPidPath(
           slave::paths::getMetaRootDir(flags.work_dir),
@@ -806,22 +805,21 @@ Future<Nothing> DockerContainerizerProcess::update(
     const Resources& _resources)
 {
   if (!promises.contains(containerId)) {
-    LOG(WARNING)
-      << "Ignoring updating unknown container: "
-      << containerId.value();
+    LOG(WARNING) << "Ignoring updating unknown container: "
+                 << containerId;
     return Nothing();
   }
 
+  // Store the resources for usage()
+  resources.put(containerId, _resources);
+
 #ifdef __linux__
   if (!_resources.cpus().isSome() && !_resources.mem().isSome()) {
     LOG(WARNING) << "Ignoring update as no supported resources are present";
     return Nothing();
   }
 
-  // Store the resources for usage()
-  resources.put(containerId, _resources);
-
-  return docker.inspect(DOCKER_NAME_PREFIX + stringify(containerId))
+  return docker.inspect(containerName(containerId))
     .then(defer(self(), &Self::_update, containerId, _resources, lambda::_1));
 #else
   return Nothing();
@@ -860,7 +858,7 @@ Future<Nothing> DockerContainerizerProcess::_update(
   // update the proper cgroup control files.
 
   // First check that this container still appears to be running.
-  Option<pid_t> pid = container.pid();
+  Option<pid_t> pid = container.pid;
   if (pid.isNone()) {
     return Nothing();
   }
@@ -873,10 +871,9 @@ Future<Nothing> DockerContainerizerProcess::_update(
     return Failure("Failed to determine cgroup for the 'cpu' subsystem: " +
                    cpuCgroup.error());
   } else if (cpuCgroup.isNone()) {
-    LOG(WARNING)
-      << "Container " << containerId
-      << " does not appear to be a member of a cgroup "
-      << "where the 'cpu' subsystem is mounted";
+    LOG(WARNING) << "Container " << containerId
+                 << " does not appear to be a member of a cgroup "
+                 << "where the 'cpu' subsystem is mounted";
   }
 
   // And update the CPU shares (if applicable).
@@ -895,10 +892,9 @@ Future<Nothing> DockerContainerizerProcess::_update(
       return Failure("Failed to update 'cpu.shares': " + write.error());
     }
 
-    LOG(INFO)
-      << "Updated 'cpu.shares' to " << shares
-      << " at " << path::join(cpuHierarchy.get(), cpuCgroup.get())
-      << " for container " << containerId;
+    LOG(INFO) << "Updated 'cpu.shares' to " << shares
+              << " at " << path::join(cpuHierarchy.get(), cpuCgroup.get())
+              << " for container " << containerId;
   }
 
   // Now determine the cgroup for the 'memory' subsystem.
@@ -908,16 +904,16 @@ Future<Nothing> DockerContainerizerProcess::_update(
     return Failure("Failed to determine cgroup for the 'memory' subsystem: " +
                    memoryCgroup.error());
   } else if (memoryCgroup.isNone()) {
-    LOG(WARNING)
-      << "Container " << containerId
-      << " does not appear to be a member of a cgroup "
-      << "where the 'memory' subsystem is mounted";
+    LOG(WARNING) << "Container " << containerId
+                 << " does not appear to be a member of a cgroup "
+                 << "where the 'memory' subsystem is mounted";
   }
 
   // And update the memory limits (if applicable).
   if (memoryHierarchy.isSome() &&
       memoryCgroup.isSome() &&
       _resources.mem().isSome()) {
+    // TODO(tnachen): investigate and handle OOM with docker.
     Bytes mem = _resources.mem().get();
     Bytes limit = std::max(mem, MIN_MEMORY);
 
@@ -931,9 +927,8 @@ Future<Nothing> DockerContainerizerProcess::_update(
                      write.error());
     }
 
-    LOG(INFO)
-      << "Updated 'memory.soft_limit_in_bytes' to " << limit
-      << " for container " << containerId;
+    LOG(INFO) << "Updated 'memory.soft_limit_in_bytes' to " << limit
+              << " for container " << containerId;
 
     // Read the existing limit.
     Try<Bytes> currentLimit =
@@ -946,6 +941,9 @@ Future<Nothing> DockerContainerizerProcess::_update(
     }
 
     // Only update if new limit is higher.
+    // TODO(benh): Introduce a MemoryWatcherProcess which monitors the
+    // discrepancy between usage and soft limit and introduces a
+    // "manual oom" if necessary.
     if (limit > currentLimit.get()) {
       write = cgroups::memory::limit_in_bytes(
           memoryHierarchy.get(), memoryCgroup.get(), limit);
@@ -955,10 +953,9 @@ Future<Nothing> DockerContainerizerProcess::_update(
                        write.error());
       }
 
-      LOG(INFO)
-        << "Updated 'memory.limit_in_bytes' to " << limit
-        << " at " << path::join(memoryHierarchy.get(), memoryCgroup.get())
-        << " for container " << containerId;
+      LOG(INFO) << "Updated 'memory.limit_in_bytes' to " << limit << " at "
+                << path::join(memoryHierarchy.get(), memoryCgroup.get())
+                << " for container " << containerId;
     }
   }
 #endif // __linux__
@@ -982,7 +979,7 @@ Future<ResourceStatistics> DockerContainerizerProcess::usage(
   }
 
   // Construct the Docker container name.
-  string name = DOCKER_NAME_PREFIX + stringify(containerId);
+  string name = containerName(containerId);
   return docker.inspect(name)
     .then(defer(self(), &Self::_usage, containerId, lambda::_1));
 #endif // __linux__
@@ -993,7 +990,7 @@ Future<ResourceStatistics> DockerContainerizerProcess::_usage(
     const ContainerID& containerId,
     const Docker::Container& container)
 {
-  Option<pid_t> pid = container.pid();
+  Option<pid_t> pid = container.pid;
   if (pid.isNone()) {
     return Failure("Container is not running");
   }
@@ -1038,7 +1035,7 @@ Future<containerizer::Termination> DockerContainerizerProcess::wait(
 
 void DockerContainerizerProcess::destroy(
     const ContainerID& containerId,
-    const bool& killed)
+    bool killed)
 {
   if (!promises.contains(containerId)) {
     LOG(WARNING) << "Ignoring destroy of unknown container: " << containerId;
@@ -1071,15 +1068,15 @@ void DockerContainerizerProcess::destroy(
 
   // TODO(benh): Retry 'docker rm -f' if it failed but the container
   // still exists (asynchronously).
-  docker.killAndRm(DOCKER_NAME_PREFIX + stringify(containerId))
+  docker.kill(containerName(containerId), true)
     .onAny(defer(self(), &Self::_destroy, containerId, killed, lambda::_1));
 }
 
 
 void DockerContainerizerProcess::_destroy(
     const ContainerID& containerId,
-    const bool& killed,
-    const Future<Option<int> >& future)
+    bool killed,
+    const Future<Nothing>& future)
 {
   if (!future.isReady()) {
     promises[containerId]->fail(
@@ -1108,7 +1105,7 @@ void DockerContainerizerProcess::_destroy(
 
 void DockerContainerizerProcess::__destroy(
     const ContainerID& containerId,
-    const bool& killed,
+    bool killed,
     const Future<Option<int> >& status)
 {
   containerizer::Termination termination;
@@ -1116,6 +1113,8 @@ void DockerContainerizerProcess::__destroy(
   if (status.isReady() && status.get().isSome()) {
     termination.set_status(status.get().get());
   }
+  termination.set_message(killed ?
+                          "Docker task killed" : "Docker process terminated");
 
   promises[containerId]->set(termination);
 
@@ -1144,29 +1143,6 @@ void DockerContainerizerProcess::reaped(const ContainerID& containerId)
 }
 
 
-Option<ContainerID> DockerContainerizerProcess::parse(
-    const Docker::Container& container)
-{
-  Option<string> name = None();
-
-  if (strings::startsWith(container.name(), DOCKER_NAME_PREFIX)) {
-    name = strings::remove(
-        container.name(), DOCKER_NAME_PREFIX, strings::PREFIX);
-  } else if (strings::startsWith(container.name(), "/" + DOCKER_NAME_PREFIX)) {
-    name = strings::remove(
-        container.name(), "/" + DOCKER_NAME_PREFIX, strings::PREFIX);
-  }
-
-  if (name.isSome()) {
-    ContainerID id;
-    id.set_value(name.get());
-    return id;
-  }
-
-  return None();
-}
-
-
 } // namespace slave {
 } // namespace internal {
 } // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/48e7d4a4/src/slave/containerizer/docker.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/docker.hpp b/src/slave/containerizer/docker.hpp
index f4eb0ff..fbbd45d 100644
--- a/src/slave/containerizer/docker.hpp
+++ b/src/slave/containerizer/docker.hpp
@@ -41,15 +41,11 @@ class DockerContainerizerProcess;
 class DockerContainerizer : public Containerizer
 {
 public:
-  static Try<DockerContainerizer*> create(
-      const Flags& flags,
-      bool local);
-
-  static Try<Nothing> prepareCgroups(const Flags& flags);
+  static Try<DockerContainerizer*> create(const Flags& flags);
 
+  // This is only public for tests.
   DockerContainerizer(
       const Flags& flags,
-      bool local,
       const Docker& docker);
 
   virtual ~DockerContainerizer();

http://git-wip-us.apache.org/repos/asf/mesos/blob/48e7d4a4/src/slave/flags.hpp
----------------------------------------------------------------------
diff --git a/src/slave/flags.hpp b/src/slave/flags.hpp
index 66bba7c..c348109 100644
--- a/src/slave/flags.hpp
+++ b/src/slave/flags.hpp
@@ -279,7 +279,7 @@ public:
 
     add(&Flags::docker,
         "docker",
-        "The path to the docker executable for docker containerizer.",
+        "The absolute path to the docker executable for docker containerizer.",
         "docker");
 
 #ifdef WITH_NETWORK_ISOLATOR

http://git-wip-us.apache.org/repos/asf/mesos/blob/48e7d4a4/src/tests/cgroups_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/cgroups_tests.cpp b/src/tests/cgroups_tests.cpp
index 01cf498..1d2fc36 100644
--- a/src/tests/cgroups_tests.cpp
+++ b/src/tests/cgroups_tests.cpp
@@ -288,6 +288,19 @@ TEST_F(CgroupsAnyHierarchyWithCpuMemoryTest, ROOT_CGROUPS_SubsystemsHierarchy)
 }
 
 
+TEST_F(CgroupsAnyHierarchyWithCpuMemoryTest, ROOT_CGROUPS_FindCgroupSubsystems)
+{
+  pid_t pid = ::getpid();
+  Result<std::string> cpuHierarchy = cgroups::cpu::cgroup(pid);
+  EXPECT_FALSE(cpuHierarchy.isError());
+  EXPECT_SOME(cpuHierarchy);
+
+  Result<std::string> memHierarchy = cgroups::memory::cgroup(pid);
+  EXPECT_FALSE(memHierarchy.isError());
+  EXPECT_SOME(memHierarchy);
+}
+
+
 TEST_F(CgroupsNoHierarchyTest, ROOT_CGROUPS_NOHIERARCHY_MountUnmountHierarchy)
 {
   EXPECT_ERROR(cgroups::mount("/tmp", "cpu"));

http://git-wip-us.apache.org/repos/asf/mesos/blob/48e7d4a4/src/tests/docker_containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/docker_containerizer_tests.cpp b/src/tests/docker_containerizer_tests.cpp
index 84324b0..e936e7e 100644
--- a/src/tests/docker_containerizer_tests.cpp
+++ b/src/tests/docker_containerizer_tests.cpp
@@ -57,15 +57,15 @@ using testing::Return;
 class DockerContainerizerTest : public MesosTest
 {
 public:
-  static bool containerExists(
+  static bool exists(
       const list<Docker::Container>& containers,
       const ContainerID& containerId)
   {
-    string expectedName = slave::DOCKER_NAME_PREFIX + containerId.value();
+    string expectedName = slave::DOCKER_NAME_PREFIX + stringify(containerId);
 
     foreach (const Docker::Container& container, containers) {
       // Docker inspect name contains an extra slash in the beginning.
-      if (strings::contains(container.name(), expectedName)) {
+      if (strings::contains(container.name, expectedName)) {
         return true;
       }
     }
@@ -79,9 +79,8 @@ class MockDockerContainerizer : public DockerContainerizer {
 public:
   MockDockerContainerizer(
       const slave::Flags& flags,
-      bool local,
       const Docker& docker)
-    : DockerContainerizer(flags, local, docker)
+    : DockerContainerizer(flags, docker)
   {
     EXPECT_CALL(*this, launch(_, _, _, _, _, _, _))
       .WillRepeatedly(Invoke(this, &MockDockerContainerizer::_launchExecutor));
@@ -188,9 +187,9 @@ TEST_F(DockerContainerizerTest, DOCKER_Launch_Executor)
 
   slave::Flags flags = CreateSlaveFlags();
 
-  Docker docker(tests::flags.docker);
+  Docker docker = Docker::create(tests::flags.docker, false).get();
 
-  MockDockerContainerizer dockerContainerizer(flags, true, docker);
+  MockDockerContainerizer dockerContainerizer(flags, docker);
 
   Try<PID<Slave> > slave = StartSlave(&dockerContainerizer);
   ASSERT_SOME(slave);
@@ -262,7 +261,7 @@ TEST_F(DockerContainerizerTest, DOCKER_Launch_Executor)
 
   AWAIT_READY(containers);
 
-  ASSERT_TRUE(containerExists(containers.get(), containerId.get()));
+  ASSERT_TRUE(exists(containers.get(), containerId.get()));
 
   Future<containerizer::Termination> termination =
     dockerContainerizer.wait(containerId.get());
@@ -275,7 +274,7 @@ TEST_F(DockerContainerizerTest, DOCKER_Launch_Executor)
   containers = docker.ps(true, slave::DOCKER_NAME_PREFIX);
   AWAIT_READY(containers);
 
-  ASSERT_FALSE(containerExists(containers.get(), containerId.get()));
+  ASSERT_FALSE(exists(containers.get(), containerId.get()));
 
   Shutdown();
 }
@@ -289,9 +288,9 @@ TEST_F(DockerContainerizerTest, DOCKER_Launch)
 
   slave::Flags flags = CreateSlaveFlags();
 
-  Docker docker(tests::flags.docker);
+  Docker docker = Docker::create(tests::flags.docker, false).get();
 
-  MockDockerContainerizer dockerContainerizer(flags, true, docker);
+  MockDockerContainerizer dockerContainerizer(flags, docker);
 
   Try<PID<Slave> > slave = StartSlave(&dockerContainerizer);
   ASSERT_SOME(slave);
@@ -358,7 +357,7 @@ TEST_F(DockerContainerizerTest, DOCKER_Launch)
 
   ASSERT_TRUE(containers.get().size() > 0);
 
-  ASSERT_TRUE(containerExists(containers.get(), containerId.get()));
+  ASSERT_TRUE(exists(containers.get(), containerId.get()));
 
   dockerContainerizer.destroy(containerId.get());
 
@@ -376,9 +375,9 @@ TEST_F(DockerContainerizerTest, DOCKER_Kill)
 
   slave::Flags flags = CreateSlaveFlags();
 
-  Docker docker(tests::flags.docker);
+  Docker docker = Docker::create(tests::flags.docker, false).get();
 
-  MockDockerContainerizer dockerContainerizer(flags, true, docker);
+  MockDockerContainerizer dockerContainerizer(flags, docker);
 
   Try<PID<Slave> > slave = StartSlave(&dockerContainerizer);
   ASSERT_SOME(slave);
@@ -456,7 +455,7 @@ TEST_F(DockerContainerizerTest, DOCKER_Kill)
 
   AWAIT_READY(containers);
 
-  ASSERT_FALSE(containerExists(containers.get(), containerId.get()));
+  ASSERT_FALSE(exists(containers.get(), containerId.get()));
 
   driver.stop();
   driver.join();
@@ -474,9 +473,9 @@ TEST_F(DockerContainerizerTest, DOCKER_Usage)
   slave::Flags flags = CreateSlaveFlags();
   flags.resources = Option<string>("cpus:2;mem:1024");
 
-  Docker docker(tests::flags.docker);
+  Docker docker = Docker::create(tests::flags.docker).get();
 
-  MockDockerContainerizer dockerContainerizer(flags, true, docker);
+  MockDockerContainerizer dockerContainerizer(flags, docker);
 
   Try<PID<Slave> > slave = StartSlave(&dockerContainerizer, flags);
   ASSERT_SOME(slave);
@@ -593,9 +592,9 @@ TEST_F(DockerContainerizerTest, DOCKER_Update)
 
   slave::Flags flags = CreateSlaveFlags();
 
-  Docker docker(tests::flags.docker);
+  Docker docker = Docker::create(tests::flags.docker).get();
 
-  MockDockerContainerizer dockerContainerizer(flags, true, docker);
+  MockDockerContainerizer dockerContainerizer(flags, docker);
 
   Try<PID<Slave> > slave = StartSlave(&dockerContainerizer);
   ASSERT_SOME(slave);
@@ -676,7 +675,7 @@ TEST_F(DockerContainerizerTest, DOCKER_Update)
   ASSERT_SOME(cpuHierarchy);
   ASSERT_SOME(memoryHierarchy);
 
-  Option<pid_t> pid = container.get().pid();
+  Option<pid_t> pid = container.get().pid;
   ASSERT_SOME(pid);
 
   Result<string> cpuCgroup = cgroups::cpu::cgroup(pid.get());
@@ -715,13 +714,20 @@ TEST_F(DockerContainerizerTest, DOCKER_Update)
 #endif //__linux__
 
 
-TEST_F(DockerContainerizerTest, DOCKER_Recover)
+// Disabling recover test as the docker rm in recover is async.
+// Even though we wait for the container to finish, when the wait
+// returns docker rm might still be in progress.
+// TODO(tnachen): Re-enable test when we wait for the async kill
+// to finish. One way to do this is to mock the Docker interface
+// and let the mocked docker collect all the remove futures and
+// at the end of the test wait for all of them before the test exits.
+TEST_F(DockerContainerizerTest, DISABLED_DOCKER_Recover)
 {
   slave::Flags flags = CreateSlaveFlags();
 
-  Docker docker(tests::flags.docker);
+  Docker docker = Docker::create(tests::flags.docker).get();
 
-  MockDockerContainerizer dockerContainerizer(flags, true, docker);
+  MockDockerContainerizer dockerContainerizer(flags, docker);
 
   ContainerID containerId;
   containerId.set_value("c1");
@@ -730,14 +736,14 @@ TEST_F(DockerContainerizerTest, DOCKER_Recover)
 
   Resources resources = Resources::parse("cpus:1;mem:512").get();
 
-  Future<Option<int> > d1 =
+  Future<Nothing> d1 =
     docker.run(
         "busybox",
         "sleep 360",
         slave::DOCKER_NAME_PREFIX + stringify(containerId),
         resources);
 
-  Future<Option<int> > d2 =
+  Future<Nothing> d2 =
     docker.run(
         "busybox",
         "sleep 360",

http://git-wip-us.apache.org/repos/asf/mesos/blob/48e7d4a4/src/tests/docker_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/docker_tests.cpp b/src/tests/docker_tests.cpp
index b7a7b6f..1951d9a 100644
--- a/src/tests/docker_tests.cpp
+++ b/src/tests/docker_tests.cpp
@@ -33,43 +33,40 @@
 using namespace mesos;
 using namespace mesos::internal;
 
-using process::Future;
+using namespace process;
 
 using std::list;
 using std::string;
 
 
-// This test tests the functionality of the
-// docker's interfaces.
+// This test tests the functionality of the  docker's interfaces.
 TEST(DockerTest, DOCKER_interface)
 {
   string containerName = "mesos-docker-test";
   Resources resources = Resources::parse("cpus:1;mem:512").get();
-  Docker docker(tests::flags.docker);
+  Docker docker = Docker::create(tests::flags.docker, false).get();
 
-  // Cleaning up the container first.
-  Future<Option<int> > status = docker.rm(containerName, true);
-  AWAIT_READY(status);
-  ASSERT_SOME(status.get());
+  // Cleaning up the container first if it exists.
+  Future<Nothing> status = docker.rm(containerName, true);
+  ASSERT_TRUE(status.await(Seconds(10)));
 
   // Verify that we do not see the container.
-  Future<list<Docker::Container> > containers = docker.ps(true);
+  Future<list<Docker::Container> > containers = docker.ps(true, containerName);
   AWAIT_READY(containers);
   foreach (const Docker::Container& container, containers.get()) {
-    EXPECT_NE("/" + containerName, container.name());
+    EXPECT_NE("/" + containerName, container.name);
   }
 
   // Start the container.
   status = docker.run("busybox", "sleep 120", containerName, resources);
   AWAIT_READY(status);
-  ASSERT_SOME(status.get());
 
   // Should be able to see the container now.
   containers = docker.ps();
   AWAIT_READY(containers);
   bool found = false;
   foreach (const Docker::Container& container, containers.get()) {
-    if ("/" + containerName == container.name()) {
+    if ("/" + containerName == container.name) {
       found = true;
       break;
     }
@@ -80,75 +77,70 @@ TEST(DockerTest, DOCKER_interface)
   AWAIT_READY(container);
 
   // Test some fields of the container.
-  EXPECT_NE("", container.get().id());
-  EXPECT_EQ("/" + containerName, container.get().name());
-  EXPECT_SOME(container.get().pid());
+  EXPECT_NE("", container.get().id);
+  EXPECT_EQ("/" + containerName, container.get().name);
+  EXPECT_SOME(container.get().pid);
 
   // Kill the container.
   status = docker.kill(containerName);
   AWAIT_READY(status);
-  ASSERT_SOME(status.get());
 
   // Now, the container should not appear in the result of ps().
   // But it should appear in the result of ps(true).
   containers = docker.ps();
   AWAIT_READY(containers);
   foreach (const Docker::Container& container, containers.get()) {
-    EXPECT_NE("/" + containerName, container.name());
+    EXPECT_NE("/" + containerName, container.name);
   }
 
-  containers = docker.ps(true);
+  containers = docker.ps(true, containerName);
   AWAIT_READY(containers);
   found = false;
   foreach (const Docker::Container& container, containers.get()) {
-    if ("/" + containerName == container.name()) {
+    if ("/" + containerName == container.name) {
       found = true;
       break;
     }
   }
   EXPECT_TRUE(found);
 
-  // Check the container's info, both id and name should remain
-  // the same since we haven't removed it, but the pid should be none
+  // Check the container's info, both id and name should remain the
+  // same since we haven't removed it, but the pid should be none
   // since it's not running.
   container = docker.inspect(containerName);
   AWAIT_READY(container);
 
-  EXPECT_NE("", container.get().id());
-  EXPECT_EQ("/" + containerName, container.get().name());
-  EXPECT_NONE(container.get().pid());
+  EXPECT_NE("", container.get().id);
+  EXPECT_EQ("/" + containerName, container.get().name);
+  EXPECT_NONE(container.get().pid);
 
   // Remove the container.
   status = docker.rm(containerName);
   AWAIT_READY(status);
-  ASSERT_SOME(status.get());
 
   // Should not be able to inspect the container.
   container = docker.inspect(containerName);
   AWAIT_FAILED(container);
 
-  // Also, now we should not be able to see the container
-  // by invoking ps(true).
-  containers = docker.ps(true);
+  // Also, now we should not be able to see the container by invoking
+  // ps(true).
+  containers = docker.ps(true, containerName);
   AWAIT_READY(containers);
   foreach (const Docker::Container& container, containers.get()) {
-    EXPECT_NE("/" + containerName, container.name());
+    EXPECT_NE("/" + containerName, container.name);
   }
 
   // Start the container again, this time we will do a "rm -f"
   // directly, instead of killing and rm.
-  //
-  // First, Invoke docker.run()
   status = docker.run("busybox", "sleep 120", containerName, resources);
   AWAIT_READY(status);
-  ASSERT_SOME(status.get());
 
   // Verify that the container is there.
   containers = docker.ps();
   AWAIT_READY(containers);
   found = false;
   foreach (const Docker::Container& container, containers.get()) {
-    if ("/" + containerName == container.name()) {
+    if ("/" + containerName == container.name) {
       found = true;
       break;
     }
@@ -158,18 +150,17 @@ TEST(DockerTest, DOCKER_interface)
   // Then do a "rm -f".
   status = docker.rm(containerName, true);
   AWAIT_READY(status);
-  ASSERT_SOME(status.get());
 
-  // Verify that the container is totally removed,
-  // that is we can't find it by ps() or ps(true).
+  // Verify that the container is totally removed, that is we can't
+  // find it by ps() or ps(true).
   containers = docker.ps();
   AWAIT_READY(containers);
   foreach (const Docker::Container& container, containers.get()) {
-    EXPECT_NE("/" + containerName, container.name());
+    EXPECT_NE("/" + containerName, container.name);
   }
-  containers = docker.ps(true);
+  containers = docker.ps(true, containerName);
   AWAIT_READY(containers);
   foreach (const Docker::Container& container, containers.get()) {
-    EXPECT_NE("/" + containerName, container.name());
+    EXPECT_NE("/" + containerName, container.name);
   }
 }

http://git-wip-us.apache.org/repos/asf/mesos/blob/48e7d4a4/src/tests/environment.cpp
----------------------------------------------------------------------
diff --git a/src/tests/environment.cpp b/src/tests/environment.cpp
index 6c80fa3..eec7d3e 100644
--- a/src/tests/environment.cpp
+++ b/src/tests/environment.cpp
@@ -29,7 +29,6 @@
 
 #include <process/gmock.hpp>
 #include <process/gtest.hpp>
-#include <process/subprocess.hpp>
 
 #include <stout/check.hpp>
 #include <stout/error.hpp>
@@ -131,29 +130,30 @@ static bool enable(const ::testing::TestInfo& test)
     }
 #endif
 
+    // Filter out benchmark tests when we run 'make check'.
+    if (strings::contains(name, "BENCHMARK_") && !flags.benchmark) {
+      return false;
+    }
+
     if (strings::contains(name, "DOCKER_")) {
-      Docker docker(flags.docker);
-      Try<Nothing> validate = Docker::validate(docker);
-      if (validate.isError()) {
+      Try<Docker> docker = Docker::create(flags.docker);
+      if (docker.isError()) {
         std::cerr
           << "-------------------------------------------------------------\n"
           << "Skipping Docker tests because validation failed\n"
-          << "[Error] " + validate.error() + "\n"
+          << "[Error] " + docker.error() + "\n"
           << "-------------------------------------------------------------"
           << std::endl;
+
+        return false;
       }
 
 #ifdef __linux__
-      return user.get() == "root" && !validate.isError();
-#else
-      return !validate.isError();
+      if (user.get() != "root") {
+        return false;
+      }
 #endif
     }
-
-    // Filter out benchmark tests when we run 'make check'.
-    if (strings::contains(name, "BENCHMARK_") && !flags.benchmark) {
-      return false;
-    }
   }
 
   // Filter out regular tests when we run 'make bench', which
@@ -170,7 +170,9 @@ static bool enable(const ::testing::TestInfo& test)
     const string& type = test.type_param();
     if (strings::contains(type, "Cgroups")) {
 #ifdef __linux__
-      return user.get() == "root" && cgroups::enabled();
+      if (user.get() != "root" || !cgroups::enabled()) {
+        return false;
+      }
 #else
       return false;
 #endif


[37/43] git commit: Implemented launching executors in DockerContainerizer.

Posted by be...@apache.org.
Implemented launching executors in DockerContainerizer.

Also added a test for running an executor that builds a Docker image
from a Dockerfile.


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

Branch: refs/heads/master
Commit: 2caf7b9a42c5edd17d980ca1f24dd6adededb1ce
Parents: fb27015
Author: Timothy Chen <tn...@gmail.com>
Authored: Mon Jul 7 11:02:45 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:17 2014 -0700

----------------------------------------------------------------------
 src/Makefile.am                                 |   3 +
 src/docker/docker.cpp                           |  36 ++--
 src/docker/docker.hpp                           |   3 +-
 src/slave/containerizer/docker.cpp              | 160 ++++++++++++++-
 src/tests/docker_containerizer_tests.cpp        | 199 ++++++++++++++++---
 src/tests/environment.cpp                       |  34 +++-
 .../mesos_test_executor_docker_image/Dockerfile |  16 ++
 .../mesos_test_executor_docker_image/install.sh |   6 +
 8 files changed, 409 insertions(+), 48 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/2caf7b9a/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index d0b3285..850fad3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1154,6 +1154,9 @@ EXTRA_DIST += examples/python/test_containerizer.py			\
 	      examples/python/test_framework.py
 
 
+# Docker test executor image files.
+EXTRA_DIST += tests/mesos_test_executor_docker_image/Dockerfile         \
+              tests/mesos_test_executor_docker_image/install.sh
 
 dist_check_SCRIPTS +=							\
   tests/balloon_framework_test.sh					\

http://git-wip-us.apache.org/repos/asf/mesos/blob/2caf7b9a/src/docker/docker.cpp
----------------------------------------------------------------------
diff --git a/src/docker/docker.cpp b/src/docker/docker.cpp
index 1142ef3..4842cee 100644
--- a/src/docker/docker.cpp
+++ b/src/docker/docker.cpp
@@ -116,27 +116,37 @@ Future<Option<int> > Docker::run(
     const string& image,
     const string& command,
     const string& name,
-    const mesos::Resources& resources) const
+    const Option<mesos::Resources>& resources,
+    const Option<map<string, string> >& env) const
 {
-  CHECK(resources.size() != 0);
 
   string cmd = " run -d";
 
-  // TODO(yifan): Support other resources (e.g. disk, ports).
-  Option<double> cpus = resources.cpus();
-  if (cpus.isSome()) {
-    uint64_t cpuShare =
-      std::max((uint64_t) (CPU_SHARES_PER_CPU * cpus.get()), MIN_CPU_SHARES);
-    cmd += " -c " + stringify(cpuShare);
+  if (resources.isSome()) {
+    // TODO(yifan): Support other resources (e.g. disk, ports).
+    Option<double> cpus = resources.get().cpus();
+    if (cpus.isSome()) {
+      uint64_t cpuShare =
+	std::max((uint64_t) (CPU_SHARES_PER_CPU * cpus.get()), MIN_CPU_SHARES);
+      cmd += " -c " + stringify(cpuShare);
+    }
+
+    Option<Bytes> mem = resources.get().mem();
+    if (mem.isSome()) {
+      Bytes memLimit = std::max(mem.get(), MIN_MEMORY);
+      cmd += " -m " + stringify(memLimit.bytes());
+    }
   }
 
-  Option<Bytes> mem = resources.mem();
-  if (mem.isSome()) {
-    Bytes memLimit = std::max(mem.get(), MIN_MEMORY);
-    cmd += " -m " + stringify(memLimit.bytes());
+  if (env.isSome()) {
+    foreachpair (string key, string value, env.get()) {
+      key = strings::replace(key, "\"", "\\\"");
+      value = strings::replace(value, "\"", "\\\"");
+      cmd += " -e \"" + key + "=" + value + "\"";
+    }
   }
 
-  cmd += " --name=" + name + " " + image + " " + command;
+  cmd += " --net=host --name=" + name + " " + image + " " + command;
 
   VLOG(1) << "Running " << path << cmd;
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/2caf7b9a/src/docker/docker.hpp
----------------------------------------------------------------------
diff --git a/src/docker/docker.hpp b/src/docker/docker.hpp
index 912859c..c4724de 100644
--- a/src/docker/docker.hpp
+++ b/src/docker/docker.hpp
@@ -67,7 +67,8 @@ public:
       const std::string& image,
       const std::string& command,
       const std::string& name,
-      const mesos::Resources& resources) const;
+      const Option<mesos::Resources>& resources = None(),
+      const Option<std::map<std::string, std::string> >& env = None()) const;
 
   // Performs 'docker kill CONTAINER'.
   process::Future<Option<int> > kill(

http://git-wip-us.apache.org/repos/asf/mesos/blob/2caf7b9a/src/slave/containerizer/docker.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/docker.cpp b/src/slave/containerizer/docker.cpp
index 60ac262..7d3549c 100644
--- a/src/slave/containerizer/docker.cpp
+++ b/src/slave/containerizer/docker.cpp
@@ -134,6 +134,23 @@ private:
       bool checkpoint,
       const Option<int>& status);
 
+  process::Future<bool> _launch(
+      const ContainerID& containerId,
+      const ExecutorInfo& executorInfo,
+      const SlaveID& slaveId,
+      const PID<Slave>& slavePid,
+      bool checkpoint,
+      const Option<int>& status);
+
+  process::Future<bool> __launch(
+      const ContainerID& containerId,
+      const ExecutorInfo& executorInfo,
+      const SlaveID& slaveId,
+      const PID<Slave>& slavePid,
+      bool checkpoint,
+      const Docker::Container& container);
+
+
   void _destroy(
       const ContainerID& containerId,
       const bool& killed,
@@ -470,8 +487,68 @@ Future<bool> DockerContainerizerProcess::launch(
     const PID<Slave>& slavePid,
     bool checkpoint)
 {
-  // TODO(benh): Implement support for launching an ExecutorInfo.
-  return false;
+  if (promises.contains(containerId)) {
+    LOG(ERROR) << "Cannot start already running container '"
+               << containerId << "'";
+    return Failure("Container already started");
+  }
+
+  CommandInfo command = executorInfo.command();
+
+  if (!command.has_container()) {
+    return false;
+  }
+
+  string image = command.container().image();
+
+  // Check if we should try and launch this command.
+  if (!strings::startsWith(image, "docker:///")) {
+    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 = DOCKER_NAME_PREFIX + stringify(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();
+  }
+
+  Resources resources = executorInfo.resources();
+
+  // 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)
+    .then(defer(self(),
+               &Self::_launch,
+               containerId,
+               executorInfo,
+               slaveId,
+               slavePid,
+               checkpoint,
+               lambda::_1))
+    .onFailed(defer(self(), &Self::destroy, containerId, false));
 }
 
 
@@ -646,6 +723,84 @@ Future<bool> DockerContainerizerProcess::_launch(
 }
 
 
+Future<bool> DockerContainerizerProcess::_launch(
+    const ContainerID& containerId,
+    const ExecutorInfo& executorInfo,
+    const SlaveID& slaveId,
+    const PID<Slave>& slavePid,
+    bool checkpoint,
+    const Option<int>& status)
+{
+  if (status.isSome() && status.get() != 0) {
+    // Best effort kill and remove the container just in case.
+    docker.killAndRm(DOCKER_NAME_PREFIX + stringify(containerId));
+    return Failure("Failed to run the container (" +
+                   WSTRINGIFY(status.get()) + ")");
+  }
+
+  return docker.inspect(DOCKER_NAME_PREFIX + stringify(containerId))
+    .then(defer(self(),
+                &Self::__launch,
+                containerId,
+                executorInfo,
+                slaveId,
+                slavePid,
+                checkpoint,
+                lambda::_1));
+}
+
+
+Future<bool> DockerContainerizerProcess::__launch(
+    const ContainerID& containerId,
+    const ExecutorInfo& executorInfo,
+    const SlaveID& slaveId,
+    const PID<Slave>& slavePid,
+    bool checkpoint,
+    const Docker::Container& container)
+{
+  Option<int> pid = container.pid();
+
+  if (!pid.isSome()) {
+    return Failure("Unable to get executor pid after launch");
+  }
+
+  if (checkpoint) {
+    // TODO(tnachen): We might not be able to checkpoint
+    // if the slave dies before it can checkpoint while
+    // the executor is still running. Optinally we can consider
+    // recording the slave id and executor id as part of the
+    // docker container name so we can recover from this.
+    const string& path =
+      slave::paths::getForkedPidPath(
+          slave::paths::getMetaRootDir(flags.work_dir),
+          slaveId,
+          executorInfo.framework_id(),
+          executorInfo.executor_id(),
+          containerId);
+
+    LOG(INFO) << "Checkpointing executor's forked pid "
+              << pid.get() << " to '" << path <<  "'";
+
+    Try<Nothing> checkpointed =
+      slave::state::checkpoint(path, stringify(pid.get()));
+
+    if (checkpointed.isError()) {
+      LOG(ERROR) << "Failed to checkpoint executor's forked pid to '"
+                 << path << "': " << checkpointed.error();
+
+      return Failure("Could not checkpoint executor's pid");
+    }
+  }
+
+  statuses[containerId] = process::reap(pid.get());
+
+  statuses[containerId]
+    .onAny(defer(self(), &Self::reaped, containerId));
+
+  return true;
+}
+
+
 Future<Nothing> DockerContainerizerProcess::update(
     const ContainerID& containerId,
     const Resources& _resources)
@@ -865,6 +1020,7 @@ Future<ResourceStatistics> DockerContainerizerProcess::_usage(
   if (cpus.isSome()) {
     result.set_cpus_limit(cpus.get());
   }
+
   return result;
 }
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/2caf7b9a/src/tests/docker_containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/docker_containerizer_tests.cpp b/src/tests/docker_containerizer_tests.cpp
index 2fa18e0..1f5bc60 100644
--- a/src/tests/docker_containerizer_tests.cpp
+++ b/src/tests/docker_containerizer_tests.cpp
@@ -54,7 +54,26 @@ using testing::DoDefault;
 using testing::Invoke;
 using testing::Return;
 
-class DockerContainerizerTest : public MesosTest {};
+class DockerContainerizerTest : public MesosTest
+{
+public:
+  static bool containerExists(
+      const list<Docker::Container>& containers,
+      const ContainerID& containerId)
+  {
+    string expectedName = slave::DOCKER_NAME_PREFIX + containerId.value();
+
+    foreach (const Docker::Container& container, containers) {
+      // Docker inspect name contains an extra slash in the beginning.
+      if (strings::contains(container.name(), expectedName)) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+};
+
 
 class MockDockerContainerizer : public DockerContainerizer {
 public:
@@ -64,6 +83,9 @@ public:
       const Docker& docker)
     : DockerContainerizer(flags, local, docker)
   {
+    EXPECT_CALL(*this, launch(_, _, _, _, _, _, _))
+      .WillRepeatedly(Invoke(this, &MockDockerContainerizer::_launchExecutor));
+
     EXPECT_CALL(*this, launch(_, _, _, _, _, _, _, _))
       .WillRepeatedly(Invoke(this, &MockDockerContainerizer::_launch));
 
@@ -71,6 +93,19 @@ public:
       .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>(
@@ -112,6 +147,25 @@ public:
         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)
@@ -123,7 +177,11 @@ public:
 };
 
 
-TEST_F(DockerContainerizerTest, DOCKER_Launch)
+// Only enable executor launch on linux as other platforms
+// requires running linux VM and need special port forwarding
+// to get host networking to work.
+#ifdef __linux__
+TEST_F(DockerContainerizerTest, DOCKER_Launch_Executor)
 {
   Try<PID<Master> > master = StartMaster();
   ASSERT_SOME(master);
@@ -165,6 +223,107 @@ TEST_F(DockerContainerizerTest, DOCKER_Launch)
   task.mutable_slave_id()->CopyFrom(offer.slave_id());
   task.mutable_resources()->CopyFrom(offer.resources());
 
+  ExecutorInfo executorInfo;
+  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:///mesos/test-executor");
+  executorInfo.mutable_command()->CopyFrom(command);
+
+  task.mutable_executor()->CopyFrom(executorInfo);
+
+  vector<TaskInfo> tasks;
+  tasks.push_back(task);
+
+  Future<ContainerID> containerId;
+  EXPECT_CALL(dockerContainerizer, launch(_, _, _, _, _, _, _))
+    .WillOnce(DoAll(FutureArg<0>(&containerId),
+                    Invoke(&dockerContainerizer,
+                           &MockDockerContainerizer::_launchExecutor)));
+
+  Future<TaskStatus> statusRunning;
+  Future<TaskStatus> statusFinished;
+  EXPECT_CALL(sched, statusUpdate(&driver, _))
+    .WillOnce(FutureArg<1>(&statusRunning))
+    .WillOnce(FutureArg<1>(&statusFinished));
+
+  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<list<Docker::Container> > containers =
+    docker.ps(true, slave::DOCKER_NAME_PREFIX);
+
+  AWAIT_READY(containers);
+
+  ASSERT_TRUE(containerExists(containers.get(), containerId.get()));
+
+  Future<containerizer::Termination> termination =
+    dockerContainerizer.wait(containerId.get());
+
+  driver.stop();
+  driver.join();
+
+  AWAIT_READY(termination);
+
+  containers = docker.ps(true, slave::DOCKER_NAME_PREFIX);
+  AWAIT_READY(containers);
+
+  ASSERT_FALSE(containerExists(containers.get(), containerId.get()));
+
+  Shutdown();
+}
+#endif // __linux__
+
+
+TEST_F(DockerContainerizerTest, DOCKER_Launch)
+{
+  Try<PID<Master> > master = StartMaster();
+  ASSERT_SOME(master);
+
+  slave::Flags flags = CreateSlaveFlags();
+
+  Docker docker(tests::flags.docker);
+
+  MockDockerContainerizer dockerContainerizer(flags, true, docker);
+
+  Try<PID<Slave> > slave = StartSlave(&dockerContainerizer);
+  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);
+  EXPECT_NE(0u, offers.get().size());
+
+  const Offer& offer = offers.get()[0];
+
+  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;
   CommandInfo::ContainerInfo* containerInfo = command.mutable_container();
   containerInfo->set_image("docker:///busybox");
@@ -199,18 +358,7 @@ TEST_F(DockerContainerizerTest, DOCKER_Launch)
 
   ASSERT_TRUE(containers.get().size() > 0);
 
-  bool foundContainer = false;
-  string expectedName = slave::DOCKER_NAME_PREFIX + containerId.get().value();
-
-  foreach (const Docker::Container& container, containers.get()) {
-    // Docker inspect name contains an extra slash in the beginning.
-    if (strings::contains(container.name(), expectedName)) {
-      foundContainer = true;
-      break;
-    }
-  }
-
-  ASSERT_TRUE(foundContainer);
+  ASSERT_TRUE(containerExists(containers.get(), containerId.get()));
 
   dockerContainerizer.destroy(containerId.get());
 
@@ -237,7 +385,7 @@ TEST_F(DockerContainerizerTest, DOCKER_Kill)
 
   MockScheduler sched;
   MesosSchedulerDriver driver(
-    &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
 
   Future<FrameworkID> frameworkId;
   EXPECT_CALL(sched, registered(&driver, _, _))
@@ -308,18 +456,7 @@ TEST_F(DockerContainerizerTest, DOCKER_Kill)
 
   AWAIT_READY(containers);
 
-  bool foundContainer = false;
-  string expectedName = slave::DOCKER_NAME_PREFIX + containerId.get().value();
-
-  foreach (const Docker::Container& container, containers.get()) {
-    // Docker inspect name contains an extra slash in the beginning.
-    if (strings::contains(container.name(), expectedName)) {
-      foundContainer = true;
-      break;
-    }
-  }
-
-  ASSERT_FALSE(foundContainer);
+  ASSERT_FALSE(containerExists(containers.get(), containerId.get()));
 
   driver.stop();
   driver.join();
@@ -346,7 +483,7 @@ TEST_F(DockerContainerizerTest, DOCKER_Usage)
 
   MockScheduler sched;
   MesosSchedulerDriver driver(
-    &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
 
   Future<FrameworkID> frameworkId;
   EXPECT_CALL(sched, registered(&driver, _, _))
@@ -465,7 +602,7 @@ TEST_F(DockerContainerizerTest, DOCKER_Update)
 
   MockScheduler sched;
   MesosSchedulerDriver driver(
-    &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
 
   Future<FrameworkID> frameworkId;
   EXPECT_CALL(sched, registered(&driver, _, _))
@@ -504,8 +641,8 @@ TEST_F(DockerContainerizerTest, DOCKER_Update)
   Future<ContainerID> containerId;
   EXPECT_CALL(dockerContainerizer, launch(_, _, _, _, _, _, _, _))
     .WillOnce(DoAll(FutureArg<0>(&containerId),
-         Invoke(&dockerContainerizer,
-                &MockDockerContainerizer::_launch)));
+                    Invoke(&dockerContainerizer,
+                           &MockDockerContainerizer::_launch)));
 
   Future<TaskStatus> statusRunning;
   EXPECT_CALL(sched, statusUpdate(&driver, _))

http://git-wip-us.apache.org/repos/asf/mesos/blob/2caf7b9a/src/tests/environment.cpp
----------------------------------------------------------------------
diff --git a/src/tests/environment.cpp b/src/tests/environment.cpp
index b75c485..b1c70e7 100644
--- a/src/tests/environment.cpp
+++ b/src/tests/environment.cpp
@@ -29,6 +29,7 @@
 
 #include <process/gmock.hpp>
 #include <process/gtest.hpp>
+#include <process/subprocess.hpp>
 
 #include <stout/check.hpp>
 #include <stout/error.hpp>
@@ -143,7 +144,38 @@ static bool enable(const ::testing::TestInfo& test)
       }
 
 #ifdef __linux__
-      return user.get() == "root" && !validate.isError();
+      if (user.get() == "root" && !validate.isError()) {
+        // Install docker test executor image for testing launching
+        // executor in docker image.
+	Try<process::Subprocess> install = 
+          process::subprocess(
+	      path::join(
+                flags.source_dir,
+		"src",
+                "tests",
+                "mesos_test_executor_docker_image",
+                "install.sh"));
+
+        if (install.isError()) {
+	  std::cerr
+            << "Unable to launch test executor install script: "
+	    << install.error()
+            << std::endl;
+          return false;
+        }
+
+	process::Future<Option<int> > status = install.get().status();
+        status.await(Minutes(2));
+
+	if (!status.isReady() || !status.get().isSome() || status.get() != 0) {
+	  std::cerr << "Unable to install test executor";
+          return false;
+	}
+
+        return true;
+      }
+
+      return false;
 #else
       return !validate.isError();
 #endif

http://git-wip-us.apache.org/repos/asf/mesos/blob/2caf7b9a/src/tests/mesos_test_executor_docker_image/Dockerfile
----------------------------------------------------------------------
diff --git a/src/tests/mesos_test_executor_docker_image/Dockerfile b/src/tests/mesos_test_executor_docker_image/Dockerfile
new file mode 100644
index 0000000..8ecc374
--- /dev/null
+++ b/src/tests/mesos_test_executor_docker_image/Dockerfile
@@ -0,0 +1,16 @@
+FROM stackbrew/ubuntu:13.10
+MAINTAINER Timothy Chen <tn...@apache.org>
+
+ADD http://downloads.mesosphere.io/master/ubuntu/13.10/mesos-test-executor.deb /tmp/mesos.deb
+
+RUN ["env", "DEBIAN_FRONTEND=noninteractive", "apt-get", "update"]
+RUN ["env", "DEBIAN_FRONTEND=noninteractive", "apt-get", "install", "-y", "--fix-missing", "--force-yes", "libsasl2-2", "libcurl3"]
+RUN ["env", "DEBIAN_FRONTEND=noninteractive", "apt-get", "install", "-y", "--fix-missing", "--force-yes", "default-jre-headless"]
+
+RUN ["env", "DEBIAN_FRONTEND=noninteractive", "dpkg", "-i", "/tmp/mesos.deb"]
+RUN ["bash", "-c", "echo manual > /etc/init/mesos-master.override"]
+RUN ["bash", "-c", "echo manual > /etc/init/mesos-slave.override"]
+
+RUN ["rm", "-rf", "/tmp/mesos.deb"]
+
+CMD ["true"]

http://git-wip-us.apache.org/repos/asf/mesos/blob/2caf7b9a/src/tests/mesos_test_executor_docker_image/install.sh
----------------------------------------------------------------------
diff --git a/src/tests/mesos_test_executor_docker_image/install.sh b/src/tests/mesos_test_executor_docker_image/install.sh
new file mode 100755
index 0000000..dcec4e0
--- /dev/null
+++ b/src/tests/mesos_test_executor_docker_image/install.sh
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+
+docker images | cut -d" " -f1 | grep -q mesos/test-executor
+if [ $? -ne 0 ]; then
+    docker build -t mesos/test-executor `dirname $0`
+fi


[18/43] git commit: Added license in docker/docker.cpp.

Posted by be...@apache.org.
Added license in docker/docker.cpp.


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

Branch: refs/heads/master
Commit: 965e29ad239d8d8938e2190c9d496636f6cb6942
Parents: 5e7c4a4
Author: Yifan Gu <gu...@gmail.com>
Authored: Thu Jul 3 11:00:58 2014 -0700
Committer: Benjamin Hindman <be...@gmail.com>
Committed: Mon Aug 4 15:08:16 2014 -0700

----------------------------------------------------------------------
 src/docker/docker.cpp | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/965e29ad/src/docker/docker.cpp
----------------------------------------------------------------------
diff --git a/src/docker/docker.cpp b/src/docker/docker.cpp
index 0652a53..0283b67 100644
--- a/src/docker/docker.cpp
+++ b/src/docker/docker.cpp
@@ -1,3 +1,21 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 #include <map>
 #include <vector>