You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by ji...@apache.org on 2015/07/25 01:37:54 UTC

[01/12] mesos git commit: Moved containerizer related tests under src/tests/containerizer.

Repository: mesos
Updated Branches:
  refs/heads/master 50696fa2f -> 963513722


http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/routing_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/routing_tests.cpp b/src/tests/routing_tests.cpp
deleted file mode 100644
index e4f1bcf..0000000
--- a/src/tests/routing_tests.cpp
+++ /dev/null
@@ -1,1416 +0,0 @@
-/**
- * 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 <signal.h>
-#include <unistd.h>
-
-#include <linux/version.h>
-
-#include <sys/types.h>
-#include <sys/wait.h>
-
-#include <gtest/gtest.h>
-
-#include <process/clock.hpp>
-#include <process/gtest.hpp>
-
-#include <stout/foreach.hpp>
-#include <stout/gtest.hpp>
-#include <stout/hashmap.hpp>
-#include <stout/ip.hpp>
-#include <stout/mac.hpp>
-#include <stout/net.hpp>
-#include <stout/stringify.hpp>
-
-#include "linux/routing/handle.hpp"
-#include "linux/routing/route.hpp"
-#include "linux/routing/utils.hpp"
-
-#include "linux/routing/diagnosis/diagnosis.hpp"
-
-#include "linux/routing/filter/basic.hpp"
-#include "linux/routing/filter/handle.hpp"
-#include "linux/routing/filter/icmp.hpp"
-#include "linux/routing/filter/ip.hpp"
-
-#include "linux/routing/link/link.hpp"
-
-#include "linux/routing/queueing/fq_codel.hpp"
-#include "linux/routing/queueing/htb.hpp"
-#include "linux/routing/queueing/ingress.hpp"
-#include "linux/routing/queueing/statistics.hpp"
-
-using namespace process;
-
-using namespace routing;
-using namespace routing::filter;
-using namespace routing::queueing;
-
-using std::endl;
-using std::set;
-using std::string;
-using std::vector;
-
-namespace mesos {
-namespace internal {
-namespace tests {
-
-
-static const string TEST_VETH_LINK = "veth-test";
-static const string TEST_PEER_LINK = "veth-peer";
-
-
-class RoutingTest : public ::testing::Test
-{
-protected:
-  virtual void SetUp()
-  {
-    ASSERT_SOME(routing::check())
-      << "-------------------------------------------------------------\n"
-      << "We cannot run any routing tests because either your libnl\n"
-      << "library or kernel is not new enough. You can either upgrade,\n"
-      << "or disable this test case\n"
-      << "-------------------------------------------------------------";
-  }
-};
-
-
-// Tests that require setting up virtual ethernet on host.
-class RoutingVethTest : public RoutingTest
-{
-protected:
-  virtual void SetUp()
-  {
-    RoutingTest::SetUp();
-
-    // Clean up the test links, in case it wasn't cleaned up properly
-    // from previous tests.
-    link::remove(TEST_VETH_LINK);
-
-    ASSERT_SOME_FALSE(link::exists(TEST_VETH_LINK));
-    ASSERT_SOME_FALSE(link::exists(TEST_PEER_LINK));
-  }
-
-  virtual void TearDown()
-  {
-    link::remove(TEST_VETH_LINK);
-  }
-};
-
-
-TEST_F(RoutingTest, PortRange)
-{
-  Try<ip::PortRange> ports = ip::PortRange::fromBeginEnd(1, 0);
-  EXPECT_ERROR(ports);
-
-  ports = ip::PortRange::fromBeginEnd(4, 11);
-  EXPECT_ERROR(ports);
-
-  ports = ip::PortRange::fromBeginEnd(4, 7);
-  ASSERT_SOME(ports);
-  EXPECT_EQ(4u, ports.get().begin());
-  EXPECT_EQ(7u, ports.get().end());
-  EXPECT_EQ(0xfffc, ports.get().mask());
-  EXPECT_EQ("[4,7]", stringify(ports.get()));
-
-  ports = ip::PortRange::fromBeginEnd(10, 10);
-  ASSERT_SOME(ports);
-  EXPECT_EQ(10u, ports.get().begin());
-  EXPECT_EQ(10u, ports.get().end());
-  EXPECT_EQ(0xffff, ports.get().mask());
-  EXPECT_EQ("[10,10]", stringify(ports.get()));
-
-  ports = ip::PortRange::fromBeginMask(20, 0xffff);
-  ASSERT_SOME(ports);
-  EXPECT_EQ(20u, ports.get().begin());
-  EXPECT_EQ(20u, ports.get().end());
-  EXPECT_EQ(0xffff, ports.get().mask());
-  EXPECT_EQ("[20,20]", stringify(ports.get()));
-
-  ports = ip::PortRange::fromBeginMask(1024, 0xfff8);
-  ASSERT_SOME(ports);
-  EXPECT_EQ(1024u, ports.get().begin());
-  EXPECT_EQ(1031u, ports.get().end());
-  EXPECT_EQ(0xfff8, ports.get().mask());
-  EXPECT_EQ("[1024,1031]", stringify(ports.get()));
-}
-
-
-TEST_F(RoutingTest, RouteTable)
-{
-  Try<vector<route::Rule> > table = route::table();
-  EXPECT_SOME(table);
-
-  Result<net::IP> gateway = route::defaultGateway();
-  EXPECT_FALSE(gateway.isError());
-}
-
-
-TEST_F(RoutingTest, LinkIndex)
-{
-  Try<set<string> > links = net::links();
-  ASSERT_SOME(links);
-
-  foreach (const string& link, links.get()) {
-    EXPECT_SOME_NE(0, link::index(link));
-  }
-
-  EXPECT_NONE(link::index("not-exist"));
-}
-
-
-TEST_F(RoutingTest, LinkName)
-{
-  Try<set<string> > links = net::links();
-  ASSERT_SOME(links);
-
-  foreach (const string& link, links.get()) {
-    EXPECT_SOME_NE(0, link::index(link));
-    EXPECT_SOME_EQ(link, link::name(link::index(link).get()));
-  }
-}
-
-
-TEST_F(RoutingTest, LinkStatistics)
-{
-  Try<set<string> > links = net::links();
-  ASSERT_SOME(links);
-
-  foreach (const string& link, links.get()) {
-    Result<hashmap<string, uint64_t> > statistics = link::statistics(link);
-
-    ASSERT_SOME(statistics);
-    EXPECT_TRUE(statistics.get().contains("rx_packets"));
-    EXPECT_TRUE(statistics.get().contains("rx_bytes"));
-    EXPECT_TRUE(statistics.get().contains("tx_packets"));
-    EXPECT_TRUE(statistics.get().contains("tx_bytes"));
-  }
-
-  EXPECT_NONE(link::statistics("not-exist"));
-}
-
-
-TEST_F(RoutingTest, LinkExists)
-{
-  Try<set<string> > links = net::links();
-  ASSERT_SOME(links);
-
-  foreach (const string& link, links.get()) {
-    EXPECT_SOME_TRUE(link::exists(link));
-  }
-
-  EXPECT_SOME_FALSE(link::exists("not-exist"));
-}
-
-
-TEST_F(RoutingTest, Eth0)
-{
-  Result<string> eth0 = link::eth0();
-  EXPECT_FALSE(eth0.isError());
-
-  if (eth0.isSome()) {
-    ASSERT_SOME_TRUE(link::exists(eth0.get()));
-  }
-}
-
-
-TEST_F(RoutingTest, Lo)
-{
-  Result<string> lo = link::lo();
-  EXPECT_FALSE(lo.isError());
-
-  if (lo.isSome()) {
-    ASSERT_SOME_TRUE(link::exists(lo.get()));
-  }
-}
-
-
-TEST_F(RoutingTest, INETSockets)
-{
-  Try<vector<diagnosis::socket::Info> > infos =
-    diagnosis::socket::infos(AF_INET, diagnosis::socket::state::ALL);
-
-  EXPECT_SOME(infos);
-
-  foreach (const diagnosis::socket::Info& info, infos.get()) {
-    // Both source and destination IPs should be present since
-    // 'AF_INET' is asked for.
-    EXPECT_SOME(info.sourceIP);
-    EXPECT_SOME(info.destinationIP);
-  }
-}
-
-
-TEST_F(RoutingVethTest, ROOT_LinkCreate)
-{
-  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
-
-  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
-  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
-
-  EXPECT_SOME_NE(0, link::index(TEST_VETH_LINK));
-  EXPECT_SOME_NE(0, link::index(TEST_PEER_LINK));
-
-  // Test the case where the veth (with the same name) already exists.
-  EXPECT_SOME_FALSE(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
-}
-
-
-TEST_F(RoutingVethTest, ROOT_LinkRemove)
-{
-  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
-
-  EXPECT_SOME_TRUE(link::remove(TEST_VETH_LINK));
-  EXPECT_SOME_FALSE(link::remove(TEST_VETH_LINK));
-  EXPECT_SOME_FALSE(link::remove(TEST_PEER_LINK));
-}
-
-
-// An old glibc might not have this symbol.
-#ifndef CLONE_NEWNET
-#define CLONE_NEWNET 0x40000000
-#endif
-
-
-// Entry point of the child process (used in clone()).
-static int child(void*)
-{
-  // Wait to be killed.
-  while (true) {
-    sleep(1);
-  }
-
-  // Should not reach here.
-  ABORT("Child process should not reach here");
-}
-
-
-TEST_F(RoutingVethTest, ROOT_LinkCreatePid)
-{
-  // Stack used in the child process.
-  unsigned long long stack[32];
-
-  pid_t pid = ::clone(child, &stack[31], CLONE_NEWNET | SIGCHLD, NULL);
-  ASSERT_NE(-1, pid);
-
-  // In parent process.
-  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, pid));
-  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
-
-  // The peer should not exist in parent network namespace.
-  EXPECT_SOME_FALSE(link::exists(TEST_PEER_LINK));
-
-  // TODO(jieyu): Enter the child network namespace and make sure that
-  // the TEST_PEER_LINK is there.
-
-  EXPECT_SOME_NE(0, link::index(TEST_VETH_LINK));
-
-  // Kill the child process.
-  ASSERT_NE(-1, kill(pid, SIGKILL));
-
-  // Wait for the child process.
-  int status;
-  EXPECT_NE(-1, waitpid((pid_t) -1, &status, 0));
-  ASSERT_TRUE(WIFSIGNALED(status));
-  EXPECT_EQ(SIGKILL, WTERMSIG(status));
-}
-
-
-TEST_F(RoutingVethTest, ROOT_LinkWait)
-{
-  AWAIT_READY(link::removed(TEST_VETH_LINK));
-
-  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
-
-  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
-  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
-
-  Future<Nothing> removed = link::removed(TEST_VETH_LINK);
-  EXPECT_TRUE(removed.isPending());
-
-  ASSERT_SOME_TRUE(link::remove(TEST_VETH_LINK));
-  AWAIT_READY(removed);
-}
-
-
-TEST_F(RoutingVethTest, ROOT_LinkSetUp)
-{
-  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
-
-  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
-  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
-
-  EXPECT_SOME_FALSE(link::isUp(TEST_VETH_LINK));
-  EXPECT_SOME_TRUE(link::setUp(TEST_VETH_LINK));
-  EXPECT_SOME_TRUE(link::isUp(TEST_VETH_LINK));
-
-  EXPECT_SOME_FALSE(link::isUp(TEST_PEER_LINK));
-  EXPECT_SOME_TRUE(link::setUp(TEST_PEER_LINK));
-  EXPECT_SOME_TRUE(link::isUp(TEST_PEER_LINK));
-
-  EXPECT_NONE(link::isUp("non-exist"));
-  EXPECT_SOME_FALSE(link::setUp("non-exist"));
-}
-
-
-TEST_F(RoutingVethTest, ROOT_LinkSetMAC)
-{
-  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
-
-  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
-  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
-
-  uint8_t bytes[6] = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc};
-
-  EXPECT_SOME_TRUE(link::setMAC(TEST_VETH_LINK, net::MAC(bytes)));
-  EXPECT_SOME_TRUE(link::setMAC(TEST_PEER_LINK, net::MAC(bytes)));
-
-  Result<net::MAC> mac = net::mac(TEST_VETH_LINK);
-
-  ASSERT_SOME(mac);
-  EXPECT_EQ(mac.get(), net::MAC(bytes));
-
-  mac = net::mac(TEST_PEER_LINK);
-
-  ASSERT_SOME(mac);
-  EXPECT_EQ(mac.get(), net::MAC(bytes));
-
-  EXPECT_SOME_FALSE(link::setMAC("non-exist", net::MAC(bytes)));
-
-  // Kernel will reject a multicast MAC address.
-  uint8_t multicast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
-
-  EXPECT_ERROR(link::setMAC(TEST_VETH_LINK, net::MAC(multicast)));
-}
-
-
-TEST_F(RoutingVethTest, ROOT_LinkMTU)
-{
-  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
-
-  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
-  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
-
-  EXPECT_SOME_TRUE(link::setMTU(TEST_VETH_LINK, 10000));
-
-  Result<unsigned int> mtu = link::mtu(TEST_VETH_LINK);
-  ASSERT_SOME(mtu);
-  EXPECT_EQ(10000u, mtu.get());
-
-  EXPECT_NONE(link::mtu("not-exist"));
-  EXPECT_SOME_FALSE(link::setMTU("not-exist", 1500));
-}
-
-
-TEST_F(RoutingVethTest, ROOT_IngressQdisc)
-{
-  // Test for a qdisc on a nonexistent interface should fail.
-  EXPECT_SOME_FALSE(ingress::exists("noSuchInterface"));
-
-  EXPECT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
-
-  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
-  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
-
-  // Interface exists but does not have an ingress qdisc.
-  EXPECT_SOME_FALSE(ingress::exists(TEST_VETH_LINK));
-  EXPECT_SOME_FALSE(ingress::exists(TEST_PEER_LINK));
-
-  // Interfaces without qdisc established no data.
-  EXPECT_NONE(ingress::statistics(TEST_VETH_LINK));
-  EXPECT_NONE(ingress::statistics(TEST_PEER_LINK));
-
-  // Try to create an ingress qdisc on a nonexistent interface.
-  EXPECT_ERROR(ingress::create("noSuchInterface"));
-
-  // Create an ingress qdisc on an existing interface.
-  EXPECT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
-
-  // Interface exists and has an ingress qdisc.
-  EXPECT_SOME_TRUE(ingress::exists(TEST_VETH_LINK));
-  EXPECT_SOME_FALSE(ingress::exists(TEST_PEER_LINK));
-
-  // Interfaces which exist return at least the core statisitcs.
-  Result<hashmap<string, uint64_t>> stats = ingress::statistics(TEST_VETH_LINK);
-  ASSERT_SOME(stats);
-  EXPECT_TRUE(stats.get().contains(statistics::PACKETS));
-  EXPECT_TRUE(stats.get().contains(statistics::BYTES));
-  EXPECT_TRUE(stats.get().contains(statistics::RATE_BPS));
-  EXPECT_TRUE(stats.get().contains(statistics::RATE_PPS));
-  EXPECT_TRUE(stats.get().contains(statistics::QLEN));
-  EXPECT_TRUE(stats.get().contains(statistics::BACKLOG));
-  EXPECT_TRUE(stats.get().contains(statistics::DROPS));
-  EXPECT_TRUE(stats.get().contains(statistics::REQUEUES));
-  EXPECT_TRUE(stats.get().contains(statistics::OVERLIMITS));
-
-  // Interface without qdisc returns no data.
-  EXPECT_NONE(ingress::statistics(TEST_PEER_LINK));
-
-  // Try to create a second ingress qdisc on an existing interface.
-  EXPECT_SOME_FALSE(ingress::create(TEST_VETH_LINK));
-  EXPECT_SOME_TRUE(ingress::exists(TEST_VETH_LINK));
-  EXPECT_SOME_FALSE(ingress::exists(TEST_PEER_LINK));
-
-  // Remove the ingress qdisc.
-  EXPECT_SOME_TRUE(ingress::remove(TEST_VETH_LINK));
-  EXPECT_SOME_FALSE(ingress::exists(TEST_VETH_LINK));
-  EXPECT_SOME_FALSE(ingress::exists(TEST_PEER_LINK));
-
-  // Try to remove it from a nonexistent interface.
-  EXPECT_SOME_FALSE(ingress::remove("noSuchInterface"));
-
-  // Remove the ingress qdisc when it does not exist.
-  EXPECT_SOME_FALSE(ingress::remove(TEST_VETH_LINK));
-  EXPECT_SOME_FALSE(ingress::exists(TEST_VETH_LINK));
-  EXPECT_SOME_FALSE(ingress::exists(TEST_PEER_LINK));
-}
-
-
-TEST_F(RoutingVethTest, ROOT_HTBQdisc)
-{
-  // Test for a qdisc on a nonexistent interface should fail.
-  EXPECT_SOME_FALSE(htb::exists("noSuchInterface", EGRESS_ROOT));
-
-  EXPECT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
-
-  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
-  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
-
-  // This test uses a common handle throughout
-  const Handle handle = Handle(1, 0);
-
-  // Interface exists but does not have an htb qdisc.
-  EXPECT_SOME_FALSE(htb::exists(TEST_VETH_LINK, EGRESS_ROOT));
-  EXPECT_SOME_FALSE(htb::exists(TEST_PEER_LINK, EGRESS_ROOT));
-
-  // Interfaces without qdisc established no data.
-  EXPECT_NONE(htb::statistics(TEST_VETH_LINK, EGRESS_ROOT));
-  EXPECT_NONE(htb::statistics(TEST_PEER_LINK, EGRESS_ROOT));
-
-  // Try to create an htb qdisc on a nonexistent interface.
-  EXPECT_ERROR(htb::create("noSuchInterface", EGRESS_ROOT, handle));
-
-  // Create an htb qdisc on an existing interface.
-  EXPECT_SOME_TRUE(htb::create(TEST_VETH_LINK, EGRESS_ROOT, handle));
-
-  // Interface exists and has an htb qdisc.
-  EXPECT_SOME_TRUE(htb::exists(TEST_VETH_LINK, EGRESS_ROOT));
-  EXPECT_SOME_FALSE(htb::exists(TEST_PEER_LINK, EGRESS_ROOT));
-
-  // Interfaces which exist return at least the core statisitcs.
-  Result<hashmap<string, uint64_t>> stats =
-      htb::statistics(TEST_VETH_LINK, EGRESS_ROOT);
-  ASSERT_SOME(stats);
-  EXPECT_TRUE(stats.get().contains(statistics::PACKETS));
-  EXPECT_TRUE(stats.get().contains(statistics::BYTES));
-  EXPECT_TRUE(stats.get().contains(statistics::RATE_BPS));
-  EXPECT_TRUE(stats.get().contains(statistics::RATE_PPS));
-  EXPECT_TRUE(stats.get().contains(statistics::QLEN));
-  EXPECT_TRUE(stats.get().contains(statistics::BACKLOG));
-  EXPECT_TRUE(stats.get().contains(statistics::DROPS));
-  EXPECT_TRUE(stats.get().contains(statistics::REQUEUES));
-  EXPECT_TRUE(stats.get().contains(statistics::OVERLIMITS));
-
-  // Interface without htb qdisc returns no data.
-  EXPECT_NONE(htb::statistics(TEST_PEER_LINK, EGRESS_ROOT));
-
-  // Try to create a second htb qdisc on an existing interface.
-  EXPECT_SOME_FALSE(htb::create(TEST_VETH_LINK, EGRESS_ROOT, handle));
-  EXPECT_SOME_TRUE(htb::exists(TEST_VETH_LINK, EGRESS_ROOT));
-  EXPECT_SOME_FALSE(htb::exists(TEST_PEER_LINK, EGRESS_ROOT));
-
-  // Remove the htb qdisc.
-  EXPECT_SOME_TRUE(htb::remove(TEST_VETH_LINK, EGRESS_ROOT));
-  EXPECT_SOME_FALSE(htb::exists(TEST_VETH_LINK, EGRESS_ROOT));
-  EXPECT_SOME_FALSE(htb::exists(TEST_PEER_LINK, EGRESS_ROOT));
-
-  // Try to remove it from a nonexistent interface.
-  EXPECT_SOME_FALSE(htb::remove("noSuchInterface", EGRESS_ROOT));
-
-  // Remove the htb qdisc when it does not exist.
-  EXPECT_SOME_FALSE(htb::remove(TEST_VETH_LINK, EGRESS_ROOT));
-  EXPECT_SOME_FALSE(htb::exists(TEST_VETH_LINK, EGRESS_ROOT));
-  EXPECT_SOME_FALSE(htb::exists(TEST_PEER_LINK, EGRESS_ROOT));
-
-  // Try to create an htb qdisc on a nonexistent interface and
-  // default handle.
-  EXPECT_ERROR(htb::create("noSuchInterface", EGRESS_ROOT, None()));
-
-  // Create an htb qdisc on an existing interface.
-  EXPECT_SOME_TRUE(htb::create(TEST_VETH_LINK, EGRESS_ROOT, None()));
-
-  // Interface exists and has an htb qdisc.
-  EXPECT_SOME_TRUE(htb::exists(TEST_VETH_LINK, EGRESS_ROOT));
-  EXPECT_SOME_FALSE(htb::exists(TEST_PEER_LINK, EGRESS_ROOT));
-
-  // Remove the htb qdisc.
-  EXPECT_SOME_TRUE(htb::remove(TEST_VETH_LINK, EGRESS_ROOT));
-  EXPECT_SOME_FALSE(htb::exists(TEST_VETH_LINK, EGRESS_ROOT));
-  EXPECT_SOME_FALSE(htb::exists(TEST_PEER_LINK, EGRESS_ROOT));
-}
-
-
-TEST_F(RoutingVethTest, ROOT_FqCodeQdisc)
-{
-  // Test for a qdisc on a nonexistent interface should fail.
-  EXPECT_SOME_FALSE(fq_codel::exists("noSuchInterface", EGRESS_ROOT));
-
-  EXPECT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
-
-  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
-  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
-
-  // This test uses a common handle throughout
-  const Handle handle = Handle(1, 0);
-
-  // Interface exists but does not have an fq_codel qdisc.
-  EXPECT_SOME_FALSE(fq_codel::exists(TEST_VETH_LINK, EGRESS_ROOT));
-  EXPECT_SOME_FALSE(fq_codel::exists(TEST_PEER_LINK, EGRESS_ROOT));
-
-  // Interfaces without qdisc established no data.
-  EXPECT_NONE(fq_codel::statistics(TEST_VETH_LINK, EGRESS_ROOT));
-  EXPECT_NONE(fq_codel::statistics(TEST_PEER_LINK, EGRESS_ROOT));
-
-  // Try to create an fq_codel qdisc on a nonexistent interface.
-  EXPECT_ERROR(fq_codel::create("noSuchInterface", EGRESS_ROOT, handle));
-
-  // Create an fq_codel qdisc on an existing interface.
-  EXPECT_SOME_TRUE(fq_codel::create(TEST_VETH_LINK, EGRESS_ROOT, handle));
-
-  // Interface exists and has an fq_codel qdisc.
-  EXPECT_SOME_TRUE(fq_codel::exists(TEST_VETH_LINK, EGRESS_ROOT));
-  EXPECT_SOME_FALSE(fq_codel::exists(TEST_PEER_LINK, EGRESS_ROOT));
-
-  // Interfaces which exist return at least the core statisitcs.
-  Result<hashmap<string, uint64_t>> stats =
-      fq_codel::statistics(TEST_VETH_LINK, EGRESS_ROOT);
-  ASSERT_SOME(stats);
-  EXPECT_TRUE(stats.get().contains(statistics::PACKETS));
-  EXPECT_TRUE(stats.get().contains(statistics::BYTES));
-  EXPECT_TRUE(stats.get().contains(statistics::RATE_BPS));
-  EXPECT_TRUE(stats.get().contains(statistics::RATE_PPS));
-  EXPECT_TRUE(stats.get().contains(statistics::QLEN));
-  EXPECT_TRUE(stats.get().contains(statistics::BACKLOG));
-  EXPECT_TRUE(stats.get().contains(statistics::DROPS));
-  EXPECT_TRUE(stats.get().contains(statistics::REQUEUES));
-  EXPECT_TRUE(stats.get().contains(statistics::OVERLIMITS));
-
-  // Interface without fq_codel qdisc returns no data.
-  EXPECT_NONE(fq_codel::statistics(TEST_PEER_LINK, EGRESS_ROOT));
-
-  // Try to create a second fq_codel qdisc on an existing interface.
-  EXPECT_SOME_FALSE(fq_codel::create(TEST_VETH_LINK, EGRESS_ROOT, handle));
-  EXPECT_SOME_TRUE(fq_codel::exists(TEST_VETH_LINK, EGRESS_ROOT));
-  EXPECT_SOME_FALSE(fq_codel::exists(TEST_PEER_LINK, EGRESS_ROOT));
-
-  // Remove the fq_codel qdisc.
-  EXPECT_SOME_TRUE(fq_codel::remove(TEST_VETH_LINK, EGRESS_ROOT));
-  EXPECT_SOME_FALSE(fq_codel::exists(TEST_VETH_LINK, EGRESS_ROOT));
-  EXPECT_SOME_FALSE(fq_codel::exists(TEST_PEER_LINK, EGRESS_ROOT));
-
-  // Try to remove it from a nonexistent interface.
-  EXPECT_SOME_FALSE(fq_codel::remove("noSuchInterface", EGRESS_ROOT));
-
-  // Remove the fq_codel qdisc when it does not exist.
-  EXPECT_SOME_FALSE(fq_codel::remove(TEST_VETH_LINK, EGRESS_ROOT));
-  EXPECT_SOME_FALSE(fq_codel::exists(TEST_VETH_LINK, EGRESS_ROOT));
-  EXPECT_SOME_FALSE(fq_codel::exists(TEST_PEER_LINK, EGRESS_ROOT));
-
-  // Try to create an fq_codel qdisc on a nonexistent interface and
-  // default handle.
-  EXPECT_ERROR(fq_codel::create("noSuchInterface", EGRESS_ROOT, None()));
-
-  // Create an fq_codel qdisc on an existing interface.
-  EXPECT_SOME_TRUE(fq_codel::create(TEST_VETH_LINK, EGRESS_ROOT, None()));
-
-  // Interface exists and has an fq_codel qdisc.
-  EXPECT_SOME_TRUE(fq_codel::exists(TEST_VETH_LINK, EGRESS_ROOT));
-  EXPECT_SOME_FALSE(fq_codel::exists(TEST_PEER_LINK, EGRESS_ROOT));
-
-  // Remove the fq_codel qdisc.
-  EXPECT_SOME_TRUE(fq_codel::remove(TEST_VETH_LINK, EGRESS_ROOT));
-  EXPECT_SOME_FALSE(fq_codel::exists(TEST_VETH_LINK, EGRESS_ROOT));
-  EXPECT_SOME_FALSE(fq_codel::exists(TEST_PEER_LINK, EGRESS_ROOT));
-}
-
-
-TEST_F(RoutingVethTest, ROOT_FqCodelClassifier)
-{
-  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
-
-  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
-  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
-
-  const Handle handle = Handle(1, 0);
-  ASSERT_SOME_TRUE(fq_codel::create(TEST_VETH_LINK, EGRESS_ROOT, handle));
-
-  EXPECT_SOME_TRUE(basic::create(
-      TEST_VETH_LINK,
-      handle,
-      ETH_P_ALL,
-      None(),
-      Handle(handle, 0)));
-
-  EXPECT_SOME_TRUE(basic::exists(TEST_VETH_LINK, handle, ETH_P_ALL));
-
-  EXPECT_SOME_TRUE(basic::create(
-      TEST_VETH_LINK,
-      handle,
-      ETH_P_ARP,
-      None(),
-      Handle(handle, 0)));
-
-  // There is a kernel bug which could cause this test fail. Please
-  // make sure your kernel, if newer than 3.14, has commit:
-  // b057df24a7536cce6c372efe9d0e3d1558afedf4
-  // (https://git.kernel.org/cgit/linux/kernel/git/davem/net.git).
-  // Please fix your kernel if you see this failure.
-  EXPECT_SOME_TRUE(basic::exists(TEST_VETH_LINK, handle, ETH_P_ARP));
-
-  EXPECT_SOME_TRUE(icmp::create(
-      TEST_VETH_LINK,
-      handle,
-      icmp::Classifier(None()),
-      None(),
-      Handle(handle, 0)));
-
-  EXPECT_SOME_TRUE(icmp::exists(
-      TEST_VETH_LINK,
-      handle,
-      icmp::Classifier(None())));
-
-  Result<net::MAC> mac = net::mac(TEST_VETH_LINK);
-  ASSERT_SOME(mac);
-
-  net::IP ip = net::IP(0x01020304); // 1.2.3.4
-
-  Try<ip::PortRange> sourcePorts =
-    ip::PortRange::fromBeginEnd(1024, 1027);
-  ASSERT_SOME(sourcePorts);
-
-  Try<ip::PortRange> destinationPorts =
-    ip::PortRange::fromBeginEnd(2000, 2000);
-  ASSERT_SOME(destinationPorts);
-
-  ip::Classifier classifier =
-    ip::Classifier(
-        mac.get(),
-        ip,
-        sourcePorts.get(),
-        destinationPorts.get());
-
-  EXPECT_SOME_TRUE(ip::create(
-      TEST_VETH_LINK,
-      handle,
-      classifier,
-      None(),
-      Handle(handle, 1)));
-
-  EXPECT_SOME_TRUE(ip::exists(TEST_VETH_LINK, handle, classifier));
-}
-
-
-TEST_F(RoutingVethTest, ROOT_ARPFilterCreate)
-{
-  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
-
-  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
-  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
-
-  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
-
-  EXPECT_SOME_TRUE(basic::create(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      ETH_P_ARP,
-      None(),
-      action::Redirect(TEST_PEER_LINK)));
-
-  EXPECT_SOME_TRUE(basic::exists(TEST_VETH_LINK, ingress::HANDLE, ETH_P_ARP));
-}
-
-
-TEST_F(RoutingVethTest, ROOT_ARPFilterCreateDuplicated)
-{
-  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
-
-  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
-  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
-
-  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
-
-  set<string> links;
-  links.insert(TEST_PEER_LINK);
-
-  EXPECT_SOME_TRUE(basic::create(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      ETH_P_ARP,
-      None(),
-      action::Mirror(links)));
-
-  EXPECT_SOME_TRUE(basic::exists(TEST_VETH_LINK, ingress::HANDLE, ETH_P_ARP));
-
-  EXPECT_SOME_FALSE(basic::create(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      ETH_P_ARP,
-      None(),
-      action::Redirect(TEST_PEER_LINK)));
-}
-
-
-TEST_F(RoutingVethTest, ROOT_ARPFilterRemove)
-{
-  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
-
-  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
-  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
-
-  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
-
-  set<string> links;
-  links.insert(TEST_PEER_LINK);
-
-  EXPECT_SOME_TRUE(basic::create(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      ETH_P_ARP,
-      None(),
-      action::Mirror(links)));
-
-  EXPECT_SOME_TRUE(basic::exists(TEST_VETH_LINK, ingress::HANDLE, ETH_P_ARP));
-  EXPECT_SOME_TRUE(basic::remove(TEST_VETH_LINK, ingress::HANDLE, ETH_P_ARP));
-  EXPECT_SOME_FALSE(basic::exists(TEST_VETH_LINK, ingress::HANDLE, ETH_P_ARP));
-}
-
-
-TEST_F(RoutingVethTest, ROOT_ARPFilterUpdate)
-{
-  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
-
-  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
-  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
-
-  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
-
-  set<string> links;
-  links.insert(TEST_PEER_LINK);
-
-  EXPECT_SOME_FALSE(basic::update(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      ETH_P_ARP,
-      action::Mirror(links)));
-
-  EXPECT_SOME_TRUE(basic::create(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      ETH_P_ARP,
-      None(),
-      action::Redirect(TEST_PEER_LINK)));
-
-  EXPECT_SOME_TRUE(basic::exists(TEST_VETH_LINK, ingress::HANDLE, ETH_P_ARP));
-
-  EXPECT_SOME_TRUE(basic::update(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      ETH_P_ARP,
-      action::Mirror(links)));
-
-  EXPECT_SOME_TRUE(basic::exists(TEST_VETH_LINK, ingress::HANDLE, ETH_P_ARP));
-}
-
-
-TEST_F(RoutingVethTest, ROOT_ICMPFilterCreate)
-{
-  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
-
-  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
-  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
-
-  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
-
-  net::IP ip = net::IP(0x01020304); // 1.2.3.4
-
-  EXPECT_SOME_TRUE(icmp::create(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      icmp::Classifier(ip),
-      None(),
-      action::Redirect(TEST_PEER_LINK)));
-
-  EXPECT_SOME_TRUE(icmp::exists(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      icmp::Classifier(ip)));
-
-  Result<vector<icmp::Classifier> > classifiers =
-    icmp::classifiers(TEST_VETH_LINK, ingress::HANDLE);
-
-  ASSERT_SOME(classifiers);
-  ASSERT_EQ(1u, classifiers.get().size());
-  EXPECT_SOME_EQ(ip, classifiers.get().front().destinationIP);
-}
-
-
-TEST_F(RoutingVethTest, ROOT_ICMPFilterCreateDuplicated)
-{
-  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
-
-  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
-  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
-
-  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
-
-  set<string> links;
-  links.insert(TEST_PEER_LINK);
-
-  EXPECT_SOME_TRUE(icmp::create(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      icmp::Classifier(None()),
-      None(),
-      action::Mirror(links)));
-
-  EXPECT_SOME_TRUE(icmp::exists(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      icmp::Classifier(None())));
-
-  EXPECT_SOME_FALSE(icmp::create(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      icmp::Classifier(None()),
-      None(),
-      action::Mirror(links)));
-}
-
-
-TEST_F(RoutingVethTest, ROOT_ICMPFilterCreateMultiple)
-{
-  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
-
-  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
-  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
-
-  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
-
-  net::IP ip1 = net::IP(0x01020304); // 1.2.3.4
-  net::IP ip2 = net::IP(0x05060708); // 5.6.7.8
-
-  EXPECT_SOME_TRUE(icmp::create(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      icmp::Classifier(ip1),
-      Priority(1, 1),
-      action::Redirect(TEST_PEER_LINK)));
-
-  EXPECT_SOME_TRUE(icmp::create(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      icmp::Classifier(ip2),
-      Priority(1, 2),
-      action::Redirect(TEST_PEER_LINK)));
-
-  Result<vector<icmp::Classifier> > classifiers =
-    icmp::classifiers(TEST_VETH_LINK, ingress::HANDLE);
-
-  ASSERT_SOME(classifiers);
-  ASSERT_EQ(2u, classifiers.get().size());
-  EXPECT_SOME_EQ(ip1, classifiers.get().front().destinationIP);
-  EXPECT_SOME_EQ(ip2, classifiers.get().back().destinationIP);
-}
-
-
-TEST_F(RoutingVethTest, ROOT_ICMPFilterRemove)
-{
-  ASSERT_SOME(link::create(
-      TEST_VETH_LINK, TEST_PEER_LINK, None()));
-
-  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
-  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
-
-  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
-
-  EXPECT_SOME_TRUE(icmp::create(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      icmp::Classifier(None()),
-      None(),
-      action::Redirect(TEST_PEER_LINK)));
-
-  EXPECT_SOME_TRUE(icmp::exists(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      icmp::Classifier(None())));
-
-  EXPECT_SOME_TRUE(icmp::remove(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      icmp::Classifier(None())));
-
-  EXPECT_SOME_FALSE(icmp::exists(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      icmp::Classifier(None())));
-}
-
-
-TEST_F(RoutingVethTest, ROOT_ICMPFilterUpdate)
-{
-  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
-
-  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
-  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
-
-  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
-
-  net::IP ip = net::IP(0x01020304); // 1.2.3.4
-
-  set<string> links;
-  links.insert(TEST_PEER_LINK);
-
-  EXPECT_SOME_FALSE(icmp::update(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      icmp::Classifier(None()),
-      action::Mirror(links)));
-
-  EXPECT_SOME_TRUE(icmp::create(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      icmp::Classifier(None()),
-      None(),
-      action::Redirect(TEST_PEER_LINK)));
-
-  EXPECT_SOME_TRUE(icmp::exists(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      icmp::Classifier(None())));
-
-  EXPECT_SOME_FALSE(icmp::update(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      icmp::Classifier(ip),
-      action::Mirror(links)));
-
-  EXPECT_SOME_TRUE(icmp::update(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      icmp::Classifier(None()),
-      action::Mirror(links)));
-
-  EXPECT_SOME_TRUE(icmp::exists(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      icmp::Classifier(None())));
-
-  EXPECT_SOME_FALSE(icmp::exists(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      icmp::Classifier(ip)));
-}
-
-
-TEST_F(RoutingVethTest, ROOT_IPFilterCreate)
-{
-  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
-
-  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
-  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
-
-  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
-
-  Result<net::MAC> mac = net::mac(TEST_VETH_LINK);
-  ASSERT_SOME(mac);
-
-  net::IP ip = net::IP(0x01020304); // 1.2.3.4
-
-  Try<ip::PortRange> sourcePorts =
-    ip::PortRange::fromBeginEnd(1024, 1027);
-
-  ASSERT_SOME(sourcePorts);
-
-  Try<ip::PortRange> destinationPorts =
-    ip::PortRange::fromBeginEnd(2000, 2000);
-
-  ASSERT_SOME(destinationPorts);
-
-  ip::Classifier classifier =
-    ip::Classifier(
-        mac.get(),
-        ip,
-        sourcePorts.get(),
-        destinationPorts.get());
-
-  EXPECT_SOME_TRUE(ip::create(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      classifier,
-      None(),
-      action::Redirect(TEST_PEER_LINK)));
-
-  EXPECT_SOME_TRUE(ip::exists(TEST_VETH_LINK, ingress::HANDLE, classifier));
-
-  Result<vector<ip::Classifier> > classifiers =
-    ip::classifiers(TEST_VETH_LINK, ingress::HANDLE);
-
-  ASSERT_SOME(classifiers);
-  ASSERT_EQ(1u, classifiers.get().size());
-  EXPECT_SOME_EQ(mac.get(), classifiers.get().front().destinationMAC);
-  EXPECT_SOME_EQ(ip, classifiers.get().front().destinationIP);
-
-  EXPECT_SOME_EQ(
-      sourcePorts.get(),
-      classifiers.get().front().sourcePorts);
-
-  EXPECT_SOME_EQ(
-      destinationPorts.get(),
-      classifiers.get().front().destinationPorts);
-}
-
-
-TEST_F(RoutingVethTest, ROOT_IPFilterCreate2)
-{
-  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
-
-  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
-  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
-
-  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
-
-  net::IP ip(0x12345678);
-
-  EXPECT_SOME_TRUE(ip::create(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      ip::Classifier(None(), ip, None(), None()),
-      None(),
-      action::Redirect(TEST_PEER_LINK)));
-
-  EXPECT_SOME_TRUE(ip::exists(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      ip::Classifier(None(), ip, None(), None())));
-
-  Result<vector<ip::Classifier> > classifiers =
-    ip::classifiers(TEST_VETH_LINK, ingress::HANDLE);
-
-  ASSERT_SOME(classifiers);
-  ASSERT_EQ(1u, classifiers.get().size());
-  EXPECT_NONE(classifiers.get().front().destinationMAC);
-  EXPECT_SOME_EQ(ip, classifiers.get().front().destinationIP);
-  EXPECT_NONE(classifiers.get().front().sourcePorts);
-  EXPECT_NONE(classifiers.get().front().destinationPorts);
-}
-
-
-TEST_F(RoutingVethTest, ROOT_IPFilterCreateDuplicated)
-{
-  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
-
-  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
-  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
-
-  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
-
-  Result<net::MAC> mac = net::mac(TEST_VETH_LINK);
-  ASSERT_SOME(mac);
-
-  net::IP ip = net::IP(0x01020304); // 1.2.3.4
-
-  Try<ip::PortRange> sourcePorts =
-    ip::PortRange::fromBeginEnd(1024, 1027);
-
-  ASSERT_SOME(sourcePorts);
-
-  Try<ip::PortRange> destinationPorts =
-    ip::PortRange::fromBeginEnd(2000, 2000);
-
-  ASSERT_SOME(destinationPorts);
-
-  ip::Classifier classifier =
-    ip::Classifier(
-        mac.get(),
-        ip,
-        sourcePorts.get(),
-        destinationPorts.get());
-
-  EXPECT_SOME_TRUE(ip::create(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      classifier,
-      None(),
-      action::Redirect(TEST_PEER_LINK)));
-
-  EXPECT_SOME_TRUE(ip::exists(TEST_VETH_LINK, ingress::HANDLE, classifier));
-
-  EXPECT_SOME_FALSE(ip::create(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      classifier,
-      None(),
-      action::Redirect(TEST_PEER_LINK)));
-}
-
-
-TEST_F(RoutingVethTest, ROOT_IPFilterCreateMultiple)
-{
-  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
-
-  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
-  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
-
-  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
-
-  Result<net::MAC> mac = net::mac(TEST_VETH_LINK);
-  ASSERT_SOME(mac);
-
-  net::IP ip = net::IP(0x01020304); // 1.2.3.4
-
-  Try<ip::PortRange> sourcePorts1 =
-    ip::PortRange::fromBeginEnd(1024, 1027);
-
-  ASSERT_SOME(sourcePorts1);
-
-  Try<ip::PortRange> destinationPorts1 =
-    ip::PortRange::fromBeginEnd(2000, 2000);
-
-  ASSERT_SOME(destinationPorts1);
-
-  Try<ip::PortRange> sourcePorts2 =
-    ip::PortRange::fromBeginEnd(3024, 3025);
-
-  ASSERT_SOME(sourcePorts2);
-
-  Try<ip::PortRange> destinationPorts2 =
-    ip::PortRange::fromBeginEnd(4000, 4003);
-
-  ASSERT_SOME(destinationPorts2);
-
-  ip::Classifier classifier1 =
-    ip::Classifier(
-        mac.get(),
-        ip,
-        sourcePorts1.get(),
-        destinationPorts1.get());
-
-  ip::Classifier classifier2 =
-    ip::Classifier(
-        mac.get(),
-        ip,
-        sourcePorts2.get(),
-        destinationPorts2.get());
-
-  EXPECT_SOME_TRUE(ip::create(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      classifier1,
-      Priority(2, 1),
-      action::Redirect(TEST_PEER_LINK)));
-
-  EXPECT_SOME_TRUE(ip::create(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      classifier2,
-      Priority(2, 2),
-      action::Redirect(TEST_PEER_LINK)));
-
-  Result<vector<ip::Classifier> > classifiers =
-    ip::classifiers(TEST_VETH_LINK, ingress::HANDLE);
-
-  ASSERT_SOME(classifiers);
-  ASSERT_EQ(2u, classifiers.get().size());
-
-  EXPECT_SOME_EQ(mac.get(), classifiers.get().front().destinationMAC);
-  EXPECT_SOME_EQ(ip, classifiers.get().front().destinationIP);
-
-  EXPECT_SOME_EQ(
-      sourcePorts1.get(),
-      classifiers.get().front().sourcePorts);
-
-  EXPECT_SOME_EQ(
-      destinationPorts1.get(),
-      classifiers.get().front().destinationPorts);
-
-  EXPECT_SOME_EQ(mac.get(), classifiers.get().back().destinationMAC);
-  EXPECT_SOME_EQ(ip, classifiers.get().back().destinationIP);
-
-  EXPECT_SOME_EQ(
-      sourcePorts2.get(),
-      classifiers.get().back().sourcePorts);
-
-  EXPECT_SOME_EQ(
-      destinationPorts2.get(),
-      classifiers.get().back().destinationPorts);
-}
-
-
-TEST_F(RoutingVethTest, ROOT_IPFilterRemove)
-{
-  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
-
-  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
-  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
-
-  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
-
-  Result<net::MAC> mac = net::mac(TEST_VETH_LINK);
-  ASSERT_SOME(mac);
-
-  net::IP ip = net::IP(0x01020304); // 1.2.3.4
-
-  Try<ip::PortRange> sourcePorts1 =
-    ip::PortRange::fromBeginEnd(1024, 1027);
-
-  ASSERT_SOME(sourcePorts1);
-
-  Try<ip::PortRange> destinationPorts1 =
-    ip::PortRange::fromBeginEnd(2000, 2000);
-
-  ASSERT_SOME(destinationPorts1);
-
-  Try<ip::PortRange> sourcePorts2 =
-    ip::PortRange::fromBeginEnd(3024, 3025);
-
-  ASSERT_SOME(sourcePorts2);
-
-  Try<ip::PortRange> destinationPorts2 =
-    ip::PortRange::fromBeginEnd(4000, 4003);
-
-  ASSERT_SOME(destinationPorts2);
-
-  ip::Classifier classifier1 =
-    ip::Classifier(
-        mac.get(),
-        ip,
-        sourcePorts1.get(),
-        destinationPorts1.get());
-
-  ip::Classifier classifier2 =
-    ip::Classifier(
-        mac.get(),
-        ip,
-        sourcePorts2.get(),
-        destinationPorts2.get());
-
-  EXPECT_SOME_TRUE(ip::create(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      classifier1,
-      None(),
-      action::Redirect(TEST_PEER_LINK)));
-
-  EXPECT_SOME_TRUE(ip::create(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      classifier2,
-      None(),
-      action::Redirect(TEST_PEER_LINK)));
-
-  EXPECT_SOME_TRUE(ip::remove(TEST_VETH_LINK, ingress::HANDLE, classifier1));
-  EXPECT_SOME_FALSE(ip::exists(TEST_VETH_LINK, ingress::HANDLE, classifier1));
-
-  EXPECT_SOME_TRUE(ip::remove(TEST_VETH_LINK, ingress::HANDLE, classifier2));
-  EXPECT_SOME_FALSE(ip::exists(TEST_VETH_LINK, ingress::HANDLE, classifier2));
-
-  Result<vector<ip::Classifier> > classifiers =
-    ip::classifiers(TEST_VETH_LINK, ingress::HANDLE);
-
-  ASSERT_SOME(classifiers);
-  EXPECT_EQ(0u, classifiers.get().size());
-}
-
-
-// Test the workaround introduced for MESOS-1617.
-TEST_F(RoutingVethTest, ROOT_HandleGeneration)
-{
-  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
-
-  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
-  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
-
-  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
-
-  Result<net::MAC> mac = net::mac(TEST_VETH_LINK);
-  ASSERT_SOME(mac);
-
-  net::IP ip = net::IP(0x01020304); // 1.2.3.4
-
-  Try<ip::PortRange> sourcePorts1 =
-    ip::PortRange::fromBeginEnd(1024, 1027);
-
-  ASSERT_SOME(sourcePorts1);
-
-  Try<ip::PortRange> destinationPorts1 =
-    ip::PortRange::fromBeginEnd(2000, 2000);
-
-  ASSERT_SOME(destinationPorts1);
-
-  Try<ip::PortRange> sourcePorts2 =
-    ip::PortRange::fromBeginEnd(3024, 3025);
-
-  ASSERT_SOME(sourcePorts2);
-
-  Try<ip::PortRange> destinationPorts2 =
-    ip::PortRange::fromBeginEnd(4000, 4003);
-
-  ASSERT_SOME(destinationPorts2);
-
-  ip::Classifier classifier1 =
-    ip::Classifier(
-        mac.get(),
-        ip,
-        sourcePorts1.get(),
-        destinationPorts1.get());
-
-  ip::Classifier classifier2 =
-    ip::Classifier(
-        mac.get(),
-        ip,
-        sourcePorts2.get(),
-        destinationPorts2.get());
-
-  // Use handle 800:00:fff for the first filter.
-  EXPECT_SOME_TRUE(ip::create(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      classifier1,
-      Priority(2, 1),
-      U32Handle(0x800, 0x0, 0xfff),
-      action::Redirect(TEST_PEER_LINK)));
-
-  // With the workaround, this filter should be assigned a handle
-  // different than 800:00:fff.
-  EXPECT_SOME_TRUE(ip::create(
-      TEST_VETH_LINK,
-      ingress::HANDLE,
-      classifier2,
-      Priority(2, 1),
-      action::Redirect(TEST_PEER_LINK)));
-
-  // Try to remove the second filter. If we don't have the workaround,
-  // removing the second filter will return false since the kernel
-  // will find the handle matches the first filter.
-  EXPECT_SOME_TRUE(ip::remove(TEST_VETH_LINK, ingress::HANDLE, classifier2));
-}
-
-} // namespace tests {
-} // namespace internal {
-} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/sched_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/sched_tests.cpp b/src/tests/sched_tests.cpp
deleted file mode 100644
index 00723d0..0000000
--- a/src/tests/sched_tests.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-/**
- * 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 "linux/sched.hpp"
-
-#include <process/gtest.hpp>
-#include <process/reap.hpp>
-
-#include <gtest/gtest.h>
-
-#include <stout/gtest.hpp>
-
-using sched::Policy;
-
-namespace mesos {
-namespace internal {
-namespace tests {
-
-// TODO(idownes): Test the priority and preemption behavior for
-// running competing SCHED_OTHER and SCHED_IDLE tasks.
-
-TEST(SchedTest, ROOT_PolicySelf)
-{
-  Try<Policy> original = sched::policy::get();
-  ASSERT_SOME(original);
-
-  Policy different = (original.get() == Policy::OTHER ? Policy::IDLE
-                                                      : Policy::OTHER);
-
-  // Change our own scheduling policy.
-  EXPECT_SOME(sched::policy::set(different));
-  EXPECT_SOME_EQ(different, sched::policy::get());
-
-  // Change it back.
-  EXPECT_SOME(sched::policy::set(original.get()));
-  EXPECT_SOME_EQ(original.get(), sched::policy::get());
-}
-
-
-// Change the scheduling policy of a different process (our child).
-TEST(SchedTest, ROOT_PolicyChild)
-{
-  Try<Policy> original = sched::policy::get();
-  ASSERT_SOME(original);
-
-  Policy different = (original.get() == Policy::OTHER ? Policy::IDLE
-                                                      : Policy::OTHER);
-
-  pid_t pid = ::fork();
-  ASSERT_NE(-1, pid);
-
-  if (pid == 0) {
-    // Child.
-    sleep(10);
-
-    ABORT("Child process should not reach here");
-  }
-
-  // Continue in parent.
-  // Check the child has inherited our policy.
-  EXPECT_SOME_EQ(original.get(), sched::policy::get(pid));
-
-  // Check we can change the child's policy.
-  EXPECT_SOME(sched::policy::set(different, pid));
-  EXPECT_SOME_EQ(different, sched::policy::get(pid));
-
-  process::Future<Option<int>> status = process::reap(pid);
-
-  // Kill the child process.
-  ASSERT_NE(-1, ::kill(pid, SIGKILL));
-
-  // Wait for the child process.
-  AWAIT_READY(status);
-  ASSERT_SOME(status.get());
-  EXPECT_TRUE(WIFSIGNALED(status.get().get()));
-  EXPECT_EQ(SIGKILL, WTERMSIG(status.get().get()));
-}
-
-} // namespace tests {
-} // namespace internal {
-} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/setns_test_helper.cpp
----------------------------------------------------------------------
diff --git a/src/tests/setns_test_helper.cpp b/src/tests/setns_test_helper.cpp
deleted file mode 100644
index eff1e6e..0000000
--- a/src/tests/setns_test_helper.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/**
- * 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 <stout/foreach.hpp>
-#include <stout/subcommand.hpp>
-#include <stout/try.hpp>
-
-#include "linux/ns.hpp"
-
-#include "tests/setns_test_helper.hpp"
-
-#include <set>
-#include <string>
-
-using std::set;
-using std::string;
-
-const char SetnsTestHelper::NAME[] = "SetnsTestHelper";
-
-int SetnsTestHelper::execute()
-{
-  // Get all the available namespaces.
-  set<string> namespaces = ns::namespaces();
-
-  // Note: /proc has not been remounted so we can look up pid 1's
-  // namespaces, even if we're in a separate pid namespace.
-  foreach (const string& ns, namespaces) {
-    if (ns == "pid") {
-      // ns::setns() does not (currently) support pid namespaces so
-      // this should return an error.
-      Try<Nothing> setns = ns::setns(1, ns);
-      if (!setns.isError()) {
-        return 1;
-      }
-    } else if (ns == "user") {
-      // ns::setns() will also fail with user namespaces, so we skip
-      // for now. See MESOS-3083.
-      continue;
-    } else {
-      Try<Nothing> setns = ns::setns(1, ns);
-      if (!setns.isSome()) {
-        return 1;
-      }
-    }
-  }
-
-  return 0;
-}

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/setns_test_helper.hpp
----------------------------------------------------------------------
diff --git a/src/tests/setns_test_helper.hpp b/src/tests/setns_test_helper.hpp
deleted file mode 100644
index 51d6378..0000000
--- a/src/tests/setns_test_helper.hpp
+++ /dev/null
@@ -1,35 +0,0 @@
-/**
- * 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 __SETNS_TEST_HELPER_HPP__
-#define __SETNS_TEST_HELPER_HPP__
-
-#include <stout/subcommand.hpp>
-
-class SetnsTestHelper : public Subcommand
-{
-public:
-  static const char NAME[];
-
-  SetnsTestHelper() : Subcommand(NAME) {}
-
-protected:
-  virtual int execute();
-};
-
-#endif // __SETNS_TEST_HELPER_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/setns_test_helper_main.cpp
----------------------------------------------------------------------
diff --git a/src/tests/setns_test_helper_main.cpp b/src/tests/setns_test_helper_main.cpp
deleted file mode 100644
index 00d3816..0000000
--- a/src/tests/setns_test_helper_main.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-/**
- * 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 <stout/subcommand.hpp>
-
-#include "tests/setns_test_helper.hpp"
-
-int main(int argc, char** argv)
-{
-  return Subcommand::dispatch(
-      None(),
-      argc,
-      argv,
-      new SetnsTestHelper());
-}


[04/12] mesos git commit: Moved containerizer related tests under src/tests/containerizer.

Posted by ji...@apache.org.
http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/docker_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/docker_tests.cpp b/src/tests/docker_tests.cpp
deleted file mode 100644
index a4a2725..0000000
--- a/src/tests/docker_tests.cpp
+++ /dev/null
@@ -1,421 +0,0 @@
-/**
- * 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 <process/owned.hpp>
-#include <process/subprocess.hpp>
-
-#include <stout/duration.hpp>
-#include <stout/option.hpp>
-#include <stout/gtest.hpp>
-
-#include "docker/docker.hpp"
-
-#include "mesos/resources.hpp"
-
-#include "tests/environment.hpp"
-#include "tests/flags.hpp"
-#include "tests/mesos.hpp"
-
-using namespace process;
-
-using std::list;
-using std::string;
-
-namespace mesos {
-namespace internal {
-namespace tests {
-
-
-static const string NAME_PREFIX="mesos-docker";
-
-
-class DockerTest : public MesosTest
-{
-  virtual void TearDown()
-  {
-    Try<Docker*> docker = Docker::create(tests::flags.docker, false);
-    ASSERT_SOME(docker);
-
-    Future<list<Docker::Container>> containers =
-      docker.get()->ps(true, NAME_PREFIX);
-
-    AWAIT_READY(containers);
-
-    // Cleanup all mesos launched containers.
-    foreach (const Docker::Container& container, containers.get()) {
-      AWAIT_READY_FOR(docker.get()->rm(container.id, true), Seconds(30));
-    }
-
-    delete docker.get();
-  }
-};
-
-// This test tests the functionality of the docker's interfaces.
-TEST_F(DockerTest, ROOT_DOCKER_interface)
-{
-  const string containerName = NAME_PREFIX + "-test";
-  Resources resources = Resources::parse("cpus:1;mem:512").get();
-
-  Owned<Docker> docker(Docker::create(tests::flags.docker, false).get());
-
-  // Verify that we do not see the container.
-  Future<list<Docker::Container> > containers = docker->ps(true, containerName);
-  AWAIT_READY(containers);
-  foreach (const Docker::Container& container, containers.get()) {
-    EXPECT_NE("/" + containerName, container.name);
-  }
-
-  Try<string> directory = environment->mkdtemp();
-  CHECK_SOME(directory) << "Failed to create temporary directory";
-
-  ContainerInfo containerInfo;
-  containerInfo.set_type(ContainerInfo::DOCKER);
-
-  ContainerInfo::DockerInfo dockerInfo;
-  dockerInfo.set_image("busybox");
-  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
-
-  CommandInfo commandInfo;
-  commandInfo.set_value("sleep 120");
-
-  // Start the container.
-  Future<Nothing> status = docker->run(
-      containerInfo,
-      commandInfo,
-      containerName,
-      directory.get(),
-      "/mnt/mesos/sandbox",
-      resources);
-
-  Future<Docker::Container> inspect =
-    docker->inspect(containerName, Seconds(1));
-  AWAIT_READY(inspect);
-
-  // 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);
-
-  // Test some fields of the container.
-  EXPECT_NE("", inspect.get().id);
-  EXPECT_EQ("/" + containerName, inspect.get().name);
-  EXPECT_SOME(inspect.get().pid);
-
-  // Stop the container.
-  status = docker->stop(containerName);
-  AWAIT_READY(status);
-
-  // 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, containerName);
-  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.
-  inspect = docker->inspect(containerName);
-  AWAIT_READY(inspect);
-
-  EXPECT_NE("", inspect.get().id);
-  EXPECT_EQ("/" + containerName, inspect.get().name);
-  EXPECT_NONE(inspect.get().pid);
-
-  // Remove the container.
-  status = docker->rm(containerName);
-  AWAIT_READY(status);
-
-  // Should not be able to inspect the container.
-  inspect = docker->inspect(containerName);
-  AWAIT_FAILED(inspect);
-
-  // 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);
-  }
-
-  // Start the container again, this time we will do a "rm -f"
-  // directly, instead of stopping and rm.
-  status = docker->run(
-      containerInfo,
-      commandInfo,
-      containerName,
-      directory.get(),
-      "/mnt/mesos/sandbox",
-      resources);
-
-  inspect = docker->inspect(containerName, Seconds(1));
-  AWAIT_READY(inspect);
-
-  // 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);
-
-  // 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, containerName);
-  AWAIT_READY(containers);
-  foreach (const Docker::Container& container, containers.get()) {
-    EXPECT_NE("/" + containerName, container.name);
-  }
-}
-
-
-TEST_F(DockerTest, ROOT_DOCKER_CheckCommandWithShell)
-{
-  Owned<Docker> docker(Docker::create(tests::flags.docker, false).get());
-
-  ContainerInfo containerInfo;
-  containerInfo.set_type(ContainerInfo::DOCKER);
-
-  ContainerInfo::DockerInfo dockerInfo;
-  dockerInfo.set_image("busybox");
-  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
-
-  CommandInfo commandInfo;
-  commandInfo.set_shell(true);
-
-  Future<Nothing> run = docker->run(
-      containerInfo,
-      commandInfo,
-      "testContainer",
-      "dir",
-      "/mnt/mesos/sandbox");
-
-  ASSERT_TRUE(run.isFailed());
-}
-
-
-TEST_F(DockerTest, ROOT_DOCKER_CheckPortResource)
-{
-  const string containerName = NAME_PREFIX + "-port-resource-test";
-  Owned<Docker> docker(Docker::create(tests::flags.docker, false).get());
-
-  // Make sure the container is removed.
-  Future<Nothing> remove = docker->rm(containerName, true);
-
-  ASSERT_TRUE(process::internal::await(remove, Seconds(10)));
-
-  ContainerInfo containerInfo;
-  containerInfo.set_type(ContainerInfo::DOCKER);
-
-  ContainerInfo::DockerInfo dockerInfo;
-  dockerInfo.set_image("busybox");
-  dockerInfo.set_network(ContainerInfo::DockerInfo::BRIDGE);
-
-  ContainerInfo::DockerInfo::PortMapping portMapping;
-  portMapping.set_host_port(10000);
-  portMapping.set_container_port(80);
-
-  dockerInfo.add_port_mappings()->CopyFrom(portMapping);
-  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
-
-  CommandInfo commandInfo;
-  commandInfo.set_shell(false);
-  commandInfo.set_value("true");
-
-  Resources resources =
-    Resources::parse("ports:[9998-9999];ports:[10001-11000]").get();
-
-  Future<Nothing> run = docker->run(
-      containerInfo,
-      commandInfo,
-      containerName,
-      "dir",
-      "/mnt/mesos/sandbox",
-      resources);
-
-  // Port should be out side of the provided ranges.
-  AWAIT_EXPECT_FAILED(run);
-
-  resources = Resources::parse("ports:[9998-9999];ports:[10000-11000]").get();
-
-  Try<string> directory = environment->mkdtemp();
-  CHECK_SOME(directory) << "Failed to create temporary directory";
-
-  run = docker->run(
-      containerInfo,
-      commandInfo,
-      containerName,
-      directory.get(),
-      "/mnt/mesos/sandbox",
-      resources);
-
-  AWAIT_READY(run);
-}
-
-
-TEST_F(DockerTest, ROOT_DOCKER_CancelPull)
-{
-  // Delete the test image if it exists.
-
-  Try<Subprocess> s = process::subprocess(
-      tests::flags.docker + " rmi lingmann/1gb",
-      Subprocess::PATH("/dev/null"),
-      Subprocess::PATH("/dev/null"),
-      Subprocess::PATH("/dev/null"));
-
-  ASSERT_SOME(s);
-
-  AWAIT_READY_FOR(s.get().status(), Seconds(30));
-
-  Owned<Docker> docker(Docker::create(tests::flags.docker, false).get());
-
-  Try<string> directory = environment->mkdtemp();
-
-  CHECK_SOME(directory) << "Failed to create temporary directory";
-
-  // Assume that pulling the very large image 'lingmann/1gb' will take
-  // sufficiently long that we can start it and discard (i.e., cancel
-  // it) right away and the future will indeed get discarded.
-  Future<Docker::Image> future =
-    docker->pull(directory.get(), "lingmann/1gb");
-
-  future.discard();
-
-  AWAIT_DISCARDED(future);
-}
-
-
-// This test verifies mounting in a relative path when running a
-// docker container works.
-TEST_F(DockerTest, ROOT_DOCKER_MountRelative)
-{
-  Owned<Docker> docker(Docker::create(tests::flags.docker, false).get());
-
-  ContainerInfo containerInfo;
-  containerInfo.set_type(ContainerInfo::DOCKER);
-
-  Volume* volume = containerInfo.add_volumes();
-  volume->set_host_path("test_file");
-  volume->set_container_path("/tmp/test_file");
-  volume->set_mode(Volume::RO);
-
-  ContainerInfo::DockerInfo dockerInfo;
-  dockerInfo.set_image("busybox");
-
-  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
-
-  CommandInfo commandInfo;
-  commandInfo.set_shell(true);
-  commandInfo.set_value("ls /tmp/test_file");
-
-  Try<string> directory = environment->mkdtemp();
-  CHECK_SOME(directory) << "Failed to create temporary directory";
-
-  const string testFile = path::join(directory.get(), "test_file");
-  EXPECT_SOME(os::write(testFile, "data"));
-
-  Future<Nothing> run = docker->run(
-      containerInfo,
-      commandInfo,
-      NAME_PREFIX + "-mount-relative-test",
-      directory.get(),
-      directory.get());
-
-  AWAIT_READY(run);
-}
-
-
-// This test verifies mounting in a absolute path when running a
-// docker container works.
-TEST_F(DockerTest, ROOT_DOCKER_MountAbsolute)
-{
-  Owned<Docker> docker(Docker::create(tests::flags.docker, false).get());
-
-  ContainerInfo containerInfo;
-  containerInfo.set_type(ContainerInfo::DOCKER);
-
-  Try<string> directory = environment->mkdtemp();
-  CHECK_SOME(directory) << "Failed to create temporary directory";
-
-  const string testFile = path::join(directory.get(), "test_file");
-  EXPECT_SOME(os::write(testFile, "data"));
-
-  Volume* volume = containerInfo.add_volumes();
-  volume->set_host_path(testFile);
-  volume->set_container_path("/tmp/test_file");
-  volume->set_mode(Volume::RO);
-
-  ContainerInfo::DockerInfo dockerInfo;
-  dockerInfo.set_image("busybox");
-
-  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
-
-  CommandInfo commandInfo;
-  commandInfo.set_shell(true);
-  commandInfo.set_value("ls /tmp/test_file");
-
-  Future<Nothing> run = docker->run(
-      containerInfo,
-      commandInfo,
-      NAME_PREFIX + "-mount-absolute-test",
-      directory.get(),
-      directory.get());
-
-  AWAIT_READY(run);
-}
-
-
-} // namespace tests {
-} // namespace internal {
-} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/external_containerizer_test.cpp
----------------------------------------------------------------------
diff --git a/src/tests/external_containerizer_test.cpp b/src/tests/external_containerizer_test.cpp
deleted file mode 100644
index 17bfb72..0000000
--- a/src/tests/external_containerizer_test.cpp
+++ /dev/null
@@ -1,266 +0,0 @@
-/**
- * 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 <unistd.h>
-
-#include <gmock/gmock.h>
-
-#include <string>
-#include <vector>
-#include <map>
-
-#include <mesos/resources.hpp>
-
-#include <process/future.hpp>
-
-#include <stout/os.hpp>
-#include <stout/path.hpp>
-
-#include "master/master.hpp"
-#include "master/detector.hpp"
-
-#include "slave/containerizer/containerizer.hpp"
-#include "slave/containerizer/external_containerizer.hpp"
-#include "slave/flags.hpp"
-#include "slave/slave.hpp"
-
-#include "tests/mesos.hpp"
-#include "tests/flags.hpp"
-
-using namespace process;
-
-using mesos::internal::master::Master;
-using mesos::internal::slave::Containerizer;
-using mesos::internal::slave::Slave;
-
-using std::string;
-using std::vector;
-
-using testing::_;
-using testing::DoAll;
-using testing::Return;
-using testing::SaveArg;
-using testing::Invoke;
-
-namespace mesos {
-namespace internal {
-namespace tests {
-
-// The external containerizer tests currently rely on a Python script
-// which needs the Mesos Python egg being built.
-// TODO(tillt): Consider providing tests that do not rely on Python.
-#ifdef MESOS_HAS_PYTHON
-
-// TODO(tillt): Update and enhance the ExternalContainerizer tests,
-// possibly following some of the patterns used within the
-// IsolatorTests or even entirely reusing the Containerizer tests.
-class ExternalContainerizerTest : public MesosTest {};
-
-
-class MockExternalContainerizer : public slave::ExternalContainerizer
-{
-public:
-  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));
-
-  MockExternalContainerizer(const slave::Flags& flags)
-    : ExternalContainerizer(flags)
-  {
-    // Set up defaults for mocked methods.
-    // NOTE: See TestContainerizer::setup for why we use
-    // 'EXPECT_CALL' and 'WillRepeatedly' here instead of
-    // 'ON_CALL' and 'WillByDefault'.
-    EXPECT_CALL(*this, launch(_, _, _, _, _, _, _, _))
-      .WillRepeatedly(Invoke(this, &MockExternalContainerizer::_launch));
-  }
-
-  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 slave::ExternalContainerizer::launch(
-        containerId,
-        taskInfo,
-        executorInfo,
-        directory,
-        user,
-        slaveId,
-        slavePid,
-        checkpoint);
-  }
-};
-
-
-// This test has been temporarily disabled due to MESOS-1257.
-TEST_F(ExternalContainerizerTest, DISABLED_Launch)
-{
-  Try<PID<Master> > master = this->StartMaster();
-  ASSERT_SOME(master);
-
-  Flags testFlags;
-
-  slave::Flags flags = this->CreateSlaveFlags();
-
-  flags.isolation = "external";
-  flags.containerizer_path =
-    testFlags.build_dir + "/src/examples/python/test-containerizer";
-
-  MockExternalContainerizer containerizer(flags);
-
-  Try<PID<Slave> > slave = this->StartSlave(&containerizer, flags);
-  ASSERT_SOME(slave);
-
-  MockScheduler sched;
-  MesosSchedulerDriver driver(
-      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
-
-  Future<FrameworkID> frameworkId;
-  EXPECT_CALL(sched, registered(&driver, _, _))
-    .WillOnce(FutureArg<1>(&frameworkId));
-
-  Future<vector<Offer> > offers;
-  EXPECT_CALL(sched, resourceOffers(&driver, _))
-    .WillOnce(FutureArg<1>(&offers))
-    .WillRepeatedly(Return()); // Ignore subsequent offers.
-
-  driver.start();
-
-  AWAIT_READY(frameworkId);
-  AWAIT_READY(offers);
-
-  EXPECT_NE(0u, offers.get().size());
-
-  TaskInfo task;
-  task.set_name("isolator_test");
-  task.mutable_task_id()->set_value("1");
-  task.mutable_slave_id()->CopyFrom(offers.get()[0].slave_id());
-  task.mutable_resources()->CopyFrom(offers.get()[0].resources());
-
-  Resources resources(offers.get()[0].resources());
-  Option<Bytes> mem = resources.mem();
-  ASSERT_SOME(mem);
-  Option<double> cpus = resources.cpus();
-  ASSERT_SOME(cpus);
-
-  const std::string& file = path::join(flags.work_dir, "ready");
-
-  // This task induces user/system load in a child process by
-  // running top in a child process for ten seconds.
-  task.mutable_command()->set_value(
-#ifdef __APPLE__
-      // Use logging mode with 30,000 samples with no interval.
-      "top -l 30000 -s 0 2>&1 > /dev/null & "
-#else
-      // Batch mode, with 30,000 samples with no interval.
-      "top -b -d 0 -n 30000 2>&1 > /dev/null & "
-#endif
-      "touch " + file +  "; " // Signals that the top command is running.
-      "sleep 60");
-
-  Future<TaskStatus> status;
-  EXPECT_CALL(sched, statusUpdate(&driver, _))
-    .WillOnce(FutureArg<1>(&status))
-    .WillRepeatedly(Return()); // Ignore rest for now.
-
-  Future<ContainerID> containerId;
-  EXPECT_CALL(containerizer, launch(_, _, _, _, _, _, _, _))
-    .WillOnce(DoAll(FutureArg<0>(&containerId),
-                    Invoke(&containerizer,
-                           &MockExternalContainerizer::_launch)));
-
-  driver.launchTasks(offers.get()[0].id(), {task});
-
-  AWAIT_READY(containerId);
-
-  AWAIT_READY(status);
-
-  EXPECT_EQ(TASK_RUNNING, status.get().state());
-
-  // Wait for the task to begin inducing cpu time.
-  while (!os::exists(file));
-
-  ExecutorID executorId;
-  executorId.set_value(task.task_id().value());
-
-  // We'll wait up to 10 seconds for the child process to induce
-  // 1/8 of a second of user and system cpu time in total.
-  // TODO(bmahler): Also induce rss memory consumption, by re-using
-  // the balloon framework.
-  ResourceStatistics statistics;
-  Duration waited = Duration::zero();
-  do {
-    Future<ResourceStatistics> usage = containerizer.usage(containerId.get());
-    AWAIT_READY(usage);
-
-    statistics = usage.get();
-
-    // If we meet our usage expectations, we're done!
-    // NOTE: We are currently getting dummy-data from the test-
-    // containerizer python script matching these expectations.
-    // TODO(tillt): Consider working with real data.
-    if (statistics.cpus_user_time_secs() >= 0.120 &&
-        statistics.cpus_system_time_secs() >= 0.05 &&
-        statistics.mem_rss_bytes() >= 1024u) {
-      break;
-    }
-
-    os::sleep(Milliseconds(100));
-    waited += Milliseconds(100);
-  } while (waited < Seconds(10));
-
-  EXPECT_GE(statistics.cpus_user_time_secs(), 0.120);
-  EXPECT_GE(statistics.cpus_system_time_secs(), 0.05);
-  EXPECT_EQ(statistics.cpus_limit(), cpus.get());
-  EXPECT_GE(statistics.mem_rss_bytes(), 1024u);
-  EXPECT_EQ(statistics.mem_limit_bytes(), mem.get().bytes());
-
-  EXPECT_CALL(sched, statusUpdate(&driver, _))
-    .WillOnce(FutureArg<1>(&status));
-
-  driver.killTask(task.task_id());
-
-  AWAIT_READY(status);
-
-  EXPECT_EQ(TASK_KILLED, status.get().state());
-
-  driver.stop();
-  driver.join();
-
-  this->Shutdown();
-}
-
-#endif // MESOS_HAS_PYTHON
-
-} // namespace tests {
-} // namespace internal {
-} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/fs_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/fs_tests.cpp b/src/tests/fs_tests.cpp
deleted file mode 100644
index 34d3c41..0000000
--- a/src/tests/fs_tests.cpp
+++ /dev/null
@@ -1,170 +0,0 @@
-/**
- * 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 <paths.h>
-
-#include <gmock/gmock.h>
-
-#include <stout/foreach.hpp>
-#include <stout/gtest.hpp>
-#include <stout/none.hpp>
-#include <stout/option.hpp>
-#include <stout/try.hpp>
-
-#include "linux/fs.hpp"
-
-namespace mesos {
-namespace internal {
-namespace tests {
-
-using fs::MountTable;
-using fs::FileSystemTable;
-using fs::MountInfoTable;
-
-
-TEST(FsTest, MountTableRead)
-{
-  Try<MountTable> table = MountTable::read(_PATH_MOUNTED);
-
-  ASSERT_SOME(table);
-
-  Option<MountTable::Entry> root = None();
-  Option<MountTable::Entry> proc = None();
-  foreach (const MountTable::Entry& entry, table.get().entries) {
-    if (entry.dir == "/") {
-      root = entry;
-    } else if (entry.dir == "/proc") {
-      proc = entry;
-    }
-  }
-
-  EXPECT_SOME(root);
-  ASSERT_SOME(proc);
-  EXPECT_EQ(proc.get().type, "proc");
-}
-
-
-TEST(FsTest, MountTableHasOption)
-{
-  Try<MountTable> table = MountTable::read(_PATH_MOUNTED);
-
-  ASSERT_SOME(table);
-
-  Option<MountTable::Entry> proc = None();
-  foreach (const MountTable::Entry& entry, table.get().entries) {
-    if (entry.dir == "/proc") {
-      proc = entry;
-    }
-  }
-
-  ASSERT_SOME(proc);
-  EXPECT_TRUE(proc.get().hasOption(MNTOPT_RW));
-}
-
-
-TEST(FsTest, FileSystemTableRead)
-{
-  Try<FileSystemTable> table = FileSystemTable::read();
-
-  ASSERT_SOME(table);
-
-  // NOTE: We do not check for /proc because, it is not always present in
-  // /etc/fstab.
-  Option<FileSystemTable::Entry> root = None();
-  foreach (const FileSystemTable::Entry& entry, table.get().entries) {
-    if (entry.file == "/") {
-      root = entry;
-    }
-  }
-
-  EXPECT_SOME(root);
-}
-
-
-TEST(FsTest, MountInfoTableParse)
-{
-  // Parse a private mount (no optional fields).
-  const std::string privateMount =
-    "19 1 8:1 / / rw,relatime - ext4 /dev/sda1 rw,seclabel,data=ordered";
-  Try<MountInfoTable::Entry> entry = MountInfoTable::Entry::parse(privateMount);
-
-  ASSERT_SOME(entry);
-  EXPECT_EQ(19, entry.get().id);
-  EXPECT_EQ(1, entry.get().parent);
-  EXPECT_EQ(makedev(8, 1), entry.get().devno);
-  EXPECT_EQ("/", entry.get().root);
-  EXPECT_EQ("/", entry.get().target);
-  EXPECT_EQ("rw,relatime", entry.get().vfsOptions);
-  EXPECT_EQ("rw,seclabel,data=ordered", entry.get().fsOptions);
-  EXPECT_EQ("", entry.get().optionalFields);
-  EXPECT_EQ("ext4", entry.get().type);
-  EXPECT_EQ("/dev/sda1", entry.get().source);
-
-  // Parse a shared mount (includes one optional field).
-  const std::string sharedMount =
-    "19 1 8:1 / / rw,relatime shared:2 - ext4 /dev/sda1 rw,seclabel";
-  entry = MountInfoTable::Entry::parse(sharedMount);
-
-  ASSERT_SOME(entry);
-  EXPECT_EQ(19, entry.get().id);
-  EXPECT_EQ(1, entry.get().parent);
-  EXPECT_EQ(makedev(8, 1), entry.get().devno);
-  EXPECT_EQ("/", entry.get().root);
-  EXPECT_EQ("/", entry.get().target);
-  EXPECT_EQ("rw,relatime", entry.get().vfsOptions);
-  EXPECT_EQ("rw,seclabel", entry.get().fsOptions);
-  EXPECT_EQ("shared:2", entry.get().optionalFields);
-  EXPECT_EQ("ext4", entry.get().type);
-  EXPECT_EQ("/dev/sda1", entry.get().source);
-}
-
-
-TEST(FsTest, DISABLED_MountInfoTableRead)
-{
-  // Examine the calling process's mountinfo table.
-  Try<fs::MountInfoTable> table = fs::MountInfoTable::read();
-  ASSERT_SOME(table);
-
-  // Every system should have at least a rootfs mounted.
-  Option<MountInfoTable::Entry> root = None();
-  foreach (const MountInfoTable::Entry& entry, table.get().entries) {
-    if (entry.target == "/") {
-      root = entry;
-    }
-  }
-
-  EXPECT_SOME(root);
-
-  // Repeat for pid 1.
-  table = fs::MountInfoTable::read(1);
-  ASSERT_SOME(table);
-
-  // Every system should have at least a rootfs mounted.
-  root = None();
-  foreach (const MountInfoTable::Entry& entry, table.get().entries) {
-    if (entry.target == "/") {
-      root = entry;
-    }
-  }
-
-  EXPECT_SOME(root);
-}
-
-} // namespace tests {
-} // namespace internal {
-} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/isolator.hpp
----------------------------------------------------------------------
diff --git a/src/tests/isolator.hpp b/src/tests/isolator.hpp
deleted file mode 100644
index 8aaf88c..0000000
--- a/src/tests/isolator.hpp
+++ /dev/null
@@ -1,101 +0,0 @@
-/**
- * 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 __TEST_ISOLATOR_HPP__
-#define __TEST_ISOLATOR_HPP__
-
-#include <gmock/gmock.h>
-
-#include "slave/containerizer/isolator.hpp"
-
-namespace mesos {
-namespace internal {
-namespace tests {
-
-class TestIsolatorProcess : public slave::MesosIsolatorProcess
-{
-public:
-  static Try<mesos::slave::Isolator*> create(
-      const Option<CommandInfo>& commandInfo)
-  {
-    process::Owned<MesosIsolatorProcess> process(
-        new TestIsolatorProcess(commandInfo));
-
-    return new slave::MesosIsolator(process);
-  }
-
-  MOCK_METHOD2(
-      recover,
-      process::Future<Nothing>(
-          const std::list<mesos::slave::ExecutorRunState>&,
-          const hashset<ContainerID>&));
-
-  virtual process::Future<Option<CommandInfo>> prepare(
-      const ContainerID& containerId,
-      const ExecutorInfo& executorInfo,
-      const std::string& directory,
-      const Option<std::string>& rootfs,
-      const Option<std::string>& user)
-  {
-    return commandInfo;
-  }
-
-  MOCK_METHOD2(
-      isolate,
-      process::Future<Nothing>(const ContainerID&, pid_t));
-
-  MOCK_METHOD1(
-      watch,
-      process::Future<mesos::slave::ExecutorLimitation>(const ContainerID&));
-
-  MOCK_METHOD2(
-      update,
-      process::Future<Nothing>(const ContainerID&, const Resources&));
-
-  MOCK_METHOD1(
-      usage,
-      process::Future<ResourceStatistics>(const ContainerID&));
-
-  MOCK_METHOD1(
-      cleanup,
-      process::Future<Nothing>(const ContainerID&));
-
-private:
-  TestIsolatorProcess(const Option<CommandInfo>& _commandInfo)
-    : commandInfo(_commandInfo)
-  {
-    EXPECT_CALL(*this, watch(testing::_))
-      .WillRepeatedly(testing::Return(promise.future()));
-
-    EXPECT_CALL(*this, isolate(testing::_, testing::_))
-      .WillRepeatedly(testing::Return(Nothing()));
-
-    EXPECT_CALL(*this, cleanup(testing::_))
-      .WillRepeatedly(testing::Return(Nothing()));
-  }
-
-  const Option<CommandInfo> commandInfo;
-
-  process::Promise<mesos::slave::ExecutorLimitation> promise;
-};
-
-} // namespace tests {
-} // namespace internal {
-} // namespace mesos {
-
-#endif // __TEST_ISOLATOR_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/isolator_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/isolator_tests.cpp b/src/tests/isolator_tests.cpp
deleted file mode 100644
index 7ad0cb6..0000000
--- a/src/tests/isolator_tests.cpp
+++ /dev/null
@@ -1,1316 +0,0 @@
-/**
- * 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 <unistd.h>
-
-#include <gmock/gmock.h>
-
-#include <iostream>
-#include <string>
-#include <vector>
-
-#include <mesos/resources.hpp>
-
-#include <mesos/module/isolator.hpp>
-
-#include <mesos/slave/isolator.hpp>
-
-#include <process/future.hpp>
-#include <process/owned.hpp>
-#include <process/reap.hpp>
-
-#include <stout/abort.hpp>
-#include <stout/gtest.hpp>
-#include <stout/os.hpp>
-#include <stout/path.hpp>
-
-#ifdef __linux__
-#include "linux/ns.hpp"
-#endif // __linux__
-
-#include "master/master.hpp"
-#include "master/detector.hpp"
-
-#include "slave/flags.hpp"
-#include "slave/slave.hpp"
-
-#ifdef __linux__
-#include "slave/containerizer/isolators/cgroups/constants.hpp"
-#include "slave/containerizer/isolators/cgroups/cpushare.hpp"
-#include "slave/containerizer/isolators/cgroups/mem.hpp"
-#include "slave/containerizer/isolators/cgroups/perf_event.hpp"
-#include "slave/containerizer/isolators/filesystem/shared.hpp"
-#endif // __linux__
-#include "slave/containerizer/isolators/posix.hpp"
-
-#include "slave/containerizer/launcher.hpp"
-#ifdef __linux__
-#include "slave/containerizer/fetcher.hpp"
-#include "slave/containerizer/linux_launcher.hpp"
-
-#include "slave/containerizer/mesos/containerizer.hpp"
-#include "slave/containerizer/mesos/launch.hpp"
-#endif // __linux__
-
-#include "tests/flags.hpp"
-#include "tests/memory_test_helper.hpp"
-#include "tests/mesos.hpp"
-#include "tests/module.hpp"
-#include "tests/utils.hpp"
-
-using namespace process;
-
-using mesos::internal::master::Master;
-#ifdef __linux__
-using mesos::internal::slave::CgroupsCpushareIsolatorProcess;
-using mesos::internal::slave::CgroupsMemIsolatorProcess;
-using mesos::internal::slave::CgroupsPerfEventIsolatorProcess;
-using mesos::internal::slave::CPU_SHARES_PER_CPU_REVOCABLE;
-using mesos::internal::slave::Fetcher;
-using mesos::internal::slave::LinuxLauncher;
-using mesos::internal::slave::SharedFilesystemIsolatorProcess;
-#endif // __linux__
-using mesos::internal::slave::Launcher;
-using mesos::internal::slave::MesosContainerizer;
-using mesos::internal::slave::PosixLauncher;
-using mesos::internal::slave::PosixCpuIsolatorProcess;
-using mesos::internal::slave::PosixMemIsolatorProcess;
-using mesos::internal::slave::Slave;
-
-using mesos::slave::Isolator;
-using mesos::slave::IsolatorProcess;
-
-using std::ostringstream;
-using std::set;
-using std::string;
-using std::vector;
-
-using testing::_;
-using testing::DoAll;
-using testing::Return;
-using testing::SaveArg;
-
-namespace mesos {
-namespace internal {
-namespace tests {
-
-static int childSetup(int pipes[2])
-{
-  // In child process.
-  while (::close(pipes[1]) == -1 && errno == EINTR);
-
-  // Wait until the parent signals us to continue.
-  char dummy;
-  ssize_t length;
-  while ((length = ::read(pipes[0], &dummy, sizeof(dummy))) == -1 &&
-         errno == EINTR);
-
-  if (length != sizeof(dummy)) {
-    ABORT("Failed to synchronize with parent");
-  }
-
-  while (::close(pipes[0]) == -1 && errno == EINTR);
-
-  return 0;
-}
-
-
-template <typename T>
-class CpuIsolatorTest : public MesosTest {};
-
-
-typedef ::testing::Types<
-    PosixCpuIsolatorProcess,
-#ifdef __linux__
-    CgroupsCpushareIsolatorProcess,
-#endif // __linux__
-    tests::Module<Isolator, TestCpuIsolator>> CpuIsolatorTypes;
-
-
-TYPED_TEST_CASE(CpuIsolatorTest, CpuIsolatorTypes);
-
-
-TYPED_TEST(CpuIsolatorTest, UserCpuUsage)
-{
-  slave::Flags flags;
-
-  Try<Isolator*> isolator = TypeParam::create(flags);
-  CHECK_SOME(isolator);
-
-  // A PosixLauncher is sufficient even when testing a cgroups isolator.
-  Try<Launcher*> launcher = PosixLauncher::create(flags);
-
-  ExecutorInfo executorInfo;
-  executorInfo.mutable_resources()->CopyFrom(
-      Resources::parse("cpus:1.0").get());
-
-  ContainerID containerId;
-  containerId.set_value(UUID::random().toString());
-
-  // Use a relative temporary directory so it gets cleaned up
-  // automatically with the test.
-  Try<string> dir = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
-  ASSERT_SOME(dir);
-
-  AWAIT_READY(isolator.get()->prepare(
-      containerId,
-      executorInfo,
-      dir.get(),
-      None(),
-      None()));
-
-  const string& file = path::join(dir.get(), "mesos_isolator_test_ready");
-
-  // Max out a single core in userspace. This will run for at most one second.
-  string command = "while true ; do true ; done &"
-    "touch " + file + "; " // Signals the command is running.
-    "sleep 60";
-
-  int pipes[2];
-  ASSERT_NE(-1, ::pipe(pipes));
-
-  vector<string> argv(3);
-  argv[0] = "sh";
-  argv[1] = "-c";
-  argv[2] = command;
-
-  Try<pid_t> pid = launcher.get()->fork(
-      containerId,
-      "/bin/sh",
-      argv,
-      Subprocess::FD(STDIN_FILENO),
-      Subprocess::FD(STDOUT_FILENO),
-      Subprocess::FD(STDERR_FILENO),
-      None(),
-      None(),
-      lambda::bind(&childSetup, pipes));
-
-  ASSERT_SOME(pid);
-
-  // Reap the forked child.
-  Future<Option<int> > status = process::reap(pid.get());
-
-  // Continue in the parent.
-  ASSERT_SOME(os::close(pipes[0]));
-
-  // Isolate the forked child.
-  AWAIT_READY(isolator.get()->isolate(containerId, pid.get()));
-
-  // Now signal the child to continue.
-  char dummy;
-  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
-
-  ASSERT_SOME(os::close(pipes[1]));
-
-  // Wait for the command to start.
-  while (!os::exists(file));
-
-  // Wait up to 1 second for the child process to induce 1/8 of a second of
-  // user cpu time.
-  ResourceStatistics statistics;
-  Duration waited = Duration::zero();
-  do {
-    Future<ResourceStatistics> usage = isolator.get()->usage(containerId);
-    AWAIT_READY(usage);
-
-    statistics = usage.get();
-
-    // If we meet our usage expectations, we're done!
-    if (statistics.cpus_user_time_secs() >= 0.125) {
-      break;
-    }
-
-    os::sleep(Milliseconds(200));
-    waited += Milliseconds(200);
-  } while (waited < Seconds(1));
-
-  EXPECT_LE(0.125, statistics.cpus_user_time_secs());
-
-  // Ensure all processes are killed.
-  AWAIT_READY(launcher.get()->destroy(containerId));
-
-  // Make sure the child was reaped.
-  AWAIT_READY(status);
-
-  // Let the isolator clean up.
-  AWAIT_READY(isolator.get()->cleanup(containerId));
-
-  delete isolator.get();
-  delete launcher.get();
-}
-
-
-TYPED_TEST(CpuIsolatorTest, SystemCpuUsage)
-{
-  slave::Flags flags;
-
-  Try<Isolator*> isolator = TypeParam::create(flags);
-  CHECK_SOME(isolator);
-
-  // A PosixLauncher is sufficient even when testing a cgroups isolator.
-  Try<Launcher*> launcher = PosixLauncher::create(flags);
-
-  ExecutorInfo executorInfo;
-  executorInfo.mutable_resources()->CopyFrom(
-      Resources::parse("cpus:1.0").get());
-
-  ContainerID containerId;
-  containerId.set_value(UUID::random().toString());
-
-  // Use a relative temporary directory so it gets cleaned up
-  // automatically with the test.
-  Try<string> dir = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
-  ASSERT_SOME(dir);
-
-  AWAIT_READY(isolator.get()->prepare(
-      containerId,
-      executorInfo,
-      dir.get(),
-      None(),
-      None()));
-
-  const string& file = path::join(dir.get(), "mesos_isolator_test_ready");
-
-  // Generating random numbers is done by the kernel and will max out a single
-  // core and run almost exclusively in the kernel, i.e., system time.
-  string command = "cat /dev/urandom > /dev/null & "
-    "touch " + file + "; " // Signals the command is running.
-    "sleep 60";
-
-  int pipes[2];
-  ASSERT_NE(-1, ::pipe(pipes));
-
-  vector<string> argv(3);
-  argv[0] = "sh";
-  argv[1] = "-c";
-  argv[2] = command;
-
-  Try<pid_t> pid = launcher.get()->fork(
-      containerId,
-      "/bin/sh",
-      argv,
-      Subprocess::FD(STDIN_FILENO),
-      Subprocess::FD(STDOUT_FILENO),
-      Subprocess::FD(STDERR_FILENO),
-      None(),
-      None(),
-      lambda::bind(&childSetup, pipes));
-
-  ASSERT_SOME(pid);
-
-  // Reap the forked child.
-  Future<Option<int> > status = process::reap(pid.get());
-
-  // Continue in the parent.
-  ASSERT_SOME(os::close(pipes[0]));
-
-  // Isolate the forked child.
-  AWAIT_READY(isolator.get()->isolate(containerId, pid.get()));
-
-  // Now signal the child to continue.
-  char dummy;
-  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
-
-  ASSERT_SOME(os::close(pipes[1]));
-
-  // Wait for the command to start.
-  while (!os::exists(file));
-
-  // Wait up to 1 second for the child process to induce 1/8 of a second of
-  // system cpu time.
-  ResourceStatistics statistics;
-  Duration waited = Duration::zero();
-  do {
-    Future<ResourceStatistics> usage = isolator.get()->usage(containerId);
-    AWAIT_READY(usage);
-
-    statistics = usage.get();
-
-    // If we meet our usage expectations, we're done!
-    if (statistics.cpus_system_time_secs() >= 0.125) {
-      break;
-    }
-
-    os::sleep(Milliseconds(200));
-    waited += Milliseconds(200);
-  } while (waited < Seconds(1));
-
-  EXPECT_LE(0.125, statistics.cpus_system_time_secs());
-
-  // Ensure all processes are killed.
-  AWAIT_READY(launcher.get()->destroy(containerId));
-
-  // Make sure the child was reaped.
-  AWAIT_READY(status);
-
-  // Let the isolator clean up.
-  AWAIT_READY(isolator.get()->cleanup(containerId));
-
-  delete isolator.get();
-  delete launcher.get();
-}
-
-
-#ifdef __linux__
-class RevocableCpuIsolatorTest : public MesosTest {};
-
-
-TEST_F(RevocableCpuIsolatorTest, ROOT_CGROUPS_RevocableCpu)
-{
-  slave::Flags flags;
-
-  Try<Isolator*> isolator = CgroupsCpushareIsolatorProcess::create(flags);
-  CHECK_SOME(isolator);
-
-  Try<Launcher*> launcher = PosixLauncher::create(flags);
-
-  // Include revocable CPU in the executor's resources.
-  Resource cpu = Resources::parse("cpus", "1", "*").get();
-  cpu.mutable_revocable();
-
-  ExecutorInfo executorInfo;
-  executorInfo.add_resources()->CopyFrom(cpu);
-
-  ContainerID containerId;
-  containerId.set_value(UUID::random().toString());
-
-  AWAIT_READY(isolator.get()->prepare(
-        containerId,
-        executorInfo,
-        os::getcwd(),
-        None(),
-        None()));
-
-  vector<string> argv{"sleep", "100"};
-
-  Try<pid_t> pid = launcher.get()->fork(
-      containerId,
-      "/bin/sleep",
-      argv,
-      Subprocess::PATH("/dev/null"),
-      Subprocess::PATH("/dev/null"),
-      Subprocess::PATH("/dev/null"),
-      None(),
-      None(),
-      None());
-
-  ASSERT_SOME(pid);
-
-  AWAIT_READY(isolator.get()->isolate(containerId, pid.get()));
-
-  // Executor should have proper cpu.shares for revocable containers.
-  Result<string> cpuHierarchy = cgroups::hierarchy("cpu");
-  ASSERT_SOME(cpuHierarchy);
-
-  Result<string> cpuCgroup = cgroups::cpu::cgroup(pid.get());
-  ASSERT_SOME(cpuCgroup);
-
-  EXPECT_SOME_EQ(
-      CPU_SHARES_PER_CPU_REVOCABLE,
-      cgroups::cpu::shares(cpuHierarchy.get(), cpuCgroup.get()));
-
-  // Kill the container and clean up.
-  Future<Option<int>> status = process::reap(pid.get());
-
-  AWAIT_READY(launcher.get()->destroy(containerId));
-
-  AWAIT_READY(status);
-
-  AWAIT_READY(isolator.get()->cleanup(containerId));
-
-  delete isolator.get();
-  delete launcher.get();
-}
-#endif // __linux__
-
-
-#ifdef __linux__
-class LimitedCpuIsolatorTest : public MesosTest {};
-
-
-TEST_F(LimitedCpuIsolatorTest, ROOT_CGROUPS_Cfs)
-{
-  slave::Flags flags;
-
-  // Enable CFS to cap CPU utilization.
-  flags.cgroups_enable_cfs = true;
-
-  Try<Isolator*> isolator = CgroupsCpushareIsolatorProcess::create(flags);
-  CHECK_SOME(isolator);
-
-  Try<Launcher*> launcher =
-    LinuxLauncher::create(flags, isolator.get()->namespaces().get());
-  CHECK_SOME(launcher);
-
-  // Set the executor's resources to 0.5 cpu.
-  ExecutorInfo executorInfo;
-  executorInfo.mutable_resources()->CopyFrom(
-      Resources::parse("cpus:0.5").get());
-
-  ContainerID containerId;
-  containerId.set_value(UUID::random().toString());
-
-  // Use a relative temporary directory so it gets cleaned up
-  // automatically with the test.
-  Try<string> dir = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
-  ASSERT_SOME(dir);
-
-  AWAIT_READY(isolator.get()->prepare(
-      containerId,
-      executorInfo,
-      dir.get(),
-      None(),
-      None()));
-
-  // Generate random numbers to max out a single core. We'll run this for 0.5
-  // seconds of wall time so it should consume approximately 250 ms of total
-  // cpu time when limited to 0.5 cpu. We use /dev/urandom to prevent blocking
-  // on Linux when there's insufficient entropy.
-  string command = "cat /dev/urandom > /dev/null & "
-    "export MESOS_TEST_PID=$! && "
-    "sleep 0.5 && "
-    "kill $MESOS_TEST_PID";
-
-  int pipes[2];
-  ASSERT_NE(-1, ::pipe(pipes));
-
-  vector<string> argv(3);
-  argv[0] = "sh";
-  argv[1] = "-c";
-  argv[2] = command;
-
-  Try<pid_t> pid = launcher.get()->fork(
-      containerId,
-      "/bin/sh",
-      argv,
-      Subprocess::FD(STDIN_FILENO),
-      Subprocess::FD(STDOUT_FILENO),
-      Subprocess::FD(STDERR_FILENO),
-      None(),
-      None(),
-      lambda::bind(&childSetup, pipes));
-
-  ASSERT_SOME(pid);
-
-  // Reap the forked child.
-  Future<Option<int> > status = process::reap(pid.get());
-
-  // Continue in the parent.
-  ASSERT_SOME(os::close(pipes[0]));
-
-  // Isolate the forked child.
-  AWAIT_READY(isolator.get()->isolate(containerId, pid.get()));
-
-  // Now signal the child to continue.
-  char dummy;
-  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
-
-  ASSERT_SOME(os::close(pipes[1]));
-
-  // Wait for the command to complete.
-  AWAIT_READY(status);
-
-  Future<ResourceStatistics> usage = isolator.get()->usage(containerId);
-  AWAIT_READY(usage);
-
-  // Expect that no more than 300 ms of cpu time has been consumed. We also
-  // check that at least 50 ms of cpu time has been consumed so this test will
-  // fail if the host system is very heavily loaded. This behavior is correct
-  // because under such conditions we aren't actually testing the CFS cpu
-  // limiter.
-  double cpuTime = usage.get().cpus_system_time_secs() +
-                   usage.get().cpus_user_time_secs();
-
-  EXPECT_GE(0.30, cpuTime);
-  EXPECT_LE(0.05, cpuTime);
-
-  // Ensure all processes are killed.
-  AWAIT_READY(launcher.get()->destroy(containerId));
-
-  // Let the isolator clean up.
-  AWAIT_READY(isolator.get()->cleanup(containerId));
-
-  delete isolator.get();
-  delete launcher.get();
-}
-
-
-// This test verifies that we can successfully launch a container with
-// a big (>= 10 cpus) cpu quota. This is to catch the regression
-// observed in MESOS-1049.
-// TODO(vinod): Revisit this if/when the isolator restricts the number
-// of cpus that an executor can use based on the slave cpus.
-TEST_F(LimitedCpuIsolatorTest, ROOT_CGROUPS_Cfs_Big_Quota)
-{
-  slave::Flags flags;
-
-  // Enable CFS to cap CPU utilization.
-  flags.cgroups_enable_cfs = true;
-
-  Try<Isolator*> isolator = CgroupsCpushareIsolatorProcess::create(flags);
-  CHECK_SOME(isolator);
-
-  Try<Launcher*> launcher =
-    LinuxLauncher::create(flags, isolator.get()->namespaces().get());
-  CHECK_SOME(launcher);
-
-  // Set the executor's resources to 100.5 cpu.
-  ExecutorInfo executorInfo;
-  executorInfo.mutable_resources()->CopyFrom(
-      Resources::parse("cpus:100.5").get());
-
-  ContainerID containerId;
-  containerId.set_value(UUID::random().toString());
-
-  // Use a relative temporary directory so it gets cleaned up
-  // automatically with the test.
-  Try<string> dir = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
-  ASSERT_SOME(dir);
-
-  AWAIT_READY(isolator.get()->prepare(
-      containerId,
-      executorInfo,
-      dir.get(),
-      None(),
-      None()));
-
-  int pipes[2];
-  ASSERT_NE(-1, ::pipe(pipes));
-
-  vector<string> argv(3);
-  argv[0] = "sh";
-  argv[1] = "-c";
-  argv[2] = "exit 0";
-
-  Try<pid_t> pid = launcher.get()->fork(
-      containerId,
-      "/bin/sh",
-      argv,
-      Subprocess::FD(STDIN_FILENO),
-      Subprocess::FD(STDOUT_FILENO),
-      Subprocess::FD(STDERR_FILENO),
-      None(),
-      None(),
-      lambda::bind(&childSetup, pipes));
-
-  ASSERT_SOME(pid);
-
-  // Reap the forked child.
-  Future<Option<int> > status = process::reap(pid.get());
-
-  // Continue in the parent.
-  ASSERT_SOME(os::close(pipes[0]));
-
-  // Isolate the forked child.
-  AWAIT_READY(isolator.get()->isolate(containerId, pid.get()));
-
-  // Now signal the child to continue.
-  char dummy;
-  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
-
-  ASSERT_SOME(os::close(pipes[1]));
-
-  // Wait for the command to complete successfully.
-  AWAIT_READY(status);
-  ASSERT_SOME_EQ(0, status.get());
-
-  // Ensure all processes are killed.
-  AWAIT_READY(launcher.get()->destroy(containerId));
-
-  // Let the isolator clean up.
-  AWAIT_READY(isolator.get()->cleanup(containerId));
-
-  delete isolator.get();
-  delete launcher.get();
-}
-
-
-// A test to verify the number of processes and threads in a
-// container.
-TEST_F(LimitedCpuIsolatorTest, ROOT_CGROUPS_Pids_and_Tids)
-{
-  slave::Flags flags;
-  flags.cgroups_cpu_enable_pids_and_tids_count = true;
-
-  Try<Isolator*> isolator = CgroupsCpushareIsolatorProcess::create(flags);
-  CHECK_SOME(isolator);
-
-  Try<Launcher*> launcher =
-    LinuxLauncher::create(flags, isolator.get()->namespaces().get());
-  CHECK_SOME(launcher);
-
-  ExecutorInfo executorInfo;
-  executorInfo.mutable_resources()->CopyFrom(
-      Resources::parse("cpus:0.5;mem:512").get());
-
-  ContainerID containerId;
-  containerId.set_value(UUID::random().toString());
-
-  // Use a relative temporary directory so it gets cleaned up
-  // automatically with the test.
-  Try<string> dir = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
-  ASSERT_SOME(dir);
-
-  AWAIT_READY(isolator.get()->prepare(
-      containerId,
-      executorInfo,
-      dir.get(),
-      None(),
-      None()));
-
-  // Right after the creation of the cgroup, which happens in
-  // 'prepare', we check that it is empty.
-  Future<ResourceStatistics> usage = isolator.get()->usage(containerId);
-  AWAIT_READY(usage);
-  EXPECT_EQ(0U, usage.get().processes());
-  EXPECT_EQ(0U, usage.get().threads());
-
-  int pipes[2];
-  ASSERT_NE(-1, ::pipe(pipes));
-
-  vector<string> argv(3);
-  argv[0] = "sh";
-  argv[1] = "-c";
-  argv[2] = "while true; do sleep 1; done;";
-
-  Try<pid_t> pid = launcher.get()->fork(
-      containerId,
-      "/bin/sh",
-      argv,
-      Subprocess::FD(STDIN_FILENO),
-      Subprocess::FD(STDOUT_FILENO),
-      Subprocess::FD(STDERR_FILENO),
-      None(),
-      None(),
-      lambda::bind(&childSetup, pipes));
-
-  ASSERT_SOME(pid);
-
-  // Reap the forked child.
-  Future<Option<int>> status = process::reap(pid.get());
-
-  // Continue in the parent.
-  ASSERT_SOME(os::close(pipes[0]));
-
-  // Before isolation, the cgroup is empty.
-  usage = isolator.get()->usage(containerId);
-  AWAIT_READY(usage);
-  EXPECT_EQ(0U, usage.get().processes());
-  EXPECT_EQ(0U, usage.get().threads());
-
-  // Isolate the forked child.
-  AWAIT_READY(isolator.get()->isolate(containerId, pid.get()));
-
-  // After the isolation, the cgroup is not empty, even though the
-  // process hasn't exec'd yet.
-  usage = isolator.get()->usage(containerId);
-  AWAIT_READY(usage);
-  EXPECT_EQ(1U, usage.get().processes());
-  EXPECT_EQ(1U, usage.get().threads());
-
-  // Now signal the child to continue.
-  char dummy;
-  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
-
-  ASSERT_SOME(os::close(pipes[1]));
-
-  // Process count should be 1 since 'sleep' is still sleeping.
-  usage = isolator.get()->usage(containerId);
-  AWAIT_READY(usage);
-  EXPECT_EQ(1U, usage.get().processes());
-  EXPECT_EQ(1U, usage.get().threads());
-
-  // Ensure all processes are killed.
-  AWAIT_READY(launcher.get()->destroy(containerId));
-
-  // Wait for the command to complete.
-  AWAIT_READY(status);
-
-  // After the process is killed, the cgroup should be empty again.
-  usage = isolator.get()->usage(containerId);
-  AWAIT_READY(usage);
-  EXPECT_EQ(0U, usage.get().processes());
-  EXPECT_EQ(0U, usage.get().threads());
-
-  // Let the isolator clean up.
-  AWAIT_READY(isolator.get()->cleanup(containerId));
-
-  delete isolator.get();
-  delete launcher.get();
-}
-#endif // __linux__
-
-
-template <typename T>
-class MemIsolatorTest : public MesosTest {};
-
-
-typedef ::testing::Types<
-    PosixMemIsolatorProcess,
-#ifdef __linux__
-    CgroupsMemIsolatorProcess,
-#endif // __linux__
-    tests::Module<Isolator, TestMemIsolator>> MemIsolatorTypes;
-
-
-TYPED_TEST_CASE(MemIsolatorTest, MemIsolatorTypes);
-
-
-TYPED_TEST(MemIsolatorTest, MemUsage)
-{
-  slave::Flags flags;
-
-  Try<Isolator*> isolator = TypeParam::create(flags);
-  CHECK_SOME(isolator);
-
-  ExecutorInfo executorInfo;
-  executorInfo.mutable_resources()->CopyFrom(
-      Resources::parse("mem:1024").get());
-
-  ContainerID containerId;
-  containerId.set_value(UUID::random().toString());
-
-  // Use a relative temporary directory so it gets cleaned up
-  // automatically with the test.
-  Try<string> dir = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
-  ASSERT_SOME(dir);
-
-  AWAIT_READY(isolator.get()->prepare(
-      containerId,
-      executorInfo,
-      dir.get(),
-      None(),
-      None()));
-
-  MemoryTestHelper helper;
-  ASSERT_SOME(helper.spawn());
-  ASSERT_SOME(helper.pid());
-
-  // Set up the reaper to wait on the subprocess.
-  Future<Option<int>> status = process::reap(helper.pid().get());
-
-  // Isolate the subprocess.
-  AWAIT_READY(isolator.get()->isolate(containerId, helper.pid().get()));
-
-  const Bytes allocation = Megabytes(128);
-  EXPECT_SOME(helper.increaseRSS(allocation));
-
-  Future<ResourceStatistics> usage = isolator.get()->usage(containerId);
-  AWAIT_READY(usage);
-
-  EXPECT_GE(usage.get().mem_rss_bytes(), allocation.bytes());
-
-  // Ensure the process is killed.
-  helper.cleanup();
-
-  // Make sure the subprocess was reaped.
-  AWAIT_READY(status);
-
-  // Let the isolator clean up.
-  AWAIT_READY(isolator.get()->cleanup(containerId));
-
-  delete isolator.get();
-}
-
-
-#ifdef __linux__
-class PerfEventIsolatorTest : public MesosTest {};
-
-
-TEST_F(PerfEventIsolatorTest, ROOT_CGROUPS_Sample)
-{
-  slave::Flags flags;
-
-  flags.perf_events = "cycles,task-clock";
-  flags.perf_duration = Milliseconds(250);
-  flags.perf_interval = Milliseconds(500);
-
-  Try<Isolator*> isolator = CgroupsPerfEventIsolatorProcess::create(flags);
-  ASSERT_SOME(isolator);
-
-  ExecutorInfo executorInfo;
-
-  ContainerID containerId;
-  containerId.set_value(UUID::random().toString());
-
-  // Use a relative temporary directory so it gets cleaned up
-  // automatically with the test.
-  Try<string> dir = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
-  ASSERT_SOME(dir);
-
-  AWAIT_READY(isolator.get()->prepare(
-      containerId,
-      executorInfo,
-      dir.get(),
-      None(),
-      None()));
-
-  // This first sample is likely to be empty because perf hasn't
-  // completed yet but we should still have the required fields.
-  Future<ResourceStatistics> statistics1 = isolator.get()->usage(containerId);
-  AWAIT_READY(statistics1);
-  ASSERT_TRUE(statistics1.get().has_perf());
-  EXPECT_TRUE(statistics1.get().perf().has_timestamp());
-  EXPECT_TRUE(statistics1.get().perf().has_duration());
-
-  // Wait until we get the next sample. We use a generous timeout of
-  // two seconds because we currently have a one second reap interval;
-  // when running perf with perf_duration of 250ms we won't notice the
-  // exit for up to one second.
-  ResourceStatistics statistics2;
-  Duration waited = Duration::zero();
-  do {
-    Future<ResourceStatistics> statistics = isolator.get()->usage(containerId);
-    AWAIT_READY(statistics);
-
-    statistics2 = statistics.get();
-
-    ASSERT_TRUE(statistics2.has_perf());
-
-    if (statistics1.get().perf().timestamp() !=
-        statistics2.perf().timestamp()) {
-      break;
-    }
-
-    os::sleep(Milliseconds(250));
-    waited += Milliseconds(250);
-  } while (waited < Seconds(2));
-
-  sleep(2);
-
-  EXPECT_NE(statistics1.get().perf().timestamp(),
-            statistics2.perf().timestamp());
-
-  EXPECT_TRUE(statistics2.perf().has_cycles());
-  EXPECT_LE(0u, statistics2.perf().cycles());
-
-  EXPECT_TRUE(statistics2.perf().has_task_clock());
-  EXPECT_LE(0.0, statistics2.perf().task_clock());
-
-  AWAIT_READY(isolator.get()->cleanup(containerId));
-
-  delete isolator.get();
-}
-
-
-class SharedFilesystemIsolatorTest : public MesosTest {};
-
-
-// Test that a container can create a private view of a system
-// directory (/var/tmp). Check that a file written by a process inside
-// the container doesn't appear on the host filesystem but does appear
-// under the container's work directory.
-// This test is disabled since we're planning to remove the shared
-// filesystem isolator and this test is not working on other distros
-// such as CentOS 7.1
-// TODO(tnachen): Remove this test when shared filesystem isolator
-// is removed.
-TEST_F(SharedFilesystemIsolatorTest, DISABLED_ROOT_RelativeVolume)
-{
-  slave::Flags flags = CreateSlaveFlags();
-  flags.isolation = "filesystem/shared";
-
-  Try<Isolator*> isolator = SharedFilesystemIsolatorProcess::create(flags);
-  CHECK_SOME(isolator);
-
-  Try<Launcher*> launcher =
-    LinuxLauncher::create(flags, isolator.get()->namespaces().get());
-  CHECK_SOME(launcher);
-
-  // Use /var/tmp so we don't mask the work directory (under /tmp).
-  const string containerPath = "/var/tmp";
-  ASSERT_TRUE(os::stat::isdir(containerPath));
-
-  // Use a host path relative to the container work directory.
-  const string hostPath = strings::remove(containerPath, "/", strings::PREFIX);
-
-  ContainerInfo containerInfo;
-  containerInfo.set_type(ContainerInfo::MESOS);
-  containerInfo.add_volumes()->CopyFrom(
-      CREATE_VOLUME(containerPath, hostPath, Volume::RW));
-
-  ExecutorInfo executorInfo;
-  executorInfo.mutable_container()->CopyFrom(containerInfo);
-
-  ContainerID containerId;
-  containerId.set_value(UUID::random().toString());
-
-  Future<Option<CommandInfo> > prepare =
-    isolator.get()->prepare(
-        containerId,
-        executorInfo,
-        flags.work_dir,
-        None(),
-        None());
-
-  AWAIT_READY(prepare);
-  ASSERT_SOME(prepare.get());
-
-  // The test will touch a file in container path.
-  const string file = path::join(containerPath, UUID::random().toString());
-  ASSERT_FALSE(os::exists(file));
-
-  // Manually run the isolator's preparation command first, then touch
-  // the file.
-  vector<string> args;
-  args.push_back("/bin/sh");
-  args.push_back("-x");
-  args.push_back("-c");
-  args.push_back(prepare.get().get().value() + " && touch " + file);
-
-  Try<pid_t> pid = launcher.get()->fork(
-      containerId,
-      "/bin/sh",
-      args,
-      Subprocess::FD(STDIN_FILENO),
-      Subprocess::FD(STDOUT_FILENO),
-      Subprocess::FD(STDERR_FILENO),
-      None(),
-      None(),
-      None());
-  ASSERT_SOME(pid);
-
-  // Set up the reaper to wait on the forked child.
-  Future<Option<int> > status = process::reap(pid.get());
-
-  AWAIT_READY(status);
-  EXPECT_SOME_EQ(0, status.get());
-
-  // Check the correct hierarchy was created under the container work
-  // directory.
-  string dir = "/";
-  foreach (const string& subdir, strings::tokenize(containerPath, "/")) {
-    dir = path::join(dir, subdir);
-
-    struct stat hostStat;
-    EXPECT_EQ(0, ::stat(dir.c_str(), &hostStat));
-
-    struct stat containerStat;
-    EXPECT_EQ(0,
-              ::stat(path::join(flags.work_dir, dir).c_str(), &containerStat));
-
-    EXPECT_EQ(hostStat.st_mode, containerStat.st_mode);
-    EXPECT_EQ(hostStat.st_uid, containerStat.st_uid);
-    EXPECT_EQ(hostStat.st_gid, containerStat.st_gid);
-  }
-
-  // Check it did *not* create a file in the host namespace.
-  EXPECT_FALSE(os::exists(file));
-
-  // Check it did create the file under the container's work directory
-  // on the host.
-  EXPECT_TRUE(os::exists(path::join(flags.work_dir, file)));
-
-  delete launcher.get();
-  delete isolator.get();
-}
-
-
-// This test is disabled since we're planning to remove the shared
-// filesystem isolator and this test is not working on other distros
-// such as CentOS 7.1
-// TODO(tnachen): Remove this test when shared filesystem isolator
-// is removed.
-TEST_F(SharedFilesystemIsolatorTest, DISABLED_ROOT_AbsoluteVolume)
-{
-  slave::Flags flags = CreateSlaveFlags();
-  flags.isolation = "filesystem/shared";
-
-  Try<Isolator*> isolator = SharedFilesystemIsolatorProcess::create(flags);
-  CHECK_SOME(isolator);
-
-  Try<Launcher*> launcher =
-    LinuxLauncher::create(flags, isolator.get()->namespaces().get());
-  CHECK_SOME(launcher);
-
-  // We'll mount the absolute test work directory as /var/tmp in the
-  // container.
-  const string hostPath = flags.work_dir;
-  const string containerPath = "/var/tmp";
-
-  ContainerInfo containerInfo;
-  containerInfo.set_type(ContainerInfo::MESOS);
-  containerInfo.add_volumes()->CopyFrom(
-      CREATE_VOLUME(containerPath, hostPath, Volume::RW));
-
-  ExecutorInfo executorInfo;
-  executorInfo.mutable_container()->CopyFrom(containerInfo);
-
-  ContainerID containerId;
-  containerId.set_value(UUID::random().toString());
-
-  Future<Option<CommandInfo> > prepare =
-    isolator.get()->prepare(
-        containerId,
-        executorInfo,
-        flags.work_dir,
-        None(),
-        None());
-
-  AWAIT_READY(prepare);
-  ASSERT_SOME(prepare.get());
-
-  // Test the volume mounting by touching a file in the container's
-  // /tmp, which should then be in flags.work_dir.
-  const string filename = UUID::random().toString();
-  ASSERT_FALSE(os::exists(path::join(containerPath, filename)));
-
-  vector<string> args;
-  args.push_back("/bin/sh");
-  args.push_back("-x");
-  args.push_back("-c");
-  args.push_back(prepare.get().get().value() +
-                 " && touch " +
-                 path::join(containerPath, filename));
-
-  Try<pid_t> pid = launcher.get()->fork(
-      containerId,
-      "/bin/sh",
-      args,
-      Subprocess::FD(STDIN_FILENO),
-      Subprocess::FD(STDOUT_FILENO),
-      Subprocess::FD(STDERR_FILENO),
-      None(),
-      None(),
-      None());
-  ASSERT_SOME(pid);
-
-  // Set up the reaper to wait on the forked child.
-  Future<Option<int> > status = process::reap(pid.get());
-
-  AWAIT_READY(status);
-  EXPECT_SOME_EQ(0, status.get());
-
-  // Check the file was created in flags.work_dir.
-  EXPECT_TRUE(os::exists(path::join(hostPath, filename)));
-
-  // Check it didn't get created in the host's view of containerPath.
-  EXPECT_FALSE(os::exists(path::join(containerPath, filename)));
-
-  delete launcher.get();
-  delete isolator.get();
-}
-
-
-class NamespacesPidIsolatorTest : public MesosTest {};
-
-
-TEST_F(NamespacesPidIsolatorTest, ROOT_PidNamespace)
-{
-  slave::Flags flags = CreateSlaveFlags();
-  flags.isolation = "namespaces/pid";
-
-  string directory = os::getcwd(); // We're inside a temporary sandbox.
-
-  Fetcher fetcher;
-
-  Try<MesosContainerizer*> containerizer =
-    MesosContainerizer::create(flags, false, &fetcher);
-  ASSERT_SOME(containerizer);
-
-  ContainerID containerId;
-  containerId.set_value(UUID::random().toString());
-
-  // Write the command's pid namespace inode and init name to files.
-  const string command =
-    "stat -c %i /proc/self/ns/pid > ns && (cat /proc/1/comm > init)";
-
-  process::Future<bool> launch = containerizer.get()->launch(
-      containerId,
-      CREATE_EXECUTOR_INFO("executor", command),
-      directory,
-      None(),
-      SlaveID(),
-      process::PID<Slave>(),
-      false);
-  AWAIT_READY(launch);
-  ASSERT_TRUE(launch.get());
-
-  // Wait on the container.
-  process::Future<containerizer::Termination> wait =
-    containerizer.get()->wait(containerId);
-  AWAIT_READY(wait);
-
-  // Check the executor exited correctly.
-  EXPECT_TRUE(wait.get().has_status());
-  EXPECT_EQ(0, wait.get().status());
-
-  // Check that the command was run in a different pid namespace.
-  Try<ino_t> testPidNamespace = ns::getns(::getpid(), "pid");
-  ASSERT_SOME(testPidNamespace);
-
-  Try<string> containerPidNamespace = os::read(path::join(directory, "ns"));
-  ASSERT_SOME(containerPidNamespace);
-
-  EXPECT_NE(stringify(testPidNamespace.get()),
-            strings::trim(containerPidNamespace.get()));
-
-  // Check that 'sh' is the container's 'init' process.
-  // This verifies that /proc has been correctly mounted for the container.
-  Try<string> init = os::read(path::join(directory, "init"));
-  ASSERT_SOME(init);
-
-  EXPECT_EQ("sh", strings::trim(init.get()));
-
-  delete containerizer.get();
-}
-
-
-// Username for the unprivileged user that will be created to test
-// unprivileged cgroup creation. It will be removed after the tests.
-// It is presumed this user does not normally exist.
-const string UNPRIVILEGED_USERNAME = "mesos.test.unprivileged.user";
-
-
-template <typename T>
-class UserCgroupIsolatorTest : public MesosTest
-{
-public:
-  static void SetUpTestCase()
-  {
-    // Remove the user in case it wasn't cleaned up from a previous
-    // test.
-    os::system("userdel -r " + UNPRIVILEGED_USERNAME + " > /dev/null");
-
-    ASSERT_EQ(0, os::system("useradd " + UNPRIVILEGED_USERNAME));
-  }
-
-
-  static void TearDownTestCase()
-  {
-    ASSERT_EQ(0, os::system("userdel -r " + UNPRIVILEGED_USERNAME));
-  }
-};
-
-
-// Test all isolators that use cgroups.
-typedef ::testing::Types<
-  CgroupsMemIsolatorProcess,
-  CgroupsCpushareIsolatorProcess,
-  CgroupsPerfEventIsolatorProcess> CgroupsIsolatorTypes;
-
-
-TYPED_TEST_CASE(UserCgroupIsolatorTest, CgroupsIsolatorTypes);
-
-
-TYPED_TEST(UserCgroupIsolatorTest, ROOT_CGROUPS_UserCgroup)
-{
-  slave::Flags flags;
-  flags.perf_events = "cpu-cycles"; // Needed for CgroupsPerfEventIsolator.
-
-  Try<Isolator*> isolator = TypeParam::create(flags);
-  ASSERT_SOME(isolator);
-
-  ExecutorInfo executorInfo;
-  executorInfo.mutable_resources()->CopyFrom(
-      Resources::parse("mem:1024;cpus:1").get()); // For cpu/mem isolators.
-
-  ContainerID containerId;
-  containerId.set_value(UUID::random().toString());
-
-  AWAIT_READY(isolator.get()->prepare(
-      containerId,
-      executorInfo,
-      os::getcwd(),
-      None(),
-      UNPRIVILEGED_USERNAME));
-
-  // Isolators don't provide a way to determine the cgroups they use
-  // so we'll inspect the cgroups for an isolated dummy process.
-  pid_t pid = fork();
-  if (pid == 0) {
-    // Child just sleeps.
-    ::sleep(100);
-
-    ABORT("Child process should not reach here");
-  }
-  ASSERT_GT(pid, 0);
-
-  AWAIT_READY(isolator.get()->isolate(containerId, pid));
-
-  // Get the container's cgroups from /proc/$PID/cgroup. We're only
-  // interested in the cgroups that this isolator has created which we
-  // can do explicitly by selecting those that have the path that
-  // corresponds to the 'cgroups_root' slave flag. For example:
-  //
-  //   $ cat /proc/pid/cgroup
-  //   6:blkio:/
-  //   5:perf_event:/
-  //   4:memory:/mesos/b7410ed8-c85b-445e-b50e-3a1698d0e18c
-  //   3:freezer:/
-  //   2:cpuacct:/
-  //   1:cpu:/
-  //
-  // Our 'grep' will only select the 'memory' line and then 'awk' will
-  // output 'memory/mesos/b7410ed8-c85b-445e-b50e-3a1698d0e18c'.
-  ostringstream output;
-  Try<int> status = os::shell(
-      &output,
-      "grep '" + path::join("/", flags.cgroups_root) + "' /proc/" +
-      stringify(pid) + "/cgroup | awk -F ':' '{print $2$3}'");
-
-  ASSERT_SOME(status);
-
-  // Kill the dummy child process.
-  ::kill(pid, SIGKILL);
-  int exitStatus;
-  EXPECT_NE(-1, ::waitpid(pid, &exitStatus, 0));
-
-  vector<string> cgroups = strings::tokenize(output.str(), "\n");
-  ASSERT_FALSE(cgroups.empty());
-
-  foreach (const string& cgroup, cgroups) {
-    // Check the user cannot manipulate the container's cgroup control
-    // files.
-    EXPECT_NE(0, os::system(
-          "su - " + UNPRIVILEGED_USERNAME +
-          " -c 'echo $$ >" +
-          path::join(flags.cgroups_hierarchy, cgroup, "cgroup.procs") +
-          "'"));
-
-    // Check the user can create a cgroup under the container's
-    // cgroup.
-    string userCgroup = path::join(cgroup, "user");
-
-    EXPECT_EQ(0, os::system(
-          "su - " +
-          UNPRIVILEGED_USERNAME +
-          " -c 'mkdir " +
-          path::join(flags.cgroups_hierarchy, userCgroup) +
-          "'"));
-
-    // Check the user can manipulate control files in the created
-    // cgroup.
-    EXPECT_EQ(0, os::system(
-          "su - " +
-          UNPRIVILEGED_USERNAME +
-          " -c 'echo $$ >" +
-          path::join(flags.cgroups_hierarchy, userCgroup, "cgroup.procs") +
-          "'"));
-  }
-
-  // Clean up the container. This will also remove the nested cgroups.
-  AWAIT_READY(isolator.get()->cleanup(containerId));
-
-  delete isolator.get();
-}
-#endif // __linux__
-
-} // namespace tests {
-} // namespace internal {
-} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/launch_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/launch_tests.cpp b/src/tests/launch_tests.cpp
deleted file mode 100644
index 73c8c5f..0000000
--- a/src/tests/launch_tests.cpp
+++ /dev/null
@@ -1,238 +0,0 @@
-/**
- * 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 <string>
-#include <vector>
-
-#include <gmock/gmock.h>
-
-#include <stout/foreach.hpp>
-#include <stout/gtest.hpp>
-#include <stout/os.hpp>
-#include <stout/try.hpp>
-
-#include <process/gtest.hpp>
-#include <process/io.hpp>
-#include <process/reap.hpp>
-#include <process/subprocess.hpp>
-
-#include "mesos/resources.hpp"
-
-#include "slave/containerizer/mesos/launch.hpp"
-
-#include "linux/fs.hpp"
-
-#include "tests/flags.hpp"
-#include "tests/utils.hpp"
-
-using namespace process;
-
-using std::string;
-using std::vector;
-
-namespace mesos {
-namespace internal {
-namespace tests {
-
-class Chroot
-{
-public:
-  Chroot(const string& _rootfs)
-    : rootfs(_rootfs) {}
-
-  virtual ~Chroot() {}
-
-  virtual Try<Subprocess> run(const string& command) = 0;
-
-  const string rootfs;
-};
-
-
-class BasicLinuxChroot : public Chroot
-{
-public:
-  static Try<Owned<Chroot>> create(const string& rootfs)
-  {
-    if (!os::exists(rootfs)) {
-      return Error("rootfs does not exist");
-    }
-
-    if (os::system("cp -r /bin " + rootfs + "/") != 0) {
-      return ErrnoError("Failed to copy /bin to chroot");
-    }
-
-    if (os::system("cp -r /lib " + rootfs + "/") != 0) {
-      return ErrnoError("Failed to copy /lib to chroot");
-    }
-
-    if (os::system("cp -r /lib64 " + rootfs + "/") != 0) {
-      return ErrnoError("Failed to copy /lib64 to chroot");
-    }
-
-    vector<string> directories = {"proc", "sys", "dev", "tmp"};
-    foreach (const string& directory, directories) {
-      Try<Nothing> mkdir = os::mkdir(path::join(rootfs, directory));
-      if (mkdir.isError()) {
-        return Error("Failed to create /" + directory + " in chroot: " +
-                     mkdir.error());
-      }
-    }
-
-    // We need to bind mount the rootfs so we can pivot on it.
-    Try<Nothing> mount =
-      fs::mount(rootfs, rootfs, None(), MS_BIND | MS_SLAVE, NULL);
-
-    if (mount.isError()) {
-      return Error("Failed to bind mount chroot rootfs: " + mount.error());
-    }
-
-    return Owned<Chroot>(new BasicLinuxChroot(rootfs));
-  }
-
-  virtual Try<Subprocess> run(const string& _command)
-  {
-    slave::MesosContainerizerLaunch::Flags launchFlags;
-
-    CommandInfo command;
-    command.set_value(_command);
-
-    launchFlags.command = JSON::Protobuf(command);
-    launchFlags.directory = "/tmp";
-    launchFlags.pipe_read = open("/dev/zero", O_RDONLY);
-    launchFlags.pipe_write = open("/dev/null", O_WRONLY);
-    launchFlags.rootfs = rootfs;
-
-    vector<string> argv(2);
-    argv[0] = "mesos-containerizer";
-    argv[1] = slave::MesosContainerizerLaunch::NAME;
-
-    Try<Subprocess> s = subprocess(
-        path::join(tests::flags.build_dir, "src", "mesos-containerizer"),
-        argv,
-        Subprocess::PATH("/dev/null"),
-        Subprocess::PIPE(),
-        Subprocess::FD(STDERR_FILENO),
-        launchFlags,
-        None(),
-        None(),
-        lambda::bind(&clone, lambda::_1));
-
-    if (s.isError()) {
-      close(launchFlags.pipe_read.get());
-      close(launchFlags.pipe_write.get());
-    } else {
-      s.get().status().onAny([=]() {
-        // Close when the subprocess terminates.
-        close(launchFlags.pipe_read.get());
-        close(launchFlags.pipe_write.get());
-      });
-    }
-
-    return s;
-  }
-
-private:
-  static pid_t clone(const lambda::function<int()>& f)
-  {
-    static unsigned long long stack[(8*1024*1024)/sizeof(unsigned long long)];
-
-    return ::clone(
-        _clone,
-        &stack[sizeof(stack)/sizeof(stack[0]) - 1],  // Stack grows down.
-        CLONE_NEWNS | SIGCHLD,   // Specify SIGCHLD as child termination signal.
-        (void*) &f);
-  }
-
-  static int _clone(void* f)
-  {
-    const lambda::function<int()>* _f =
-      static_cast<const lambda::function<int()>*> (f);
-
-    return (*_f)();
-  }
-
-  BasicLinuxChroot(const string& rootfs) : Chroot(rootfs) {}
-
-  ~BasicLinuxChroot()
-  {
-    // Because the test process has the rootfs as its cwd the umount
-    // won't actually happen until the
-    // TemporaryDirectoryTest::TearDown() changes back to the original
-    // directory.
-    fs::unmount(rootfs, MNT_DETACH);
-  }
-};
-
-
-template <typename T>
-class LaunchChrootTest : public TemporaryDirectoryTest {};
-
-
-// TODO(idownes): Add tests for OSX chroots.
-typedef ::testing::Types<BasicLinuxChroot> ChrootTypes;
-
-
-TYPED_TEST_CASE(LaunchChrootTest, ChrootTypes);
-
-
-TYPED_TEST(LaunchChrootTest, ROOT_DifferentRoot)
-{
-  Try<Owned<Chroot>> chroot = TypeParam::create(os::getcwd());
-  ASSERT_SOME(chroot);
-
-  // Add /usr/bin/stat into the chroot.
-  const string usrbin = path::join(chroot.get()->rootfs, "usr", "bin");
-  ASSERT_SOME(os::mkdir(usrbin));
-  ASSERT_EQ(0, os::system("cp /usr/bin/stat " + path::join(usrbin, "stat")));
-
-  Clock::pause();
-
-  Try<Subprocess> s = chroot.get()->run(
-      "/usr/bin/stat -c %i / >" + path::join("/", "stat.output"));
-
-  CHECK_SOME(s);
-
-  // Advance time until the internal reaper reaps the subprocess.
-  while (s.get().status().isPending()) {
-    Clock::advance(Seconds(1));
-    Clock::settle();
-  }
-
-  AWAIT_ASSERT_READY(s.get().status());
-  ASSERT_SOME(s.get().status().get());
-
-  int status = s.get().status().get().get();
-  ASSERT_TRUE(WIFEXITED(status));
-  ASSERT_EQ(0, WEXITSTATUS(status));
-
-  // Check the chroot has a different root by comparing the inodes.
-  Try<ino_t> self = os::stat::inode("/");
-  ASSERT_SOME(self);
-
-  Try<string> read = os::read(path::join(chroot.get()->rootfs, "stat.output"));
-  CHECK_SOME(read);
-
-  Try<ino_t> other = numify<ino_t>(strings::trim(read.get()));
-  ASSERT_SOME(other);
-
-  EXPECT_NE(self.get(), other.get());
-}
-
-} // namespace tests {
-} // namespace internal {
-} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/launcher.hpp
----------------------------------------------------------------------
diff --git a/src/tests/launcher.hpp b/src/tests/launcher.hpp
deleted file mode 100644
index 78216e0..0000000
--- a/src/tests/launcher.hpp
+++ /dev/null
@@ -1,119 +0,0 @@
-/**
- * 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 <vector>
-
-#include <gmock/gmock.h>
-
-#include <mesos/mesos.hpp>
-
-#include <mesos/slave/isolator.hpp>
-
-#include <process/future.hpp>
-#include <process/owned.hpp>
-#include <process/subprocess.hpp>
-
-#include <stout/flags.hpp>
-#include <stout/lambda.hpp>
-#include <stout/nothing.hpp>
-#include <stout/option.hpp>
-
-#include "slave/containerizer/launcher.hpp"
-
-namespace mesos {
-namespace internal {
-namespace tests {
-
-
-ACTION_P(InvokeRecover, launcher)
-{
-  return launcher->real->recover(arg0);
-}
-
-
-ACTION_P(InvokeFork, launcher)
-{
-  return launcher->real->fork(
-      arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
-}
-
-
-ACTION_P(InvokeDestroy, launcher)
-{
-  return launcher->real->destroy(arg0);
-}
-
-
-class TestLauncher : public slave::Launcher
-{
-public:
-  TestLauncher(const process::Owned<slave::Launcher>& _real)
-    : real(_real)
-  {
-    using testing::_;
-    using testing::DoDefault;
-
-    ON_CALL(*this, recover(_))
-      .WillByDefault(InvokeRecover(this));
-    EXPECT_CALL(*this, recover(_))
-      .WillRepeatedly(DoDefault());
-
-    ON_CALL(*this, fork(_, _, _, _, _, _, _, _, _))
-      .WillByDefault(InvokeFork(this));
-    EXPECT_CALL(*this, fork(_, _, _, _, _, _, _, _, _))
-      .WillRepeatedly(DoDefault());
-
-    ON_CALL(*this, destroy(_))
-      .WillByDefault(InvokeDestroy(this));
-    EXPECT_CALL(*this, destroy(_))
-      .WillRepeatedly(DoDefault());
-  }
-
-  ~TestLauncher() {}
-
-  MOCK_METHOD1(
-      recover,
-      process::Future<hashset<ContainerID>>(
-          const std::list<mesos::slave::ExecutorRunState>& states));
-
-  MOCK_METHOD9(
-      fork,
-      Try<pid_t>(
-          const ContainerID& containerId,
-          const std::string& path,
-          const std::vector<std::string>& argv,
-          const process::Subprocess::IO& in,
-          const process::Subprocess::IO& out,
-          const process::Subprocess::IO& err,
-          const Option<flags::FlagsBase>& flags,
-          const Option<std::map<std::string, std::string> >& env,
-          const Option<lambda::function<int()> >& setup));
-
-  MOCK_METHOD1(
-      destroy,
-      process::Future<Nothing>(const ContainerID& containerId));
-
-  process::Owned<slave::Launcher> real;
-};
-
-} // namespace tests {
-} // namespace internal {
-} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/memory_pressure_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/memory_pressure_tests.cpp b/src/tests/memory_pressure_tests.cpp
deleted file mode 100644
index 8089879..0000000
--- a/src/tests/memory_pressure_tests.cpp
+++ /dev/null
@@ -1,293 +0,0 @@
-/**
- * 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 <vector>
-
-#include <mesos/resources.hpp>
-#include <mesos/scheduler.hpp>
-
-#include <process/gtest.hpp>
-
-#include <stout/gtest.hpp>
-#include <stout/os.hpp>
-
-#include "master/master.hpp"
-
-#include "slave/slave.hpp"
-
-#include "slave/containerizer/containerizer.hpp"
-#include "slave/containerizer/fetcher.hpp"
-
-#include "messages/messages.hpp"
-
-#include "tests/mesos.hpp"
-
-using namespace process;
-
-using mesos::internal::master::Master;
-
-using mesos::internal::slave::Fetcher;
-using mesos::internal::slave::MesosContainerizer;
-using mesos::internal::slave::Slave;
-
-using std::vector;
-
-using testing::_;
-using testing::Eq;
-using testing::Return;
-
-namespace mesos {
-namespace internal {
-namespace tests {
-
-class MemoryPressureMesosTest : public ContainerizerTest<MesosContainerizer>
-{
-public:
-  static void SetUpTestCase()
-  {
-    // Verify that the dd command and its flags used in a bit are valid
-    // on this system.
-    ASSERT_EQ(0, os::system("dd count=1 bs=1M if=/dev/zero of=/dev/null"))
-      << "Cannot find a compatible 'dd' command";
-  }
-};
-
-
-TEST_F(MemoryPressureMesosTest, CGROUPS_ROOT_Statistics)
-{
-  Try<PID<Master>> master = StartMaster();
-  ASSERT_SOME(master);
-
-  slave::Flags flags = CreateSlaveFlags();
-
-  // We only care about memory cgroup for this test.
-  flags.isolation = "cgroups/mem";
-  flags.slave_subsystems = None();
-
-  Fetcher fetcher;
-
-  Try<MesosContainerizer*> containerizer =
-    MesosContainerizer::create(flags, true, &fetcher);
-
-  ASSERT_SOME(containerizer);
-
-  Try<PID<Slave>> slave = StartSlave(containerizer.get(), flags);
-  ASSERT_SOME(slave);
-
-  MockScheduler sched;
-
-  MesosSchedulerDriver driver(
-      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(sched, registered(_, _, _));
-
-  Future<vector<Offer>> offers;
-  EXPECT_CALL(sched, resourceOffers(_, _))
-    .WillOnce(FutureArg<1>(&offers))
-    .WillRepeatedly(Return());      // Ignore subsequent offers.
-
-  driver.start();
-
-  AWAIT_READY(offers);
-  EXPECT_NE(0u, offers.get().size());
-
-  Offer offer = offers.get()[0];
-
-  // Run a task that triggers memory pressure event. We request 1G
-  // disk because we are going to write a 512 MB file repeatedly.
-  TaskInfo task = createTask(
-      offer.slave_id(),
-      Resources::parse("cpus:1;mem:256;disk:1024").get(),
-      "while true; do dd count=512 bs=1M if=/dev/zero of=./temp; done");
-
-  Future<TaskStatus> status;
-  EXPECT_CALL(sched, statusUpdate(&driver, _))
-    .WillOnce(FutureArg<1>(&status))
-    .WillRepeatedly(Return());       // Ignore subsequent updates.
-
-  driver.launchTasks(offer.id(), {task});
-
-  AWAIT_READY(status);
-  EXPECT_EQ(task.task_id(), status.get().task_id());
-  EXPECT_EQ(TASK_RUNNING, status.get().state());
-
-  Future<hashset<ContainerID>> containers = containerizer.get()->containers();
-  AWAIT_READY(containers);
-  ASSERT_EQ(1u, containers.get().size());
-
-  ContainerID containerId = *(containers.get().begin());
-
-  Duration waited = Duration::zero();
-  do {
-    Future<ResourceStatistics> usage = containerizer.get()->usage(containerId);
-    AWAIT_READY(usage);
-
-    if (usage.get().mem_low_pressure_counter() > 0) {
-      EXPECT_GE(usage.get().mem_low_pressure_counter(),
-                usage.get().mem_medium_pressure_counter());
-      EXPECT_GE(usage.get().mem_medium_pressure_counter(),
-                usage.get().mem_critical_pressure_counter());
-      break;
-    }
-
-    os::sleep(Milliseconds(100));
-    waited += Milliseconds(100);
-  } while (waited < Seconds(5));
-
-  EXPECT_LE(waited, Seconds(5));
-
-  driver.stop();
-  driver.join();
-
-  Shutdown();
-  delete containerizer.get();
-}
-
-
-// Test that memory pressure listening is restarted after recovery.
-TEST_F(MemoryPressureMesosTest, CGROUPS_ROOT_SlaveRecovery)
-{
-  Try<PID<Master>> master = StartMaster();
-  ASSERT_SOME(master);
-
-  slave::Flags flags = CreateSlaveFlags();
-
-  // We only care about memory cgroup for this test.
-  flags.isolation = "cgroups/mem";
-  flags.slave_subsystems = None();
-
-  Fetcher fetcher;
-
-  Try<MesosContainerizer*> containerizer1 =
-    MesosContainerizer::create(flags, true, &fetcher);
-
-  ASSERT_SOME(containerizer1);
-
-  Try<PID<Slave>> slave = StartSlave(containerizer1.get(), flags);
-  ASSERT_SOME(slave);
-
-  MockScheduler sched;
-
-  // Enable checkpointing for the framework.
-  FrameworkInfo frameworkInfo = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo.set_checkpoint(true);
-
-  MesosSchedulerDriver driver(
-      &sched, frameworkInfo, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(sched, registered(_, _, _));
-
-  Future<vector<Offer>> offers;
-  EXPECT_CALL(sched, resourceOffers(_, _))
-    .WillOnce(FutureArg<1>(&offers))
-    .WillRepeatedly(Return());      // Ignore subsequent offers.
-
-  driver.start();
-
-  AWAIT_READY(offers);
-  EXPECT_NE(0u, offers.get().size());
-
-  Offer offer = offers.get()[0];
-
-  // Run a task that triggers memory pressure event. We request 1G
-  // disk because we are going to write a 512 MB file repeatedly.
-  TaskInfo task = createTask(
-      offer.slave_id(),
-      Resources::parse("cpus:1;mem:256;disk:1024").get(),
-      "while true; do dd count=512 bs=1M if=/dev/zero of=./temp; done");
-
-  Future<TaskStatus> status;
-  EXPECT_CALL(sched, statusUpdate(&driver, _))
-    .WillOnce(FutureArg<1>(&status))
-    .WillRepeatedly(Return());       // Ignore subsequent updates.
-
-  driver.launchTasks(offers.get()[0].id(), {task});
-
-  AWAIT_READY(status);
-  EXPECT_EQ(task.task_id(), status.get().task_id());
-  EXPECT_EQ(TASK_RUNNING, status.get().state());
-
-  // We restart the slave to let it recover.
-  Stop(slave.get());
-  delete containerizer1.get();
-
-  Future<Nothing> _recover = FUTURE_DISPATCH(_, &Slave::_recover);
-
-  Future<SlaveReregisteredMessage> slaveReregisteredMessage =
-    FUTURE_PROTOBUF(SlaveReregisteredMessage(), _, _);
-
-  // Use the same flags.
-  Try<MesosContainerizer*> containerizer2 =
-    MesosContainerizer::create(flags, true, &fetcher);
-
-  ASSERT_SOME(containerizer2);
-
-  slave = StartSlave(containerizer2.get(), flags);
-  ASSERT_SOME(slave);
-
-  Clock::pause();
-
-  AWAIT_READY(_recover);
-
-  // Wait for slave to schedule reregister timeout.
-  Clock::settle();
-
-  // Ensure the slave considers itself recovered.
-  Clock::advance(slave::EXECUTOR_REREGISTER_TIMEOUT);
-
-  Clock::resume();
-
-  // Wait for the slave to re-register.
-  AWAIT_READY(slaveReregisteredMessage);
-
-  Future<hashset<ContainerID>> containers = containerizer2.get()->containers();
-  AWAIT_READY(containers);
-  ASSERT_EQ(1u, containers.get().size());
-
-  ContainerID containerId = *(containers.get().begin());
-
-  Duration waited = Duration::zero();
-  do {
-    Future<ResourceStatistics> usage = containerizer2.get()->usage(containerId);
-    AWAIT_READY(usage);
-
-    if (usage.get().mem_low_pressure_counter() > 0) {
-      EXPECT_GE(usage.get().mem_low_pressure_counter(),
-                usage.get().mem_medium_pressure_counter());
-      EXPECT_GE(usage.get().mem_medium_pressure_counter(),
-                usage.get().mem_critical_pressure_counter());
-      break;
-    }
-
-    os::sleep(Milliseconds(100));
-    waited += Milliseconds(100);
-  } while (waited < Seconds(5));
-
-  EXPECT_LE(waited, Seconds(5));
-
-  driver.stop();
-  driver.join();
-
-  Shutdown();
-  delete containerizer2.get();
-}
-
-} // namespace tests {
-} // namespace internal {
-} // namespace mesos {


[08/12] mesos git commit: Moved containerizer related tests under src/tests/containerizer.

Posted by ji...@apache.org.
http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer/memory_test_helper.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/memory_test_helper.cpp b/src/tests/containerizer/memory_test_helper.cpp
new file mode 100644
index 0000000..48a3563
--- /dev/null
+++ b/src/tests/containerizer/memory_test_helper.cpp
@@ -0,0 +1,321 @@
+/**
+ * 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.n
+ */
+
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <string>
+#include <vector>
+
+#include <stout/bytes.hpp>
+#include <stout/error.hpp>
+#include <stout/hashmap.hpp>
+#include <stout/lambda.hpp>
+#include <stout/os.hpp>
+#include <stout/stringify.hpp>
+#include <stout/strings.hpp>
+#include <stout/try.hpp>
+
+#include "tests/flags.hpp"
+
+#include "tests/containerizer/memory_test_helper.hpp"
+
+using process::Subprocess;
+
+using std::cerr;
+using std::cin;
+using std::cout;
+using std::endl;
+using std::flush;
+using std::getline;
+using std::string;
+using std::vector;
+
+namespace mesos {
+namespace internal {
+namespace tests {
+
+// Constants used to sync MemoryTestHelper and its subprocess.
+
+// Used by the subprocess to inform that it has started.
+const char STARTED = 'S';
+
+// Used by the subprocess to inform that the work requested is done.
+const char DONE = 'D';
+
+// Used to signal an increaseRSS request.
+const char INCREASE_RSS[] = "INCREASE_RSS";
+
+// Used to signal an increasePageCache request.
+const char INCREASE_PAGE_CACHE[] = "INCREASE_PAGE_CACHE";
+
+
+// This helper allocates and locks specified anonymous memory (RSS).
+// It uses mlock and memset to make sure allocated memory is mapped.
+static Try<void*> allocateRSS(const Bytes& size, bool lock = true)
+{
+  void* rss = NULL;
+
+  if (posix_memalign(&rss, getpagesize(), size.bytes()) != 0) {
+    return ErrnoError("Failed to increase RSS memory, posix_memalign");
+  }
+
+  // Use memset to actually page in the memory in the kernel.
+  memset(rss, 1, size.bytes());
+
+  // Locking a page makes it unevictable in the kernel.
+  if (lock && mlock(rss, size.bytes()) != 0) {
+    return ErrnoError("Failed to lock memory, mlock");
+  }
+
+  return rss;
+}
+
+
+static Try<Nothing> increaseRSS(const vector<string>& tokens)
+{
+  if (tokens.size() < 2) {
+    return Error("Expect at least one argument");
+  }
+
+  Try<Bytes> size = Bytes::parse(tokens[1]);
+  if (size.isError()) {
+    return Error("The first argument '" + tokens[1] + "' is not a byte size");
+  }
+
+  Try<void*> memory = allocateRSS(size.get());
+  if (memory.isError()) {
+    return Error("Failed to allocate RSS memory: " + memory.error());
+  }
+
+  return Nothing();
+}
+
+
+static Try<Nothing> increasePageCache(const vector<string>& tokens)
+{
+  const Bytes UNIT = Megabytes(1);
+
+  if (tokens.size() < 2) {
+    return Error("Expect at least one argument");
+  }
+
+  Try<Bytes> size = Bytes::parse(tokens[1]);
+  if (size.isError()) {
+    return Error("The first argument '" + tokens[1] + "' is not a byte size");
+  }
+
+  // TODO(chzhcn): Currently, we assume the current working directory
+  // is a temporary directory and will be cleaned up when the test
+  // finishes. Since the child process will inherit the current
+  // working directory from the parent process, that means the test
+  // that uses this helper probably needs to inherit from
+  // TemporaryDirectoryTest. Consider relaxing this constraint.
+  Try<string> path = os::mktemp(path::join(os::getcwd(), "XXXXXX"));
+  if (path.isError()) {
+    return Error("Failed to create a temporary file: " + path.error());
+  }
+
+  Try<int> fd = os::open(path.get(), O_WRONLY);
+  if (fd.isError()) {
+    return Error("Failed to open file: " + fd.error());
+  }
+
+  // NOTE: We are doing round-down here to calculate the number of
+  // writes to do.
+  for (uint64_t i = 0; i < size.get().bytes() / UNIT.bytes(); i++) {
+    // Write UNIT size to disk at a time. The content isn't important.
+    Try<Nothing> write = os::write(fd.get(), string(UNIT.bytes(), 'a'));
+    if (write.isError()) {
+      os::close(fd.get());
+      return Error("Failed to write file: " + write.error());
+    }
+
+    // Use fsync to make sure data is written to disk.
+    if (fsync(fd.get()) == -1) {
+      // Save the error message because os::close below might
+      // overwrite the errno.
+      const string message = strerror(errno);
+
+      os::close(fd.get());
+      return Error("Failed to fsync: " + message);
+    }
+  }
+
+  os::close(fd.get());
+  return Nothing();
+}
+
+
+MemoryTestHelper::~MemoryTestHelper()
+{
+  cleanup();
+}
+
+
+Try<Nothing> MemoryTestHelper::spawn()
+{
+  if (s.isSome()) {
+    return Error("A subprocess has been spawned already");
+  }
+
+  vector<string> argv;
+  argv.push_back("memory-test-helper");
+  argv.push_back(MemoryTestHelperMain::NAME);
+
+  Try<Subprocess> process = subprocess(
+      path::join(flags.build_dir,
+                 "src",
+                 "memory-test-helper"),
+      argv,
+      Subprocess::PIPE(),
+      Subprocess::PIPE(),
+      Subprocess::FD(STDERR_FILENO));
+
+  if (process.isError()) {
+    return Error("Failed to spawn a subprocess: " + process.error());
+  }
+
+  s = process.get();
+
+  // Wait for the child to inform it has started before returning.
+  // Otherwise, the user might set the memory limit too earlier, and
+  // cause the child oom-killed because 'ld' could use a lot of
+  // memory.
+  Result<string> read = os::read(s.get().out().get(), sizeof(STARTED));
+  if (!read.isSome() || read.get() != string(sizeof(STARTED), STARTED)) {
+    cleanup();
+    return Error("Failed to sync with the subprocess");
+  }
+
+  return Nothing();
+}
+
+
+void MemoryTestHelper::cleanup()
+{
+  if (s.isSome()) {
+    // We just want to make sure the subprocess is terminated in case
+    // it's stuck, but we don't care about its status. Any error
+    // should have been logged in the subprocess directly.
+    ::kill(s.get().pid(), SIGKILL);
+    ::waitpid(s.get().pid(), NULL, 0);
+    s = None();
+  }
+}
+
+
+Try<pid_t> MemoryTestHelper::pid()
+{
+  if (s.isNone()) {
+    return Error("The subprocess has not been spawned yet");
+  }
+
+  return s.get().pid();
+}
+
+
+// Send a request to the subprocess and wait for its signal that the
+// work has been done.
+Try<Nothing> MemoryTestHelper::requestAndWait(const string& request)
+{
+  if (s.isNone()) {
+    return Error("The subprocess has not been spawned yet");
+  }
+
+  Try<Nothing> write = os::write(s.get().in().get(), request + "\n");
+  if (write.isError()) {
+    cleanup();
+    return Error("Fail to sync with the subprocess: " + write.error());
+  }
+
+  Result<string> read = os::read(s.get().out().get(), sizeof(DONE));
+  if (!read.isSome() || read.get() != string(sizeof(DONE), DONE)) {
+    cleanup();
+    return Error("Failed to sync with the subprocess");
+  }
+
+  return Nothing();
+}
+
+
+Try<Nothing> MemoryTestHelper::increaseRSS(const Bytes& size)
+{
+  return requestAndWait(string(INCREASE_RSS) + " " + stringify(size));
+}
+
+
+Try<Nothing> MemoryTestHelper::increasePageCache(const Bytes& size)
+{
+  return requestAndWait(string(INCREASE_PAGE_CACHE) + " " + stringify(size));
+}
+
+
+const char MemoryTestHelperMain::NAME[] = "MemoryTestHelperMain";
+
+
+int MemoryTestHelperMain::execute()
+{
+  hashmap<string, Try<Nothing>(*)(const vector<string>&)> commands;
+  commands[INCREASE_RSS] = &increaseRSS;
+  commands[INCREASE_PAGE_CACHE] = &increasePageCache;
+
+  // Tell the parent that child has started.
+  cout << STARTED << flush;
+
+  string line;
+  while(cin.good()) {
+    getline(cin, line);
+    vector<string> tokens = strings::tokenize(line, " ");
+
+    if (tokens.empty()) {
+      cerr << "No command from the parent" << endl;
+      return 1;
+    }
+
+    if (!commands.contains(tokens[0])) {
+      cerr << "Unknown command from the parent '" << tokens[0] << "'" << endl;
+      return 1;
+    }
+
+    Try<Nothing> result = commands[tokens[0]](tokens);
+    if (result.isError()) {
+      cerr << result.error();
+      return 1;
+    }
+
+    cout << DONE << flush;
+  }
+
+  if (!cin) {
+    cerr << "Failed to sync with the parent" << endl;
+    return 1;
+  }
+
+  return 0;
+}
+
+} // namespace tests {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer/memory_test_helper.hpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/memory_test_helper.hpp b/src/tests/containerizer/memory_test_helper.hpp
new file mode 100644
index 0000000..11712d7
--- /dev/null
+++ b/src/tests/containerizer/memory_test_helper.hpp
@@ -0,0 +1,89 @@
+/**
+ * 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 __MEMORY_TEST_HELPER_HPP__
+#define __MEMORY_TEST_HELPER_HPP__
+
+#include <process/subprocess.hpp>
+
+#include <stout/bytes.hpp>
+#include <stout/option.hpp>
+#include <stout/subcommand.hpp>
+#include <stout/try.hpp>
+
+namespace mesos {
+namespace internal {
+namespace tests {
+
+// The abstraction for controlling the memory usage of a subprocess.
+// TODO(chzhcn): Currently, this helper is only supposed to be used by
+// one thread. Consider making it thread safe.
+class MemoryTestHelper
+{
+public:
+  MemoryTestHelper() {};
+  ~MemoryTestHelper();
+
+  // Spawns a subprocess.
+  // TODO(chzhcn): Consider returning a future instead of blocking.
+  Try<Nothing> spawn();
+
+  // Kill and reap the subprocess if exists.
+  // TODO(chzhcn): Consider returning a future instead of blocking.
+  void cleanup();
+
+  // Returns the pid of the subprocess.
+  Try<pid_t> pid();
+
+  // Allocate and lock specified page-aligned anonymous memory (RSS)
+  // in the subprocess. It uses mlock and memset to make sure
+  // allocated memory is mapped.
+  // TODO(chzhcn): Consider returning a future instead of blocking.
+  Try<Nothing> increaseRSS(const Bytes& size);
+
+  // This function attempts to generate requested size of page cache
+  // in the subprocess by using a small buffer and writing it to disk
+  // multiple times.
+  // TODO(chzhcn): Consider returning a future instead of blocking.
+  Try<Nothing> increasePageCache(const Bytes& size = Megabytes(1));
+
+private:
+  Try<Nothing> requestAndWait(const std::string& request);
+
+  Option<process::Subprocess> s;
+};
+
+
+// The actual subprocess behind MemoryTestHelper. It runs in a loop
+// and executes commands passed from stdin.
+class MemoryTestHelperMain : public Subcommand
+{
+public:
+  static const char NAME[];
+
+  MemoryTestHelperMain() : Subcommand(NAME) {};
+
+protected:
+  virtual int execute();
+};
+
+} // namespace tests {
+} // namespace internal {
+} // namespace mesos {
+
+#endif // __MEMORY_TEST_HELPER_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer/memory_test_helper_main.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/memory_test_helper_main.cpp b/src/tests/containerizer/memory_test_helper_main.cpp
new file mode 100644
index 0000000..df98cbb
--- /dev/null
+++ b/src/tests/containerizer/memory_test_helper_main.cpp
@@ -0,0 +1,32 @@
+/**
+ * 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 <stout/subcommand.hpp>
+
+#include "tests/containerizer/memory_test_helper.hpp"
+
+using mesos::internal::tests::MemoryTestHelperMain;
+
+int main(int argc, char** argv)
+{
+  return Subcommand::dispatch(
+      None(),
+      argc,
+      argv,
+      new MemoryTestHelperMain());
+}

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer/ns_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/ns_tests.cpp b/src/tests/containerizer/ns_tests.cpp
new file mode 100644
index 0000000..c71c33f
--- /dev/null
+++ b/src/tests/containerizer/ns_tests.cpp
@@ -0,0 +1,302 @@
+/**
+ * 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 <sys/wait.h>
+
+#include <iostream>
+
+#include <pthread.h>
+#include <unistd.h>
+
+#include <list>
+#include <set>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <stout/gtest.hpp>
+#include <stout/lambda.hpp>
+#include <stout/os.hpp>
+
+#include <process/gtest.hpp>
+#include <process/subprocess.hpp>
+
+#include "linux/ns.hpp"
+
+#include "tests/flags.hpp"
+
+#include "tests/containerizer/setns_test_helper.hpp"
+
+using namespace process;
+
+using std::list;
+using std::set;
+using std::string;
+using std::vector;
+
+namespace mesos {
+namespace internal {
+namespace tests {
+
+
+// Helper for cloneChild() which expects an int(void*).
+static int cloneChildHelper(void* _func)
+{
+  const lambda::function<int()>* func =
+    static_cast<const lambda::function<int()>*> (_func);
+
+  return (*func)();
+}
+
+
+static pid_t cloneChild(
+    int flags,
+    const lambda::function<int()>& func)
+
+{
+  // 8 MiB stack for child.
+  static unsigned long long stack[(8*1024*1024)/sizeof(unsigned long long)];
+
+  return ::clone(
+      cloneChildHelper,
+      &stack[sizeof(stack)/sizeof(stack[0]) - 1], // Stack grows down.
+      flags | SIGCHLD,
+      (void*) &func);
+}
+
+
+// Test that a child in different namespace(s) can setns back to the
+// root namespace. We must fork a child to test this because setns
+// doesn't support multi-threaded processes (which gtest is).
+TEST(NsTest, ROOT_setns)
+{
+  // Clone then exec the setns-test-helper into a new namespace for
+  // each available namespace.
+  set<string> namespaces = ns::namespaces();
+  ASSERT_FALSE(namespaces.empty());
+
+  int flags = 0;
+
+  foreach (const string& ns, namespaces) {
+    // Skip 'user' namespace because it causes 'clone' to change us
+    // from being user 'root' to user 'nobody', but these tests
+    // require root. See MESOS-3083.
+    if (ns == "user") {
+      continue;
+    }
+
+    Try<int> nstype = ns::nstype(ns);
+    ASSERT_SOME(nstype);
+
+    flags |= nstype.get();
+  }
+
+  vector<string> argv;
+  argv.push_back("setns-test-helper");
+  argv.push_back(SetnsTestHelper::NAME);
+
+  Try<Subprocess> s = subprocess(
+      path::join(tests::flags.build_dir, "src", "setns-test-helper"),
+      argv,
+      Subprocess::FD(STDIN_FILENO),
+      Subprocess::FD(STDOUT_FILENO),
+      Subprocess::FD(STDERR_FILENO),
+      None(),
+      None(),
+      None(),
+      lambda::bind(&cloneChild, flags, lambda::_1));
+
+  // Continue in parent.
+  ASSERT_SOME(s);
+
+  // The child should exit 0.
+  Future<Option<int>> status = s.get().status();
+  AWAIT_READY(status);
+
+  ASSERT_SOME(status.get());
+  EXPECT_TRUE(WIFEXITED(status.get().get()));
+  EXPECT_EQ(0, status.get().get());
+}
+
+
+static void* childThread(void* arg)
+{
+  // Newly created threads have PTHREAD_CANCEL_ENABLE and
+  // PTHREAD_CANCEL_DEFERRED so they can be cancelled.
+  while (true) { os::sleep(Seconds(1)); }
+
+  return NULL;
+}
+
+
+// Test that setns correctly refuses to re-associate to a namespace if
+// the caller is multi-threaded.
+TEST(NsTest, ROOT_setnsMultipleThreads)
+{
+  set<string> namespaces = ns::namespaces();
+  EXPECT_LT(0u, namespaces.size());
+
+  // Do not allow multi-threaded environment.
+  pthread_t pthread;
+  ASSERT_EQ(0, pthread_create(&pthread, NULL, childThread, NULL));
+
+  foreach (const string& ns, namespaces) {
+    EXPECT_ERROR(ns::setns(::getpid(), ns));
+  }
+
+  // Terminate the threads.
+  EXPECT_EQ(0, pthread_cancel(pthread));
+  EXPECT_EQ(0, pthread_join(pthread, NULL));
+}
+
+
+// Use a different child function for clone because it requires
+// int(*)(void*).
+static int childGetns(void* arg)
+{
+  // Sleep until killed.
+  while (true) { sleep(1); }
+
+  ABORT("Error, child should be killed before reaching here");
+}
+
+
+// Test that we can get the namespace inodes for a forked child.
+TEST(NsTest, ROOT_getns)
+{
+  set<string> namespaces = ns::namespaces();
+
+  // ns::setns() does not support "pid".
+  namespaces.erase("pid");
+
+  // Use the first other namespace available.
+  ASSERT_FALSE(namespaces.empty());
+  string ns = *(namespaces.begin());
+
+  ASSERT_SOME(ns::getns(::getpid(), ns));
+
+  Try<int> nstype = ns::nstype(ns);
+  ASSERT_SOME(nstype);
+
+  // 8 MiB stack for child.
+  static unsigned long long stack[(8*1024*1024)/sizeof(unsigned long long)];
+
+  pid_t pid = clone(
+      childGetns,
+      &stack[sizeof(stack)/sizeof(stack[0]) - 1], // Stack grows down.
+      SIGCHLD | nstype.get(),
+      NULL);
+
+  ASSERT_NE(-1, pid);
+
+  // Continue in parent.
+  Try<ino_t> nsParent = ns::getns(::getpid(), ns);
+  ASSERT_SOME(nsParent);
+
+  Try<ino_t> nsChild = ns::getns(pid, ns);
+  ASSERT_SOME(nsChild);
+
+  // Child should be in a different namespace.
+  EXPECT_NE(nsParent.get(), nsChild.get());
+
+  // Kill the child process.
+  ASSERT_NE(-1, ::kill(pid, SIGKILL));
+
+  // Wait for the child process.
+  int status;
+  EXPECT_NE(-1, ::waitpid((pid_t) -1, &status, 0));
+  ASSERT_TRUE(WIFSIGNALED(status));
+  EXPECT_EQ(SIGKILL, WTERMSIG(status));
+}
+
+
+static int childDestroy(void* arg)
+{
+  // Fork a bunch of children.
+  ::fork();
+  ::fork();
+  ::fork();
+
+  // Parent and all children sleep.
+  while (true) { sleep(1); }
+
+  ABORT("Error, child should be killed before reaching here");
+}
+
+
+// Test we can destroy a pid namespace, i.e., kill all processes.
+TEST(NsTest, ROOT_destroy)
+{
+  set<string> namespaces = ns::namespaces();
+
+  if (namespaces.count("pid") == 0) {
+    // Pid namespace is not available.
+    return;
+  }
+
+  Try<int> nstype = ns::nstype("pid");
+  ASSERT_SOME(nstype);
+
+  // 8 MiB stack for child.
+  static unsigned long long stack[(8*1024*1024)/sizeof(unsigned long long)];
+
+  pid_t pid = clone(
+      childDestroy,
+      &stack[sizeof(stack)/sizeof(stack[0]) - 1], // Stack grows down.
+      SIGCHLD | nstype.get(),
+      NULL);
+
+  ASSERT_NE(-1, pid);
+
+  Future<Option<int>> status = process::reap(pid);
+
+  // Ensure the child is in a different pid namespace.
+  Try<ino_t> childNs = ns::getns(pid, "pid");
+  ASSERT_SOME(childNs);
+
+  Try<ino_t> ourNs = ns::getns(::getpid(), "pid");
+  ASSERT_SOME(ourNs);
+
+  ASSERT_NE(ourNs.get(), childNs.get());
+
+  // Kill the child.
+  AWAIT_READY(ns::pid::destroy(childNs.get()));
+
+  AWAIT_READY(status);
+  ASSERT_SOME(status.get());
+  ASSERT_TRUE(WIFSIGNALED(status.get().get()));
+  EXPECT_EQ(SIGKILL, WTERMSIG(status.get().get()));
+
+  // Finally, verify that no processes are in the child's pid
+  // namespace, i.e., destroy() also killed all descendants.
+  Try<set<pid_t>> pids = os::pids();
+  ASSERT_SOME(pids);
+
+  foreach (pid_t pid, pids.get()) {
+    Try<ino_t> otherNs = ns::getns(pid, "pid");
+    // pid may have exited since getting the snapshot of pids so
+    // ignore any error.
+    if (otherNs.isSome()) {
+      ASSERT_SOME_NE(childNs.get(), otherNs);
+    }
+  }
+}
+
+} // namespace tests {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer/perf_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/perf_tests.cpp b/src/tests/containerizer/perf_tests.cpp
new file mode 100644
index 0000000..6b3d70f
--- /dev/null
+++ b/src/tests/containerizer/perf_tests.cpp
@@ -0,0 +1,183 @@
+/**
+ * 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 <sys/prctl.h>
+
+#include <set>
+
+#include <gmock/gmock.h>
+
+#include <process/clock.hpp>
+#include <process/gtest.hpp>
+#include <process/reap.hpp>
+
+#include <stout/gtest.hpp>
+#include <stout/stringify.hpp>
+
+#include "linux/perf.hpp"
+
+using std::set;
+using std::string;
+
+using namespace process;
+
+namespace mesos {
+namespace internal {
+namespace tests {
+
+class PerfTest : public ::testing::Test {};
+
+
+TEST_F(PerfTest, ROOT_Events)
+{
+  set<string> events;
+  // Valid events.
+  events.insert("cycles");
+  events.insert("task-clock");
+  EXPECT_TRUE(perf::valid(events));
+
+  // Add an invalid event.
+  events.insert("this-is-an-invalid-event");
+  EXPECT_FALSE(perf::valid(events));
+}
+
+
+TEST_F(PerfTest, Parse)
+{
+  // uint64 and floats should be parsed.
+  Try<hashmap<string, mesos::PerfStatistics> > parse =
+    perf::parse("123,cycles\n0.123,task-clock");
+  CHECK_SOME(parse);
+
+  ASSERT_TRUE(parse.get().contains(""));
+  mesos::PerfStatistics statistics = parse.get().get("").get();
+
+  ASSERT_TRUE(statistics.has_cycles());
+  EXPECT_EQ(123u, statistics.cycles());
+  ASSERT_TRUE(statistics.has_task_clock());
+  EXPECT_EQ(0.123, statistics.task_clock());
+
+  // Parse multiple cgroups.
+  parse = perf::parse("123,cycles,cgroup1\n"
+                      "456,cycles,cgroup2\n"
+                      "0.456,task-clock,cgroup2\n"
+                      "0.123,task-clock,cgroup1");
+  CHECK_SOME(parse);
+  EXPECT_FALSE(parse.get().contains(""));
+
+  ASSERT_TRUE(parse.get().contains("cgroup1"));
+  statistics = parse.get().get("cgroup1").get();
+
+  ASSERT_TRUE(statistics.has_cycles());
+  EXPECT_EQ(123u, statistics.cycles());
+  ASSERT_TRUE(statistics.has_task_clock());
+  EXPECT_EQ(0.123, statistics.task_clock());
+
+  ASSERT_TRUE(parse.get().contains("cgroup2"));
+  statistics = parse.get().get("cgroup2").get();
+
+  ASSERT_TRUE(statistics.has_cycles());
+  EXPECT_EQ(456u, statistics.cycles());
+  EXPECT_TRUE(statistics.has_task_clock());
+  EXPECT_EQ(0.456, statistics.task_clock());
+
+  // Statistics reporting <not supported> should not appear.
+  parse = perf::parse("<not supported>,cycles");
+  CHECK_SOME(parse);
+
+  ASSERT_TRUE(parse.get().contains(""));
+  statistics = parse.get().get("").get();
+  EXPECT_FALSE(statistics.has_cycles());
+
+  // Statistics reporting <not counted> should be zero.
+  parse = perf::parse("<not counted>,cycles\n<not counted>,task-clock");
+  CHECK_SOME(parse);
+
+  ASSERT_TRUE(parse.get().contains(""));
+  statistics = parse.get().get("").get();
+
+  EXPECT_TRUE(statistics.has_cycles());
+  EXPECT_EQ(0u, statistics.cycles());
+  EXPECT_TRUE(statistics.has_task_clock());
+  EXPECT_EQ(0.0, statistics.task_clock());
+
+  // Check parsing fails.
+  parse = perf::parse("1,cycles\ngarbage");
+  EXPECT_ERROR(parse);
+
+  parse = perf::parse("1,unknown-field");
+  EXPECT_ERROR(parse);
+}
+
+
+TEST_F(PerfTest, ROOT_SamplePid)
+{
+  // TODO(idownes): Replace this with a Subprocess when it supports
+  // DEATHSIG.
+  // Fork a child which we'll run perf against.
+  pid_t pid = fork();
+  ASSERT_GE(pid, 0);
+
+  if (pid == 0) {
+    // Kill ourself if the parent dies to prevent leaking the child.
+    prctl(PR_SET_PDEATHSIG, SIGKILL);
+
+    // Spin child to consume cpu cycles.
+    while (true);
+  }
+
+  // Continue in parent.
+  set<string> events;
+  // Hardware event.
+  events.insert("cycles");
+  // Software event.
+  events.insert("task-clock");
+
+  // Sample the child.
+  Duration duration = Milliseconds(100);
+  Future<mesos::PerfStatistics> statistics =
+    perf::sample(events, pid, duration);
+  AWAIT_READY(statistics);
+
+  // Kill the child and reap it.
+  Future<Option<int>> status = reap(pid);
+  kill(pid, SIGKILL);
+  AWAIT_READY(status);
+
+  // Check the sample timestamp is within the last 5 seconds. This is generous
+  // because there's the process reap delay in addition to the sampling
+  // duration.
+  ASSERT_TRUE(statistics.get().has_timestamp());
+  EXPECT_GT(
+      Seconds(5).secs(), Clock::now().secs() - statistics.get().timestamp());
+  EXPECT_EQ(duration.secs(), statistics.get().duration());
+
+  ASSERT_TRUE(statistics.get().has_cycles());
+
+  // TODO(benh): Some Linux distributions (Ubuntu 14.04) fail to
+  // properly sample 'cycles' with 'perf', so we don't explicitly
+  // check the value here. See MESOS-3082.
+  // EXPECT_LT(0u, statistics.get().cycles());
+
+  ASSERT_TRUE(statistics.get().has_task_clock());
+  EXPECT_LT(0.0, statistics.get().task_clock());
+}
+
+} // namespace tests {
+} // namespace internal {
+} // namespace mesos {


[07/12] mesos git commit: Moved containerizer related tests under src/tests/containerizer.

Posted by ji...@apache.org.
http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer/port_mapping_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/port_mapping_tests.cpp b/src/tests/containerizer/port_mapping_tests.cpp
new file mode 100644
index 0000000..45ef97a
--- /dev/null
+++ b/src/tests/containerizer/port_mapping_tests.cpp
@@ -0,0 +1,2296 @@
+/**
+ * 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 <iostream>
+#include <string>
+#include <vector>
+
+#include <process/future.hpp>
+#include <process/io.hpp>
+#include <process/reap.hpp>
+#include <process/subprocess.hpp>
+
+#include <stout/bytes.hpp>
+#include <stout/gtest.hpp>
+#include <stout/ip.hpp>
+#include <stout/json.hpp>
+#include <stout/mac.hpp>
+#include <stout/net.hpp>
+#include <stout/stopwatch.hpp>
+
+#include <stout/os/stat.hpp>
+#include <stout/os/exists.hpp>
+
+#include "linux/fs.hpp"
+
+#include "linux/routing/utils.hpp"
+
+#include "linux/routing/filter/ip.hpp"
+
+#include "linux/routing/link/link.hpp"
+
+#include "linux/routing/queueing/ingress.hpp"
+
+#include "master/master.hpp"
+
+#include "mesos/mesos.hpp"
+
+#include "slave/flags.hpp"
+#include "slave/slave.hpp"
+
+#include "slave/containerizer/isolators/network/port_mapping.hpp"
+
+#include "slave/containerizer/fetcher.hpp"
+#include "slave/containerizer/launcher.hpp"
+#include "slave/containerizer/linux_launcher.hpp"
+
+#include "slave/containerizer/mesos/containerizer.hpp"
+#include "slave/containerizer/mesos/launch.hpp"
+
+#include "tests/flags.hpp"
+#include "tests/mesos.hpp"
+#include "tests/utils.hpp"
+
+using namespace mesos::internal::slave;
+
+using namespace process;
+
+using namespace routing;
+using namespace routing::filter;
+using namespace routing::queueing;
+
+using mesos::internal::master::Master;
+
+using mesos::slave::Isolator;
+
+using std::list;
+using std::ostringstream;
+using std::set;
+using std::string;
+using std::vector;
+
+using testing::_;
+using testing::Eq;
+using testing::Return;
+
+namespace mesos {
+namespace internal {
+namespace tests {
+
+
+// An old glibc might not have this symbol.
+#ifndef MNT_DETACH
+#define MNT_DETACH 2
+#endif
+
+
+// Each test container works with a common specification of 2 CPUs,
+// 1GB of memory and 1GB of disk space, which experience has shown
+// to be sufficient to not encounter resource starvation issues when
+// running the test suite.
+const char* const containerCPU = "cpus:2";
+const char* const containerMemory = "mem:1024";
+const char* const containerDisk = "disk:1024";
+
+// We configure ephemeral and persistent port ranges outside the
+// default linux ip_local_port_range [32768-61000] in order to reduce
+// the probability of a conflict which could result in spurious
+// results (positive or negative) from these tests.
+const char* const ephemeralPorts = "ephemeral_ports:[30001-30999]";
+const char* const persistentPorts = "ports:[31000-32000]";
+
+// To keep things simple, we used fixed port ranges for our containers
+// in these tests rather than try to dynamically track port usage.
+// Note that container ports must be contained in the persistent port
+// range.
+const char* const container1Ports = "ports:[31000-31499]";
+const char* const container2Ports = "ports:[31500-32000]";
+
+// We define a validPort in the container1 assigned range which can
+// therefore accept incoming traffic.
+const int validPort = 31001;
+
+// We also define a port outside the persistent port range; containers
+// connecting to this port will never receive incoming traffic.
+const int invalidPort = 32502;
+
+
+static void cleanup(const string& eth0, const string& lo)
+{
+  // Clean up the ingress qdisc on eth0 and lo if exists.
+  Try<bool> hostEth0ExistsQdisc = ingress::exists(eth0);
+  ASSERT_SOME(hostEth0ExistsQdisc);
+
+  if (hostEth0ExistsQdisc.get()) {
+    ASSERT_SOME_TRUE(ingress::remove(eth0));
+  }
+
+  Try<bool> hostLoExistsQdisc = ingress::exists(lo);
+  ASSERT_SOME(hostLoExistsQdisc);
+
+  if (hostLoExistsQdisc.get()) {
+    ASSERT_SOME_TRUE(ingress::remove(lo));
+  }
+
+  // Clean up all 'veth' devices if exist.
+  Try<set<string> > links = net::links();
+  ASSERT_SOME(links);
+
+  foreach (const string& name, links.get()) {
+    if (strings::startsWith(name, slave::PORT_MAPPING_VETH_PREFIX())) {
+      ASSERT_SOME_TRUE(link::remove(name));
+    }
+  }
+
+  Try<list<string> > entries = os::ls(slave::PORT_MAPPING_BIND_MOUNT_ROOT());
+  ASSERT_SOME(entries);
+
+  foreach (const string& file, entries.get()) {
+    string target = path::join(slave::PORT_MAPPING_BIND_MOUNT_ROOT(), file);
+
+    // NOTE: Here, we ignore the unmount errors because previous tests
+    // may have created the file and died before mounting.
+    if (!os::stat::islink(target)) {
+      mesos::internal::fs::unmount(target, MNT_DETACH);
+    }
+
+    // Remove the network namespace handle and the corresponding
+    // symlinks. The removal here is best effort.
+    os::rm(target);
+  }
+}
+
+
+class PortMappingIsolatorTest : public TemporaryDirectoryTest
+{
+public:
+  static void SetUpTestCase()
+  {
+    ASSERT_SOME(routing::check())
+      << "-------------------------------------------------------------\n"
+      << "We cannot run any PortMappingIsolatorTests because your\n"
+      << "libnl library is not new enough. You can either install a\n"
+      << "new libnl library, or disable this test case\n"
+      << "-------------------------------------------------------------";
+
+    ASSERT_SOME_EQ(0, os::shell(NULL, "which nc"))
+      << "-------------------------------------------------------------\n"
+      << "We cannot run any PortMappingIsolatorTests because 'nc'\n"
+      << "could not be found. You can either install 'nc', or disable\n"
+      << "this test case\n"
+      << "-------------------------------------------------------------";
+
+    ASSERT_SOME_EQ(0, os::shell(NULL, "which arping"))
+      << "-------------------------------------------------------------\n"
+      << "We cannot run some PortMappingIsolatorTests because 'arping'\n"
+      << "could not be found. You can either isntall 'arping', or\n"
+      << "disable this test case\n"
+      << "-------------------------------------------------------------";
+  }
+
+  PortMappingIsolatorTest() : hostIP(net::IP(INADDR_ANY)) {}
+
+protected:
+  virtual void SetUp()
+  {
+    TemporaryDirectoryTest::SetUp();
+
+    flags = CreateSlaveFlags();
+
+    // Guess the name of the public interface.
+    Result<string> _eth0 = link::eth0();
+    ASSERT_SOME(_eth0) << "Failed to guess the name of the public interface";
+
+    eth0 = _eth0.get();
+
+    LOG(INFO) << "Using " << eth0 << " as the public interface";
+
+    // Guess the name of the loopback interface.
+    Result<string> _lo = link::lo();
+    ASSERT_SOME(_lo) << "Failed to guess the name of the loopback interface";
+
+    lo = _lo.get();
+
+    LOG(INFO) << "Using " << lo << " as the loopback interface";
+
+    // Clean up qdiscs and veth devices.
+    cleanup(eth0, lo);
+
+    // Get host IP address.
+    Result<net::IPNetwork> hostIPNetwork =
+        net::IPNetwork::fromLinkDevice(eth0, AF_INET);
+
+    CHECK_SOME(hostIPNetwork)
+      << "Failed to retrieve the host public IP network from " << eth0 << ": "
+      << hostIPNetwork.error();
+
+    hostIP = hostIPNetwork.get().address();
+
+    // Get all the external name servers for tests that need to talk
+    // to an external host, e.g., ping, DNS.
+    Try<string> read = os::read("/etc/resolv.conf");
+    CHECK_SOME(read);
+
+    foreach (const string& line, strings::split(read.get(), "\n")) {
+      if (!strings::startsWith(line, "nameserver")) {
+        continue;
+      }
+
+      vector<string> tokens = strings::split(line, " ");
+      ASSERT_EQ(2u, tokens.size()) << "Unexpected format in '/etc/resolv.conf'";
+      if (tokens[1] != "127.0.0.1") {
+        nameServers.push_back(tokens[1]);
+      }
+    }
+
+    container1Ready = path::join(os::getcwd(), "container1_ready");
+    container2Ready = path::join(os::getcwd(), "container2_ready");
+    trafficViaLoopback = path::join(os::getcwd(), "traffic_via_loopback");
+    trafficViaPublic = path::join(os::getcwd(), "traffic_via_public");
+    exitStatus = path::join(os::getcwd(), "exit_status");
+  }
+
+  virtual void TearDown()
+  {
+    cleanup(eth0, lo);
+    TemporaryDirectoryTest::TearDown();
+  }
+
+  slave::Flags CreateSlaveFlags()
+  {
+    slave::Flags flags;
+
+    flags.launcher_dir = path::join(tests::flags.build_dir, "src");
+
+    flags.resources = strings::join(";", vector<string>({
+        containerCPU,
+        containerMemory,
+        containerDisk,
+        ephemeralPorts,
+        persistentPorts }));
+
+    // NOTE: '16' should be enough for all our tests.
+    flags.ephemeral_ports_per_container = 16;
+
+    flags.isolation = "network/port_mapping";
+
+    return flags;
+  }
+
+  Try<pid_t> launchHelper(
+      Launcher* launcher,
+      int pipes[2],
+      const ContainerID& containerId,
+      const string& command,
+      const Option<CommandInfo>& preparation)
+  {
+    CommandInfo commandInfo;
+    commandInfo.set_value(command);
+
+    // The flags to pass to the helper process.
+    MesosContainerizerLaunch::Flags launchFlags;
+
+    launchFlags.command = JSON::Protobuf(commandInfo);
+    launchFlags.directory = os::getcwd();
+
+    CHECK_SOME(os::user());
+    launchFlags.user = os::user().get();
+
+    launchFlags.pipe_read = pipes[0];
+    launchFlags.pipe_write = pipes[1];
+
+    JSON::Object commands;
+    JSON::Array array;
+    array.values.push_back(JSON::Protobuf(preparation.get()));
+    commands.values["commands"] = array;
+
+    launchFlags.commands = commands;
+
+    vector<string> argv(2);
+    argv[0] = MESOS_CONTAINERIZER;
+    argv[1] = MesosContainerizerLaunch::NAME;
+
+    Try<pid_t> pid = launcher->fork(
+        containerId,
+        path::join(flags.launcher_dir, MESOS_CONTAINERIZER),
+        argv,
+        Subprocess::FD(STDIN_FILENO),
+        Subprocess::FD(STDOUT_FILENO),
+        Subprocess::FD(STDERR_FILENO),
+        launchFlags,
+        None(),
+        None());
+
+    return pid;
+  }
+
+  Result<ResourceStatistics> statisticsHelper(
+      pid_t pid,
+      bool enable_summary,
+      bool enable_details)
+  {
+    // Retrieve the socket information from inside the container.
+    PortMappingStatistics statistics;
+    statistics.flags.pid = pid;
+    statistics.flags.eth0_name = eth0;
+    statistics.flags.enable_socket_statistics_summary = enable_summary;
+    statistics.flags.enable_socket_statistics_details = enable_details;
+
+    vector<string> argv(2);
+    argv[0] = "mesos-network-helper";
+    argv[1] = PortMappingStatistics::NAME;
+
+    // We don't need STDIN; we need STDOUT for the result; we leave
+    // STDERR as is to log to slave process.
+    Try<Subprocess> s = subprocess(
+        path::join(flags.launcher_dir, "mesos-network-helper"),
+        argv,
+        Subprocess::PATH("/dev/null"),
+        Subprocess::PIPE(),
+        Subprocess::FD(STDERR_FILENO),
+        statistics.flags);
+
+    CHECK_SOME(s);
+
+    Future<Option<int> > status = s.get().status();
+    AWAIT_EXPECT_READY(status);
+    EXPECT_SOME_EQ(0, status.get());
+
+    Future<string> out = io::read(s.get().out().get());
+    AWAIT_EXPECT_READY(out);
+
+    Try<JSON::Object> object = JSON::parse<JSON::Object>(out.get());
+    CHECK_SOME(object);
+
+    return ::protobuf::parse<ResourceStatistics>(object.get());
+  }
+
+  slave::Flags flags;
+
+  // Name of the host eth0 and lo.
+  string eth0;
+  string lo;
+
+  // Host public IP network.
+  net::IP hostIP;
+
+  // All the external name servers as read from /etc/resolv.conf.
+  vector<string> nameServers;
+
+  // Some auxiliary files for the tests.
+  string container1Ready;
+  string container2Ready;
+  string trafficViaLoopback;
+  string trafficViaPublic;
+  string exitStatus;
+};
+
+
+// Wait up to timeout seconds for a file to be created. If timeout is
+// zero, then wait indefinitely. Return true if file exists.
+//
+// TODO(pbrett): Consider generalizing this function and moving it to
+// a common header.
+static bool waitForFileCreation(
+    const string& path,
+    const Duration& duration = Seconds(60))
+{
+  Stopwatch timer;
+  timer.start();
+  while (!os::exists(path)) {
+    if ((duration > Duration::zero()) && (timer.elapsed() > duration))
+      break;
+    os::sleep(Milliseconds(50));
+  }
+  return os::exists(path);
+}
+
+
+// This test uses two containers: one listens to 'validPort' and
+// 'invalidPort' and writes data received to files; the other
+// container attempts to connect to the previous container using
+// 'validPort' and 'invalidPort'. Verify that only the connection
+// through 'validPort' is successful by confirming that the expected
+// data has been written to its output file.
+TEST_F(PortMappingIsolatorTest, ROOT_NC_ContainerToContainerTCP)
+{
+  Try<Isolator*> isolator = PortMappingIsolatorProcess::create(flags);
+  CHECK_SOME(isolator);
+
+  Try<Launcher*> launcher =
+    LinuxLauncher::create(flags, isolator.get()->namespaces().get());
+  CHECK_SOME(launcher);
+
+  // Set the executor's resources.
+  ExecutorInfo executorInfo;
+  executorInfo.mutable_resources()->CopyFrom(
+      Resources::parse(container1Ports).get());
+
+  ContainerID containerId1;
+  containerId1.set_value("container1");
+
+  // Use a relative temporary directory so it gets cleaned up
+  // automatically with the test.
+  Try<string> dir1 = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
+  ASSERT_SOME(dir1);
+
+  Future<Option<CommandInfo> > preparation1 =
+    isolator.get()->prepare(
+        containerId1,
+        executorInfo,
+        dir1.get(),
+        None(),
+        None());
+
+  AWAIT_READY(preparation1);
+  ASSERT_SOME(preparation1.get());
+
+  ostringstream command1;
+
+  // Listen to 'localhost' and 'port'.
+  command1 << "nc -l localhost " << validPort << " > " << trafficViaLoopback
+           << "& ";
+
+  // Listen to 'public ip' and 'port'.
+  command1 << "nc -l " << hostIP << " " << validPort << " > "
+           << trafficViaPublic << "& ";
+
+  // Listen to 'invalidPort'.  This should not receive any data.
+  command1 << "nc -l " << invalidPort << " | tee " << trafficViaLoopback << " "
+           << trafficViaPublic << "& ";
+
+  // Touch the guard file.
+  command1 << "touch " << container1Ready;
+
+  int pipes[2];
+  ASSERT_NE(-1, ::pipe(pipes));
+
+  Try<pid_t> pid = launchHelper(
+      launcher.get(),
+      pipes,
+      containerId1,
+      command1.str(),
+      preparation1.get());
+  ASSERT_SOME(pid);
+
+  // Reap the forked child.
+  Future<Option<int> > status1 = process::reap(pid.get());
+
+  // Continue in the parent.
+  ::close(pipes[0]);
+
+  // Isolate the forked child.
+  AWAIT_READY(isolator.get()->isolate(containerId1, pid.get()));
+
+  // Now signal the child to continue.
+  char dummy;
+  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
+  ::close(pipes[1]);
+
+  // Wait for the command to start.
+  ASSERT_TRUE(waitForFileCreation(container1Ready));
+
+  ContainerID containerId2;
+  containerId2.set_value("container2");
+
+  executorInfo.mutable_resources()->CopyFrom(
+      Resources::parse(container2Ports).get());
+
+  // Use a relative temporary directory so it gets cleaned up
+  // automatically with the test.
+  Try<string> dir2 = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
+  ASSERT_SOME(dir2);
+
+  Future<Option<CommandInfo> > preparation2 =
+    isolator.get()->prepare(
+        containerId2,
+        executorInfo,
+        dir2.get(),
+        None(),
+        None());
+
+  AWAIT_READY(preparation2);
+  ASSERT_SOME(preparation2.get());
+
+  ostringstream command2;
+
+  // Send to 'localhost' and 'port'.
+  command2 << "printf hello1 | nc localhost " << validPort << ";";
+  // Send to 'localhost' and 'invalidPort'. This should fail.
+  command2 << "printf hello2 | nc localhost " << invalidPort << ";";
+  // Send to 'public IP' and 'port'.
+  command2 << "printf hello3 | nc " << hostIP << " " << validPort << ";";
+  // Send to 'public IP' and 'invalidPort'. This should fail.
+  command2 << "printf hello4 | nc " << hostIP << " " << invalidPort << ";";
+  // Touch the guard file.
+  command2 << "touch " << container2Ready;
+
+  ASSERT_NE(-1, ::pipe(pipes));
+
+  pid = launchHelper(
+      launcher.get(),
+      pipes,
+      containerId2,
+      command2.str(),
+      preparation2.get());
+  ASSERT_SOME(pid);
+
+  // Reap the forked child.
+  Future<Option<int> > status2 = process::reap(pid.get());
+
+  // Continue in the parent.
+  ::close(pipes[0]);
+
+  // Isolate the forked child.
+  AWAIT_READY(isolator.get()->isolate(containerId2, pid.get()));
+
+  // Now signal the child to continue.
+  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
+  ::close(pipes[1]);
+
+  // Wait for the command to start.
+  ASSERT_TRUE(waitForFileCreation(container2Ready));
+
+  // Wait for the command to complete.
+  AWAIT_READY(status1);
+  AWAIT_READY(status2);
+
+  EXPECT_SOME_EQ("hello1", os::read(trafficViaLoopback));
+  EXPECT_SOME_EQ("hello3", os::read(trafficViaPublic));
+
+  // Ensure all processes are killed.
+  AWAIT_READY(launcher.get()->destroy(containerId1));
+  AWAIT_READY(launcher.get()->destroy(containerId2));
+
+  // Let the isolator clean up.
+  AWAIT_READY(isolator.get()->cleanup(containerId1));
+  AWAIT_READY(isolator.get()->cleanup(containerId2));
+
+  delete isolator.get();
+  delete launcher.get();
+}
+
+
+// The same container-to-container test but with UDP.
+TEST_F(PortMappingIsolatorTest, ROOT_NC_ContainerToContainerUDP)
+{
+  Try<Isolator*> isolator = PortMappingIsolatorProcess::create(flags);
+  CHECK_SOME(isolator);
+
+  Try<Launcher*> launcher =
+    LinuxLauncher::create(flags, isolator.get()->namespaces().get());
+  CHECK_SOME(launcher);
+
+  // Set the executor's resources.
+  ExecutorInfo executorInfo;
+  executorInfo.mutable_resources()->CopyFrom(
+      Resources::parse(container1Ports).get());
+
+  ContainerID containerId1;
+  containerId1.set_value("container1");
+
+  // Use a relative temporary directory so it gets cleaned up
+  // automatically with the test.
+  Try<string> dir1 = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
+  ASSERT_SOME(dir1);
+
+  Future<Option<CommandInfo> > preparation1 =
+    isolator.get()->prepare(
+        containerId1,
+        executorInfo,
+        dir1.get(),
+        None(),
+        None());
+
+  AWAIT_READY(preparation1);
+  ASSERT_SOME(preparation1.get());
+
+  ostringstream command1;
+
+  // Listen to 'localhost' and 'port'.
+  command1 << "nc -u -l localhost " << validPort << " > " << trafficViaLoopback
+           << "& ";
+
+  // Listen to 'public ip' and 'port'.
+  command1 << "nc -u -l " << hostIP << " " << validPort << " > "
+           << trafficViaPublic << "& ";
+
+  // Listen to 'invalidPort'. This should not receive anything.
+  command1 << "nc -u -l " << invalidPort << " | tee " << trafficViaLoopback
+           << " " << trafficViaPublic << "& ";
+
+  // Touch the guard file.
+  command1 << "touch " << container1Ready;
+
+  int pipes[2];
+  ASSERT_NE(-1, ::pipe(pipes));
+
+  Try<pid_t> pid = launchHelper(
+      launcher.get(),
+      pipes,
+      containerId1,
+      command1.str(),
+      preparation1.get());
+  ASSERT_SOME(pid);
+
+  // Reap the forked child.
+  Future<Option<int> > status1 = process::reap(pid.get());
+
+  // Continue in the parent.
+  ::close(pipes[0]);
+
+  // Isolate the forked child.
+  AWAIT_READY(isolator.get()->isolate(containerId1, pid.get()));
+
+  // Now signal the child to continue.
+  char dummy;
+  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
+  ::close(pipes[1]);
+
+  // Wait for the command to start.
+  ASSERT_TRUE(waitForFileCreation(container1Ready));
+
+  ContainerID containerId2;
+  containerId2.set_value("container2");
+
+  executorInfo.mutable_resources()->CopyFrom(
+      Resources::parse(container2Ports).get());
+
+  // Use a relative temporary directory so it gets cleaned up
+  // automatically with the test.
+  Try<string> dir2 = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
+  ASSERT_SOME(dir2);
+
+  Future<Option<CommandInfo> > preparation2 =
+    isolator.get()->prepare(
+        containerId2,
+        executorInfo,
+        dir2.get(),
+        None(),
+        None());
+
+  AWAIT_READY(preparation2);
+  ASSERT_SOME(preparation2.get());
+
+  ostringstream command2;
+
+  // Send to 'localhost' and 'port'.
+  command2 << "printf hello1 | nc -w1 -u localhost " << validPort << ";";
+  // Send to 'localhost' and 'invalidPort'. No data should be sent.
+  command2 << "printf hello2 | nc -w1 -u localhost " << invalidPort << ";";
+  // Send to 'public IP' and 'port'.
+  command2 << "printf hello3 | nc -w1 -u " << hostIP << " " << validPort << ";";
+  // Send to 'public IP' and 'invalidPort'. No data should be sent.
+  command2 << "printf hello4 | nc -w1 -u " << hostIP << " " << invalidPort
+           << ";";
+  // Touch the guard file.
+  command2 << "touch " << container2Ready;
+
+  ASSERT_NE(-1, ::pipe(pipes));
+
+  pid = launchHelper(
+      launcher.get(),
+      pipes,
+      containerId2,
+      command2.str(),
+      preparation2.get());
+
+  ASSERT_SOME(pid);
+
+  // Reap the forked child.
+  Future<Option<int> > status2 = process::reap(pid.get());
+
+  // Continue in the parent.
+  ::close(pipes[0]);
+
+  // Isolate the forked child.
+  AWAIT_READY(isolator.get()->isolate(containerId2, pid.get()));
+
+  // Now signal the child to continue.
+  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
+  ::close(pipes[1]);
+
+  // Wait for the command to start.
+  ASSERT_TRUE(waitForFileCreation(container2Ready));
+
+  // Wait for the command to complete.
+  AWAIT_READY(status1);
+  AWAIT_READY(status2);
+
+  EXPECT_SOME_EQ("hello1", os::read(trafficViaLoopback));
+  EXPECT_SOME_EQ("hello3", os::read(trafficViaPublic));
+
+  AWAIT_READY(launcher.get()->destroy(containerId1));
+  AWAIT_READY(launcher.get()->destroy(containerId2));
+
+  // Let the isolator clean up.
+  AWAIT_READY(isolator.get()->cleanup(containerId1));
+  AWAIT_READY(isolator.get()->cleanup(containerId2));
+
+  delete isolator.get();
+  delete launcher.get();
+}
+
+
+// Test the scenario where a UDP server is in a container while host
+// tries to establish a UDP connection.
+TEST_F(PortMappingIsolatorTest, ROOT_NC_HostToContainerUDP)
+{
+  Try<Isolator*> isolator = PortMappingIsolatorProcess::create(flags);
+  CHECK_SOME(isolator);
+
+  Try<Launcher*> launcher =
+    LinuxLauncher::create(flags, isolator.get()->namespaces().get());
+  CHECK_SOME(launcher);
+
+  // Set the executor's resources.
+  ExecutorInfo executorInfo;
+  executorInfo.mutable_resources()->CopyFrom(
+      Resources::parse(container1Ports).get());
+
+  ContainerID containerId;
+  containerId.set_value("container1");
+
+  // Use a relative temporary directory so it gets cleaned up
+  // automatically with the test.
+  Try<string> dir = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
+  ASSERT_SOME(dir);
+
+  Future<Option<CommandInfo> > preparation1 =
+    isolator.get()->prepare(
+        containerId,
+        executorInfo,
+        dir.get(),
+        None(),
+        None());
+
+  AWAIT_READY(preparation1);
+  ASSERT_SOME(preparation1.get());
+
+  ostringstream command1;
+
+  // Listen to 'localhost' and 'Port'.
+  command1 << "nc -u -l localhost " << validPort << " > " << trafficViaLoopback
+           << "&";
+
+  // Listen to 'public IP' and 'Port'.
+  command1 << "nc -u -l " << hostIP << " " << validPort << " > "
+           << trafficViaPublic << "&";
+
+  // Listen to 'public IP' and 'invalidPort'. This should not receive anything.
+  command1 << "nc -u -l " << invalidPort << " | tee " << trafficViaLoopback
+           << " " << trafficViaPublic << "&";
+
+  // Touch the guard file.
+  command1 << "touch " << container1Ready;
+
+  int pipes[2];
+  ASSERT_NE(-1, ::pipe(pipes));
+
+  Try<pid_t> pid = launchHelper(
+      launcher.get(),
+      pipes,
+      containerId,
+      command1.str(),
+      preparation1.get());
+
+  ASSERT_SOME(pid);
+
+  // Reap the forked child.
+  Future<Option<int> > status = process::reap(pid.get());
+
+  // Continue in the parent.
+  ::close(pipes[0]);
+
+  // Isolate the forked child.
+  AWAIT_READY(isolator.get()->isolate(containerId, pid.get()));
+
+  // Now signal the child to continue.
+  char dummy;
+  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
+  ::close(pipes[1]);
+
+  // Wait for the command to start.
+  ASSERT_TRUE(waitForFileCreation(container1Ready));
+
+  // Send to 'localhost' and 'port'.
+  ostringstream command2;
+  command2 << "printf hello1 | nc -w1 -u localhost " << validPort;
+  ASSERT_SOME_EQ(0, os::shell(NULL, command2.str().c_str()));
+
+  // Send to 'localhost' and 'invalidPort'. The command should return
+  // successfully because UDP is stateless but no data could be sent.
+  ostringstream command3;
+  command3 << "printf hello2 | nc -w1 -u localhost " << invalidPort;
+  ASSERT_SOME_EQ(0, os::shell(NULL, command3.str().c_str()));
+
+  // Send to 'public IP' and 'port'.
+  ostringstream command4;
+  command4 << "printf hello3 | nc -w1 -u " << hostIP << " " << validPort;
+  ASSERT_SOME_EQ(0, os::shell(NULL, command4.str().c_str()));
+
+  // Send to 'public IP' and 'invalidPort'. The command should return
+  // successfully because UDP is stateless but no data could be sent.
+  ostringstream command5;
+  command5 << "printf hello4 | nc -w1 -u " << hostIP << " " << invalidPort;
+  ASSERT_SOME_EQ(0, os::shell(NULL, command5.str().c_str()));
+
+  EXPECT_SOME_EQ("hello1", os::read(trafficViaLoopback));
+  EXPECT_SOME_EQ("hello3", os::read(trafficViaPublic));
+
+  // Ensure all processes are killed.
+  AWAIT_READY(launcher.get()->destroy(containerId));
+
+  // Let the isolator clean up.
+  AWAIT_READY(isolator.get()->cleanup(containerId));
+
+  delete isolator.get();
+  delete launcher.get();
+}
+
+
+// Test the scenario where a TCP server is in a container while host
+// tries to establish a TCP connection.
+TEST_F(PortMappingIsolatorTest, ROOT_NC_HostToContainerTCP)
+{
+  Try<Isolator*> isolator = PortMappingIsolatorProcess::create(flags);
+  CHECK_SOME(isolator);
+
+  Try<Launcher*> launcher =
+    LinuxLauncher::create(flags, isolator.get()->namespaces().get());
+  CHECK_SOME(launcher);
+
+  // Set the executor's resources.
+  ExecutorInfo executorInfo;
+  executorInfo.mutable_resources()->CopyFrom(
+      Resources::parse(container1Ports).get());
+
+  ContainerID containerId;
+  containerId.set_value("container1");
+
+  // Use a relative temporary directory so it gets cleaned up
+  // automatically with the test.
+  Try<string> dir = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
+  ASSERT_SOME(dir);
+
+  Future<Option<CommandInfo> > preparation1 =
+    isolator.get()->prepare(
+        containerId,
+        executorInfo,
+        dir.get(),
+        None(),
+        None());
+
+  AWAIT_READY(preparation1);
+  ASSERT_SOME(preparation1.get());
+
+  ostringstream command1;
+
+  // Listen to 'localhost' and 'Port'.
+  command1 << "nc -l localhost " << validPort << " > " << trafficViaLoopback
+           << "&";
+
+  // Listen to 'public IP' and 'Port'.
+  command1 << "nc -l " << hostIP << " " << validPort << " > "
+           << trafficViaPublic << "&";
+
+  // Listen to 'public IP' and 'invalidPort'. This should fail.
+  command1 << "nc -l " << invalidPort << " | tee " << trafficViaLoopback << " "
+           << trafficViaPublic << "&";
+
+  // Touch the guard file.
+  command1 << "touch " << container1Ready;
+
+  int pipes[2];
+  ASSERT_NE(-1, ::pipe(pipes));
+
+  Try<pid_t> pid = launchHelper(
+      launcher.get(),
+      pipes,
+      containerId,
+      command1.str(),
+      preparation1.get());
+
+  ASSERT_SOME(pid);
+
+  // Reap the forked child.
+  Future<Option<int> > status = process::reap(pid.get());
+
+  // Continue in the parent.
+  ::close(pipes[0]);
+
+  // Isolate the forked child.
+  AWAIT_READY(isolator.get()->isolate(containerId, pid.get()));
+
+  // Now signal the child to continue.
+  char dummy;
+  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
+  ::close(pipes[1]);
+
+  // Wait for the command to start.
+  ASSERT_TRUE(waitForFileCreation(container1Ready));
+
+  // Send to 'localhost' and 'port'.
+  ostringstream command2;
+  command2 << "printf hello1 | nc localhost " << validPort;
+  ASSERT_SOME_EQ(0, os::shell(NULL, command2.str().c_str()));
+
+  // Send to 'localhost' and 'invalidPort'. This should fail because TCP
+  // connection couldn't be established..
+  ostringstream command3;
+  command3 << "printf hello2 | nc localhost " << invalidPort;
+  ASSERT_SOME_EQ(256, os::shell(NULL, command3.str().c_str()));
+
+  // Send to 'public IP' and 'port'.
+  ostringstream command4;
+  command4 << "printf hello3 | nc " << hostIP << " " << validPort;
+  ASSERT_SOME_EQ(0, os::shell(NULL, command4.str().c_str()));
+
+  // Send to 'public IP' and 'invalidPort'. This should fail because TCP
+  // connection couldn't be established.
+  ostringstream command5;
+  command5 << "printf hello4 | nc " << hostIP << " " << invalidPort;
+  ASSERT_SOME_EQ(256, os::shell(NULL, command5.str().c_str()));
+
+  EXPECT_SOME_EQ("hello1", os::read(trafficViaLoopback));
+  EXPECT_SOME_EQ("hello3", os::read(trafficViaPublic));
+
+  // Ensure all processes are killed.
+  AWAIT_READY(launcher.get()->destroy(containerId));
+
+  // Let the isolator clean up.
+  AWAIT_READY(isolator.get()->cleanup(containerId));
+
+  delete isolator.get();
+  delete launcher.get();
+}
+
+
+// Test the scenario where a container issues ICMP requests to
+// external hosts.
+TEST_F(PortMappingIsolatorTest, ROOT_ContainerICMPExternal)
+{
+  // TODO(chzhcn): Even though this is unlikely, consider a better
+  // way to get external servers.
+  ASSERT_FALSE(nameServers.empty())
+    << "-------------------------------------------------------------\n"
+    << "We cannot run some PortMappingIsolatorTests because we could\n"
+    << "not find any external name servers in /etc/resolv.conf.\n"
+    << "-------------------------------------------------------------";
+
+  Try<Isolator*> isolator = PortMappingIsolatorProcess::create(flags);
+  CHECK_SOME(isolator);
+
+  Try<Launcher*> launcher =
+    LinuxLauncher::create(flags, isolator.get()->namespaces().get());
+  CHECK_SOME(launcher);
+
+  // Set the executor's resources.
+  ExecutorInfo executorInfo;
+  executorInfo.mutable_resources()->CopyFrom(
+      Resources::parse(container1Ports).get());
+
+  ContainerID containerId;
+  containerId.set_value("container1");
+
+  // Use a relative temporary directory so it gets cleaned up
+  // automatically with the test.
+  Try<string> dir = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
+  ASSERT_SOME(dir);
+
+  Future<Option<CommandInfo> > preparation1 =
+    isolator.get()->prepare(
+        containerId,
+        executorInfo,
+        dir.get(),
+        None(),
+        None());
+
+  AWAIT_READY(preparation1);
+  ASSERT_SOME(preparation1.get());
+
+  ostringstream command1;
+  for (unsigned int i = 0; i < nameServers.size(); i++) {
+    const string& IP = nameServers[i];
+    command1 << "ping -c1 " << IP;
+    if (i + 1 < nameServers.size()) {
+      command1 << " && ";
+    }
+  }
+  command1 << "; printf $? > " << exitStatus << "; sync";
+
+  int pipes[2];
+  ASSERT_NE(-1, ::pipe(pipes));
+
+  Try<pid_t> pid = launchHelper(
+      launcher.get(),
+      pipes,
+      containerId,
+      command1.str(),
+      preparation1.get());
+
+  ASSERT_SOME(pid);
+
+  // Reap the forked child.
+  Future<Option<int> > status = process::reap(pid.get());
+
+  // Continue in the parent.
+  ::close(pipes[0]);
+
+  // Isolate the forked child.
+  AWAIT_READY(isolator.get()->isolate(containerId, pid.get()));
+
+  // Now signal the child to continue.
+  char dummy;
+  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
+  ::close(pipes[1]);
+
+  // Wait for the command to complete.
+  AWAIT_READY(status);
+
+  EXPECT_SOME_EQ("0", os::read(exitStatus));
+
+  // Ensure all processes are killed.
+  AWAIT_READY(launcher.get()->destroy(containerId));
+
+  // Let the isolator clean up.
+  AWAIT_READY(isolator.get()->cleanup(containerId));
+
+  delete isolator.get();
+  delete launcher.get();
+}
+
+
+// Test the scenario where a container issues ICMP requests to itself.
+TEST_F(PortMappingIsolatorTest, ROOT_ContainerICMPInternal)
+{
+  Try<Isolator*> isolator = PortMappingIsolatorProcess::create(flags);
+  CHECK_SOME(isolator);
+
+  Try<Launcher*> launcher =
+    LinuxLauncher::create(flags, isolator.get()->namespaces().get());
+  CHECK_SOME(launcher);
+
+  // Set the executor's resources.
+  ExecutorInfo executorInfo;
+  executorInfo.mutable_resources()->CopyFrom(
+      Resources::parse(container1Ports).get());
+
+  ContainerID containerId;
+  containerId.set_value("container1");
+
+  // Use a relative temporary directory so it gets cleaned up
+  // automatically with the test.
+  Try<string> dir = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
+  ASSERT_SOME(dir);
+
+  Future<Option<CommandInfo> > preparation1 =
+    isolator.get()->prepare(
+        containerId,
+        executorInfo,
+        dir.get(),
+        None(),
+        None());
+
+  AWAIT_READY(preparation1);
+  ASSERT_SOME(preparation1.get());
+
+  ostringstream command1;
+  command1 << "ping -c1 127.0.0.1 && ping -c1 " << hostIP
+           << "; printf $? > " << exitStatus << "; sync";
+
+  int pipes[2];
+  ASSERT_NE(-1, ::pipe(pipes));
+
+  Try<pid_t> pid = launchHelper(
+      launcher.get(),
+      pipes,
+      containerId,
+      command1.str(),
+      preparation1.get());
+
+  ASSERT_SOME(pid);
+
+  // Reap the forked child.
+  Future<Option<int> > status = process::reap(pid.get());
+
+  // Continue in the parent.
+  ::close(pipes[0]);
+
+  // Isolate the forked child.
+  AWAIT_READY(isolator.get()->isolate(containerId, pid.get()));
+
+  // Now signal the child to continue.
+  char dummy;
+  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
+  ::close(pipes[1]);
+
+  // Wait for the command to complete.
+  AWAIT_READY(status);
+
+  EXPECT_SOME_EQ("0", os::read(exitStatus));
+
+  // Ensure all processes are killed.
+  AWAIT_READY(launcher.get()->destroy(containerId));
+
+  // Let the isolator clean up.
+  AWAIT_READY(isolator.get()->cleanup(containerId));
+
+  delete isolator.get();
+  delete launcher.get();
+}
+
+
+// Test the scenario where a container issues ARP requests to
+// external hosts.
+TEST_F(PortMappingIsolatorTest, ROOT_ContainerARPExternal)
+{
+  // TODO(chzhcn): Even though this is unlikely, consider a better
+  // way to get external servers.
+  ASSERT_FALSE(nameServers.empty())
+    << "-------------------------------------------------------------\n"
+    << "We cannot run some PortMappingIsolatorTests because we could\n"
+    << "not find any external name servers in /etc/resolv.conf.\n"
+    << "-------------------------------------------------------------";
+
+  Try<Isolator*> isolator = PortMappingIsolatorProcess::create(flags);
+  CHECK_SOME(isolator);
+
+  Try<Launcher*> launcher =
+    LinuxLauncher::create(flags, isolator.get()->namespaces().get());
+  CHECK_SOME(launcher);
+
+  // Set the executor's resources.
+  ExecutorInfo executorInfo;
+  executorInfo.mutable_resources()->CopyFrom(
+      Resources::parse(container1Ports).get());
+
+  ContainerID containerId;
+  containerId.set_value("container1");
+
+  // Use a relative temporary directory so it gets cleaned up
+  // automatically with the test.
+  Try<string> dir = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
+  ASSERT_SOME(dir);
+
+  Future<Option<CommandInfo> > preparation1 =
+    isolator.get()->prepare(
+        containerId,
+        executorInfo,
+        dir.get(),
+        None(),
+        None());
+
+  AWAIT_READY(preparation1);
+  ASSERT_SOME(preparation1.get());
+
+  ostringstream command1;
+  for (unsigned int i = 0; i < nameServers.size(); i++) {
+    const string& IP = nameServers[i];
+    // Time out after 1s and terminate upon receiving the first reply.
+    command1 << "arping -f -w1 " << IP << " -I " << eth0;
+    if (i + 1 < nameServers.size()) {
+      command1 << " && ";
+    }
+  }
+  command1 << "; printf $? > " << exitStatus << "; sync";
+
+  int pipes[2];
+  ASSERT_NE(-1, ::pipe(pipes));
+
+  Try<pid_t> pid = launchHelper(
+      launcher.get(),
+      pipes,
+      containerId,
+      command1.str(),
+      preparation1.get());
+
+  ASSERT_SOME(pid);
+
+  // Reap the forked child.
+  Future<Option<int> > status = process::reap(pid.get());
+
+  // Continue in the parent.
+  ::close(pipes[0]);
+
+  // Isolate the forked child.
+  AWAIT_READY(isolator.get()->isolate(containerId, pid.get()));
+
+  // Now signal the child to continue.
+  char dummy;
+  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
+  ::close(pipes[1]);
+
+  // Wait for the command to complete.
+  AWAIT_READY(status);
+
+  EXPECT_SOME_EQ("0", os::read(exitStatus));
+
+  // Ensure all processes are killed.
+  AWAIT_READY(launcher.get()->destroy(containerId));
+
+  // Let the isolator clean up.
+  AWAIT_READY(isolator.get()->cleanup(containerId));
+
+  delete isolator.get();
+  delete launcher.get();
+}
+
+
+// Test DNS connectivity.
+TEST_F(PortMappingIsolatorTest, ROOT_DNS)
+{
+  // TODO(chzhcn): Even though this is unlikely, consider a better
+  // way to get external servers.
+  ASSERT_FALSE(nameServers.empty())
+    << "-------------------------------------------------------------\n"
+    << "We cannot run some PortMappingIsolatorTests because we could\n"
+    << "not find any external name servers in /etc/resolv.conf.\n"
+    << "-------------------------------------------------------------";
+
+  Try<Isolator*> isolator = PortMappingIsolatorProcess::create(flags);
+  CHECK_SOME(isolator);
+
+  Try<Launcher*> launcher =
+    LinuxLauncher::create(flags, isolator.get()->namespaces().get());
+  CHECK_SOME(launcher);
+
+  // Set the executor's resources.
+  ExecutorInfo executorInfo;
+  executorInfo.mutable_resources()->CopyFrom(
+      Resources::parse(container1Ports).get());
+
+  ContainerID containerId;
+  containerId.set_value("container1");
+
+  // Use a relative temporary directory so it gets cleaned up
+  // automatically with the test.
+  Try<string> dir = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
+  ASSERT_SOME(dir);
+
+  Future<Option<CommandInfo> > preparation1 =
+    isolator.get()->prepare(
+        containerId,
+        executorInfo,
+        dir.get(),
+        None(),
+        None());
+
+  AWAIT_READY(preparation1);
+  ASSERT_SOME(preparation1.get());
+
+  ostringstream command1;
+  for (unsigned int i = 0; i < nameServers.size(); i++) {
+    const string& IP = nameServers[i];
+    command1 << "host " << IP;
+    if (i + 1 < nameServers.size()) {
+      command1 << " && ";
+    }
+  }
+  command1 << "; printf $? > " << exitStatus << "; sync";
+
+  int pipes[2];
+  ASSERT_NE(-1, ::pipe(pipes));
+
+  Try<pid_t> pid = launchHelper(
+      launcher.get(),
+      pipes,
+      containerId,
+      command1.str(),
+      preparation1.get());
+
+  ASSERT_SOME(pid);
+
+  // Reap the forked child.
+  Future<Option<int> > status = process::reap(pid.get());
+
+  // Continue in the parent.
+  ::close(pipes[0]);
+
+  // Isolate the forked child.
+  AWAIT_READY(isolator.get()->isolate(containerId, pid.get()));
+
+  // Now signal the child to continue.
+  char dummy;
+  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
+  ::close(pipes[1]);
+
+  // Wait for the command to complete.
+  AWAIT_READY(status);
+
+  EXPECT_SOME_EQ("0", os::read(exitStatus));
+
+  // Ensure all processes are killed.
+  AWAIT_READY(launcher.get()->destroy(containerId));
+
+  // Let the isolator clean up.
+  AWAIT_READY(isolator.get()->cleanup(containerId));
+
+  delete isolator.get();
+  delete launcher.get();
+}
+
+
+// Test the scenario where a container has run out of ephemeral ports
+// to use.
+TEST_F(PortMappingIsolatorTest, ROOT_TooManyContainers)
+{
+  // Increase the ephemeral ports per container so that we dont have
+  // enough ephemeral ports to launch a second container.
+  flags.ephemeral_ports_per_container = 512;
+
+  Try<Isolator*> isolator = PortMappingIsolatorProcess::create(flags);
+  CHECK_SOME(isolator);
+
+  Try<Launcher*> launcher =
+    LinuxLauncher::create(flags, isolator.get()->namespaces().get());
+  CHECK_SOME(launcher);
+
+  // Set the executor's resources.
+  ExecutorInfo executorInfo;
+  executorInfo.mutable_resources()->CopyFrom(
+      Resources::parse(container1Ports).get());
+
+  ContainerID containerId1;
+  containerId1.set_value("container1");
+
+  // Use a relative temporary directory so it gets cleaned up
+  // automatically with the test.
+  Try<string> dir1 = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
+  ASSERT_SOME(dir1);
+
+  Future<Option<CommandInfo> > preparation1 =
+    isolator.get()->prepare(
+        containerId1,
+        executorInfo,
+        dir1.get(),
+        None(),
+        None());
+
+  AWAIT_READY(preparation1);
+  ASSERT_SOME(preparation1.get());
+
+  ostringstream command1;
+  command1 << "sleep 1000";
+
+  int pipes[2];
+  ASSERT_NE(-1, ::pipe(pipes));
+
+  Try<pid_t> pid = launchHelper(
+      launcher.get(),
+      pipes,
+      containerId1,
+      command1.str(),
+      preparation1.get());
+
+  ASSERT_SOME(pid);
+
+  // Reap the forked child.
+  Future<Option<int> > status1 = process::reap(pid.get());
+
+  // Continue in the parent.
+  ::close(pipes[0]);
+
+  // Isolate the forked child.
+  AWAIT_READY(isolator.get()->isolate(containerId1, pid.get()));
+
+  // Now signal the child to continue.
+  char dummy;
+  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
+  ::close(pipes[1]);
+
+  ContainerID containerId2;
+  containerId2.set_value("container2");
+
+  executorInfo.mutable_resources()->CopyFrom(
+      Resources::parse(container2Ports).get());
+
+  // Use a relative temporary directory so it gets cleaned up
+  // automatically with the test.
+  Try<string> dir2 = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
+  ASSERT_SOME(dir2);
+
+  Future<Option<CommandInfo> > preparation2 =
+    isolator.get()->prepare(
+        containerId2,
+        executorInfo,
+        dir2.get(),
+        None(),
+        None());
+
+  AWAIT_FAILED(preparation2);
+
+  // Ensure all processes are killed.
+  AWAIT_READY(launcher.get()->destroy(containerId1));
+
+  // Let the isolator clean up.
+  AWAIT_READY(isolator.get()->cleanup(containerId1));
+
+  delete isolator.get();
+  delete launcher.get();
+}
+
+
+// Test the scenario where PortMappingIsolator uses a very small
+// egress rate limit.
+TEST_F(PortMappingIsolatorTest, ROOT_NC_SmallEgressLimit)
+{
+  // Note that the underlying rate limiting mechanism usually has a
+  // small allowance for burst. Empirically, as least 10x of the rate
+  // limit amount of data is required to make sure the burst is an
+  // insignificant factor of the transmission time.
+
+  // To-be-tested egress rate limit, in Bytes/s.
+  const Bytes rate = 2000;
+  // Size of the data to send, in Bytes.
+  const Bytes size = 20480;
+
+  // Use a very small egress limit.
+  flags.egress_rate_limit_per_container = rate;
+
+  Try<Isolator*> isolator = PortMappingIsolatorProcess::create(flags);
+  CHECK_SOME(isolator);
+
+  Try<Launcher*> launcher =
+    LinuxLauncher::create(flags, isolator.get()->namespaces().get());
+  CHECK_SOME(launcher);
+
+  // Open an nc server on the host side. Note that 'invalidPort' is in
+  // neither 'ports' nor 'ephemeral_ports', which makes it a good port
+  // to use on the host.
+  ostringstream command1;
+  command1 << "nc -l localhost " << invalidPort << " > /devnull";
+  Try<Subprocess> s = subprocess(command1.str().c_str());
+  CHECK_SOME(s);
+
+  // Set the executor's resources.
+  ExecutorInfo executorInfo;
+  executorInfo.mutable_resources()->CopyFrom(
+      Resources::parse(container1Ports).get());
+
+  ContainerID containerId;
+  containerId.set_value("container1");
+
+  // Use a relative temporary directory so it gets cleaned up
+  // automatically with the test.
+  Try<string> dir = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
+  ASSERT_SOME(dir);
+
+  Future<Option<CommandInfo> > preparation1 =
+    isolator.get()->prepare(
+        containerId,
+        executorInfo,
+        dir.get(),
+        None(),
+        None());
+
+  AWAIT_READY(preparation1);
+  ASSERT_SOME(preparation1.get());
+
+  // Fill 'size' bytes of data. The actual content does not matter.
+  string data(size.bytes(), 'a');
+
+  ostringstream command2;
+  const string transmissionTime = path::join(os::getcwd(), "transmission_time");
+
+  command2 << "echo 'Sending " << size.bytes()
+           << " bytes of data under egress rate limit " << rate.bytes()
+           << "Bytes/s...';";
+
+  command2 << "{ time -p echo " << data  << " | nc localhost "
+           << invalidPort << " ; } 2> " << transmissionTime << " && ";
+
+  // Touch the guard file.
+  command2 << "touch " << container1Ready;
+
+  int pipes[2];
+  ASSERT_NE(-1, ::pipe(pipes));
+
+  Try<pid_t> pid = launchHelper(
+      launcher.get(),
+      pipes,
+      containerId,
+      command2.str(),
+      preparation1.get());
+
+  ASSERT_SOME(pid);
+
+  // Reap the forked child.
+  Future<Option<int> > reap = process::reap(pid.get());
+
+  // Continue in the parent.
+  ::close(pipes[0]);
+
+  // Isolate the forked child.
+  AWAIT_READY(isolator.get()->isolate(containerId, pid.get()));
+
+  // Now signal the child to continue.
+  char dummy;
+  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
+  ::close(pipes[1]);
+
+  // Wait for the command to finish.
+  ASSERT_TRUE(waitForFileCreation(container1Ready));
+
+  Try<string> read = os::read(transmissionTime);
+  CHECK_SOME(read);
+
+  // Get the real elapsed time from `time` output. Sample output:
+  // real 12.37
+  // user 0.00
+  // sys 0.00
+  vector<string> lines = strings::split(strings::trim(read.get()), "\n");
+  ASSERT_EQ(3u, lines.size());
+
+  vector<string> split = strings::split(lines[0], " ");
+  ASSERT_EQ(2u, split.size());
+
+  Try<float> time = numify<float>(split[1]);
+  ASSERT_SOME(time);
+  ASSERT_GT(time.get(), (size.bytes() / rate.bytes()));
+
+  // Make sure the nc server exits normally.
+  Future<Option<int> > status = s.get().status();
+  AWAIT_READY(status);
+  EXPECT_SOME_EQ(0, status.get());
+
+  // Ensure all processes are killed.
+  AWAIT_READY(launcher.get()->destroy(containerId));
+
+  // Let the isolator clean up.
+  AWAIT_READY(isolator.get()->cleanup(containerId));
+
+  delete isolator.get();
+  delete launcher.get();
+}
+
+
+bool HasTCPSocketsCount(const ResourceStatistics& statistics)
+{
+  return statistics.has_net_tcp_active_connections() &&
+    statistics.has_net_tcp_time_wait_connections();
+}
+
+
+bool HasTCPSocketsRTT(const ResourceStatistics& statistics)
+{
+  // We either have all of the following metrics or we have nothing.
+  if (statistics.has_net_tcp_rtt_microsecs_p50() &&
+      statistics.has_net_tcp_rtt_microsecs_p90() &&
+      statistics.has_net_tcp_rtt_microsecs_p95() &&
+      statistics.has_net_tcp_rtt_microsecs_p99()) {
+    return true;
+  } else {
+    return false;
+  }
+}
+
+
+// Test that RTT can be returned properly from usage(). This test is
+// very similar to SmallEgressLimitTest in its setup.
+TEST_F(PortMappingIsolatorTest, ROOT_NC_PortMappingStatistics)
+{
+  // To-be-tested egress rate limit, in Bytes/s.
+  const Bytes rate = 2000;
+  // Size of the data to send, in Bytes.
+  const Bytes size = 20480;
+
+  // Use a very small egress limit.
+  flags.egress_rate_limit_per_container = rate;
+  flags.network_enable_socket_statistics_summary = true;
+  flags.network_enable_socket_statistics_details = true;
+
+  Try<Isolator*> isolator = PortMappingIsolatorProcess::create(flags);
+  CHECK_SOME(isolator);
+
+  Try<Launcher*> launcher =
+    LinuxLauncher::create(flags, isolator.get()->namespaces().get());
+  CHECK_SOME(launcher);
+
+  // Open an nc server on the host side. Note that 'invalidPort' is
+  // in neither 'ports' nor 'ephemeral_ports', which makes it a good
+  // port to use on the host. We use this host's public IP because
+  // connections to the localhost IP are filtered out when retrieving
+  // the RTT information inside containers.
+  ostringstream command1;
+  command1 << "nc -l " << hostIP << " " << invalidPort << " > /devnull";
+  Try<Subprocess> s = subprocess(command1.str().c_str());
+  CHECK_SOME(s);
+
+  // Set the executor's resources.
+  ExecutorInfo executorInfo;
+  executorInfo.mutable_resources()->CopyFrom(
+      Resources::parse(container1Ports).get());
+
+  ContainerID containerId;
+  containerId.set_value("container1");
+
+  // Use a relative temporary directory so it gets cleaned up
+  // automatically with the test.
+  Try<string> dir1 = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
+  ASSERT_SOME(dir1);
+
+  Future<Option<CommandInfo> > preparation1 =
+    isolator.get()->prepare(
+        containerId,
+        executorInfo,
+        dir1.get(),
+        None(),
+        None());
+
+  AWAIT_READY(preparation1);
+  ASSERT_SOME(preparation1.get());
+
+  // Fill 'size' bytes of data. The actual content does not matter.
+  string data(size.bytes(), 'a');
+
+  ostringstream command2;
+  const string transmissionTime = path::join(os::getcwd(), "transmission_time");
+
+  command2 << "echo 'Sending " << size.bytes()
+           << " bytes of data under egress rate limit " << rate.bytes()
+           << "Bytes/s...';";
+
+  command2 << "{ time -p echo " << data  << " | nc " << hostIP << " "
+           << invalidPort << " ; } 2> " << transmissionTime << " && ";
+
+  // Touch the guard file.
+  command2 << "touch " << container1Ready;
+
+  int pipes[2];
+  ASSERT_NE(-1, ::pipe(pipes));
+
+  Try<pid_t> pid = launchHelper(
+      launcher.get(),
+      pipes,
+      containerId,
+      command2.str(),
+      preparation1.get());
+
+  ASSERT_SOME(pid);
+
+  // Reap the forked child.
+  Future<Option<int> > reap = process::reap(pid.get());
+
+  // Continue in the parent.
+  ::close(pipes[0]);
+
+  // Isolate the forked child.
+  AWAIT_READY(isolator.get()->isolate(containerId, pid.get()));
+
+  // Now signal the child to continue.
+  char dummy;
+  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
+  ::close(pipes[1]);
+
+  // Test that RTT can be returned while transmission is going. It is
+  // possible that the first few statistics returned don't have a RTT
+  // value because it takes a few round-trips to actually establish a
+  // tcp connection and start sending data. Nevertheless, we should
+  // see a meaningful result well within seconds.
+  Duration waited = Duration::zero();
+  do {
+    os::sleep(Milliseconds(200));
+    waited += Milliseconds(200);
+
+    // Do an end-to-end test by calling `usage`.
+    Future<ResourceStatistics> usage = isolator.get()->usage(containerId);
+    AWAIT_READY(usage);
+
+    if (usage.get().has_net_tcp_rtt_microsecs_p50() &&
+        usage.get().has_net_tcp_active_connections()) {
+      EXPECT_GT(usage.get().net_tcp_active_connections(), 0);
+      break;
+    }
+  } while (waited < Seconds(5));
+  ASSERT_LT(waited, Seconds(5));
+
+  // While the connection is still active, try out different flag
+  // combinations.
+  Result<ResourceStatistics> statistics =
+      statisticsHelper(pid.get(), true, true);
+  ASSERT_SOME(statistics);
+  EXPECT_TRUE(HasTCPSocketsCount(statistics.get()));
+  EXPECT_TRUE(HasTCPSocketsRTT(statistics.get()));
+
+  statistics = statisticsHelper(pid.get(), true, false);
+  ASSERT_SOME(statistics);
+  EXPECT_TRUE(HasTCPSocketsCount(statistics.get()));
+  EXPECT_FALSE(HasTCPSocketsRTT(statistics.get()));
+
+  statistics = statisticsHelper(pid.get(), false, true);
+  ASSERT_SOME(statistics);
+  EXPECT_FALSE(HasTCPSocketsCount(statistics.get()));
+  EXPECT_TRUE(HasTCPSocketsRTT(statistics.get()));
+
+  statistics = statisticsHelper(pid.get(), false, false);
+  ASSERT_SOME(statistics);
+  EXPECT_FALSE(HasTCPSocketsCount(statistics.get()));
+  EXPECT_FALSE(HasTCPSocketsRTT(statistics.get()));
+
+  // Wait for the command to finish.
+  ASSERT_TRUE(waitForFileCreation(container1Ready));
+
+  // Make sure the nc server exits normally.
+  Future<Option<int> > status = s.get().status();
+  AWAIT_READY(status);
+  EXPECT_SOME_EQ(0, status.get());
+
+  // Ensure all processes are killed.
+  AWAIT_READY(launcher.get()->destroy(containerId));
+
+  // Let the isolator clean up.
+  AWAIT_READY(isolator.get()->cleanup(containerId));
+
+  delete isolator.get();
+  delete launcher.get();
+}
+
+
+class PortMappingMesosTest : public ContainerizerTest<MesosContainerizer>
+{
+public:
+  virtual void SetUp()
+  {
+    ContainerizerTest<MesosContainerizer>::SetUp();
+
+    // Guess the name of the public interface.
+    Result<string> _eth0 = link::eth0();
+    ASSERT_SOME(_eth0) << "Failed to guess the name of the public interface";
+
+    eth0 = _eth0.get();
+
+    LOG(INFO) << "Using " << eth0 << " as the public interface";
+
+    // Guess the name of the loopback interface.
+    Result<string> _lo = link::lo();
+    ASSERT_SOME(_lo) << "Failed to guess the name of the loopback interface";
+
+    lo = _lo.get();
+
+    LOG(INFO) << "Using " << lo << " as the loopback interface";
+
+    cleanup(eth0, lo);
+  }
+
+  virtual void TearDown()
+  {
+    cleanup(eth0, lo);
+
+    ContainerizerTest<MesosContainerizer>::TearDown();
+  }
+
+  Fetcher fetcher;
+
+  // Name of the host eth0 and lo.
+  string eth0;
+  string lo;
+};
+
+
+// Test the scenario where the network isolator is asked to recover
+// both types of containers: containers that were previously managed
+// by network isolator, and containers that weren't.
+TEST_F(PortMappingMesosTest, CGROUPS_ROOT_RecoverMixedContainers)
+{
+  master::Flags masterFlags = CreateMasterFlags();
+
+  Try<PID<Master>> master = StartMaster(masterFlags);
+  ASSERT_SOME(master);
+
+  // Start the first slave without the network isolator.
+  slave::Flags slaveFlags = CreateSlaveFlags();
+
+  // NOTE: This is to make sure that we use the linux launcher which
+  // is consistent with the launchers we use for other containerizers
+  // we create in this test. Also, this will bypass MESOS-2554.
+  slaveFlags.isolation = "cgroups/cpu,cgroups/mem";
+
+  Try<MesosContainerizer*> containerizer1 =
+    MesosContainerizer::create(slaveFlags, true, &fetcher);
+
+  ASSERT_SOME(containerizer1);
+
+  Try<PID<Slave> > slave = StartSlave(containerizer1.get(), slaveFlags);
+  ASSERT_SOME(slave);
+
+  MockScheduler sched;
+
+  // Enable checkpointing for the framework.
+  FrameworkInfo frameworkInfo = DEFAULT_FRAMEWORK_INFO;
+  frameworkInfo.set_checkpoint(true);
+
+  MesosSchedulerDriver driver(
+      &sched, frameworkInfo, master.get(), DEFAULT_CREDENTIAL);
+
+  EXPECT_CALL(sched, registered(_, _, _));
+
+  Filters filters;
+  filters.set_refuse_seconds(0);
+
+  // NOTE: We set filter explicitly here so that the resources will
+  // not be filtered for 5 seconds (by default).
+  Future<vector<Offer> > offers1;
+  EXPECT_CALL(sched, resourceOffers(_, _))
+    .WillOnce(FutureArg<1>(&offers1))
+    .WillRepeatedly(DeclineOffers(filters));      // Ignore subsequent offers.
+
+  driver.start();
+
+  AWAIT_READY(offers1);
+  EXPECT_NE(0u, offers1.get().size());
+
+  Offer offer1 = offers1.get()[0];
+
+  // Start a long running task without using the network isolator.
+  TaskInfo task1 = createTask(
+      offer1.slave_id(),
+      Resources::parse("cpus:1;mem:512").get(),
+      "sleep 1000");
+
+  EXPECT_CALL(sched, statusUpdate(_, _));
+
+  Future<Nothing> _statusUpdateAcknowledgement1 =
+    FUTURE_DISPATCH(_, &Slave::_statusUpdateAcknowledgement);
+
+  driver.launchTasks(offers1.get()[0].id(), {task1}, filters);
+
+  // Wait for the ACK to be checkpointed.
+  AWAIT_READY(_statusUpdateAcknowledgement1);
+
+  Stop(slave.get());
+  delete containerizer1.get();
+
+  Future<Nothing> _recover1 = FUTURE_DISPATCH(_, &Slave::_recover);
+
+  Future<SlaveReregisteredMessage> slaveReregisteredMessage1 =
+    FUTURE_PROTOBUF(SlaveReregisteredMessage(), _, _);
+
+  Future<vector<Offer> > offers2;
+  EXPECT_CALL(sched, resourceOffers(_, _))
+    .WillOnce(FutureArg<1>(&offers2))
+    .WillRepeatedly(DeclineOffers(filters));      // Ignore subsequent offers.
+
+  // Restart the slave with the network isolator.
+  slaveFlags.isolation += ",network/port_mapping";
+
+  Try<MesosContainerizer*> containerizer2 =
+    MesosContainerizer::create(slaveFlags, true, &fetcher);
+
+  ASSERT_SOME(containerizer2);
+
+  slave = StartSlave(containerizer2.get(), slaveFlags);
+  ASSERT_SOME(slave);
+
+  Clock::pause();
+
+  AWAIT_READY(_recover1);
+
+  Clock::settle(); // Wait for slave to schedule reregister timeout.
+  Clock::advance(EXECUTOR_REREGISTER_TIMEOUT);
+
+  AWAIT_READY(slaveReregisteredMessage1);
+
+  Clock::settle(); // Make sure an allocation is scheduled.
+  Clock::advance(masterFlags.allocation_interval);
+
+  Clock::resume();
+
+  AWAIT_READY(offers2);
+  EXPECT_NE(0u, offers2.get().size());
+
+  Offer offer2 = offers2.get()[0];
+
+  // Start a long running task using the network isolator.
+  TaskInfo task2 = createTask(offer2, "sleep 1000");
+
+  EXPECT_CALL(sched, statusUpdate(_, _));
+
+  Future<Nothing> _statusUpdateAcknowledgement2 =
+    FUTURE_DISPATCH(_, &Slave::_statusUpdateAcknowledgement);
+
+  driver.launchTasks(offers2.get()[0].id(), {task2});
+
+  // Wait for the ACK to be checkpointed.
+  AWAIT_READY(_statusUpdateAcknowledgement2);
+
+  Stop(slave.get());
+  delete containerizer2.get();
+
+  Future<Nothing> _recover2 = FUTURE_DISPATCH(_, &Slave::_recover);
+
+  Future<SlaveReregisteredMessage> slaveReregisteredMessage2 =
+    FUTURE_PROTOBUF(SlaveReregisteredMessage(), _, _);
+
+  // Restart the slave with the network isolator. This is to verify
+  // the slave recovery case where one task is running with the
+  // network isolator and another task is running without it.
+  Try<MesosContainerizer*> containerizer3 =
+    MesosContainerizer::create(slaveFlags, true, &fetcher);
+
+  ASSERT_SOME(containerizer3);
+
+  slave = StartSlave(containerizer3.get(), slaveFlags);
+  ASSERT_SOME(slave);
+
+  Clock::pause();
+
+  AWAIT_READY(_recover2);
+
+  Clock::settle(); // Wait for slave to schedule reregister timeout.
+  Clock::advance(EXECUTOR_REREGISTER_TIMEOUT);
+
+  AWAIT_READY(slaveReregisteredMessage2);
+
+  Clock::resume();
+
+  // Ensure that both containers (with and without network isolation)
+  // were recovered.
+  Future<hashset<ContainerID>> containers = containerizer3.get()->containers();
+  AWAIT_READY(containers);
+  EXPECT_EQ(2u, containers.get().size());
+
+  foreach (const ContainerID& containerId, containers.get()) {
+    // Do some basic checks to make sure the network isolator can
+    // handle mixed types of containers correctly.
+    Future<ResourceStatistics> usage = containerizer3.get()->usage(containerId);
+    AWAIT_READY(usage);
+
+    // TODO(chzhcn): Write a more thorough test for update.
+  }
+
+  driver.stop();
+  driver.join();
+
+  Shutdown();
+  delete containerizer3.get();
+}
+
+
+// Test that all configurations (tc filters etc) is cleaned up for an
+// orphaned container using the network isolator.
+TEST_F(PortMappingMesosTest, CGROUPS_ROOT_CleanUpOrphan)
+{
+  Try<PID<Master> > master = StartMaster();
+  ASSERT_SOME(master);
+
+  slave::Flags flags = CreateSlaveFlags();
+
+  // NOTE: We add 'cgroups/cpu,cgroups/mem' to bypass MESOS-2554.
+  flags.isolation = "cgroups/cpu,cgroups/mem,network/port_mapping";
+
+  Try<PID<Slave> > slave = StartSlave(flags);
+  ASSERT_SOME(slave);
+
+  MockScheduler sched;
+
+  // Enable checkpointing for the framework.
+  FrameworkInfo frameworkInfo = DEFAULT_FRAMEWORK_INFO;
+  frameworkInfo.set_checkpoint(true);
+
+  MesosSchedulerDriver driver(
+      &sched, frameworkInfo, master.get(), DEFAULT_CREDENTIAL);
+
+  Future<FrameworkID> frameworkId;
+  EXPECT_CALL(sched, registered(_, _, _))
+    .WillOnce(FutureArg<1>(&frameworkId));
+
+  Future<vector<Offer> > offers;
+  EXPECT_CALL(sched, resourceOffers(_, _))
+    .WillOnce(FutureArg<1>(&offers))
+    .WillRepeatedly(DeclineOffers());      // Ignore subsequent offers.
+
+  driver.start();
+
+  AWAIT_READY(offers);
+  EXPECT_NE(0u, offers.get().size());
+
+  // Start a long running task using network islator.
+  TaskInfo task = createTask(offers.get()[0], "sleep 1000");
+
+  EXPECT_CALL(sched, statusUpdate(_, _));
+
+  Future<Nothing> _statusUpdateAcknowledgement =
+    FUTURE_DISPATCH(_, &Slave::_statusUpdateAcknowledgement);
+
+  driver.launchTasks(offers.get()[0].id(), {task});
+
+  // Wait for the ACK to be checkpointed.
+  AWAIT_READY(_statusUpdateAcknowledgement);
+
+  Stop(slave.get());
+
+  // Wipe the slave meta directory so that the slave will treat the
+  // above running task as an orphan.
+  ASSERT_SOME(os::rmdir(paths::getMetaRootDir(flags.work_dir)));
+
+  Future<SlaveRegisteredMessage> slaveRegisteredMessage =
+    FUTURE_PROTOBUF(SlaveRegisteredMessage(), _, _);
+
+  Future<Nothing> orphansDestroyed =
+    FUTURE_DISPATCH(_, &MesosContainerizerProcess::___recover);
+
+  // Restart the slave.
+  slave = StartSlave(flags);
+  ASSERT_SOME(slave);
+
+  AWAIT_READY(slaveRegisteredMessage);
+
+  AWAIT_READY(orphansDestroyed);
+
+  // Expect that qdiscs still exist on eth0 and lo but with no filters.
+  Try<bool> hostEth0ExistsQdisc = ingress::exists(eth0);
+  EXPECT_SOME_TRUE(hostEth0ExistsQdisc);
+
+  Try<bool> hostLoExistsQdisc = ingress::exists(lo);
+  EXPECT_SOME_TRUE(hostLoExistsQdisc);
+
+  Result<vector<ip::Classifier> > classifiers =
+    ip::classifiers(eth0, ingress::HANDLE);
+
+  EXPECT_SOME(classifiers);
+  EXPECT_EQ(0u, classifiers.get().size());
+
+  classifiers = ip::classifiers(lo, ingress::HANDLE);
+  EXPECT_SOME(classifiers);
+  EXPECT_EQ(0u, classifiers.get().size());
+
+  // Expect no 'veth' devices.
+  Try<set<string> > links = net::links();
+  ASSERT_SOME(links);
+  foreach (const string& name, links.get()) {
+    EXPECT_FALSE(strings::startsWith(name, slave::PORT_MAPPING_VETH_PREFIX()));
+  }
+
+  // Expect no files in bind mount directory.
+  Try<list<string> > files = os::ls(slave::PORT_MAPPING_BIND_MOUNT_ROOT());
+  ASSERT_SOME(files);
+  EXPECT_EQ(0u, files.get().size());
+
+  driver.stop();
+  driver.join();
+
+  Shutdown();
+}
+
+
+// This test verfies the creation and destroy of the network namespace
+// handle symlink. The symlink was introduced in 0.23.0.
+TEST_F(PortMappingMesosTest, ROOT_NetworkNamespaceHandleSymlink)
+{
+  Try<PID<Master> > master = StartMaster();
+  ASSERT_SOME(master);
+
+  slave::Flags flags = CreateSlaveFlags();
+  flags.isolation = "network/port_mapping";
+
+  Try<MesosContainerizer*> containerizer =
+    MesosContainerizer::create(flags, true, &fetcher);
+
+  ASSERT_SOME(containerizer);
+
+  Try<PID<Slave>> slave = StartSlave(containerizer.get(), flags);
+  ASSERT_SOME(slave);
+
+  MockScheduler sched;
+
+  MesosSchedulerDriver driver(
+      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+  EXPECT_CALL(sched, registered(_, _, _));
+
+  Future<vector<Offer> > offers;
+  EXPECT_CALL(sched, resourceOffers(_, _))
+    .WillOnce(FutureArg<1>(&offers))
+    .WillRepeatedly(DeclineOffers());      // Ignore subsequent offers.
+
+  driver.start();
+
+  AWAIT_READY(offers);
+  EXPECT_NE(0u, offers.get().size());
+
+  // Start a long running task using network islator.
+  TaskInfo task = createTask(offers.get()[0], "sleep 1000");
+
+  Future<TaskStatus> status1;
+  Future<TaskStatus> status2;
+  EXPECT_CALL(sched, statusUpdate(&driver, _))
+    .WillOnce(FutureArg<1>(&status1))
+    .WillOnce(FutureArg<1>(&status2))
+    .WillRepeatedly(Return());       // Ignore subsequent updates.
+
+  driver.launchTasks(offers.get()[0].id(), {task});
+
+  AWAIT_READY(status1);
+  EXPECT_EQ(task.task_id(), status1.get().task_id());
+  EXPECT_EQ(TASK_RUNNING, status1.get().state());
+
+  Future<hashset<ContainerID>> containers = containerizer.get()->containers();
+  AWAIT_READY(containers);
+  ASSERT_EQ(1u, containers.get().size());
+
+  ContainerID containerId = *(containers.get().begin());
+
+  const string symlink = path::join(
+      slave::PORT_MAPPING_BIND_MOUNT_SYMLINK_ROOT(),
+      stringify(containerId));
+
+  EXPECT_TRUE(os::exists(symlink));
+  EXPECT_TRUE(os::stat::islink(symlink));
+
+  Future<containerizer::Termination> termination =
+    containerizer.get()->wait(containerId);
+
+  driver.killTask(task.task_id());
+
+  AWAIT_READY(status2);
+  EXPECT_EQ(task.task_id(), status2.get().task_id());
+  EXPECT_EQ(TASK_KILLED, status2.get().state());
+
+  AWAIT_READY(termination);
+  EXPECT_FALSE(os::exists(symlink));
+
+  driver.stop();
+  driver.join();
+
+  Shutdown();
+
+  delete containerizer.get();
+}
+
+
+// This test verfies that the isolator is able to recover a mix of
+// known and unkonwn orphans. This is used to capture the regression
+// described in MESOS-2914.
+TEST_F(PortMappingMesosTest, CGROUPS_ROOT_RecoverMixedKnownAndUnKnownOrphans)
+{
+  Try<PID<Master>> master = StartMaster(CreateMasterFlags());
+  ASSERT_SOME(master);
+
+  slave::Flags flags = CreateSlaveFlags();
+  flags.isolation = "network/port_mapping";
+
+  Try<MesosContainerizer*> containerizer =
+    MesosContainerizer::create(flags, true, &fetcher);
+
+  ASSERT_SOME(containerizer);
+
+  Try<PID<Slave> > slave = StartSlave(containerizer.get(), flags);
+  ASSERT_SOME(slave);
+
+  MockScheduler sched;
+
+  FrameworkInfo frameworkInfo = DEFAULT_FRAMEWORK_INFO;
+  frameworkInfo.set_checkpoint(true);
+
+  MesosSchedulerDriver driver(
+      &sched, frameworkInfo, master.get(), DEFAULT_CREDENTIAL);
+
+  EXPECT_CALL(sched, registered(_, _, _));
+
+  Future<vector<Offer> > offers;
+  EXPECT_CALL(sched, resourceOffers(_, _))
+    .WillOnce(FutureArg<1>(&offers))
+    .WillRepeatedly(DeclineOffers());      // Ignore subsequent offers.
+
+  driver.start();
+
+  AWAIT_READY(offers);
+  EXPECT_NE(0u, offers.get().size());
+
+  Offer offer = offers.get()[0];
+
+  TaskInfo task1 = createTask(
+      offer.slave_id(),
+      Resources::parse("cpus:1;mem:64").get(),
+      "sleep 1000");
+
+  TaskInfo task2 = createTask(
+      offer.slave_id(),
+      Resources::parse("cpus:1;mem:64").get(),
+      "sleep 1000");
+
+  Future<TaskStatus> status1;
+  Future<TaskStatus> status2;
+  EXPECT_CALL(sched, statusUpdate(&driver, _))
+    .WillOnce(FutureArg<1>(&status1))
+    .WillOnce(FutureArg<1>(&status2))
+    .WillRepeatedly(Return());       // Ignore subsequent updates.
+
+  driver.launchTasks(offers.get()[0].id(), {task1, task2});
+
+  AWAIT_READY(status1);
+  ASSERT_EQ(TASK_RUNNING, status1.get().state());
+
+  AWAIT_READY(status2);
+  ASSERT_EQ(TASK_RUNNING, status2.get().state());
+
+  // Obtain the container IDs.
+  Future<hashset<ContainerID>> containers = containerizer.get()->containers();
+  AWAIT_READY(containers);
+  ASSERT_EQ(2u, containers.get().size());
+
+  Stop(slave.get());
+  delete containerizer.get();
+
+  // Wipe the slave meta directory so that the slave will treat the
+  // above running tasks as orphans.
+  ASSERT_SOME(os::rmdir(paths::getMetaRootDir(flags.work_dir)));
+
+  // Remove the network namespace symlink for one container so that it
+  // becomes an unknown orphan.
+  const ContainerID containerId = *(containers.get().begin());
+  const string symlink = path::join(
+      slave::PORT_MAPPING_BIND_MOUNT_SYMLINK_ROOT(),
+      stringify(containerId));
+
+  ASSERT_TRUE(os::exists(symlink));
+  ASSERT_TRUE(os::stat::islink(symlink));
+  ASSERT_SOME(os::rm(symlink));
+
+  Future<SlaveRegisteredMessage> slaveRegisteredMessage =
+    FUTURE_PROTOBUF(SlaveRegisteredMessage(), _, _);
+
+  Future<Nothing> knownOrphansDestroyed =
+    FUTURE_DISPATCH(_, &MesosContainerizerProcess::___recover);
+
+  // Restart the slave.
+  slave = StartSlave(flags);
+  ASSERT_SOME(slave);
+
+  AWAIT_READY(slaveRegisteredMessage);
+  AWAIT_READY(knownOrphansDestroyed);
+
+  // We settle the clock here to ensure that the processing of
+  // 'MesosContainerizerProcess::___destroy()' is complete and the
+  // metric is updated.
+  Clock::pause();
+  Clock::settle();
+  Clock::resume();
+
+  JSON::Object metrics = Metrics();
+  EXPECT_EQ(
+      0u,
+      metrics.values["containerizer/mesos/container_destroy_errors"]);
+}
+
+} // namespace tests {
+} // namespace internal {
+} // namespace mesos {


[05/12] mesos git commit: Moved containerizer related tests under src/tests/containerizer.

Posted by ji...@apache.org.
http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/docker_containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/docker_containerizer_tests.cpp b/src/tests/docker_containerizer_tests.cpp
deleted file mode 100644
index 80ed60e..0000000
--- a/src/tests/docker_containerizer_tests.cpp
+++ /dev/null
@@ -1,2955 +0,0 @@
-/**
- * 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 <process/gmock.hpp>
-#include <process/owned.hpp>
-#include <process/subprocess.hpp>
-
-#include <stout/duration.hpp>
-
-#include "linux/cgroups.hpp"
-
-#include "messages/messages.hpp"
-
-#include "tests/flags.hpp"
-#include "tests/mesos.hpp"
-
-#include "slave/containerizer/docker.hpp"
-#include "slave/containerizer/fetcher.hpp"
-
-#include "slave/paths.hpp"
-#include "slave/slave.hpp"
-#include "slave/state.hpp"
-
-using namespace mesos::internal::slave::paths;
-using namespace mesos::internal::slave::state;
-
-using namespace process;
-
-using mesos::internal::master::Master;
-
-using mesos::internal::slave::DockerContainerizer;
-using mesos::internal::slave::DockerContainerizerProcess;
-using mesos::internal::slave::Fetcher;
-using mesos::internal::slave::Slave;
-
-using process::Future;
-using process::Message;
-using process::PID;
-using process::UPID;
-
-using std::vector;
-using std::list;
-using std::string;
-
-using testing::_;
-using testing::DoAll;
-using testing::DoDefault;
-using testing::Eq;
-using testing::Invoke;
-using testing::Return;
-
-namespace mesos {
-namespace internal {
-namespace tests {
-
-
-class MockDocker : public Docker
-{
-public:
-  MockDocker(const string& path) : Docker(path)
-  {
-    EXPECT_CALL(*this, pull(_, _, _))
-      .WillRepeatedly(Invoke(this, &MockDocker::_pull));
-
-    EXPECT_CALL(*this, stop(_, _, _))
-      .WillRepeatedly(Invoke(this, &MockDocker::_stop));
-
-    EXPECT_CALL(*this, run(_, _, _, _, _, _, _, _, _))
-      .WillRepeatedly(Invoke(this, &MockDocker::_run));
-
-    EXPECT_CALL(*this, inspect(_, _))
-      .WillRepeatedly(Invoke(this, &MockDocker::_inspect));
-  }
-
-  MOCK_CONST_METHOD9(
-      run,
-      process::Future<Nothing>(
-          const mesos::ContainerInfo&,
-          const mesos::CommandInfo&,
-          const std::string&,
-          const std::string&,
-          const std::string&,
-          const Option<mesos::Resources>&,
-          const Option<std::map<std::string, std::string>>&,
-          const Option<std::string>&,
-          const Option<std::string>&));
-
-  MOCK_CONST_METHOD3(
-      pull,
-      process::Future<Docker::Image>(
-          const string&,
-          const string&,
-          bool));
-
-  MOCK_CONST_METHOD3(
-      stop,
-      process::Future<Nothing>(
-          const string&,
-          const Duration&,
-          bool));
-
-  MOCK_CONST_METHOD2(
-      inspect,
-      process::Future<Docker::Container>(
-          const string&,
-          const Option<Duration>&));
-
-  process::Future<Nothing> _run(
-      const mesos::ContainerInfo& containerInfo,
-      const mesos::CommandInfo& commandInfo,
-      const std::string& name,
-      const std::string& sandboxDirectory,
-      const std::string& mappedDirectory,
-      const Option<mesos::Resources>& resources,
-      const Option<std::map<std::string, std::string>>& env,
-      const Option<std::string>& stdoutPath,
-      const Option<std::string>& stderrPath) const
-  {
-    return Docker::run(
-        containerInfo,
-        commandInfo,
-        name,
-        sandboxDirectory,
-        mappedDirectory,
-        resources,
-        env,
-        stdoutPath,
-        stderrPath);
-  }
-
-  process::Future<Docker::Image> _pull(
-      const string& directory,
-      const string& image,
-      bool force) const
-  {
-    return Docker::pull(directory, image, force);
-  }
-
-  process::Future<Nothing> _stop(
-      const string& containerName,
-      const Duration& timeout,
-      bool remove) const
-  {
-    return Docker::stop(containerName, timeout, remove);
-  }
-
-  process::Future<Docker::Container> _inspect(
-      const string& containerName,
-      const Option<Duration>& retryInterval)
-  {
-    return Docker::inspect(containerName, retryInterval);
-  }
-};
-
-
-class DockerContainerizerTest : public MesosTest
-{
-public:
-  static string containerName(
-      const SlaveID& slaveId,
-      const ContainerID& containerId)
-  {
-    return slave::DOCKER_NAME_PREFIX + slaveId.value() +
-      slave::DOCKER_NAME_SEPERATOR + containerId.value();
-  }
-
-  enum ContainerState {
-    EXISTS,
-    RUNNING
-  };
-
-  static bool exists(
-      const process::Shared<Docker>& docker,
-      const SlaveID& slaveId,
-      const ContainerID& containerId,
-      ContainerState state = ContainerState::EXISTS)
-  {
-    Duration waited = Duration::zero();
-    string expectedName = containerName(slaveId, containerId);
-
-    do {
-      Future<Docker::Container> inspect = docker->inspect(expectedName);
-
-      if (!inspect.await(Seconds(3))) {
-        return false;
-      }
-
-      if (inspect.isReady()) {
-        switch (state) {
-          case ContainerState::RUNNING:
-            if (inspect.get().pid.isSome()) {
-              return true;
-            }
-            // Retry looking for running pid until timeout.
-            break;
-          case ContainerState::EXISTS:
-            return true;
-        }
-      }
-
-      os::sleep(Milliseconds(200));
-      waited += Milliseconds(200);
-    } while (waited < Seconds(5));
-
-    return false;
-  }
-
-  static bool containsLine(
-    const vector<string>& lines,
-    const string& expectedLine)
-  {
-    foreach (const string& line, lines) {
-      if (line == expectedLine) {
-        return true;
-      }
-    }
-
-    return false;
-  }
-
-  virtual void TearDown()
-  {
-    Try<Docker*> docker = Docker::create(tests::flags.docker, false);
-    ASSERT_SOME(docker);
-    Future<list<Docker::Container>> containers =
-      docker.get()->ps(true, slave::DOCKER_NAME_PREFIX);
-
-    AWAIT_READY(containers);
-
-    // Cleanup all mesos launched containers.
-    foreach (const Docker::Container& container, containers.get()) {
-      AWAIT_READY_FOR(docker.get()->rm(container.id, true), Seconds(30));
-    }
-
-    delete docker.get();
-  }
-};
-
-
-class MockDockerContainerizer : public DockerContainerizer {
-public:
-  MockDockerContainerizer(
-      const slave::Flags& flags,
-      Fetcher* fetcher,
-      Shared<Docker> docker)
-    : DockerContainerizer(flags, fetcher, docker)
-  {
-    initialize();
-  }
-
-  MockDockerContainerizer(const Owned<DockerContainerizerProcess>& process)
-    : DockerContainerizer(process)
-  {
-    initialize();
-  }
-
-  void initialize()
-  {
-    // NOTE: See TestContainerizer::setup for why we use
-    // 'EXPECT_CALL' and 'WillRepeatedly' here instead of
-    // 'ON_CALL' and 'WillByDefault'.
-    EXPECT_CALL(*this, launch(_, _, _, _, _, _, _))
-      .WillRepeatedly(Invoke(this, &MockDockerContainerizer::_launchExecutor));
-
-    EXPECT_CALL(*this, launch(_, _, _, _, _, _, _, _))
-      .WillRepeatedly(Invoke(this, &MockDockerContainerizer::_launch));
-
-    EXPECT_CALL(*this, update(_, _))
-      .WillRepeatedly(Invoke(this, &MockDockerContainerizer::_update));
-  }
-
-  MOCK_METHOD7(
-      launch,
-      process::Future<bool>(
-          const ContainerID&,
-          const ExecutorInfo&,
-          const std::string&,
-          const Option<std::string>&,
-          const SlaveID&,
-          const process::PID<slave::Slave>&,
-          bool checkpoint));
-
-  MOCK_METHOD8(
-      launch,
-      process::Future<bool>(
-          const ContainerID&,
-          const TaskInfo&,
-          const ExecutorInfo&,
-          const std::string&,
-          const Option<std::string>&,
-          const SlaveID&,
-          const process::PID<slave::Slave>&,
-          bool checkpoint));
-
-  MOCK_METHOD2(
-      update,
-      process::Future<Nothing>(
-          const ContainerID&,
-          const Resources&));
-
-  // Default 'launch' implementation (necessary because we can't just
-  // use &DockerContainerizer::launch with 'Invoke').
-  process::Future<bool> _launch(
-      const ContainerID& containerId,
-      const TaskInfo& taskInfo,
-      const ExecutorInfo& executorInfo,
-      const string& directory,
-      const Option<string>& user,
-      const SlaveID& slaveId,
-      const PID<Slave>& slavePid,
-      bool checkpoint)
-  {
-    return DockerContainerizer::launch(
-        containerId,
-        taskInfo,
-        executorInfo,
-        directory,
-        user,
-        slaveId,
-        slavePid,
-        checkpoint);
-  }
-
-  process::Future<bool> _launchExecutor(
-      const ContainerID& containerId,
-      const ExecutorInfo& executorInfo,
-      const string& directory,
-      const Option<string>& user,
-      const SlaveID& slaveId,
-      const PID<Slave>& slavePid,
-      bool checkpoint)
-  {
-    return DockerContainerizer::launch(
-        containerId,
-        executorInfo,
-        directory,
-        user,
-        slaveId,
-        slavePid,
-        checkpoint);
-  }
-
-  process::Future<Nothing> _update(
-      const ContainerID& containerId,
-      const Resources& resources)
-  {
-    return DockerContainerizer::update(
-        containerId,
-        resources);
-  }
-};
-
-
-class MockDockerContainerizerProcess : public DockerContainerizerProcess
-{
-public:
-  MockDockerContainerizerProcess(
-      const slave::Flags& flags,
-      Fetcher* fetcher,
-      const Shared<Docker>& docker)
-    : DockerContainerizerProcess(flags, fetcher, docker)
-  {
-    EXPECT_CALL(*this, fetch(_, _))
-      .WillRepeatedly(Invoke(this, &MockDockerContainerizerProcess::_fetch));
-
-    EXPECT_CALL(*this, pull(_))
-      .WillRepeatedly(Invoke(this, &MockDockerContainerizerProcess::_pull));
-  }
-
-  MOCK_METHOD2(
-      fetch,
-      process::Future<Nothing>(
-          const ContainerID& containerId,
-          const SlaveID& slaveId));
-
-  MOCK_METHOD1(
-      pull,
-      process::Future<Nothing>(const ContainerID& containerId));
-
-  process::Future<Nothing> _fetch(
-      const ContainerID& containerId,
-      const SlaveID& slaveId)
-  {
-    return DockerContainerizerProcess::fetch(containerId, slaveId);
-  }
-
-  process::Future<Nothing> _pull(const ContainerID& containerId)
-  {
-    return DockerContainerizerProcess::pull(containerId);
-  }
-};
-
-
-// Only enable executor launch on linux as other platforms
-// requires running linux VM and need special port forwarding
-// to get host networking to work.
-#ifdef __linux__
-TEST_F(DockerContainerizerTest, ROOT_DOCKER_Launch_Executor)
-{
-  Try<PID<Master> > master = StartMaster();
-  ASSERT_SOME(master);
-
-  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
-  Shared<Docker> docker(mockDocker);
-
-  slave::Flags flags = CreateSlaveFlags();
-
-  Fetcher fetcher;
-
-  MockDockerContainerizer dockerContainerizer(flags, &fetcher, docker);
-
-  Try<PID<Slave> > slave = StartSlave(&dockerContainerizer, flags);
-  ASSERT_SOME(slave);
-
-  MockScheduler sched;
-  MesosSchedulerDriver driver(
-    &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
-
-  Future<FrameworkID> frameworkId;
-  EXPECT_CALL(sched, registered(&driver, _, _))
-    .WillOnce(FutureArg<1>(&frameworkId));
-
-  Future<vector<Offer> > offers;
-  EXPECT_CALL(sched, resourceOffers(&driver, _))
-    .WillOnce(FutureArg<1>(&offers))
-    .WillRepeatedly(Return()); // Ignore subsequent offers.
-
-  driver.start();
-
-  AWAIT_READY(frameworkId);
-
-  AWAIT_READY(offers);
-  ASSERT_NE(0u, offers.get().size());
-
-  const Offer& offer = offers.get()[0];
-
-  SlaveID slaveId = offer.slave_id();
-
-  TaskInfo task;
-  task.set_name("");
-  task.mutable_task_id()->set_value("1");
-  task.mutable_slave_id()->CopyFrom(offer.slave_id());
-  task.mutable_resources()->CopyFrom(offer.resources());
-
-  ExecutorInfo executorInfo;
-  ExecutorID executorId;
-  executorId.set_value("e1");
-  executorInfo.mutable_executor_id()->CopyFrom(executorId);
-
-  CommandInfo command;
-  command.set_value("/bin/test-executor");
-  executorInfo.mutable_command()->CopyFrom(command);
-
-  ContainerInfo containerInfo;
-  containerInfo.set_type(ContainerInfo::DOCKER);
-
-  // TODO(tnachen): Use local image to test if possible.
-  ContainerInfo::DockerInfo dockerInfo;
-  dockerInfo.set_image("tnachen/test-executor");
-
-  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
-  executorInfo.mutable_container()->CopyFrom(containerInfo);
-
-  task.mutable_executor()->CopyFrom(executorInfo);
-
-  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(), {task});
-
-  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());
-
-  ASSERT_TRUE(exists(docker, slaveId, containerId.get()));
-
-  Future<containerizer::Termination> termination =
-    dockerContainerizer.wait(containerId.get());
-
-  driver.stop();
-  driver.join();
-
-  AWAIT_READY(termination);
-
-  ASSERT_FALSE(
-    exists(docker, slaveId, containerId.get(), ContainerState::RUNNING));
-
-  Shutdown();
-}
-
-
-// This test verifies that a custom executor can be launched and
-// registered with the slave with docker bridge network enabled.
-// We're assuming that the custom executor is registering it's public
-// ip instead of 0.0.0.0 or equivelent to the slave as that's the
-// default behavior for libprocess.
-TEST_F(DockerContainerizerTest, ROOT_DOCKER_Launch_Executor_Bridged)
-{
-  Try<PID<Master> > master = StartMaster();
-  ASSERT_SOME(master);
-
-  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
-  Shared<Docker> docker(mockDocker);
-
-  slave::Flags flags = CreateSlaveFlags();
-
-  Fetcher fetcher;
-
-  MockDockerContainerizer dockerContainerizer(flags, &fetcher, docker);
-
-  Try<PID<Slave> > slave = StartSlave(&dockerContainerizer, flags);
-  ASSERT_SOME(slave);
-
-  MockScheduler sched;
-  MesosSchedulerDriver driver(
-    &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
-
-  Future<FrameworkID> frameworkId;
-  EXPECT_CALL(sched, registered(&driver, _, _))
-    .WillOnce(FutureArg<1>(&frameworkId));
-
-  Future<vector<Offer> > offers;
-  EXPECT_CALL(sched, resourceOffers(&driver, _))
-    .WillOnce(FutureArg<1>(&offers))
-    .WillRepeatedly(Return()); // Ignore subsequent offers.
-
-  driver.start();
-
-  AWAIT_READY(frameworkId);
-
-  AWAIT_READY(offers);
-  ASSERT_NE(0u, offers.get().size());
-
-  const Offer& offer = offers.get()[0];
-
-  SlaveID slaveId = offer.slave_id();
-
-  TaskInfo task;
-  task.set_name("");
-  task.mutable_task_id()->set_value("1");
-  task.mutable_slave_id()->CopyFrom(offer.slave_id());
-  task.mutable_resources()->CopyFrom(offer.resources());
-
-  ExecutorInfo executorInfo;
-  ExecutorID executorId;
-  executorId.set_value("e1");
-  executorInfo.mutable_executor_id()->CopyFrom(executorId);
-
-  CommandInfo command;
-  command.set_value("/bin/test-executor");
-  executorInfo.mutable_command()->CopyFrom(command);
-
-  ContainerInfo containerInfo;
-  containerInfo.set_type(ContainerInfo::DOCKER);
-
-  // TODO(tnachen): Use local image to test if possible.
-  ContainerInfo::DockerInfo dockerInfo;
-  dockerInfo.set_image("tnachen/test-executor");
-  dockerInfo.set_network(ContainerInfo::DockerInfo::BRIDGE);
-
-  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
-  executorInfo.mutable_container()->CopyFrom(containerInfo);
-
-  task.mutable_executor()->CopyFrom(executorInfo);
-
-  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(), {task});
-
-  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());
-
-  ASSERT_TRUE(exists(docker, slaveId, containerId.get()));
-
-  Future<containerizer::Termination> termination =
-    dockerContainerizer.wait(containerId.get());
-
-  driver.stop();
-  driver.join();
-
-  AWAIT_READY(termination);
-
-  ASSERT_FALSE(
-    exists(docker, slaveId, containerId.get(), ContainerState::RUNNING));
-
-  Shutdown();
-}
-#endif // __linux__
-
-
-TEST_F(DockerContainerizerTest, ROOT_DOCKER_Launch)
-{
-  Try<PID<Master> > master = StartMaster();
-  ASSERT_SOME(master);
-
-  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
-  Shared<Docker> docker(mockDocker);
-
-  slave::Flags flags = CreateSlaveFlags();
-
-  Fetcher fetcher;
-
-  MockDockerContainerizer dockerContainerizer(flags, &fetcher, docker);
-
-  Try<PID<Slave> > slave = StartSlave(&dockerContainerizer, flags);
-  ASSERT_SOME(slave);
-
-  MockScheduler sched;
-  MesosSchedulerDriver driver(
-      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
-
-  Future<FrameworkID> frameworkId;
-  EXPECT_CALL(sched, registered(&driver, _, _))
-    .WillOnce(FutureArg<1>(&frameworkId));
-
-  Future<vector<Offer> > offers;
-  EXPECT_CALL(sched, resourceOffers(&driver, _))
-    .WillOnce(FutureArg<1>(&offers))
-    .WillRepeatedly(Return()); // Ignore subsequent offers.
-
-  driver.start();
-
-  AWAIT_READY(frameworkId);
-
-  AWAIT_READY(offers);
-  ASSERT_NE(0u, offers.get().size());
-
-  const Offer& offer = offers.get()[0];
-
-  SlaveID slaveId = offer.slave_id();
-
-  TaskInfo task;
-  task.set_name("");
-  task.mutable_task_id()->set_value("1");
-  task.mutable_slave_id()->CopyFrom(offer.slave_id());
-  task.mutable_resources()->CopyFrom(offer.resources());
-
-  CommandInfo command;
-  command.set_value("sleep 1000");
-
-  ContainerInfo containerInfo;
-  containerInfo.set_type(ContainerInfo::DOCKER);
-
-  // TODO(tnachen): Use local image to test if possible.
-  ContainerInfo::DockerInfo dockerInfo;
-  dockerInfo.set_image("busybox");
-  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
-
-  task.mutable_command()->CopyFrom(command);
-  task.mutable_container()->CopyFrom(containerInfo);
-
-  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))
-    .WillRepeatedly(DoDefault());
-
-  driver.launchTasks(offers.get()[0].id(), {task});
-
-  AWAIT_READY_FOR(containerId, Seconds(60));
-  AWAIT_READY_FOR(statusRunning, Seconds(60));
-  EXPECT_EQ(TASK_RUNNING, statusRunning.get().state());
-  ASSERT_TRUE(statusRunning.get().has_data());
-
-  Try<JSON::Array> parse = JSON::parse<JSON::Array>(statusRunning.get().data());
-  ASSERT_SOME(parse);
-
-  // Now verify that the Docker.NetworkSettings.IPAddress label is
-  // present.
-  ASSERT_TRUE(statusRunning.get().has_labels());
-  EXPECT_EQ(1, statusRunning.get().labels().labels().size());
-  EXPECT_EQ("Docker.NetworkSettings.IPAddress",
-            statusRunning.get().labels().labels(0).key());
-
-  ASSERT_TRUE(exists(docker, slaveId, containerId.get()));
-
-  Future<containerizer::Termination> termination =
-    dockerContainerizer.wait(containerId.get());
-
-  driver.stop();
-  driver.join();
-
-  AWAIT_READY(termination);
-
-  ASSERT_FALSE(
-    exists(docker, slaveId, containerId.get(), ContainerState::RUNNING));
-
-  Shutdown();
-}
-
-
-TEST_F(DockerContainerizerTest, ROOT_DOCKER_Kill)
-{
-  Try<PID<Master> > master = StartMaster();
-  ASSERT_SOME(master);
-
-  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
-  Shared<Docker> docker(mockDocker);
-
-  slave::Flags flags = CreateSlaveFlags();
-
-  Fetcher fetcher;
-
-  MockDockerContainerizer dockerContainerizer(flags, &fetcher, docker);
-
-  Try<PID<Slave> > slave = StartSlave(&dockerContainerizer, flags);
-  ASSERT_SOME(slave);
-
-  MockScheduler sched;
-  MesosSchedulerDriver driver(
-      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
-
-  Future<FrameworkID> frameworkId;
-  EXPECT_CALL(sched, registered(&driver, _, _))
-    .WillOnce(FutureArg<1>(&frameworkId));
-
-  Future<vector<Offer> > offers;
-  EXPECT_CALL(sched, resourceOffers(&driver, _))
-    .WillOnce(FutureArg<1>(&offers))
-    .WillRepeatedly(Return()); // Ignore subsequent offers.
-
-  driver.start();
-
-  AWAIT_READY(frameworkId);
-
-  AWAIT_READY(offers);
-  ASSERT_NE(0u, offers.get().size());
-
-  const Offer& offer = offers.get()[0];
-
-  SlaveID slaveId = offer.slave_id();
-
-  TaskInfo task;
-  task.set_name("");
-  task.mutable_task_id()->set_value("1");
-  task.mutable_slave_id()->CopyFrom(offer.slave_id());
-  task.mutable_resources()->CopyFrom(offer.resources());
-
-  CommandInfo command;
-  command.set_value("sleep 1000");
-
-  ContainerInfo containerInfo;
-  containerInfo.set_type(ContainerInfo::DOCKER);
-
-  // TODO(tnachen): Use local image to test if possible.
-  ContainerInfo::DockerInfo dockerInfo;
-  dockerInfo.set_image("busybox");
-  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
-
-  task.mutable_command()->CopyFrom(command);
-  task.mutable_container()->CopyFrom(containerInfo);
-
-  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(), {task});
-
-  AWAIT_READY_FOR(containerId, Seconds(60));
-  AWAIT_READY_FOR(statusRunning, Seconds(60));
-  EXPECT_EQ(TASK_RUNNING, statusRunning.get().state());
-
-  ASSERT_TRUE(
-    exists(docker, slaveId, containerId.get(), ContainerState::RUNNING));
-
-  Future<TaskStatus> statusKilled;
-  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);
-
-  ASSERT_FALSE(
-    exists(docker, slaveId, containerId.get(), ContainerState::RUNNING));
-
-  driver.stop();
-  driver.join();
-
-  Shutdown();
-}
-
-
-// This test tests DockerContainerizer::usage().
-TEST_F(DockerContainerizerTest, ROOT_DOCKER_Usage)
-{
-  Try<PID<Master> > master = StartMaster();
-  ASSERT_SOME(master);
-
-  slave::Flags flags = CreateSlaveFlags();
-  flags.resources = Option<string>("cpus:2;mem:1024");
-
-  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
-  Shared<Docker> docker(mockDocker);
-
-  Fetcher fetcher;
-
-  MockDockerContainerizer dockerContainerizer(flags, &fetcher, docker);
-
-  Try<PID<Slave> > slave = StartSlave(&dockerContainerizer, flags);
-  ASSERT_SOME(slave);
-
-  MockScheduler sched;
-  MesosSchedulerDriver driver(
-      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
-
-  Future<FrameworkID> frameworkId;
-  EXPECT_CALL(sched, registered(&driver, _, _))
-    .WillOnce(FutureArg<1>(&frameworkId));
-
-  Future<vector<Offer> > offers;
-  EXPECT_CALL(sched, resourceOffers(&driver, _))
-    .WillOnce(FutureArg<1>(&offers))
-    .WillRepeatedly(Return()); // Ignore subsequent offers.
-
-  driver.start();
-
-  AWAIT_READY(frameworkId);
-
-  AWAIT_READY(offers);
-  ASSERT_NE(0u, offers.get().size());
-
-  const Offer& offer = offers.get()[0];
-
-  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;
-  // Run a CPU intensive command, so we can measure utime and stime later.
-  command.set_value("dd if=/dev/zero of=/dev/null");
-
-  ContainerInfo containerInfo;
-  containerInfo.set_type(ContainerInfo::DOCKER);
-
-  // TODO(tnachen): Use local image to test if possible.
-  ContainerInfo::DockerInfo dockerInfo;
-  dockerInfo.set_image("busybox");
-  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
-
-  task.mutable_command()->CopyFrom(command);
-  task.mutable_container()->CopyFrom(containerInfo);
-
-  Future<ContainerID> containerId;
-  EXPECT_CALL(dockerContainerizer, launch(_, _, _, _, _, _, _, _))
-    .WillOnce(DoAll(FutureArg<0>(&containerId),
-                    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))
-    .WillRepeatedly(DoDefault());
-
-  driver.launchTasks(offers.get()[0].id(), {task});
-
-  AWAIT_READY_FOR(containerId, Seconds(60));
-  AWAIT_READY_FOR(statusRunning, Seconds(60));
-  EXPECT_EQ(TASK_RUNNING, statusRunning.get().state());
-
-  // Verify the usage.
-  ResourceStatistics statistics;
-  Duration waited = Duration::zero();
-  do {
-    Future<ResourceStatistics> usage =
-      dockerContainerizer.usage(containerId.get());
-    // TODO(tnachen): Replace await with AWAIT_COMPLETED once
-    // implemented.
-    ASSERT_TRUE(usage.await(Seconds(3)));
-
-    if (usage.isReady()) {
-      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));
-
-  // Usage includes the executor resources.
-  EXPECT_EQ(2.0 + slave::DEFAULT_EXECUTOR_CPUS, statistics.cpus_limit());
-  EXPECT_EQ((Gigabytes(1) + slave::DEFAULT_EXECUTOR_MEM).bytes(),
-            statistics.mem_limit_bytes());
-  EXPECT_LT(0, statistics.cpus_user_time_secs());
-  EXPECT_LT(0, statistics.cpus_system_time_secs());
-  EXPECT_GT(statistics.mem_rss_bytes(), 0u);
-
-  Future<containerizer::Termination> termination =
-    dockerContainerizer.wait(containerId.get());
-
-  dockerContainerizer.destroy(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();
-  driver.join();
-
-  Shutdown();
-}
-
-
-#ifdef __linux__
-TEST_F(DockerContainerizerTest, ROOT_DOCKER_Update)
-{
-  Try<PID<Master> > master = StartMaster();
-  ASSERT_SOME(master);
-
-  slave::Flags flags = CreateSlaveFlags();
-
-  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
-  Shared<Docker> docker(mockDocker);
-
-  Fetcher fetcher;
-
-  MockDockerContainerizer dockerContainerizer(flags, &fetcher, docker);
-
-  Try<PID<Slave> > slave = StartSlave(&dockerContainerizer, flags);
-  ASSERT_SOME(slave);
-
-  MockScheduler sched;
-  MesosSchedulerDriver driver(
-      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
-
-  Future<FrameworkID> frameworkId;
-  EXPECT_CALL(sched, registered(&driver, _, _))
-    .WillOnce(FutureArg<1>(&frameworkId));
-
-  Future<vector<Offer> > offers;
-  EXPECT_CALL(sched, resourceOffers(&driver, _))
-    .WillOnce(FutureArg<1>(&offers))
-    .WillRepeatedly(Return()); // Ignore subsequent offers.
-
-  driver.start();
-
-  AWAIT_READY(frameworkId);
-
-  AWAIT_READY(offers);
-  ASSERT_NE(0u, offers.get().size());
-
-  const Offer& offer = offers.get()[0];
-
-  SlaveID slaveId = offer.slave_id();
-
-  TaskInfo task;
-  task.set_name("");
-  task.mutable_task_id()->set_value("1");
-  task.mutable_slave_id()->CopyFrom(offer.slave_id());
-  task.mutable_resources()->CopyFrom(offer.resources());
-
-  CommandInfo command;
-  command.set_value("sleep 1000");
-
-  ContainerInfo containerInfo;
-  containerInfo.set_type(ContainerInfo::DOCKER);
-
-  // TODO(tnachen): Use local image to test if possible.
-  ContainerInfo::DockerInfo dockerInfo;
-  dockerInfo.set_image("busybox");
-  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
-
-  task.mutable_command()->CopyFrom(command);
-  task.mutable_container()->CopyFrom(containerInfo);
-
-  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))
-    .WillRepeatedly(DoDefault());
-
-  driver.launchTasks(offers.get()[0].id(), {task});
-
-  AWAIT_READY(containerId);
-
-  AWAIT_READY_FOR(statusRunning, Seconds(60));
-  EXPECT_EQ(TASK_RUNNING, statusRunning.get().state());
-
-  ASSERT_TRUE(
-    exists(docker, slaveId, containerId.get(), ContainerState::RUNNING));
-
-  string name = containerName(slaveId, containerId.get());
-
-  Future<Docker::Container> inspect = docker->inspect(name);
-
-  AWAIT_READY(inspect);
-
-  Try<Resources> newResources = Resources::parse("cpus:1;mem:128");
-
-  ASSERT_SOME(newResources);
-
-  Future<Nothing> update =
-    dockerContainerizer.update(containerId.get(), newResources.get());
-
-  AWAIT_READY(update);
-
-  Result<string> cpuHierarchy = cgroups::hierarchy("cpu");
-  Result<string> memoryHierarchy = cgroups::hierarchy("memory");
-
-  ASSERT_SOME(cpuHierarchy);
-  ASSERT_SOME(memoryHierarchy);
-
-  Option<pid_t> pid = inspect.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(
-      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());
-
-  newResources = Resources::parse("cpus:1;mem:144");
-
-  // Issue second update that uses the cached pid instead of inspect.
-  update = dockerContainerizer.update(containerId.get(), newResources.get());
-
-  AWAIT_READY(update);
-
-  cpu = cgroups::cpu::shares(cpuHierarchy.get(), cpuCgroup.get());
-
-  ASSERT_SOME(cpu);
-
-  mem = cgroups::memory::soft_limit_in_bytes(
-      memoryHierarchy.get(),
-      memoryCgroup.get());
-
-  ASSERT_SOME(mem);
-
-  EXPECT_EQ(1024u, cpu.get());
-  EXPECT_EQ(144u, mem.get().megabytes());
-
-  driver.stop();
-  driver.join();
-
-  Shutdown();
-}
-#endif //__linux__
-
-
-TEST_F(DockerContainerizerTest, ROOT_DOCKER_Recover)
-{
-  slave::Flags flags = CreateSlaveFlags();
-
-  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
-  Shared<Docker> docker(mockDocker);
-
-  Future<string> stoppedContainer;
-  EXPECT_CALL(*mockDocker, stop(_, _, _))
-    .WillOnce(DoAll(FutureArg<0>(&stoppedContainer),
-                    Return(Nothing())));
-
-  Fetcher fetcher;
-
-  MockDockerContainerizer dockerContainerizer(flags, &fetcher, docker);
-
-  SlaveID slaveId;
-  slaveId.set_value("s1");
-  ContainerID containerId;
-  containerId.set_value("c1");
-  ContainerID reapedContainerId;
-  reapedContainerId.set_value("c2");
-
-  string container1 = containerName(slaveId, containerId);
-  string container2 = containerName(slaveId, reapedContainerId);
-
-  // Clean up artifacts if containers still exists.
-  ASSERT_TRUE(docker->rm(container1, true).await(Seconds(30)));
-  ASSERT_TRUE(docker->rm(container2, true).await(Seconds(30)));
-
-  Resources resources = Resources::parse("cpus:1;mem:512").get();
-
-  ContainerInfo containerInfo;
-  containerInfo.set_type(ContainerInfo::DOCKER);
-
-  // TODO(tnachen): Use local image to test if possible.
-  ContainerInfo::DockerInfo dockerInfo;
-  dockerInfo.set_image("busybox");
-  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
-
-  CommandInfo commandInfo;
-  commandInfo.set_value("sleep 1000");
-
-  Future<Nothing> d1 =
-    docker->run(
-        containerInfo,
-        commandInfo,
-        container1,
-        flags.work_dir,
-        flags.sandbox_directory,
-        resources);
-
-  Future<Nothing> d2 =
-    docker->run(
-        containerInfo,
-        commandInfo,
-        container2,
-        flags.work_dir,
-        flags.sandbox_directory,
-        resources);
-
-  ASSERT_TRUE(
-    exists(docker, slaveId, containerId, ContainerState::RUNNING));
-  ASSERT_TRUE(
-    exists(docker, slaveId, reapedContainerId, ContainerState::RUNNING));
-
-  Future<Docker::Container> inspect = docker->inspect(container2);
-  AWAIT_READY(inspect);
-
-  SlaveState slaveState;
-  slaveState.id = slaveId;
-  FrameworkState frameworkState;
-
-  ExecutorID execId;
-  execId.set_value("e1");
-
-  ExecutorState execState;
-  ExecutorInfo execInfo;
-  execState.info = execInfo;
-  execState.latest = containerId;
-
-  Try<process::Subprocess> wait =
-    process::subprocess(tests::flags.docker + " wait " + container1);
-
-  ASSERT_SOME(wait);
-
-  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));
-
-  AWAIT_EQ(inspect.get().id, stoppedContainer);
-
-  Shutdown();
-}
-
-
-// This test checks the docker containerizer doesn't recover executors
-// that were started by another containerizer (e.g: mesos).
-TEST_F(DockerContainerizerTest, ROOT_DOCKER_SkipRecoverNonDocker)
-{
-  slave::Flags flags = CreateSlaveFlags();
-
-  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
-  Shared<Docker> docker(mockDocker);
-
-  Fetcher fetcher;
-
-  MockDockerContainerizer dockerContainerizer(flags, &fetcher, docker);
-
-  ContainerID containerId;
-  containerId.set_value("c1");
-  ContainerID reapedContainerId;
-  reapedContainerId.set_value("c2");
-
-  ExecutorID executorId;
-  executorId.set_value(UUID::random().toString());
-
-  ExecutorInfo executorInfo;
-  executorInfo.mutable_container()->set_type(ContainerInfo::MESOS);
-
-  ExecutorState executorState;
-  executorState.info = executorInfo;
-  executorState.latest = containerId;
-
-  RunState runState;
-  runState.id = containerId;
-  executorState.runs.put(containerId, runState);
-
-  FrameworkState frameworkState;
-  frameworkState.executors.put(executorId, executorState);
-
-  SlaveState slaveState;
-  FrameworkID frameworkId;
-  frameworkId.set_value(UUID::random().toString());
-  slaveState.frameworks.put(frameworkId, frameworkState);
-
-  Future<Nothing> recover = dockerContainerizer.recover(slaveState);
-  AWAIT_READY(recover);
-
-  Future<hashset<ContainerID>> containers = dockerContainerizer.containers();
-  AWAIT_READY(containers);
-
-  // A MesosContainerizer task shouldn't be recovered by
-  // DockerContainerizer.
-  EXPECT_EQ(0u, containers.get().size());
-}
-
-
-TEST_F(DockerContainerizerTest, ROOT_DOCKER_Logs)
-{
-  Try<PID<Master> > master = StartMaster();
-  ASSERT_SOME(master);
-
-  slave::Flags flags = CreateSlaveFlags();
-
-  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
-  Shared<Docker> docker(mockDocker);
-
-  // We skip stopping the docker container because stopping a container
-  // even when it terminated might not flush the logs and we end up
-  // not getting stdout/stderr in our tests.
-  EXPECT_CALL(*mockDocker, stop(_, _, _))
-    .WillRepeatedly(Return(Nothing()));
-
-  Fetcher fetcher;
-
-  MockDockerContainerizer dockerContainerizer(flags, &fetcher, docker);
-
-  Try<PID<Slave> > slave = StartSlave(&dockerContainerizer, flags);
-  ASSERT_SOME(slave);
-
-  MockScheduler sched;
-  MesosSchedulerDriver driver(
-      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
-
-  Future<FrameworkID> frameworkId;
-  EXPECT_CALL(sched, registered(&driver, _, _))
-    .WillOnce(FutureArg<1>(&frameworkId));
-
-  Future<vector<Offer> > offers;
-  EXPECT_CALL(sched, resourceOffers(&driver, _))
-    .WillOnce(FutureArg<1>(&offers))
-    .WillRepeatedly(Return()); // Ignore subsequent offers.
-
-  driver.start();
-
-  AWAIT_READY(frameworkId);
-
-  AWAIT_READY(offers);
-  ASSERT_NE(0u, offers.get().size());
-
-  const Offer& offer = offers.get()[0];
-
-  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());
-
-  string uuid = UUID::random().toString();
-
-  CommandInfo command;
-  command.set_value("echo out" + uuid + " ; echo err" + uuid + " 1>&2");
-
-  ContainerInfo containerInfo;
-  containerInfo.set_type(ContainerInfo::DOCKER);
-
-  // TODO(tnachen): Use local image to test if possible.
-  ContainerInfo::DockerInfo dockerInfo;
-  dockerInfo.set_image("busybox");
-  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
-
-  task.mutable_command()->CopyFrom(command);
-  task.mutable_container()->CopyFrom(containerInfo);
-
-  Future<ContainerID> containerId;
-  Future<string> directory;
-  EXPECT_CALL(dockerContainerizer, launch(_, _, _, _, _, _, _, _))
-    .WillOnce(DoAll(FutureArg<0>(&containerId),
-                    FutureArg<3>(&directory),
-                    Invoke(&dockerContainerizer,
-                           &MockDockerContainerizer::_launch)));
-
-  Future<TaskStatus> statusRunning;
-  Future<TaskStatus> statusFinished;
-  EXPECT_CALL(sched, statusUpdate(&driver, _))
-    .WillOnce(FutureArg<1>(&statusRunning))
-    .WillOnce(FutureArg<1>(&statusFinished))
-    .WillRepeatedly(DoDefault());
-
-  driver.launchTasks(offers.get()[0].id(), {task});
-
-  AWAIT_READY_FOR(containerId, Seconds(60));
-  AWAIT_READY(directory);
-  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());
-
-  // Now check that the proper output is in stderr and stdout (which
-  // might also contain other things, hence the use of a UUID).
-  Try<string> read = os::read(path::join(directory.get(), "stderr"));
-  ASSERT_SOME(read);
-
-  vector<string> lines = strings::split(read.get(), "\n");
-
-  EXPECT_TRUE(containsLine(lines, "err" + uuid));
-  EXPECT_FALSE(containsLine(lines, "out" + uuid));
-
-  read = os::read(path::join(directory.get(), "stdout"));
-  ASSERT_SOME(read);
-
-  lines = strings::split(read.get(), "\n");
-
-  EXPECT_TRUE(containsLine(lines, "out" + uuid));
-  EXPECT_FALSE(containsLine(lines, "err" + uuid));
-
-  driver.stop();
-  driver.join();
-
-  Shutdown();
-}
-
-
-// The following test uses a Docker image (mesosphere/inky) that has
-// an entrypoint "echo" and a default command "inky".
-TEST_F(DockerContainerizerTest, ROOT_DOCKER_Default_CMD)
-{
-  Try<PID<Master> > master = StartMaster();
-  ASSERT_SOME(master);
-
-  slave::Flags flags = CreateSlaveFlags();
-
-  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
-  Shared<Docker> docker(mockDocker);
-
-  // We skip stopping the docker container because stopping a container
-  // even when it terminated might not flush the logs and we end up
-  // not getting stdout/stderr in our tests.
-  EXPECT_CALL(*mockDocker, stop(_, _, _))
-    .WillRepeatedly(Return(Nothing()));
-
-  Fetcher fetcher;
-
-  MockDockerContainerizer dockerContainerizer(flags, &fetcher, docker);
-
-  Try<PID<Slave> > slave = StartSlave(&dockerContainerizer, flags);
-  ASSERT_SOME(slave);
-
-  MockScheduler sched;
-  MesosSchedulerDriver driver(
-      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
-
-  Future<FrameworkID> frameworkId;
-  EXPECT_CALL(sched, registered(&driver, _, _))
-    .WillOnce(FutureArg<1>(&frameworkId));
-
-  Future<vector<Offer> > offers;
-  EXPECT_CALL(sched, resourceOffers(&driver, _))
-    .WillOnce(FutureArg<1>(&offers))
-    .WillRepeatedly(Return()); // Ignore subsequent offers.
-
-  driver.start();
-
-  AWAIT_READY(frameworkId);
-
-  AWAIT_READY(offers);
-  ASSERT_NE(0u, offers.get().size());
-
-  const Offer& offer = offers.get()[0];
-
-  SlaveID slaveId = offer.slave_id();
-
-  TaskInfo task;
-  task.set_name("");
-  task.mutable_task_id()->set_value("1");
-  task.mutable_slave_id()->CopyFrom(offer.slave_id());
-  task.mutable_resources()->CopyFrom(offer.resources());
-
-  CommandInfo command;
-  command.set_shell(false);
-
-  // NOTE: By not setting CommandInfo::value we're testing that we
-  // will still be able to run the container because it has a default
-  // entrypoint!
-
-  ContainerInfo containerInfo;
-  containerInfo.set_type(ContainerInfo::DOCKER);
-
-  // TODO(tnachen): Use local image to test if possible.
-  ContainerInfo::DockerInfo dockerInfo;
-  dockerInfo.set_image("mesosphere/inky");
-  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
-
-  task.mutable_command()->CopyFrom(command);
-  task.mutable_container()->CopyFrom(containerInfo);
-
-  Future<ContainerID> containerId;
-  Future<string> directory;
-  EXPECT_CALL(dockerContainerizer, launch(_, _, _, _, _, _, _, _))
-    .WillOnce(DoAll(FutureArg<0>(&containerId),
-                    FutureArg<3>(&directory),
-                    Invoke(&dockerContainerizer,
-                           &MockDockerContainerizer::_launch)));
-
-  Future<TaskStatus> statusRunning;
-  Future<TaskStatus> statusFinished;
-  EXPECT_CALL(sched, statusUpdate(&driver, _))
-    .WillOnce(FutureArg<1>(&statusRunning))
-    .WillOnce(FutureArg<1>(&statusFinished))
-    .WillRepeatedly(DoDefault());
-
-  driver.launchTasks(offers.get()[0].id(), {task});
-
-  AWAIT_READY_FOR(containerId, Seconds(60));
-  AWAIT_READY(directory);
-  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());
-
-  Try<string> read = os::read(path::join(directory.get(), "stdout"));
-  ASSERT_SOME(read);
-
-  vector<string> lines = strings::split(read.get(), "\n");
-
-  // Since we're not passing any command value, we're expecting the
-  // default entry point to be run which is 'echo' with the default
-  // command from the image which is 'inky'.
-  EXPECT_TRUE(containsLine(lines, "inky"));
-
-  read = os::read(path::join(directory.get(), "stderr"));
-  ASSERT_SOME(read);
-
-  lines = strings::split(read.get(), "\n");
-
-  EXPECT_FALSE(containsLine(lines, "inky"));
-
-  driver.stop();
-  driver.join();
-
-  Shutdown();
-}
-
-
-// The following test uses a Docker image (mesosphere/inky) that has
-// an entrypoint "echo" and a default command "inky".
-TEST_F(DockerContainerizerTest, ROOT_DOCKER_Default_CMD_Override)
-{
-  Try<PID<Master> > master = StartMaster();
-  ASSERT_SOME(master);
-
-  slave::Flags flags = CreateSlaveFlags();
-
-  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
-  Shared<Docker> docker(mockDocker);
-
-  // We skip stopping the docker container because stopping  a container
-  // even when it terminated might not flush the logs and we end up
-  // not getting stdout/stderr in our tests.
-  EXPECT_CALL(*mockDocker, stop(_, _, _))
-    .WillRepeatedly(Return(Nothing()));
-
-  Fetcher fetcher;
-
-  MockDockerContainerizer dockerContainerizer(flags, &fetcher, docker);
-
-  Try<PID<Slave> > slave = StartSlave(&dockerContainerizer, flags);
-  ASSERT_SOME(slave);
-
-  MockScheduler sched;
-  MesosSchedulerDriver driver(
-      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
-
-  Future<FrameworkID> frameworkId;
-  EXPECT_CALL(sched, registered(&driver, _, _))
-    .WillOnce(FutureArg<1>(&frameworkId));
-
-  Future<vector<Offer> > offers;
-  EXPECT_CALL(sched, resourceOffers(&driver, _))
-    .WillOnce(FutureArg<1>(&offers))
-    .WillRepeatedly(Return()); // Ignore subsequent offers.
-
-  driver.start();
-
-  AWAIT_READY(frameworkId);
-
-  AWAIT_READY(offers);
-  ASSERT_NE(0u, offers.get().size());
-
-  const Offer& offer = offers.get()[0];
-
-  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());
-
-  string uuid = UUID::random().toString();
-
-  CommandInfo command;
-  command.set_shell(false);
-
-  // We can set the value to just the 'uuid' since it should get
-  // passed as an argument to the entrypoint, i.e., 'echo uuid'.
-  command.set_value(uuid);
-
-  ContainerInfo containerInfo;
-  containerInfo.set_type(ContainerInfo::DOCKER);
-
-  // TODO(tnachen): Use local image to test if possible.
-  ContainerInfo::DockerInfo dockerInfo;
-  dockerInfo.set_image("mesosphere/inky");
-  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
-
-  task.mutable_command()->CopyFrom(command);
-  task.mutable_container()->CopyFrom(containerInfo);
-
-  Future<ContainerID> containerId;
-  Future<string> directory;
-  EXPECT_CALL(dockerContainerizer, launch(_, _, _, _, _, _, _, _))
-    .WillOnce(DoAll(FutureArg<0>(&containerId),
-                    FutureArg<3>(&directory),
-                    Invoke(&dockerContainerizer,
-                           &MockDockerContainerizer::_launch)));
-
-  Future<TaskStatus> statusRunning;
-  Future<TaskStatus> statusFinished;
-  EXPECT_CALL(sched, statusUpdate(&driver, _))
-    .WillOnce(FutureArg<1>(&statusRunning))
-    .WillOnce(FutureArg<1>(&statusFinished))
-    .WillRepeatedly(DoDefault());
-
-  driver.launchTasks(offers.get()[0].id(), {task});
-
-  AWAIT_READY_FOR(containerId, Seconds(60));
-  AWAIT_READY(directory);
-  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());
-
-  // Now check that the proper output is in stderr and stdout.
-  Try<string> read = os::read(path::join(directory.get(), "stdout"));
-  ASSERT_SOME(read);
-
-  vector<string> lines = strings::split(read.get(), "\n");
-
-  // We expect the passed in command value to override the image's
-  // default command, thus we should see the value of 'uuid' in the
-  // output instead of the default command which is 'inky'.
-  EXPECT_TRUE(containsLine(lines, uuid));
-  EXPECT_FALSE(containsLine(lines, "inky"));
-
-  read = os::read(path::join(directory.get(), "stderr"));
-  ASSERT_SOME(read);
-
-  lines = strings::split(read.get(), "\n");
-
-  EXPECT_FALSE(containsLine(lines, "inky"));
-  EXPECT_FALSE(containsLine(lines, uuid));
-
-  driver.stop();
-  driver.join();
-
-  Shutdown();
-}
-
-
-// The following test uses a Docker image (mesosphere/inky) that has
-// an entrypoint "echo" and a default command "inky".
-TEST_F(DockerContainerizerTest, ROOT_DOCKER_Default_CMD_Args)
-{
-  Try<PID<Master> > master = StartMaster();
-  ASSERT_SOME(master);
-
-  slave::Flags flags = CreateSlaveFlags();
-
-  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
-  Shared<Docker> docker(mockDocker);
-
-  // We skip stopping the docker container because stopping a container
-  // even when it terminated might not flush the logs and we end up
-  // not getting stdout/stderr in our tests.
-  EXPECT_CALL(*mockDocker, stop(_, _, _))
-    .WillRepeatedly(Return(Nothing()));
-
-  Fetcher fetcher;
-
-  MockDockerContainerizer dockerContainerizer(flags, &fetcher, docker);
-
-  Try<PID<Slave> > slave = StartSlave(&dockerContainerizer, flags);
-  ASSERT_SOME(slave);
-
-  MockScheduler sched;
-  MesosSchedulerDriver driver(
-      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
-
-  Future<FrameworkID> frameworkId;
-  EXPECT_CALL(sched, registered(&driver, _, _))
-    .WillOnce(FutureArg<1>(&frameworkId));
-
-  Future<vector<Offer> > offers;
-  EXPECT_CALL(sched, resourceOffers(&driver, _))
-    .WillOnce(FutureArg<1>(&offers))
-    .WillRepeatedly(Return()); // Ignore subsequent offers.
-
-  driver.start();
-
-  AWAIT_READY(frameworkId);
-
-  AWAIT_READY(offers);
-  ASSERT_NE(0u, offers.get().size());
-
-  const Offer& offer = offers.get()[0];
-
-  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());
-
-  string uuid = UUID::random().toString();
-
-  CommandInfo command;
-  command.set_shell(false);
-
-  // We should also be able to skip setting the comamnd value and just
-  // set the arguments and those should also get passed through to the
-  // entrypoint!
-  command.add_arguments(uuid);
-
-  ContainerInfo containerInfo;
-  containerInfo.set_type(ContainerInfo::DOCKER);
-
-  // TODO(tnachen): Use local image to test if possible.
-  ContainerInfo::DockerInfo dockerInfo;
-  dockerInfo.set_image("mesosphere/inky");
-  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
-
-  task.mutable_command()->CopyFrom(command);
-  task.mutable_container()->CopyFrom(containerInfo);
-
-  Future<ContainerID> containerId;
-  Future<string> directory;
-  EXPECT_CALL(dockerContainerizer, launch(_, _, _, _, _, _, _, _))
-    .WillOnce(DoAll(FutureArg<0>(&containerId),
-                    FutureArg<3>(&directory),
-                    Invoke(&dockerContainerizer,
-                           &MockDockerContainerizer::_launch)));
-
-  Future<TaskStatus> statusRunning;
-  Future<TaskStatus> statusFinished;
-  EXPECT_CALL(sched, statusUpdate(&driver, _))
-    .WillOnce(FutureArg<1>(&statusRunning))
-    .WillOnce(FutureArg<1>(&statusFinished))
-    .WillRepeatedly(DoDefault());
-
-  driver.launchTasks(offers.get()[0].id(), {task});
-
-  AWAIT_READY_FOR(containerId, Seconds(60));
-  AWAIT_READY(directory);
-  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());
-
-  // Now check that the proper output is in stderr and stdout.
-  Try<string> read = os::read(path::join(directory.get(), "stdout"));
-  ASSERT_SOME(read);
-
-  vector<string> lines = strings::split(read.get(), "\n");
-
-  // We expect the passed in command arguments to override the image's
-  // default command, thus we should see the value of 'uuid' in the
-  // output instead of the default command which is 'inky'.
-  EXPECT_TRUE(containsLine(lines, uuid));
-  EXPECT_FALSE(containsLine(lines, "inky"));
-
-  read = os::read(path::join(directory.get(), "stderr"));
-  ASSERT_SOME(read);
-
-  lines = strings::split(read.get(), "\n");
-
-  EXPECT_FALSE(containsLine(lines, "inky"));
-  EXPECT_FALSE(containsLine(lines, uuid));
-
-  driver.stop();
-  driver.join();
-
-  Shutdown();
-}
-
-
-// The slave is stopped before the first update for a task is received
-// from the executor. When it comes back up we make sure the executor
-// re-registers and the slave properly sends the update.
-TEST_F(DockerContainerizerTest, ROOT_DOCKER_SlaveRecoveryTaskContainer)
-{
-  Try<PID<Master> > master = StartMaster();
-  ASSERT_SOME(master);
-
-  slave::Flags flags = CreateSlaveFlags();
-
-  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
-  Shared<Docker> docker(mockDocker);
-
-  Fetcher fetcher;
-
-  // We put the containerizer on the heap so we can more easily
-  // control it's lifetime, i.e., when we invoke the destructor.
-  MockDockerContainerizer* dockerContainerizer1 =
-    new MockDockerContainerizer(flags, &fetcher, docker);
-
-  Try<PID<Slave> > slave1 = StartSlave(dockerContainerizer1, flags);
-  ASSERT_SOME(slave1);
-
-  // Enable checkpointing for the framework.
-  FrameworkInfo frameworkInfo = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo.set_checkpoint(true);
-
-  MockScheduler sched;
-  MesosSchedulerDriver driver(
-      &sched, frameworkInfo, master.get(), DEFAULT_CREDENTIAL);
-
-  Future<FrameworkID> frameworkId;
-  EXPECT_CALL(sched, registered(&driver, _, _))
-    .WillOnce(FutureArg<1>(&frameworkId));
-
-  Future<vector<Offer> > offers;
-  EXPECT_CALL(sched, resourceOffers(&driver, _))
-    .WillOnce(FutureArg<1>(&offers))
-    .WillRepeatedly(Return()); // Ignore subsequent offers.
-
-  driver.start();
-
-  AWAIT_READY(frameworkId);
-
-  AWAIT_READY(offers);
-  ASSERT_NE(0u, offers.get().size());
-
-  const Offer& offer = offers.get()[0];
-
-  SlaveID slaveId = offer.slave_id();
-
-  TaskInfo task;
-  task.set_name("");
-  task.mutable_task_id()->set_value("1");
-  task.mutable_slave_id()->CopyFrom(offer.slave_id());
-  task.mutable_resources()->CopyFrom(offer.resources());
-
-  CommandInfo command;
-  command.set_value("sleep 1000");
-
-  ContainerInfo containerInfo;
-  containerInfo.set_type(ContainerInfo::DOCKER);
-
-  // TODO(tnachen): Use local image to test if possible.
-  ContainerInfo::DockerInfo dockerInfo;
-  dockerInfo.set_image("busybox");
-  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
-
-  task.mutable_command()->CopyFrom(command);
-  task.mutable_container()->CopyFrom(containerInfo);
-
-  Future<ContainerID> containerId;
-  EXPECT_CALL(*dockerContainerizer1, launch(_, _, _, _, _, _, _, _))
-    .WillOnce(DoAll(FutureArg<0>(&containerId),
-                    Invoke(dockerContainerizer1,
-                           &MockDockerContainerizer::_launch)));
-
-  // Drop the first update from the executor.
-  Future<StatusUpdateMessage> statusUpdateMessage =
-    DROP_PROTOBUF(StatusUpdateMessage(), _, _);
-
-  driver.launchTasks(offers.get()[0].id(), {task});
-
-  AWAIT_READY(containerId);
-
-  // Stop the slave before the status update is received.
-  AWAIT_READY(statusUpdateMessage);
-
-  Stop(slave1.get());
-
-  delete dockerContainerizer1;
-
-  Future<Message> reregisterExecutorMessage =
-    FUTURE_MESSAGE(Eq(ReregisterExecutorMessage().GetTypeName()), _, _);
-
-  Future<TaskStatus> status;
-  EXPECT_CALL(sched, statusUpdate(_, _))
-    .WillOnce(FutureArg<1>(&status))
-    .WillRepeatedly(Return());       // Ignore subsequent updates.
-
-  MockDockerContainerizer* dockerContainerizer2 =
-    new MockDockerContainerizer(flags, &fetcher, docker);
-
-  Try<PID<Slave> > slave2 = StartSlave(dockerContainerizer2, flags);
-  ASSERT_SOME(slave2);
-
-  // Ensure the executor re-registers.
-  AWAIT_READY(reregisterExecutorMessage);
-  UPID executorPid = reregisterExecutorMessage.get().from;
-
-  ReregisterExecutorMessage reregister;
-  reregister.ParseFromString(reregisterExecutorMessage.get().body);
-
-  // Executor should inform about the unacknowledged update.
-  ASSERT_EQ(1, reregister.updates_size());
-  const StatusUpdate& update = reregister.updates(0);
-  ASSERT_EQ(task.task_id(), update.status().task_id());
-  ASSERT_EQ(TASK_RUNNING, update.status().state());
-
-  // Scheduler should receive the recovered update.
-  AWAIT_READY(status);
-  ASSERT_EQ(TASK_RUNNING, status.get().state());
-
-  ASSERT_TRUE(exists(docker, slaveId, containerId.get()));
-
-  Future<containerizer::Termination> termination =
-    dockerContainerizer2->wait(containerId.get());
-
-  driver.stop();
-  driver.join();
-
-  AWAIT_READY(termination);
-
-  Shutdown();
-
-  delete dockerContainerizer2;
-}
-
-
-// The slave is stopped before the first update for a task is received
-// from the executor. When it comes back up we make sure the executor
-// re-registers and the slave properly sends the update.
-//
-// TODO(benh): This test is currently disabled because the executor
-// inside the image mesosphere/test-executor does not properly set the
-// executor PID that is uses during registration, so when the new
-// slave recovers it can't reconnect and instead destroys that
-// container. In particular, it uses '0' for it's IP which we properly
-// parse and can even properly use for sending other messages, but the
-// current implementation of 'UPID::operator bool ()' fails if the IP
-// component of a PID is '0'.
-TEST_F(DockerContainerizerTest,
-       DISABLED_ROOT_DOCKER_SlaveRecoveryExecutorContainer)
-{
-  Try<PID<Master> > master = StartMaster();
-  ASSERT_SOME(master);
-
-  slave::Flags flags = CreateSlaveFlags();
-
-  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
-  Shared<Docker> docker(mockDocker);
-
-  Fetcher fetcher;
-
-  MockDockerContainerizer* dockerContainerizer1 =
-    new MockDockerContainerizer(flags, &fetcher, docker);
-
-  Try<PID<Slave> > slave1 = StartSlave(dockerContainerizer1, flags);
-  ASSERT_SOME(slave1);
-
-  // Enable checkpointing for the framework.
-  FrameworkInfo frameworkInfo = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo.set_checkpoint(true);
-
-  MockScheduler sched;
-  MesosSchedulerDriver driver(
-      &sched, frameworkInfo, master.get(), DEFAULT_CREDENTIAL);
-
-  Future<FrameworkID> frameworkId;
-  EXPECT_CALL(sched, registered(&driver, _, _))
-    .WillOnce(FutureArg<1>(&frameworkId));
-
-  Future<vector<Offer> > offers;
-  EXPECT_CALL(sched, resourceOffers(&driver, _))
-    .WillOnce(FutureArg<1>(&offers))
-    .WillRepeatedly(Return()); // Ignore subsequent offers.
-
-  driver.start();
-
-  AWAIT_READY(frameworkId);
-
-  AWAIT_READY(offers);
-  ASSERT_NE(0u, offers.get().size());
-
-  const Offer& offer = offers.get()[0];
-
-  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());
-
-  ExecutorInfo executorInfo;
-  ExecutorID executorId;
-  executorId.set_value("e1");
-  executorInfo.mutable_executor_id()->CopyFrom(executorId);
-
-  CommandInfo command;
-  command.set_value("test-executor");
-  executorInfo.mutable_command()->CopyFrom(command);
-
-  ContainerInfo containerInfo;
-  containerInfo.set_type(ContainerInfo::DOCKER);
-
-  // TODO(tnachen): Use local image to test if possible.
-  ContainerInfo::DockerInfo dockerInfo;
-  dockerInfo.set_image("mesosphere/test-executor");
-
-  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
-  executorInfo.mutable_container()->CopyFrom(containerInfo);
-
-  task.mutable_executor()->CopyFrom(executorInfo);
-
-  Future<ContainerID> containerId;
-  Future<SlaveID> slaveId;
-  EXPECT_CALL(*dockerContainerizer1, launch(_, _, _, _, _, _, _))
-    .WillOnce(DoAll(FutureArg<0>(&containerId),
-                    FutureArg<4>(&slaveId),
-                    Invoke(dockerContainerizer1,
-                           &MockDockerContainerizer::_launchExecutor)));
-
-  // We need to wait until the container's pid has been been
-  // checkpointed so that when the next slave recovers it won't treat
-  // the executor as having gone lost! We know this has completed
-  // after Containerizer::launch returns and the
-  // Slave::executorLaunched gets dispatched.
-  Future<Nothing> executorLaunched =
-    FUTURE_DISPATCH(_, &Slave::executorLaunched);
-
-  // The test-executor in the image immediately sends a TASK_RUNNING
-  // followed by TASK_FINISHED (no sleep/delay in between) so we need
-  // to drop the first TWO updates that come from the executor rather
-  // than only the first update like above where we can control how
-  // the length of the task.
-  Future<StatusUpdateMessage> statusUpdateMessage1 =
-    DROP_PROTOBUF(StatusUpdateMessage(), _, _);
-
-  // Drop the first update from the executor.
-  Future<StatusUpdateMessage> statusUpdateMessage2 =
-    DROP_PROTOBUF(StatusUpdateMessage(), _, _);
-
-  driver.launchTasks(offers.get()[0].id(), {task});
-
-  AWAIT_READY(containerId);
-  AWAIT_READY(slaveId);
-
-  AWAIT_READY(executorLaunched);
-  AWAIT_READY(statusUpdateMessage1);
-  AWAIT_READY(statusUpdateMessage2);
-
-  Stop(slave1.get());
-
-  delete dockerContainerizer1;
-
-  Future<Message> reregisterExecutorMessage =
-    FUTURE_MESSAGE(Eq(ReregisterExecutorMessage().GetTypeName()), _, _);
-
-  Future<TaskStatus> status;
-  EXPECT_CALL(sched, statusUpdate(_, _))
-    .WillOnce(FutureArg<1>(&status))
-    .WillRepeatedly(Return());       // Ignore subsequent updates.
-
-  MockDockerContainerizer* dockerContainerizer2 =
-    new MockDockerContainerizer(flags, &fetcher, docker);
-
-  Try<PID<Slave> > slave2 = StartSlave(dockerContainerizer2, flags);
-  ASSERT_SOME(slave2);
-
-  // Ensure the executor re-registers.
-  AWAIT_READY(reregisterExecutorMessage);
-  UPID executorPid = reregisterExecutorMessage.get().from;
-
-  ReregisterExecutorMessage reregister;
-  reregister.ParseFromString(reregisterExecutorMessage.get().body);
-
-  // Executor should inform about the unacknowledged update.
-  ASSERT_EQ(1, reregister.updates_size());
-  const StatusUpdate& update = reregister.updates(0);
-  ASSERT_EQ(task.task_id(), update.status().task_id());
-  ASSERT_EQ(TASK_RUNNING, update.status().state());
-
-  // Scheduler should receive the recovered update.
-  AWAIT_READY(status);
-  ASSERT_EQ(TASK_RUNNING, status.get().state());
-
-  ASSERT_TRUE(exists(docker, slaveId.get(), containerId.get()));
-
-  driver.stop();
-  driver.join();
-
-  delete dockerContainerizer2;
-}
-
-
-// This test verifies that port mapping with bridge network is
-// exposing the host port to the container port, by sending data
-// to the host port and receiving it in the container by listening
-// to the mapped container port.
-TEST_F(DockerContainerizerTest, ROOT_DOCKER_NC_PortMapping)
-{
-  Try<PID<Master> > master = StartMaster();
-  ASSERT_SOME(master);
-
-  slave::Flags flags = CreateSlaveFlags();
-
-  flags.resources = "cpus:1;mem:1024;ports:[10000-10000]";
-
-  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
-  Shared<Docker> docker(mockDocker);
-
-  // We skip stopping the docker container because stopping a container
-  // even when it terminated might not flush the logs and we end up
-  // not getting stdout/stderr in our tests.
-  EXPECT_CALL(*mockDocker, stop(_, _, _))
-    .WillRepeatedly(Return(Nothing()));
-
-  Fetcher fetcher;
-
-  MockDockerContainerizer dockerContainerizer(flags, &fetcher, docker);
-
-  Try<PID<Slave> > slave = StartSlave(&dockerContainerizer, flags);
-  ASSERT_SOME(slave);
-
-  MockScheduler sched;
-  MesosSchedulerDriver driver(
-      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
-
-  Future<FrameworkID> frameworkId;
-  EXPECT_CALL(sched, registered(&driver, _, _))
-    .WillOnce(FutureArg<1>(&frameworkId));
-
-  Future<vector<Offer> > offers;
-  EXPECT_CALL(sched, resourceOffers(&driver, _))
-    .WillOnce(FutureArg<1>(&offers))
-    .WillRepeatedly(Return()); // Ignore subsequent offers.
-
-  driver.start();
-
-  AWAIT_READY(frameworkId);
-
-  AWAIT_READY(offers);
-  ASSERT_NE(0u, offers.get().size());
-
-  const Offer& offer = offers.get()[0];
-
-  SlaveID slaveId = offer.slave_id();
-
-  TaskInfo task;
-  task.set_name("");
-  task.mutable_task_id()->set_value("1");
-  task.mutable_slave_id()->CopyFrom(offer.slave_id());
-  task.mutable_resources()->CopyFrom(offer.resources());
-
-  CommandInfo command;
-  command.set_shell(false);
-  command.set_value("nc");
-  command.add_arguments("-l");
-  command.add_arguments("-p");
-  command.add_arguments("1000");
-
-  ContainerInfo containerInfo;
-  containerInfo.set_type(ContainerInfo::DOCKER);
-
-  // TODO(tnachen): Use local image to test if possible.
-  ContainerInfo::DockerInfo dockerInfo;
-  dockerInfo.set_image("busybox");
-  dockerInfo.set_network(ContainerInfo::DockerInfo::BRIDGE);
-
-  ContainerInfo::DockerInfo::PortMapping portMapping;
-  portMapping.set_host_port(10000);
-  portMapping.set_container_port(1000);
-
-  dockerInfo.add_port_mappings()->CopyFrom(portMapping);
-  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
-
-  task.mutable_command()->CopyFrom(command);
-  task.mutable_container()->CopyFrom(containerInfo);
-
-  Future<ContainerID> containerId;
-  Future<string> directory;
-  EXPECT_CALL(dockerContainerizer, launch(_, _, _, _, _, _, _, _))
-    .WillOnce(DoAll(FutureArg<0>(&containerId),
-                    FutureArg<3>(&directory),
-                    Invoke(&dockerContainerizer,
-                           &MockDockerContainerizer::_launch)));
-
-  Future<TaskStatus> statusRunning;
-  Future<TaskStatus> statusFinished;
-  EXPECT_CALL(sched, statusUpdate(&driver, _))
-    .WillOnce(FutureArg<1>(&statusRunning))
-    .WillOnce(FutureArg<1>(&statusFinished))
-    .WillRepeatedly(DoDefault());
-
-  driver.launchTasks(offers.get()[0].id(), {task});
-
-  AWAIT_READY_FOR(containerId, Seconds(60));
-  AWAIT_READY(directory);
-  AWAIT_READY_FOR(statusRunning, Seconds(60));
-  EXPECT_EQ(TASK_RUNNING, statusRunning.get().state());
-
-  ASSERT_TRUE(
-    exists(docker, slaveId, containerId.get(), ContainerState::RUNNING));
-
-  string uuid = UUID::random().toString();
-
-  // Write uuid to docker mapped host port.
-  Try<process::Subprocess> s = process::subprocess(
-      "echo " + uuid + " | nc localhost 10000");
-
-  ASSERT_SOME(s);
-  AWAIT_READY_FOR(s.get().status(), Seconds(60));
-
-  AWAIT_READY_FOR(statusFinished, Seconds(60));
-  EXPECT_EQ(TASK_FINISHED, statusFinished.get().state());
-
-  // Now check that the proper output is in stdout.
-  Try<string> read = os::read(path::join(directory.get(), "stdout"));
-  ASSERT_SOME(read);
-
-  const vector<string> lines = strings::split(read.get(), "\n");
-
-  // We expect the uuid that is sent to host port to be written
-  // to stdout by the docker container running nc -l.
-  EXPECT_TRUE(containsLine(lines, uuid));
-
-  Future<containerizer::Termination> termination =
-    dockerContainerizer.wait(containerId.get());
-
-  driver.stop();
-  driver.join();
-
-  AWAIT_READY(termination);
-
-  Shutdown();
-}
-
-
-// This test verifies that sandbox with ':' in the path can still
-// run successfully. This a limitation of the Docker CLI where
-// the volume map parameter treats colons (:) as seperators,
-// and incorrectly seperates the sandbox directory.
-TEST_F(DockerContainerizerTest, ROOT_DOCKER_LaunchSandboxWithColon)
-{
-  Try<PID<Master>> master = StartMaster();
-  ASSERT_SOME(master);
-
-  slave::Flags flags = CreateSlaveFlags();
-
-  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
-  Shared<Docker> docker(mockDocker);
-
-  Fetcher fetcher;
-
-  MockDockerContainerizer dockerContainerizer(flags, &fetcher, docker);
-
-  Try<PID<Slave> > slave = StartSlave(&dockerContainerizer, flags);
-  ASSERT_SOME(slave);
-
-  MockScheduler sched;
-  MesosSchedulerDriver driver(
-      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
-
-  Future<FrameworkID> frameworkId;
-  EXPECT_CALL(sched, registered(&driver, _, _))
-    .WillOnce(FutureArg<1>(&frameworkId));
-
-  Future<vector<Offer> > offers;
-  EXPECT_CALL(sched, resourceOffers(&driver, _))
-    .WillOnce(FutureArg<1>(&offers))
-    .WillRepeatedly(Return()); // Ignore subsequent offers.
-
-  driver.start();
-
-  AWAIT_READY(frameworkId);
-
-  AWAIT_READY(offers);
-  ASSERT_NE(0u, offers.get().size());
-
-  const Offer& offer = offers.get()[0];
-
-  SlaveID slaveId = offer.slave_id();
-
-  TaskInfo task;
-  task.set_name("");
-  task.mutable_task_id()->set_value("test:colon");
-  task.mutable_slave_id()->CopyFrom(offer.slave_id());
-  task.mutable_resources()->CopyFrom(offer.resources());
-
-  CommandInfo command;
-  command.set_value("sleep 1000");
-
-  ContainerInfo containerInfo;
-  containerInfo.set_type(ContainerInfo::DOCKER);
-
-  // TODO(tnachen): Use local image to test if possible.
-  ContainerInfo::DockerInfo dockerInfo;
-  dockerInfo.set_image("busybox");
-  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
-
-  task.mutable_command()->CopyFrom(command);
-  task.mutable_container()->CopyFrom(containerInfo);
-
-  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))
-    .WillRepeatedly(DoDefault());
-
-  driver.launchTasks(offers.get()[0].id(), {task});
-
-  AWAIT_READY_FOR(containerId, Seconds(60));
-  AWAIT_READY_FOR(statusRunning, Seconds(60));
-  EXPECT_EQ(TASK_RUNNING, statusRunning.get().state());
-
-  ASSERT_TRUE(exists(docker, slaveId, containerId.get()));
-
-  Future<containerizer::Termination> termination =
-    dockerContainerizer.wait(containerId.get());
-
-  driver.stop();
-  driver.join();
-
-  AWAIT_READY(termination);
-
-  Shutdown();
-}
-
-
-TEST_F(DockerContainerizerTest, ROOT_DOCKER_DestroyWhileFetching)
-{
-  Try<PID<Master> > master = StartMaster();
-  ASSERT_SOME(master);
-
-  slave::Flags flags = CreateSlaveFlags();
-
-  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
-  Shared<Docker> docker(mockDocker);
-
-  Fetcher fetcher;
-
-  // The docker containerizer will free the process, so we must
-  // allocate on the heap.
-  MockDockerContainerizerProcess* process =
-    new MockDockerContainerizerProcess(flags, &fetcher, docker);
-
-  MockDockerContainerizer dockerContainerizer(
-      (Owned<DockerContainerizerProcess>(process)));
-
-  Promise<Nothing> promise;
-  Future<Nothing> fetch;
-
-  // We want to pause the fetch call to simulate a long fetch time.
-  EXPECT_CALL(*process, fetch(_, _))
-    .WillOnce(DoAll(FutureSatisfy(&fetch),
-                    Return(promise.future())));
-
-  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);
-  ASSERT_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;
-  command.set_value("sleep 1000");
-
-  ContainerInfo containerInfo;
-  containerInfo.set_type(ContainerInfo::DOCKER);
-
-  // TODO(tnachen): Use local image to test if possible.
-  ContainerInfo::DockerInfo dockerInfo;
-  dockerInfo.set_image("busybox");
-  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
-
-  task.mutable_command()->CopyFrom(command);
-  task.mutable_container()->CopyFrom(containerInfo);
-
-  Future<TaskStatus> statusFailed;
-  EXPECT_CALL(sched, statusUpdate(&driver, _))
-    .WillOnce(FutureArg<1>(&statusFailed));
-
-  Future<ContainerID> containerId;
-  EXPECT_CALL(dockerContainerizer, launch(_, _, _, _, _, _, _, _))
-    .WillOnce(DoAll(FutureArg<0>(&containerId),
-                    Invoke(&dockerContainerizer,
-                           &MockDockerContainerizer::_launch)));
-
-  driver.launchTasks(offers.get()[0].id(), {task});
-
-  AWAIT_READY_FOR(containerId, Seconds(60));
-
-  AWAIT_READY(fetch);
-
-  dockerContainerizer.destroy(containerId.get());
-
-  // Resume docker launch.
-  promise.set(Nothing());
-
-  AWAIT_READY(statusFailed);
-
-  EXPECT_EQ(TASK_FAILED, statusFailed.get().state());
-
-  driver.stop();
-  driver.join();
-
-  Shutdown();
-}
-
-
-TEST_F(DockerContainerizerTest, ROOT_DOCKER_DestroyWhilePulling)
-{
-  Try<PID<Master> > master = StartMaster();
-  ASSERT_SOME(master);
-
-  slave::Flags flags = CreateSlaveFlags();
-
-  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
-  Shared<Docker> docker(mockDocker);
-
-  Fetcher fetcher;
-
-  // The docker containerizer will free the process, so we must
-  // allocate on the heap.
-  MockDockerContainerizerProcess* process =
-    new MockDockerContainerizerProcess(flags, &fetcher, docker);
-
-  MockDockerContainerizer dockerContainerizer(
-      (Owned<DockerContainerizerProcess>(process)));
-
-  Future<Nothing> fetch;
-  EXPECT_CALL(*process, fetch(_, _))
-    .WillOnce(DoAll(FutureSatisfy(&fetch),
-                    Return(Nothing())));
-
-  Promise<Nothing> promise;
-
-  // We want to pause the fetch call to simulate a long fetch time.
-  EXPECT_CALL(*process, pull(_))
-    .WillOnce(Return(promise.future()));
-
-  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);
-  ASSERT_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;
-  command.set_value("sleep 1000");
-
-  ContainerInfo containerInfo;
-  containerInfo.set_type(ContainerInfo::DOCKER);
-
-  // TODO(tnachen): Use local image to test if possible.
-  ContainerInfo::DockerInfo dockerInfo;
-  dockerInfo.set_image("busybox");
-  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
-
-  task.mutable_command()->CopyFrom(command);
-  task.mutable_container()->CopyFrom(containerInfo);
-
-  Future<TaskStatus> statusFailed;
-  EXPECT_CALL(sched, statusUpdate(&driver, _))
-    .WillOnce(FutureArg<1>(&statusFailed));
-
-  Future<ContainerID> containerId;
-  EXPECT_CALL(dockerContainerizer, launch(_, _, _, _, _, _, _, _))
-    .WillOnce(DoAll(FutureArg<0>(&containerId),
-                    Invoke(&dockerContainerizer,
-                           &MockDockerContainerizer::_launch)));
-
-  driver.launchTasks(offers.get()[0].id(), {task});
-
-  AWAIT_READY_FOR(containerId, Seconds(60));
-
-  // Wait until fetch is finished.
-  AWAIT_READY(fetch);
-
-  dockerContainerizer.destroy(containerId.get());
-
-  // Resume docker launch.
-  promise.set(Nothing());
-
-  AWAIT_READY(statusFailed);
-
-  EXPECT_EQ(TASK_FAILED, statusFailed.get().state());
-
-  driver.stop();
-  driver.join();
-
-  Shutdown();
-}
-
-
-// This test checks that when a docker containerizer update failed
-// and the container failed before the executor started, the executor
-// is properly killed and cleaned up.
-TEST_F(DockerContainerizerTest, ROOT_DOCKER_ExecutorCleanupWhenLaunchFailed)
-{
-  Try<PID<Master>> master = StartMaster();
-  ASSERT_SOME(master);
-
-  slave::Flags flags = CreateSlaveFlags();
-
-  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
-  Shared<Docker> docker(mockDocker);
-
-  Fetcher fetcher;
-
-  // The docker containerizer will free the process, so we must
-  // allocate on the heap.
-  MockDockerContainerizerProcess* process =
-    new MockDockerContainerizerProcess(flags, &fetcher, docker);
-
-  MockDockerContainerizer dockerContainerizer(
-      (Owned<DockerContainerizerProcess>(process)));
-
-  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);
-  ASSERT_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;
-  command.set_value("ls");
-
-  ContainerInfo containerInfo;
-  containerInfo.set_type(ContainerInfo::DOCKER);
-
-  ContainerInfo::DockerInfo dockerInfo;
-  dockerInfo.set_image("busybox");
-  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
-
-  task.mutable_command()->CopyFrom(command);
-  task.mutable_container()->CopyFrom(containerInfo);
-
-  Future<TaskStatus> statusFailed;
-  EXPECT_CALL(sched, statusUpdate(&driver, _))
-    .WillOnce(FutureArg<1>(&statusFailed));
-
-  Future<ContainerID> containerId;
-  EXPECT_CALL(dockerContainerizer, launch(_, _, _, _, _, _, _, _))
-    .WillOnce(DoAll(FutureArg<0>(&containerId),
-                    Invoke(&dockerContainerizer,
-                           &MockDockerContainerizer::_launch)));
-
-  // Fail the update so we don't proceed to send run task to the executor.
-  EXPECT_CALL(dockerContainerizer, update(_, _))
-    .WillRepeatedly(Return(Failure("Fail resource update")));
-
-  driver.launchTasks(offers.get()[0].id(), {task});
-
-  AWAIT_READY_FOR(containerId, Seconds(60));
-
-  AWAIT_READY(statusFailed);
-
-  EXPECT_EQ(TASK_FAILED, statusFailed.get().state());
-
-  driver.stop();
-  driver.join();
-
-  // We expect the executor to have exited, and if not in Shutdown
-  // the test will fail because of the executor process still running.
-  Shutdown();
-}
-
-
-// When the fetch fails we should send the scheduler a status
-// update with message the shows the actual error.
-TEST_F(DockerContainerizerTest, ROOT_DOCKER_FetchFailure)
-{
-  Try<PID<Master>> master = StartMaster();
-  ASSERT_SOME(master);
-
-  slave::Flags flags = CreateSlaveFlags();
-
-  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
-  Shared<Docker> docker(mockDocker);
-
-  Fetcher fetcher;
-
-  // The docker containerizer will free the process, so we must
-  // allocate on the heap.
-  MockDockerContainerizerProcess* process =
-    new MockDockerContainerizerProcess(flags, &fetcher, docker);
-
-  MockDockerContainerizer dockerContainerizer(
-      (Owned<DockerContainerizerProcess>(process)));
-
-  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);
-  ASSERT_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;
-  command.set_value("ls");
-
-  ContainerInfo containerInfo;
-  containerInfo.set_type(ContainerInfo::DOCKER);
-
-  ContainerInfo::DockerInfo dockerInfo;
-  dockerInfo.set_image("busybox");
-  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
-
-  task.mutable_command()->CopyFrom(command);
-  task.mutable_container()->CopyFrom(containerInfo);
-
-  Future<TaskStatus> statusFailed;
-  EXPECT_CALL(sched, statusUpdate(&driver, _))
-    .WillOnce(FutureArg<1>(&statusFailed));
-
-  Future<ContainerID> containerId;
-  EXPECT_CALL(dockerContainerizer, launch(_, _, _, _, _, _, _, _))
-    .WillOnce(DoAll(FutureArg<0>(&containerId),
-                    Invoke(&dockerContainerizer,
-                           &MockDockerContainerizer::_launch)));
-
-  EXPECT_CALL(*process, fetch(_, _))
-    .WillOnce(Return(Failure("some error from fetch")));
-
-  driver.launchTasks(offers.get()[0].id(), {task});
-
-  AWAIT_READY_FOR(containerId, Seconds(60));
-
-  AWAIT_READY(statusFailed);
-
-  EXPECT_EQ(TASK_FAILED, statusFailed.get().state());
-  EXPECT_EQ("Failed to launch container: some error from fetch",
-             statusFailed.get().message());
-
-  // TODO(jaybuff): When MESOS-2035 is addressed we should validate
-  // that statusFailed.get().reason() is correctly set here.
-
-  driver.stop();
-  driver.join();
-
-  // We expect the executor to have exited, and if not in Shutdown
-  // the test will fail because of the executor process still running.
-  Shutdown();
-}
-
-
-// When the docker pull fails we should send the scheduler a status
-// update with message the shows the actual error.
-TEST_F(DockerContainerizerTest, ROOT_DOCKER_DockerPullFailure)
-{
-  Try<PID<Master>> master = StartMaster();
-  ASSERT_SOME(master);
-
-  slave::Flags flags = CreateSlaveFlags();
-
-  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
-  Shared<Docker> docker(mockDocker);
-
-  Fetcher fetcher;
-
-  // The docker containerizer will free the process, so we must
-  // allocate on the heap.
-  MockDockerContainerizerProcess* process =
-    new MockDockerContainerizerProcess(flags, &fetcher, docker);
-
-  MockDockerContainerizer dockerContainerizer(
-      (Owned<DockerContainerizerProcess>(process)));
-
-  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);
-  ASSERT_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;
-  command.set_value("ls");
-
-  ContainerInfo containerInfo;
-  containerInfo.set_type(ContainerInfo::DOCKER);
-
-  ContainerInfo::DockerInfo dockerInfo;
-  dockerInfo.set_image("busybox");
-  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
-
-  task.mutable_command()->CopyFrom(command);
-  task.mutable_container()->CopyFrom(containerInfo);
-
-  Future<TaskStatus> statusFailed;
-  EXPECT_CALL(sched, statusUpdate(&driver, _))
-    .WillOnce(FutureArg<1>(&statusFailed));
-
-  Future<ContainerID> containerId;
-  EXPECT_CALL(dockerContainerizer, launch(_, _, _, _, _, _, _, _))
-    .WillOnce(DoAll(FutureArg<0>(&containerId),
-                    Invoke(&dockerContainerizer,
-                           &MockDockerContainerizer::_launch)));
-
-  EXPECT_CALL(*mockDocker, pull(_, _, _))
-    .WillOnce(Return(Failure("some error from docker pull")));
-
-  driver.launchTasks(offers.get()[0].id(), {task});
-
-  AWAIT_READY_FOR(containerId, Seconds(60));
-
-  AWAIT_READY(statusFailed);
-
-  EXPECT_EQ(TASK_FAILED, statusFailed.get().state());
-  EXPECT_EQ("Failed to launch container: some error from docker pull",
-             statusFailed.get().message());
-
-  // TODO(jaybuff): When MESOS-2035 is addressed we should validate
-  // that statusFailed.get().reason() is correctly set here.
-
-  driver.stop();
-  driver.join();
-
-  // We expect the executor to have exited, and if not in Shutdown
-  // the test will fail because of the executor process still running.
-  Shutdown();
-}
-
-
-// When the docker executor container fails to launch, docker inspect
-// future that is in a retry loop should be discarded.
-TEST_F(DockerContainerizerTest, ROOT_DOCKER_DockerInspectDiscard)
-{
-  Try<PID<Master>> master = StartMaster();
-  ASSERT_SOME(master);
-
-  slave::Flags flags = CreateSlaveFlags();
-
-  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
-  Shared<Docker> docker(mockDocker);
-
-  Future<Docker::Container> inspect;
-  EXPECT_CALL(*mockDocker, inspect(_, _))
-    .WillOnce(FutureResult(&inspect,
-                           Invoke((MockDocker*) docker.get(),
-                                  &MockDocker::_inspect)));
-
-  EXPECT_CALL(*mockDocker, run(_, _, _, _, _, _, _, _, _))
-    .WillOnce(Return(Failure("Run failed")));
-
-  Fetcher fetcher;
-
-  // The docker containerizer will free the process, so we must
-  // allocate on the heap.
-  MockDockerContainerizerProcess* process =
-    new MockDockerContainerizerProcess(flags, &fetcher, docker);
-
-  MockDockerContainerizer dockerContainerizer(
-      (Owned<DockerContainerizerProcess>(process)));
-
-  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);
-  ASSERT_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());
-
-  ExecutorInfo executorInfo;
-  ExecutorID executorId;
-  executorId.set_value("e1");
-  executorInfo.mutable_executor_id()->CopyFrom(executorId);
-
-  CommandInfo command;
-  command.set_value("/bin/test-executor");
-  executorInfo.mutable_command()->CopyFrom(command);
-
-  ContainerInfo containerInfo;
-  containerInfo.set_type(ContainerInfo::DOCKER);
-
-  // TODO(tnachen): Use local image to test if possible.
-  ContainerInfo::DockerInfo dockerInfo;
-  dockerInfo.set_image("tnachen/test-executor");
-
-  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
-  executorInfo.mutable_container()->CopyFrom(containerInfo);
-
-  task.mutable_executor()->CopyFrom(executorInfo);
-
-  Future<TaskStatus> statusFailed;
-  EXPECT_CALL(sched, statusUpdate(&driver, _))
-    .WillOnce(FutureArg<1>(&statusFailed));
-
-  Future<ContainerID> containerId;
-  EXPECT

<TRUNCATED>

[02/12] mesos git commit: Moved containerizer related tests under src/tests/containerizer.

Posted by ji...@apache.org.
http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/port_mapping_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/port_mapping_tests.cpp b/src/tests/port_mapping_tests.cpp
deleted file mode 100644
index 45ef97a..0000000
--- a/src/tests/port_mapping_tests.cpp
+++ /dev/null
@@ -1,2296 +0,0 @@
-/**
- * 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 <iostream>
-#include <string>
-#include <vector>
-
-#include <process/future.hpp>
-#include <process/io.hpp>
-#include <process/reap.hpp>
-#include <process/subprocess.hpp>
-
-#include <stout/bytes.hpp>
-#include <stout/gtest.hpp>
-#include <stout/ip.hpp>
-#include <stout/json.hpp>
-#include <stout/mac.hpp>
-#include <stout/net.hpp>
-#include <stout/stopwatch.hpp>
-
-#include <stout/os/stat.hpp>
-#include <stout/os/exists.hpp>
-
-#include "linux/fs.hpp"
-
-#include "linux/routing/utils.hpp"
-
-#include "linux/routing/filter/ip.hpp"
-
-#include "linux/routing/link/link.hpp"
-
-#include "linux/routing/queueing/ingress.hpp"
-
-#include "master/master.hpp"
-
-#include "mesos/mesos.hpp"
-
-#include "slave/flags.hpp"
-#include "slave/slave.hpp"
-
-#include "slave/containerizer/isolators/network/port_mapping.hpp"
-
-#include "slave/containerizer/fetcher.hpp"
-#include "slave/containerizer/launcher.hpp"
-#include "slave/containerizer/linux_launcher.hpp"
-
-#include "slave/containerizer/mesos/containerizer.hpp"
-#include "slave/containerizer/mesos/launch.hpp"
-
-#include "tests/flags.hpp"
-#include "tests/mesos.hpp"
-#include "tests/utils.hpp"
-
-using namespace mesos::internal::slave;
-
-using namespace process;
-
-using namespace routing;
-using namespace routing::filter;
-using namespace routing::queueing;
-
-using mesos::internal::master::Master;
-
-using mesos::slave::Isolator;
-
-using std::list;
-using std::ostringstream;
-using std::set;
-using std::string;
-using std::vector;
-
-using testing::_;
-using testing::Eq;
-using testing::Return;
-
-namespace mesos {
-namespace internal {
-namespace tests {
-
-
-// An old glibc might not have this symbol.
-#ifndef MNT_DETACH
-#define MNT_DETACH 2
-#endif
-
-
-// Each test container works with a common specification of 2 CPUs,
-// 1GB of memory and 1GB of disk space, which experience has shown
-// to be sufficient to not encounter resource starvation issues when
-// running the test suite.
-const char* const containerCPU = "cpus:2";
-const char* const containerMemory = "mem:1024";
-const char* const containerDisk = "disk:1024";
-
-// We configure ephemeral and persistent port ranges outside the
-// default linux ip_local_port_range [32768-61000] in order to reduce
-// the probability of a conflict which could result in spurious
-// results (positive or negative) from these tests.
-const char* const ephemeralPorts = "ephemeral_ports:[30001-30999]";
-const char* const persistentPorts = "ports:[31000-32000]";
-
-// To keep things simple, we used fixed port ranges for our containers
-// in these tests rather than try to dynamically track port usage.
-// Note that container ports must be contained in the persistent port
-// range.
-const char* const container1Ports = "ports:[31000-31499]";
-const char* const container2Ports = "ports:[31500-32000]";
-
-// We define a validPort in the container1 assigned range which can
-// therefore accept incoming traffic.
-const int validPort = 31001;
-
-// We also define a port outside the persistent port range; containers
-// connecting to this port will never receive incoming traffic.
-const int invalidPort = 32502;
-
-
-static void cleanup(const string& eth0, const string& lo)
-{
-  // Clean up the ingress qdisc on eth0 and lo if exists.
-  Try<bool> hostEth0ExistsQdisc = ingress::exists(eth0);
-  ASSERT_SOME(hostEth0ExistsQdisc);
-
-  if (hostEth0ExistsQdisc.get()) {
-    ASSERT_SOME_TRUE(ingress::remove(eth0));
-  }
-
-  Try<bool> hostLoExistsQdisc = ingress::exists(lo);
-  ASSERT_SOME(hostLoExistsQdisc);
-
-  if (hostLoExistsQdisc.get()) {
-    ASSERT_SOME_TRUE(ingress::remove(lo));
-  }
-
-  // Clean up all 'veth' devices if exist.
-  Try<set<string> > links = net::links();
-  ASSERT_SOME(links);
-
-  foreach (const string& name, links.get()) {
-    if (strings::startsWith(name, slave::PORT_MAPPING_VETH_PREFIX())) {
-      ASSERT_SOME_TRUE(link::remove(name));
-    }
-  }
-
-  Try<list<string> > entries = os::ls(slave::PORT_MAPPING_BIND_MOUNT_ROOT());
-  ASSERT_SOME(entries);
-
-  foreach (const string& file, entries.get()) {
-    string target = path::join(slave::PORT_MAPPING_BIND_MOUNT_ROOT(), file);
-
-    // NOTE: Here, we ignore the unmount errors because previous tests
-    // may have created the file and died before mounting.
-    if (!os::stat::islink(target)) {
-      mesos::internal::fs::unmount(target, MNT_DETACH);
-    }
-
-    // Remove the network namespace handle and the corresponding
-    // symlinks. The removal here is best effort.
-    os::rm(target);
-  }
-}
-
-
-class PortMappingIsolatorTest : public TemporaryDirectoryTest
-{
-public:
-  static void SetUpTestCase()
-  {
-    ASSERT_SOME(routing::check())
-      << "-------------------------------------------------------------\n"
-      << "We cannot run any PortMappingIsolatorTests because your\n"
-      << "libnl library is not new enough. You can either install a\n"
-      << "new libnl library, or disable this test case\n"
-      << "-------------------------------------------------------------";
-
-    ASSERT_SOME_EQ(0, os::shell(NULL, "which nc"))
-      << "-------------------------------------------------------------\n"
-      << "We cannot run any PortMappingIsolatorTests because 'nc'\n"
-      << "could not be found. You can either install 'nc', or disable\n"
-      << "this test case\n"
-      << "-------------------------------------------------------------";
-
-    ASSERT_SOME_EQ(0, os::shell(NULL, "which arping"))
-      << "-------------------------------------------------------------\n"
-      << "We cannot run some PortMappingIsolatorTests because 'arping'\n"
-      << "could not be found. You can either isntall 'arping', or\n"
-      << "disable this test case\n"
-      << "-------------------------------------------------------------";
-  }
-
-  PortMappingIsolatorTest() : hostIP(net::IP(INADDR_ANY)) {}
-
-protected:
-  virtual void SetUp()
-  {
-    TemporaryDirectoryTest::SetUp();
-
-    flags = CreateSlaveFlags();
-
-    // Guess the name of the public interface.
-    Result<string> _eth0 = link::eth0();
-    ASSERT_SOME(_eth0) << "Failed to guess the name of the public interface";
-
-    eth0 = _eth0.get();
-
-    LOG(INFO) << "Using " << eth0 << " as the public interface";
-
-    // Guess the name of the loopback interface.
-    Result<string> _lo = link::lo();
-    ASSERT_SOME(_lo) << "Failed to guess the name of the loopback interface";
-
-    lo = _lo.get();
-
-    LOG(INFO) << "Using " << lo << " as the loopback interface";
-
-    // Clean up qdiscs and veth devices.
-    cleanup(eth0, lo);
-
-    // Get host IP address.
-    Result<net::IPNetwork> hostIPNetwork =
-        net::IPNetwork::fromLinkDevice(eth0, AF_INET);
-
-    CHECK_SOME(hostIPNetwork)
-      << "Failed to retrieve the host public IP network from " << eth0 << ": "
-      << hostIPNetwork.error();
-
-    hostIP = hostIPNetwork.get().address();
-
-    // Get all the external name servers for tests that need to talk
-    // to an external host, e.g., ping, DNS.
-    Try<string> read = os::read("/etc/resolv.conf");
-    CHECK_SOME(read);
-
-    foreach (const string& line, strings::split(read.get(), "\n")) {
-      if (!strings::startsWith(line, "nameserver")) {
-        continue;
-      }
-
-      vector<string> tokens = strings::split(line, " ");
-      ASSERT_EQ(2u, tokens.size()) << "Unexpected format in '/etc/resolv.conf'";
-      if (tokens[1] != "127.0.0.1") {
-        nameServers.push_back(tokens[1]);
-      }
-    }
-
-    container1Ready = path::join(os::getcwd(), "container1_ready");
-    container2Ready = path::join(os::getcwd(), "container2_ready");
-    trafficViaLoopback = path::join(os::getcwd(), "traffic_via_loopback");
-    trafficViaPublic = path::join(os::getcwd(), "traffic_via_public");
-    exitStatus = path::join(os::getcwd(), "exit_status");
-  }
-
-  virtual void TearDown()
-  {
-    cleanup(eth0, lo);
-    TemporaryDirectoryTest::TearDown();
-  }
-
-  slave::Flags CreateSlaveFlags()
-  {
-    slave::Flags flags;
-
-    flags.launcher_dir = path::join(tests::flags.build_dir, "src");
-
-    flags.resources = strings::join(";", vector<string>({
-        containerCPU,
-        containerMemory,
-        containerDisk,
-        ephemeralPorts,
-        persistentPorts }));
-
-    // NOTE: '16' should be enough for all our tests.
-    flags.ephemeral_ports_per_container = 16;
-
-    flags.isolation = "network/port_mapping";
-
-    return flags;
-  }
-
-  Try<pid_t> launchHelper(
-      Launcher* launcher,
-      int pipes[2],
-      const ContainerID& containerId,
-      const string& command,
-      const Option<CommandInfo>& preparation)
-  {
-    CommandInfo commandInfo;
-    commandInfo.set_value(command);
-
-    // The flags to pass to the helper process.
-    MesosContainerizerLaunch::Flags launchFlags;
-
-    launchFlags.command = JSON::Protobuf(commandInfo);
-    launchFlags.directory = os::getcwd();
-
-    CHECK_SOME(os::user());
-    launchFlags.user = os::user().get();
-
-    launchFlags.pipe_read = pipes[0];
-    launchFlags.pipe_write = pipes[1];
-
-    JSON::Object commands;
-    JSON::Array array;
-    array.values.push_back(JSON::Protobuf(preparation.get()));
-    commands.values["commands"] = array;
-
-    launchFlags.commands = commands;
-
-    vector<string> argv(2);
-    argv[0] = MESOS_CONTAINERIZER;
-    argv[1] = MesosContainerizerLaunch::NAME;
-
-    Try<pid_t> pid = launcher->fork(
-        containerId,
-        path::join(flags.launcher_dir, MESOS_CONTAINERIZER),
-        argv,
-        Subprocess::FD(STDIN_FILENO),
-        Subprocess::FD(STDOUT_FILENO),
-        Subprocess::FD(STDERR_FILENO),
-        launchFlags,
-        None(),
-        None());
-
-    return pid;
-  }
-
-  Result<ResourceStatistics> statisticsHelper(
-      pid_t pid,
-      bool enable_summary,
-      bool enable_details)
-  {
-    // Retrieve the socket information from inside the container.
-    PortMappingStatistics statistics;
-    statistics.flags.pid = pid;
-    statistics.flags.eth0_name = eth0;
-    statistics.flags.enable_socket_statistics_summary = enable_summary;
-    statistics.flags.enable_socket_statistics_details = enable_details;
-
-    vector<string> argv(2);
-    argv[0] = "mesos-network-helper";
-    argv[1] = PortMappingStatistics::NAME;
-
-    // We don't need STDIN; we need STDOUT for the result; we leave
-    // STDERR as is to log to slave process.
-    Try<Subprocess> s = subprocess(
-        path::join(flags.launcher_dir, "mesos-network-helper"),
-        argv,
-        Subprocess::PATH("/dev/null"),
-        Subprocess::PIPE(),
-        Subprocess::FD(STDERR_FILENO),
-        statistics.flags);
-
-    CHECK_SOME(s);
-
-    Future<Option<int> > status = s.get().status();
-    AWAIT_EXPECT_READY(status);
-    EXPECT_SOME_EQ(0, status.get());
-
-    Future<string> out = io::read(s.get().out().get());
-    AWAIT_EXPECT_READY(out);
-
-    Try<JSON::Object> object = JSON::parse<JSON::Object>(out.get());
-    CHECK_SOME(object);
-
-    return ::protobuf::parse<ResourceStatistics>(object.get());
-  }
-
-  slave::Flags flags;
-
-  // Name of the host eth0 and lo.
-  string eth0;
-  string lo;
-
-  // Host public IP network.
-  net::IP hostIP;
-
-  // All the external name servers as read from /etc/resolv.conf.
-  vector<string> nameServers;
-
-  // Some auxiliary files for the tests.
-  string container1Ready;
-  string container2Ready;
-  string trafficViaLoopback;
-  string trafficViaPublic;
-  string exitStatus;
-};
-
-
-// Wait up to timeout seconds for a file to be created. If timeout is
-// zero, then wait indefinitely. Return true if file exists.
-//
-// TODO(pbrett): Consider generalizing this function and moving it to
-// a common header.
-static bool waitForFileCreation(
-    const string& path,
-    const Duration& duration = Seconds(60))
-{
-  Stopwatch timer;
-  timer.start();
-  while (!os::exists(path)) {
-    if ((duration > Duration::zero()) && (timer.elapsed() > duration))
-      break;
-    os::sleep(Milliseconds(50));
-  }
-  return os::exists(path);
-}
-
-
-// This test uses two containers: one listens to 'validPort' and
-// 'invalidPort' and writes data received to files; the other
-// container attempts to connect to the previous container using
-// 'validPort' and 'invalidPort'. Verify that only the connection
-// through 'validPort' is successful by confirming that the expected
-// data has been written to its output file.
-TEST_F(PortMappingIsolatorTest, ROOT_NC_ContainerToContainerTCP)
-{
-  Try<Isolator*> isolator = PortMappingIsolatorProcess::create(flags);
-  CHECK_SOME(isolator);
-
-  Try<Launcher*> launcher =
-    LinuxLauncher::create(flags, isolator.get()->namespaces().get());
-  CHECK_SOME(launcher);
-
-  // Set the executor's resources.
-  ExecutorInfo executorInfo;
-  executorInfo.mutable_resources()->CopyFrom(
-      Resources::parse(container1Ports).get());
-
-  ContainerID containerId1;
-  containerId1.set_value("container1");
-
-  // Use a relative temporary directory so it gets cleaned up
-  // automatically with the test.
-  Try<string> dir1 = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
-  ASSERT_SOME(dir1);
-
-  Future<Option<CommandInfo> > preparation1 =
-    isolator.get()->prepare(
-        containerId1,
-        executorInfo,
-        dir1.get(),
-        None(),
-        None());
-
-  AWAIT_READY(preparation1);
-  ASSERT_SOME(preparation1.get());
-
-  ostringstream command1;
-
-  // Listen to 'localhost' and 'port'.
-  command1 << "nc -l localhost " << validPort << " > " << trafficViaLoopback
-           << "& ";
-
-  // Listen to 'public ip' and 'port'.
-  command1 << "nc -l " << hostIP << " " << validPort << " > "
-           << trafficViaPublic << "& ";
-
-  // Listen to 'invalidPort'.  This should not receive any data.
-  command1 << "nc -l " << invalidPort << " | tee " << trafficViaLoopback << " "
-           << trafficViaPublic << "& ";
-
-  // Touch the guard file.
-  command1 << "touch " << container1Ready;
-
-  int pipes[2];
-  ASSERT_NE(-1, ::pipe(pipes));
-
-  Try<pid_t> pid = launchHelper(
-      launcher.get(),
-      pipes,
-      containerId1,
-      command1.str(),
-      preparation1.get());
-  ASSERT_SOME(pid);
-
-  // Reap the forked child.
-  Future<Option<int> > status1 = process::reap(pid.get());
-
-  // Continue in the parent.
-  ::close(pipes[0]);
-
-  // Isolate the forked child.
-  AWAIT_READY(isolator.get()->isolate(containerId1, pid.get()));
-
-  // Now signal the child to continue.
-  char dummy;
-  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
-  ::close(pipes[1]);
-
-  // Wait for the command to start.
-  ASSERT_TRUE(waitForFileCreation(container1Ready));
-
-  ContainerID containerId2;
-  containerId2.set_value("container2");
-
-  executorInfo.mutable_resources()->CopyFrom(
-      Resources::parse(container2Ports).get());
-
-  // Use a relative temporary directory so it gets cleaned up
-  // automatically with the test.
-  Try<string> dir2 = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
-  ASSERT_SOME(dir2);
-
-  Future<Option<CommandInfo> > preparation2 =
-    isolator.get()->prepare(
-        containerId2,
-        executorInfo,
-        dir2.get(),
-        None(),
-        None());
-
-  AWAIT_READY(preparation2);
-  ASSERT_SOME(preparation2.get());
-
-  ostringstream command2;
-
-  // Send to 'localhost' and 'port'.
-  command2 << "printf hello1 | nc localhost " << validPort << ";";
-  // Send to 'localhost' and 'invalidPort'. This should fail.
-  command2 << "printf hello2 | nc localhost " << invalidPort << ";";
-  // Send to 'public IP' and 'port'.
-  command2 << "printf hello3 | nc " << hostIP << " " << validPort << ";";
-  // Send to 'public IP' and 'invalidPort'. This should fail.
-  command2 << "printf hello4 | nc " << hostIP << " " << invalidPort << ";";
-  // Touch the guard file.
-  command2 << "touch " << container2Ready;
-
-  ASSERT_NE(-1, ::pipe(pipes));
-
-  pid = launchHelper(
-      launcher.get(),
-      pipes,
-      containerId2,
-      command2.str(),
-      preparation2.get());
-  ASSERT_SOME(pid);
-
-  // Reap the forked child.
-  Future<Option<int> > status2 = process::reap(pid.get());
-
-  // Continue in the parent.
-  ::close(pipes[0]);
-
-  // Isolate the forked child.
-  AWAIT_READY(isolator.get()->isolate(containerId2, pid.get()));
-
-  // Now signal the child to continue.
-  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
-  ::close(pipes[1]);
-
-  // Wait for the command to start.
-  ASSERT_TRUE(waitForFileCreation(container2Ready));
-
-  // Wait for the command to complete.
-  AWAIT_READY(status1);
-  AWAIT_READY(status2);
-
-  EXPECT_SOME_EQ("hello1", os::read(trafficViaLoopback));
-  EXPECT_SOME_EQ("hello3", os::read(trafficViaPublic));
-
-  // Ensure all processes are killed.
-  AWAIT_READY(launcher.get()->destroy(containerId1));
-  AWAIT_READY(launcher.get()->destroy(containerId2));
-
-  // Let the isolator clean up.
-  AWAIT_READY(isolator.get()->cleanup(containerId1));
-  AWAIT_READY(isolator.get()->cleanup(containerId2));
-
-  delete isolator.get();
-  delete launcher.get();
-}
-
-
-// The same container-to-container test but with UDP.
-TEST_F(PortMappingIsolatorTest, ROOT_NC_ContainerToContainerUDP)
-{
-  Try<Isolator*> isolator = PortMappingIsolatorProcess::create(flags);
-  CHECK_SOME(isolator);
-
-  Try<Launcher*> launcher =
-    LinuxLauncher::create(flags, isolator.get()->namespaces().get());
-  CHECK_SOME(launcher);
-
-  // Set the executor's resources.
-  ExecutorInfo executorInfo;
-  executorInfo.mutable_resources()->CopyFrom(
-      Resources::parse(container1Ports).get());
-
-  ContainerID containerId1;
-  containerId1.set_value("container1");
-
-  // Use a relative temporary directory so it gets cleaned up
-  // automatically with the test.
-  Try<string> dir1 = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
-  ASSERT_SOME(dir1);
-
-  Future<Option<CommandInfo> > preparation1 =
-    isolator.get()->prepare(
-        containerId1,
-        executorInfo,
-        dir1.get(),
-        None(),
-        None());
-
-  AWAIT_READY(preparation1);
-  ASSERT_SOME(preparation1.get());
-
-  ostringstream command1;
-
-  // Listen to 'localhost' and 'port'.
-  command1 << "nc -u -l localhost " << validPort << " > " << trafficViaLoopback
-           << "& ";
-
-  // Listen to 'public ip' and 'port'.
-  command1 << "nc -u -l " << hostIP << " " << validPort << " > "
-           << trafficViaPublic << "& ";
-
-  // Listen to 'invalidPort'. This should not receive anything.
-  command1 << "nc -u -l " << invalidPort << " | tee " << trafficViaLoopback
-           << " " << trafficViaPublic << "& ";
-
-  // Touch the guard file.
-  command1 << "touch " << container1Ready;
-
-  int pipes[2];
-  ASSERT_NE(-1, ::pipe(pipes));
-
-  Try<pid_t> pid = launchHelper(
-      launcher.get(),
-      pipes,
-      containerId1,
-      command1.str(),
-      preparation1.get());
-  ASSERT_SOME(pid);
-
-  // Reap the forked child.
-  Future<Option<int> > status1 = process::reap(pid.get());
-
-  // Continue in the parent.
-  ::close(pipes[0]);
-
-  // Isolate the forked child.
-  AWAIT_READY(isolator.get()->isolate(containerId1, pid.get()));
-
-  // Now signal the child to continue.
-  char dummy;
-  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
-  ::close(pipes[1]);
-
-  // Wait for the command to start.
-  ASSERT_TRUE(waitForFileCreation(container1Ready));
-
-  ContainerID containerId2;
-  containerId2.set_value("container2");
-
-  executorInfo.mutable_resources()->CopyFrom(
-      Resources::parse(container2Ports).get());
-
-  // Use a relative temporary directory so it gets cleaned up
-  // automatically with the test.
-  Try<string> dir2 = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
-  ASSERT_SOME(dir2);
-
-  Future<Option<CommandInfo> > preparation2 =
-    isolator.get()->prepare(
-        containerId2,
-        executorInfo,
-        dir2.get(),
-        None(),
-        None());
-
-  AWAIT_READY(preparation2);
-  ASSERT_SOME(preparation2.get());
-
-  ostringstream command2;
-
-  // Send to 'localhost' and 'port'.
-  command2 << "printf hello1 | nc -w1 -u localhost " << validPort << ";";
-  // Send to 'localhost' and 'invalidPort'. No data should be sent.
-  command2 << "printf hello2 | nc -w1 -u localhost " << invalidPort << ";";
-  // Send to 'public IP' and 'port'.
-  command2 << "printf hello3 | nc -w1 -u " << hostIP << " " << validPort << ";";
-  // Send to 'public IP' and 'invalidPort'. No data should be sent.
-  command2 << "printf hello4 | nc -w1 -u " << hostIP << " " << invalidPort
-           << ";";
-  // Touch the guard file.
-  command2 << "touch " << container2Ready;
-
-  ASSERT_NE(-1, ::pipe(pipes));
-
-  pid = launchHelper(
-      launcher.get(),
-      pipes,
-      containerId2,
-      command2.str(),
-      preparation2.get());
-
-  ASSERT_SOME(pid);
-
-  // Reap the forked child.
-  Future<Option<int> > status2 = process::reap(pid.get());
-
-  // Continue in the parent.
-  ::close(pipes[0]);
-
-  // Isolate the forked child.
-  AWAIT_READY(isolator.get()->isolate(containerId2, pid.get()));
-
-  // Now signal the child to continue.
-  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
-  ::close(pipes[1]);
-
-  // Wait for the command to start.
-  ASSERT_TRUE(waitForFileCreation(container2Ready));
-
-  // Wait for the command to complete.
-  AWAIT_READY(status1);
-  AWAIT_READY(status2);
-
-  EXPECT_SOME_EQ("hello1", os::read(trafficViaLoopback));
-  EXPECT_SOME_EQ("hello3", os::read(trafficViaPublic));
-
-  AWAIT_READY(launcher.get()->destroy(containerId1));
-  AWAIT_READY(launcher.get()->destroy(containerId2));
-
-  // Let the isolator clean up.
-  AWAIT_READY(isolator.get()->cleanup(containerId1));
-  AWAIT_READY(isolator.get()->cleanup(containerId2));
-
-  delete isolator.get();
-  delete launcher.get();
-}
-
-
-// Test the scenario where a UDP server is in a container while host
-// tries to establish a UDP connection.
-TEST_F(PortMappingIsolatorTest, ROOT_NC_HostToContainerUDP)
-{
-  Try<Isolator*> isolator = PortMappingIsolatorProcess::create(flags);
-  CHECK_SOME(isolator);
-
-  Try<Launcher*> launcher =
-    LinuxLauncher::create(flags, isolator.get()->namespaces().get());
-  CHECK_SOME(launcher);
-
-  // Set the executor's resources.
-  ExecutorInfo executorInfo;
-  executorInfo.mutable_resources()->CopyFrom(
-      Resources::parse(container1Ports).get());
-
-  ContainerID containerId;
-  containerId.set_value("container1");
-
-  // Use a relative temporary directory so it gets cleaned up
-  // automatically with the test.
-  Try<string> dir = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
-  ASSERT_SOME(dir);
-
-  Future<Option<CommandInfo> > preparation1 =
-    isolator.get()->prepare(
-        containerId,
-        executorInfo,
-        dir.get(),
-        None(),
-        None());
-
-  AWAIT_READY(preparation1);
-  ASSERT_SOME(preparation1.get());
-
-  ostringstream command1;
-
-  // Listen to 'localhost' and 'Port'.
-  command1 << "nc -u -l localhost " << validPort << " > " << trafficViaLoopback
-           << "&";
-
-  // Listen to 'public IP' and 'Port'.
-  command1 << "nc -u -l " << hostIP << " " << validPort << " > "
-           << trafficViaPublic << "&";
-
-  // Listen to 'public IP' and 'invalidPort'. This should not receive anything.
-  command1 << "nc -u -l " << invalidPort << " | tee " << trafficViaLoopback
-           << " " << trafficViaPublic << "&";
-
-  // Touch the guard file.
-  command1 << "touch " << container1Ready;
-
-  int pipes[2];
-  ASSERT_NE(-1, ::pipe(pipes));
-
-  Try<pid_t> pid = launchHelper(
-      launcher.get(),
-      pipes,
-      containerId,
-      command1.str(),
-      preparation1.get());
-
-  ASSERT_SOME(pid);
-
-  // Reap the forked child.
-  Future<Option<int> > status = process::reap(pid.get());
-
-  // Continue in the parent.
-  ::close(pipes[0]);
-
-  // Isolate the forked child.
-  AWAIT_READY(isolator.get()->isolate(containerId, pid.get()));
-
-  // Now signal the child to continue.
-  char dummy;
-  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
-  ::close(pipes[1]);
-
-  // Wait for the command to start.
-  ASSERT_TRUE(waitForFileCreation(container1Ready));
-
-  // Send to 'localhost' and 'port'.
-  ostringstream command2;
-  command2 << "printf hello1 | nc -w1 -u localhost " << validPort;
-  ASSERT_SOME_EQ(0, os::shell(NULL, command2.str().c_str()));
-
-  // Send to 'localhost' and 'invalidPort'. The command should return
-  // successfully because UDP is stateless but no data could be sent.
-  ostringstream command3;
-  command3 << "printf hello2 | nc -w1 -u localhost " << invalidPort;
-  ASSERT_SOME_EQ(0, os::shell(NULL, command3.str().c_str()));
-
-  // Send to 'public IP' and 'port'.
-  ostringstream command4;
-  command4 << "printf hello3 | nc -w1 -u " << hostIP << " " << validPort;
-  ASSERT_SOME_EQ(0, os::shell(NULL, command4.str().c_str()));
-
-  // Send to 'public IP' and 'invalidPort'. The command should return
-  // successfully because UDP is stateless but no data could be sent.
-  ostringstream command5;
-  command5 << "printf hello4 | nc -w1 -u " << hostIP << " " << invalidPort;
-  ASSERT_SOME_EQ(0, os::shell(NULL, command5.str().c_str()));
-
-  EXPECT_SOME_EQ("hello1", os::read(trafficViaLoopback));
-  EXPECT_SOME_EQ("hello3", os::read(trafficViaPublic));
-
-  // Ensure all processes are killed.
-  AWAIT_READY(launcher.get()->destroy(containerId));
-
-  // Let the isolator clean up.
-  AWAIT_READY(isolator.get()->cleanup(containerId));
-
-  delete isolator.get();
-  delete launcher.get();
-}
-
-
-// Test the scenario where a TCP server is in a container while host
-// tries to establish a TCP connection.
-TEST_F(PortMappingIsolatorTest, ROOT_NC_HostToContainerTCP)
-{
-  Try<Isolator*> isolator = PortMappingIsolatorProcess::create(flags);
-  CHECK_SOME(isolator);
-
-  Try<Launcher*> launcher =
-    LinuxLauncher::create(flags, isolator.get()->namespaces().get());
-  CHECK_SOME(launcher);
-
-  // Set the executor's resources.
-  ExecutorInfo executorInfo;
-  executorInfo.mutable_resources()->CopyFrom(
-      Resources::parse(container1Ports).get());
-
-  ContainerID containerId;
-  containerId.set_value("container1");
-
-  // Use a relative temporary directory so it gets cleaned up
-  // automatically with the test.
-  Try<string> dir = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
-  ASSERT_SOME(dir);
-
-  Future<Option<CommandInfo> > preparation1 =
-    isolator.get()->prepare(
-        containerId,
-        executorInfo,
-        dir.get(),
-        None(),
-        None());
-
-  AWAIT_READY(preparation1);
-  ASSERT_SOME(preparation1.get());
-
-  ostringstream command1;
-
-  // Listen to 'localhost' and 'Port'.
-  command1 << "nc -l localhost " << validPort << " > " << trafficViaLoopback
-           << "&";
-
-  // Listen to 'public IP' and 'Port'.
-  command1 << "nc -l " << hostIP << " " << validPort << " > "
-           << trafficViaPublic << "&";
-
-  // Listen to 'public IP' and 'invalidPort'. This should fail.
-  command1 << "nc -l " << invalidPort << " | tee " << trafficViaLoopback << " "
-           << trafficViaPublic << "&";
-
-  // Touch the guard file.
-  command1 << "touch " << container1Ready;
-
-  int pipes[2];
-  ASSERT_NE(-1, ::pipe(pipes));
-
-  Try<pid_t> pid = launchHelper(
-      launcher.get(),
-      pipes,
-      containerId,
-      command1.str(),
-      preparation1.get());
-
-  ASSERT_SOME(pid);
-
-  // Reap the forked child.
-  Future<Option<int> > status = process::reap(pid.get());
-
-  // Continue in the parent.
-  ::close(pipes[0]);
-
-  // Isolate the forked child.
-  AWAIT_READY(isolator.get()->isolate(containerId, pid.get()));
-
-  // Now signal the child to continue.
-  char dummy;
-  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
-  ::close(pipes[1]);
-
-  // Wait for the command to start.
-  ASSERT_TRUE(waitForFileCreation(container1Ready));
-
-  // Send to 'localhost' and 'port'.
-  ostringstream command2;
-  command2 << "printf hello1 | nc localhost " << validPort;
-  ASSERT_SOME_EQ(0, os::shell(NULL, command2.str().c_str()));
-
-  // Send to 'localhost' and 'invalidPort'. This should fail because TCP
-  // connection couldn't be established..
-  ostringstream command3;
-  command3 << "printf hello2 | nc localhost " << invalidPort;
-  ASSERT_SOME_EQ(256, os::shell(NULL, command3.str().c_str()));
-
-  // Send to 'public IP' and 'port'.
-  ostringstream command4;
-  command4 << "printf hello3 | nc " << hostIP << " " << validPort;
-  ASSERT_SOME_EQ(0, os::shell(NULL, command4.str().c_str()));
-
-  // Send to 'public IP' and 'invalidPort'. This should fail because TCP
-  // connection couldn't be established.
-  ostringstream command5;
-  command5 << "printf hello4 | nc " << hostIP << " " << invalidPort;
-  ASSERT_SOME_EQ(256, os::shell(NULL, command5.str().c_str()));
-
-  EXPECT_SOME_EQ("hello1", os::read(trafficViaLoopback));
-  EXPECT_SOME_EQ("hello3", os::read(trafficViaPublic));
-
-  // Ensure all processes are killed.
-  AWAIT_READY(launcher.get()->destroy(containerId));
-
-  // Let the isolator clean up.
-  AWAIT_READY(isolator.get()->cleanup(containerId));
-
-  delete isolator.get();
-  delete launcher.get();
-}
-
-
-// Test the scenario where a container issues ICMP requests to
-// external hosts.
-TEST_F(PortMappingIsolatorTest, ROOT_ContainerICMPExternal)
-{
-  // TODO(chzhcn): Even though this is unlikely, consider a better
-  // way to get external servers.
-  ASSERT_FALSE(nameServers.empty())
-    << "-------------------------------------------------------------\n"
-    << "We cannot run some PortMappingIsolatorTests because we could\n"
-    << "not find any external name servers in /etc/resolv.conf.\n"
-    << "-------------------------------------------------------------";
-
-  Try<Isolator*> isolator = PortMappingIsolatorProcess::create(flags);
-  CHECK_SOME(isolator);
-
-  Try<Launcher*> launcher =
-    LinuxLauncher::create(flags, isolator.get()->namespaces().get());
-  CHECK_SOME(launcher);
-
-  // Set the executor's resources.
-  ExecutorInfo executorInfo;
-  executorInfo.mutable_resources()->CopyFrom(
-      Resources::parse(container1Ports).get());
-
-  ContainerID containerId;
-  containerId.set_value("container1");
-
-  // Use a relative temporary directory so it gets cleaned up
-  // automatically with the test.
-  Try<string> dir = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
-  ASSERT_SOME(dir);
-
-  Future<Option<CommandInfo> > preparation1 =
-    isolator.get()->prepare(
-        containerId,
-        executorInfo,
-        dir.get(),
-        None(),
-        None());
-
-  AWAIT_READY(preparation1);
-  ASSERT_SOME(preparation1.get());
-
-  ostringstream command1;
-  for (unsigned int i = 0; i < nameServers.size(); i++) {
-    const string& IP = nameServers[i];
-    command1 << "ping -c1 " << IP;
-    if (i + 1 < nameServers.size()) {
-      command1 << " && ";
-    }
-  }
-  command1 << "; printf $? > " << exitStatus << "; sync";
-
-  int pipes[2];
-  ASSERT_NE(-1, ::pipe(pipes));
-
-  Try<pid_t> pid = launchHelper(
-      launcher.get(),
-      pipes,
-      containerId,
-      command1.str(),
-      preparation1.get());
-
-  ASSERT_SOME(pid);
-
-  // Reap the forked child.
-  Future<Option<int> > status = process::reap(pid.get());
-
-  // Continue in the parent.
-  ::close(pipes[0]);
-
-  // Isolate the forked child.
-  AWAIT_READY(isolator.get()->isolate(containerId, pid.get()));
-
-  // Now signal the child to continue.
-  char dummy;
-  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
-  ::close(pipes[1]);
-
-  // Wait for the command to complete.
-  AWAIT_READY(status);
-
-  EXPECT_SOME_EQ("0", os::read(exitStatus));
-
-  // Ensure all processes are killed.
-  AWAIT_READY(launcher.get()->destroy(containerId));
-
-  // Let the isolator clean up.
-  AWAIT_READY(isolator.get()->cleanup(containerId));
-
-  delete isolator.get();
-  delete launcher.get();
-}
-
-
-// Test the scenario where a container issues ICMP requests to itself.
-TEST_F(PortMappingIsolatorTest, ROOT_ContainerICMPInternal)
-{
-  Try<Isolator*> isolator = PortMappingIsolatorProcess::create(flags);
-  CHECK_SOME(isolator);
-
-  Try<Launcher*> launcher =
-    LinuxLauncher::create(flags, isolator.get()->namespaces().get());
-  CHECK_SOME(launcher);
-
-  // Set the executor's resources.
-  ExecutorInfo executorInfo;
-  executorInfo.mutable_resources()->CopyFrom(
-      Resources::parse(container1Ports).get());
-
-  ContainerID containerId;
-  containerId.set_value("container1");
-
-  // Use a relative temporary directory so it gets cleaned up
-  // automatically with the test.
-  Try<string> dir = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
-  ASSERT_SOME(dir);
-
-  Future<Option<CommandInfo> > preparation1 =
-    isolator.get()->prepare(
-        containerId,
-        executorInfo,
-        dir.get(),
-        None(),
-        None());
-
-  AWAIT_READY(preparation1);
-  ASSERT_SOME(preparation1.get());
-
-  ostringstream command1;
-  command1 << "ping -c1 127.0.0.1 && ping -c1 " << hostIP
-           << "; printf $? > " << exitStatus << "; sync";
-
-  int pipes[2];
-  ASSERT_NE(-1, ::pipe(pipes));
-
-  Try<pid_t> pid = launchHelper(
-      launcher.get(),
-      pipes,
-      containerId,
-      command1.str(),
-      preparation1.get());
-
-  ASSERT_SOME(pid);
-
-  // Reap the forked child.
-  Future<Option<int> > status = process::reap(pid.get());
-
-  // Continue in the parent.
-  ::close(pipes[0]);
-
-  // Isolate the forked child.
-  AWAIT_READY(isolator.get()->isolate(containerId, pid.get()));
-
-  // Now signal the child to continue.
-  char dummy;
-  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
-  ::close(pipes[1]);
-
-  // Wait for the command to complete.
-  AWAIT_READY(status);
-
-  EXPECT_SOME_EQ("0", os::read(exitStatus));
-
-  // Ensure all processes are killed.
-  AWAIT_READY(launcher.get()->destroy(containerId));
-
-  // Let the isolator clean up.
-  AWAIT_READY(isolator.get()->cleanup(containerId));
-
-  delete isolator.get();
-  delete launcher.get();
-}
-
-
-// Test the scenario where a container issues ARP requests to
-// external hosts.
-TEST_F(PortMappingIsolatorTest, ROOT_ContainerARPExternal)
-{
-  // TODO(chzhcn): Even though this is unlikely, consider a better
-  // way to get external servers.
-  ASSERT_FALSE(nameServers.empty())
-    << "-------------------------------------------------------------\n"
-    << "We cannot run some PortMappingIsolatorTests because we could\n"
-    << "not find any external name servers in /etc/resolv.conf.\n"
-    << "-------------------------------------------------------------";
-
-  Try<Isolator*> isolator = PortMappingIsolatorProcess::create(flags);
-  CHECK_SOME(isolator);
-
-  Try<Launcher*> launcher =
-    LinuxLauncher::create(flags, isolator.get()->namespaces().get());
-  CHECK_SOME(launcher);
-
-  // Set the executor's resources.
-  ExecutorInfo executorInfo;
-  executorInfo.mutable_resources()->CopyFrom(
-      Resources::parse(container1Ports).get());
-
-  ContainerID containerId;
-  containerId.set_value("container1");
-
-  // Use a relative temporary directory so it gets cleaned up
-  // automatically with the test.
-  Try<string> dir = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
-  ASSERT_SOME(dir);
-
-  Future<Option<CommandInfo> > preparation1 =
-    isolator.get()->prepare(
-        containerId,
-        executorInfo,
-        dir.get(),
-        None(),
-        None());
-
-  AWAIT_READY(preparation1);
-  ASSERT_SOME(preparation1.get());
-
-  ostringstream command1;
-  for (unsigned int i = 0; i < nameServers.size(); i++) {
-    const string& IP = nameServers[i];
-    // Time out after 1s and terminate upon receiving the first reply.
-    command1 << "arping -f -w1 " << IP << " -I " << eth0;
-    if (i + 1 < nameServers.size()) {
-      command1 << " && ";
-    }
-  }
-  command1 << "; printf $? > " << exitStatus << "; sync";
-
-  int pipes[2];
-  ASSERT_NE(-1, ::pipe(pipes));
-
-  Try<pid_t> pid = launchHelper(
-      launcher.get(),
-      pipes,
-      containerId,
-      command1.str(),
-      preparation1.get());
-
-  ASSERT_SOME(pid);
-
-  // Reap the forked child.
-  Future<Option<int> > status = process::reap(pid.get());
-
-  // Continue in the parent.
-  ::close(pipes[0]);
-
-  // Isolate the forked child.
-  AWAIT_READY(isolator.get()->isolate(containerId, pid.get()));
-
-  // Now signal the child to continue.
-  char dummy;
-  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
-  ::close(pipes[1]);
-
-  // Wait for the command to complete.
-  AWAIT_READY(status);
-
-  EXPECT_SOME_EQ("0", os::read(exitStatus));
-
-  // Ensure all processes are killed.
-  AWAIT_READY(launcher.get()->destroy(containerId));
-
-  // Let the isolator clean up.
-  AWAIT_READY(isolator.get()->cleanup(containerId));
-
-  delete isolator.get();
-  delete launcher.get();
-}
-
-
-// Test DNS connectivity.
-TEST_F(PortMappingIsolatorTest, ROOT_DNS)
-{
-  // TODO(chzhcn): Even though this is unlikely, consider a better
-  // way to get external servers.
-  ASSERT_FALSE(nameServers.empty())
-    << "-------------------------------------------------------------\n"
-    << "We cannot run some PortMappingIsolatorTests because we could\n"
-    << "not find any external name servers in /etc/resolv.conf.\n"
-    << "-------------------------------------------------------------";
-
-  Try<Isolator*> isolator = PortMappingIsolatorProcess::create(flags);
-  CHECK_SOME(isolator);
-
-  Try<Launcher*> launcher =
-    LinuxLauncher::create(flags, isolator.get()->namespaces().get());
-  CHECK_SOME(launcher);
-
-  // Set the executor's resources.
-  ExecutorInfo executorInfo;
-  executorInfo.mutable_resources()->CopyFrom(
-      Resources::parse(container1Ports).get());
-
-  ContainerID containerId;
-  containerId.set_value("container1");
-
-  // Use a relative temporary directory so it gets cleaned up
-  // automatically with the test.
-  Try<string> dir = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
-  ASSERT_SOME(dir);
-
-  Future<Option<CommandInfo> > preparation1 =
-    isolator.get()->prepare(
-        containerId,
-        executorInfo,
-        dir.get(),
-        None(),
-        None());
-
-  AWAIT_READY(preparation1);
-  ASSERT_SOME(preparation1.get());
-
-  ostringstream command1;
-  for (unsigned int i = 0; i < nameServers.size(); i++) {
-    const string& IP = nameServers[i];
-    command1 << "host " << IP;
-    if (i + 1 < nameServers.size()) {
-      command1 << " && ";
-    }
-  }
-  command1 << "; printf $? > " << exitStatus << "; sync";
-
-  int pipes[2];
-  ASSERT_NE(-1, ::pipe(pipes));
-
-  Try<pid_t> pid = launchHelper(
-      launcher.get(),
-      pipes,
-      containerId,
-      command1.str(),
-      preparation1.get());
-
-  ASSERT_SOME(pid);
-
-  // Reap the forked child.
-  Future<Option<int> > status = process::reap(pid.get());
-
-  // Continue in the parent.
-  ::close(pipes[0]);
-
-  // Isolate the forked child.
-  AWAIT_READY(isolator.get()->isolate(containerId, pid.get()));
-
-  // Now signal the child to continue.
-  char dummy;
-  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
-  ::close(pipes[1]);
-
-  // Wait for the command to complete.
-  AWAIT_READY(status);
-
-  EXPECT_SOME_EQ("0", os::read(exitStatus));
-
-  // Ensure all processes are killed.
-  AWAIT_READY(launcher.get()->destroy(containerId));
-
-  // Let the isolator clean up.
-  AWAIT_READY(isolator.get()->cleanup(containerId));
-
-  delete isolator.get();
-  delete launcher.get();
-}
-
-
-// Test the scenario where a container has run out of ephemeral ports
-// to use.
-TEST_F(PortMappingIsolatorTest, ROOT_TooManyContainers)
-{
-  // Increase the ephemeral ports per container so that we dont have
-  // enough ephemeral ports to launch a second container.
-  flags.ephemeral_ports_per_container = 512;
-
-  Try<Isolator*> isolator = PortMappingIsolatorProcess::create(flags);
-  CHECK_SOME(isolator);
-
-  Try<Launcher*> launcher =
-    LinuxLauncher::create(flags, isolator.get()->namespaces().get());
-  CHECK_SOME(launcher);
-
-  // Set the executor's resources.
-  ExecutorInfo executorInfo;
-  executorInfo.mutable_resources()->CopyFrom(
-      Resources::parse(container1Ports).get());
-
-  ContainerID containerId1;
-  containerId1.set_value("container1");
-
-  // Use a relative temporary directory so it gets cleaned up
-  // automatically with the test.
-  Try<string> dir1 = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
-  ASSERT_SOME(dir1);
-
-  Future<Option<CommandInfo> > preparation1 =
-    isolator.get()->prepare(
-        containerId1,
-        executorInfo,
-        dir1.get(),
-        None(),
-        None());
-
-  AWAIT_READY(preparation1);
-  ASSERT_SOME(preparation1.get());
-
-  ostringstream command1;
-  command1 << "sleep 1000";
-
-  int pipes[2];
-  ASSERT_NE(-1, ::pipe(pipes));
-
-  Try<pid_t> pid = launchHelper(
-      launcher.get(),
-      pipes,
-      containerId1,
-      command1.str(),
-      preparation1.get());
-
-  ASSERT_SOME(pid);
-
-  // Reap the forked child.
-  Future<Option<int> > status1 = process::reap(pid.get());
-
-  // Continue in the parent.
-  ::close(pipes[0]);
-
-  // Isolate the forked child.
-  AWAIT_READY(isolator.get()->isolate(containerId1, pid.get()));
-
-  // Now signal the child to continue.
-  char dummy;
-  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
-  ::close(pipes[1]);
-
-  ContainerID containerId2;
-  containerId2.set_value("container2");
-
-  executorInfo.mutable_resources()->CopyFrom(
-      Resources::parse(container2Ports).get());
-
-  // Use a relative temporary directory so it gets cleaned up
-  // automatically with the test.
-  Try<string> dir2 = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
-  ASSERT_SOME(dir2);
-
-  Future<Option<CommandInfo> > preparation2 =
-    isolator.get()->prepare(
-        containerId2,
-        executorInfo,
-        dir2.get(),
-        None(),
-        None());
-
-  AWAIT_FAILED(preparation2);
-
-  // Ensure all processes are killed.
-  AWAIT_READY(launcher.get()->destroy(containerId1));
-
-  // Let the isolator clean up.
-  AWAIT_READY(isolator.get()->cleanup(containerId1));
-
-  delete isolator.get();
-  delete launcher.get();
-}
-
-
-// Test the scenario where PortMappingIsolator uses a very small
-// egress rate limit.
-TEST_F(PortMappingIsolatorTest, ROOT_NC_SmallEgressLimit)
-{
-  // Note that the underlying rate limiting mechanism usually has a
-  // small allowance for burst. Empirically, as least 10x of the rate
-  // limit amount of data is required to make sure the burst is an
-  // insignificant factor of the transmission time.
-
-  // To-be-tested egress rate limit, in Bytes/s.
-  const Bytes rate = 2000;
-  // Size of the data to send, in Bytes.
-  const Bytes size = 20480;
-
-  // Use a very small egress limit.
-  flags.egress_rate_limit_per_container = rate;
-
-  Try<Isolator*> isolator = PortMappingIsolatorProcess::create(flags);
-  CHECK_SOME(isolator);
-
-  Try<Launcher*> launcher =
-    LinuxLauncher::create(flags, isolator.get()->namespaces().get());
-  CHECK_SOME(launcher);
-
-  // Open an nc server on the host side. Note that 'invalidPort' is in
-  // neither 'ports' nor 'ephemeral_ports', which makes it a good port
-  // to use on the host.
-  ostringstream command1;
-  command1 << "nc -l localhost " << invalidPort << " > /devnull";
-  Try<Subprocess> s = subprocess(command1.str().c_str());
-  CHECK_SOME(s);
-
-  // Set the executor's resources.
-  ExecutorInfo executorInfo;
-  executorInfo.mutable_resources()->CopyFrom(
-      Resources::parse(container1Ports).get());
-
-  ContainerID containerId;
-  containerId.set_value("container1");
-
-  // Use a relative temporary directory so it gets cleaned up
-  // automatically with the test.
-  Try<string> dir = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
-  ASSERT_SOME(dir);
-
-  Future<Option<CommandInfo> > preparation1 =
-    isolator.get()->prepare(
-        containerId,
-        executorInfo,
-        dir.get(),
-        None(),
-        None());
-
-  AWAIT_READY(preparation1);
-  ASSERT_SOME(preparation1.get());
-
-  // Fill 'size' bytes of data. The actual content does not matter.
-  string data(size.bytes(), 'a');
-
-  ostringstream command2;
-  const string transmissionTime = path::join(os::getcwd(), "transmission_time");
-
-  command2 << "echo 'Sending " << size.bytes()
-           << " bytes of data under egress rate limit " << rate.bytes()
-           << "Bytes/s...';";
-
-  command2 << "{ time -p echo " << data  << " | nc localhost "
-           << invalidPort << " ; } 2> " << transmissionTime << " && ";
-
-  // Touch the guard file.
-  command2 << "touch " << container1Ready;
-
-  int pipes[2];
-  ASSERT_NE(-1, ::pipe(pipes));
-
-  Try<pid_t> pid = launchHelper(
-      launcher.get(),
-      pipes,
-      containerId,
-      command2.str(),
-      preparation1.get());
-
-  ASSERT_SOME(pid);
-
-  // Reap the forked child.
-  Future<Option<int> > reap = process::reap(pid.get());
-
-  // Continue in the parent.
-  ::close(pipes[0]);
-
-  // Isolate the forked child.
-  AWAIT_READY(isolator.get()->isolate(containerId, pid.get()));
-
-  // Now signal the child to continue.
-  char dummy;
-  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
-  ::close(pipes[1]);
-
-  // Wait for the command to finish.
-  ASSERT_TRUE(waitForFileCreation(container1Ready));
-
-  Try<string> read = os::read(transmissionTime);
-  CHECK_SOME(read);
-
-  // Get the real elapsed time from `time` output. Sample output:
-  // real 12.37
-  // user 0.00
-  // sys 0.00
-  vector<string> lines = strings::split(strings::trim(read.get()), "\n");
-  ASSERT_EQ(3u, lines.size());
-
-  vector<string> split = strings::split(lines[0], " ");
-  ASSERT_EQ(2u, split.size());
-
-  Try<float> time = numify<float>(split[1]);
-  ASSERT_SOME(time);
-  ASSERT_GT(time.get(), (size.bytes() / rate.bytes()));
-
-  // Make sure the nc server exits normally.
-  Future<Option<int> > status = s.get().status();
-  AWAIT_READY(status);
-  EXPECT_SOME_EQ(0, status.get());
-
-  // Ensure all processes are killed.
-  AWAIT_READY(launcher.get()->destroy(containerId));
-
-  // Let the isolator clean up.
-  AWAIT_READY(isolator.get()->cleanup(containerId));
-
-  delete isolator.get();
-  delete launcher.get();
-}
-
-
-bool HasTCPSocketsCount(const ResourceStatistics& statistics)
-{
-  return statistics.has_net_tcp_active_connections() &&
-    statistics.has_net_tcp_time_wait_connections();
-}
-
-
-bool HasTCPSocketsRTT(const ResourceStatistics& statistics)
-{
-  // We either have all of the following metrics or we have nothing.
-  if (statistics.has_net_tcp_rtt_microsecs_p50() &&
-      statistics.has_net_tcp_rtt_microsecs_p90() &&
-      statistics.has_net_tcp_rtt_microsecs_p95() &&
-      statistics.has_net_tcp_rtt_microsecs_p99()) {
-    return true;
-  } else {
-    return false;
-  }
-}
-
-
-// Test that RTT can be returned properly from usage(). This test is
-// very similar to SmallEgressLimitTest in its setup.
-TEST_F(PortMappingIsolatorTest, ROOT_NC_PortMappingStatistics)
-{
-  // To-be-tested egress rate limit, in Bytes/s.
-  const Bytes rate = 2000;
-  // Size of the data to send, in Bytes.
-  const Bytes size = 20480;
-
-  // Use a very small egress limit.
-  flags.egress_rate_limit_per_container = rate;
-  flags.network_enable_socket_statistics_summary = true;
-  flags.network_enable_socket_statistics_details = true;
-
-  Try<Isolator*> isolator = PortMappingIsolatorProcess::create(flags);
-  CHECK_SOME(isolator);
-
-  Try<Launcher*> launcher =
-    LinuxLauncher::create(flags, isolator.get()->namespaces().get());
-  CHECK_SOME(launcher);
-
-  // Open an nc server on the host side. Note that 'invalidPort' is
-  // in neither 'ports' nor 'ephemeral_ports', which makes it a good
-  // port to use on the host. We use this host's public IP because
-  // connections to the localhost IP are filtered out when retrieving
-  // the RTT information inside containers.
-  ostringstream command1;
-  command1 << "nc -l " << hostIP << " " << invalidPort << " > /devnull";
-  Try<Subprocess> s = subprocess(command1.str().c_str());
-  CHECK_SOME(s);
-
-  // Set the executor's resources.
-  ExecutorInfo executorInfo;
-  executorInfo.mutable_resources()->CopyFrom(
-      Resources::parse(container1Ports).get());
-
-  ContainerID containerId;
-  containerId.set_value("container1");
-
-  // Use a relative temporary directory so it gets cleaned up
-  // automatically with the test.
-  Try<string> dir1 = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
-  ASSERT_SOME(dir1);
-
-  Future<Option<CommandInfo> > preparation1 =
-    isolator.get()->prepare(
-        containerId,
-        executorInfo,
-        dir1.get(),
-        None(),
-        None());
-
-  AWAIT_READY(preparation1);
-  ASSERT_SOME(preparation1.get());
-
-  // Fill 'size' bytes of data. The actual content does not matter.
-  string data(size.bytes(), 'a');
-
-  ostringstream command2;
-  const string transmissionTime = path::join(os::getcwd(), "transmission_time");
-
-  command2 << "echo 'Sending " << size.bytes()
-           << " bytes of data under egress rate limit " << rate.bytes()
-           << "Bytes/s...';";
-
-  command2 << "{ time -p echo " << data  << " | nc " << hostIP << " "
-           << invalidPort << " ; } 2> " << transmissionTime << " && ";
-
-  // Touch the guard file.
-  command2 << "touch " << container1Ready;
-
-  int pipes[2];
-  ASSERT_NE(-1, ::pipe(pipes));
-
-  Try<pid_t> pid = launchHelper(
-      launcher.get(),
-      pipes,
-      containerId,
-      command2.str(),
-      preparation1.get());
-
-  ASSERT_SOME(pid);
-
-  // Reap the forked child.
-  Future<Option<int> > reap = process::reap(pid.get());
-
-  // Continue in the parent.
-  ::close(pipes[0]);
-
-  // Isolate the forked child.
-  AWAIT_READY(isolator.get()->isolate(containerId, pid.get()));
-
-  // Now signal the child to continue.
-  char dummy;
-  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
-  ::close(pipes[1]);
-
-  // Test that RTT can be returned while transmission is going. It is
-  // possible that the first few statistics returned don't have a RTT
-  // value because it takes a few round-trips to actually establish a
-  // tcp connection and start sending data. Nevertheless, we should
-  // see a meaningful result well within seconds.
-  Duration waited = Duration::zero();
-  do {
-    os::sleep(Milliseconds(200));
-    waited += Milliseconds(200);
-
-    // Do an end-to-end test by calling `usage`.
-    Future<ResourceStatistics> usage = isolator.get()->usage(containerId);
-    AWAIT_READY(usage);
-
-    if (usage.get().has_net_tcp_rtt_microsecs_p50() &&
-        usage.get().has_net_tcp_active_connections()) {
-      EXPECT_GT(usage.get().net_tcp_active_connections(), 0);
-      break;
-    }
-  } while (waited < Seconds(5));
-  ASSERT_LT(waited, Seconds(5));
-
-  // While the connection is still active, try out different flag
-  // combinations.
-  Result<ResourceStatistics> statistics =
-      statisticsHelper(pid.get(), true, true);
-  ASSERT_SOME(statistics);
-  EXPECT_TRUE(HasTCPSocketsCount(statistics.get()));
-  EXPECT_TRUE(HasTCPSocketsRTT(statistics.get()));
-
-  statistics = statisticsHelper(pid.get(), true, false);
-  ASSERT_SOME(statistics);
-  EXPECT_TRUE(HasTCPSocketsCount(statistics.get()));
-  EXPECT_FALSE(HasTCPSocketsRTT(statistics.get()));
-
-  statistics = statisticsHelper(pid.get(), false, true);
-  ASSERT_SOME(statistics);
-  EXPECT_FALSE(HasTCPSocketsCount(statistics.get()));
-  EXPECT_TRUE(HasTCPSocketsRTT(statistics.get()));
-
-  statistics = statisticsHelper(pid.get(), false, false);
-  ASSERT_SOME(statistics);
-  EXPECT_FALSE(HasTCPSocketsCount(statistics.get()));
-  EXPECT_FALSE(HasTCPSocketsRTT(statistics.get()));
-
-  // Wait for the command to finish.
-  ASSERT_TRUE(waitForFileCreation(container1Ready));
-
-  // Make sure the nc server exits normally.
-  Future<Option<int> > status = s.get().status();
-  AWAIT_READY(status);
-  EXPECT_SOME_EQ(0, status.get());
-
-  // Ensure all processes are killed.
-  AWAIT_READY(launcher.get()->destroy(containerId));
-
-  // Let the isolator clean up.
-  AWAIT_READY(isolator.get()->cleanup(containerId));
-
-  delete isolator.get();
-  delete launcher.get();
-}
-
-
-class PortMappingMesosTest : public ContainerizerTest<MesosContainerizer>
-{
-public:
-  virtual void SetUp()
-  {
-    ContainerizerTest<MesosContainerizer>::SetUp();
-
-    // Guess the name of the public interface.
-    Result<string> _eth0 = link::eth0();
-    ASSERT_SOME(_eth0) << "Failed to guess the name of the public interface";
-
-    eth0 = _eth0.get();
-
-    LOG(INFO) << "Using " << eth0 << " as the public interface";
-
-    // Guess the name of the loopback interface.
-    Result<string> _lo = link::lo();
-    ASSERT_SOME(_lo) << "Failed to guess the name of the loopback interface";
-
-    lo = _lo.get();
-
-    LOG(INFO) << "Using " << lo << " as the loopback interface";
-
-    cleanup(eth0, lo);
-  }
-
-  virtual void TearDown()
-  {
-    cleanup(eth0, lo);
-
-    ContainerizerTest<MesosContainerizer>::TearDown();
-  }
-
-  Fetcher fetcher;
-
-  // Name of the host eth0 and lo.
-  string eth0;
-  string lo;
-};
-
-
-// Test the scenario where the network isolator is asked to recover
-// both types of containers: containers that were previously managed
-// by network isolator, and containers that weren't.
-TEST_F(PortMappingMesosTest, CGROUPS_ROOT_RecoverMixedContainers)
-{
-  master::Flags masterFlags = CreateMasterFlags();
-
-  Try<PID<Master>> master = StartMaster(masterFlags);
-  ASSERT_SOME(master);
-
-  // Start the first slave without the network isolator.
-  slave::Flags slaveFlags = CreateSlaveFlags();
-
-  // NOTE: This is to make sure that we use the linux launcher which
-  // is consistent with the launchers we use for other containerizers
-  // we create in this test. Also, this will bypass MESOS-2554.
-  slaveFlags.isolation = "cgroups/cpu,cgroups/mem";
-
-  Try<MesosContainerizer*> containerizer1 =
-    MesosContainerizer::create(slaveFlags, true, &fetcher);
-
-  ASSERT_SOME(containerizer1);
-
-  Try<PID<Slave> > slave = StartSlave(containerizer1.get(), slaveFlags);
-  ASSERT_SOME(slave);
-
-  MockScheduler sched;
-
-  // Enable checkpointing for the framework.
-  FrameworkInfo frameworkInfo = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo.set_checkpoint(true);
-
-  MesosSchedulerDriver driver(
-      &sched, frameworkInfo, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(sched, registered(_, _, _));
-
-  Filters filters;
-  filters.set_refuse_seconds(0);
-
-  // NOTE: We set filter explicitly here so that the resources will
-  // not be filtered for 5 seconds (by default).
-  Future<vector<Offer> > offers1;
-  EXPECT_CALL(sched, resourceOffers(_, _))
-    .WillOnce(FutureArg<1>(&offers1))
-    .WillRepeatedly(DeclineOffers(filters));      // Ignore subsequent offers.
-
-  driver.start();
-
-  AWAIT_READY(offers1);
-  EXPECT_NE(0u, offers1.get().size());
-
-  Offer offer1 = offers1.get()[0];
-
-  // Start a long running task without using the network isolator.
-  TaskInfo task1 = createTask(
-      offer1.slave_id(),
-      Resources::parse("cpus:1;mem:512").get(),
-      "sleep 1000");
-
-  EXPECT_CALL(sched, statusUpdate(_, _));
-
-  Future<Nothing> _statusUpdateAcknowledgement1 =
-    FUTURE_DISPATCH(_, &Slave::_statusUpdateAcknowledgement);
-
-  driver.launchTasks(offers1.get()[0].id(), {task1}, filters);
-
-  // Wait for the ACK to be checkpointed.
-  AWAIT_READY(_statusUpdateAcknowledgement1);
-
-  Stop(slave.get());
-  delete containerizer1.get();
-
-  Future<Nothing> _recover1 = FUTURE_DISPATCH(_, &Slave::_recover);
-
-  Future<SlaveReregisteredMessage> slaveReregisteredMessage1 =
-    FUTURE_PROTOBUF(SlaveReregisteredMessage(), _, _);
-
-  Future<vector<Offer> > offers2;
-  EXPECT_CALL(sched, resourceOffers(_, _))
-    .WillOnce(FutureArg<1>(&offers2))
-    .WillRepeatedly(DeclineOffers(filters));      // Ignore subsequent offers.
-
-  // Restart the slave with the network isolator.
-  slaveFlags.isolation += ",network/port_mapping";
-
-  Try<MesosContainerizer*> containerizer2 =
-    MesosContainerizer::create(slaveFlags, true, &fetcher);
-
-  ASSERT_SOME(containerizer2);
-
-  slave = StartSlave(containerizer2.get(), slaveFlags);
-  ASSERT_SOME(slave);
-
-  Clock::pause();
-
-  AWAIT_READY(_recover1);
-
-  Clock::settle(); // Wait for slave to schedule reregister timeout.
-  Clock::advance(EXECUTOR_REREGISTER_TIMEOUT);
-
-  AWAIT_READY(slaveReregisteredMessage1);
-
-  Clock::settle(); // Make sure an allocation is scheduled.
-  Clock::advance(masterFlags.allocation_interval);
-
-  Clock::resume();
-
-  AWAIT_READY(offers2);
-  EXPECT_NE(0u, offers2.get().size());
-
-  Offer offer2 = offers2.get()[0];
-
-  // Start a long running task using the network isolator.
-  TaskInfo task2 = createTask(offer2, "sleep 1000");
-
-  EXPECT_CALL(sched, statusUpdate(_, _));
-
-  Future<Nothing> _statusUpdateAcknowledgement2 =
-    FUTURE_DISPATCH(_, &Slave::_statusUpdateAcknowledgement);
-
-  driver.launchTasks(offers2.get()[0].id(), {task2});
-
-  // Wait for the ACK to be checkpointed.
-  AWAIT_READY(_statusUpdateAcknowledgement2);
-
-  Stop(slave.get());
-  delete containerizer2.get();
-
-  Future<Nothing> _recover2 = FUTURE_DISPATCH(_, &Slave::_recover);
-
-  Future<SlaveReregisteredMessage> slaveReregisteredMessage2 =
-    FUTURE_PROTOBUF(SlaveReregisteredMessage(), _, _);
-
-  // Restart the slave with the network isolator. This is to verify
-  // the slave recovery case where one task is running with the
-  // network isolator and another task is running without it.
-  Try<MesosContainerizer*> containerizer3 =
-    MesosContainerizer::create(slaveFlags, true, &fetcher);
-
-  ASSERT_SOME(containerizer3);
-
-  slave = StartSlave(containerizer3.get(), slaveFlags);
-  ASSERT_SOME(slave);
-
-  Clock::pause();
-
-  AWAIT_READY(_recover2);
-
-  Clock::settle(); // Wait for slave to schedule reregister timeout.
-  Clock::advance(EXECUTOR_REREGISTER_TIMEOUT);
-
-  AWAIT_READY(slaveReregisteredMessage2);
-
-  Clock::resume();
-
-  // Ensure that both containers (with and without network isolation)
-  // were recovered.
-  Future<hashset<ContainerID>> containers = containerizer3.get()->containers();
-  AWAIT_READY(containers);
-  EXPECT_EQ(2u, containers.get().size());
-
-  foreach (const ContainerID& containerId, containers.get()) {
-    // Do some basic checks to make sure the network isolator can
-    // handle mixed types of containers correctly.
-    Future<ResourceStatistics> usage = containerizer3.get()->usage(containerId);
-    AWAIT_READY(usage);
-
-    // TODO(chzhcn): Write a more thorough test for update.
-  }
-
-  driver.stop();
-  driver.join();
-
-  Shutdown();
-  delete containerizer3.get();
-}
-
-
-// Test that all configurations (tc filters etc) is cleaned up for an
-// orphaned container using the network isolator.
-TEST_F(PortMappingMesosTest, CGROUPS_ROOT_CleanUpOrphan)
-{
-  Try<PID<Master> > master = StartMaster();
-  ASSERT_SOME(master);
-
-  slave::Flags flags = CreateSlaveFlags();
-
-  // NOTE: We add 'cgroups/cpu,cgroups/mem' to bypass MESOS-2554.
-  flags.isolation = "cgroups/cpu,cgroups/mem,network/port_mapping";
-
-  Try<PID<Slave> > slave = StartSlave(flags);
-  ASSERT_SOME(slave);
-
-  MockScheduler sched;
-
-  // Enable checkpointing for the framework.
-  FrameworkInfo frameworkInfo = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo.set_checkpoint(true);
-
-  MesosSchedulerDriver driver(
-      &sched, frameworkInfo, master.get(), DEFAULT_CREDENTIAL);
-
-  Future<FrameworkID> frameworkId;
-  EXPECT_CALL(sched, registered(_, _, _))
-    .WillOnce(FutureArg<1>(&frameworkId));
-
-  Future<vector<Offer> > offers;
-  EXPECT_CALL(sched, resourceOffers(_, _))
-    .WillOnce(FutureArg<1>(&offers))
-    .WillRepeatedly(DeclineOffers());      // Ignore subsequent offers.
-
-  driver.start();
-
-  AWAIT_READY(offers);
-  EXPECT_NE(0u, offers.get().size());
-
-  // Start a long running task using network islator.
-  TaskInfo task = createTask(offers.get()[0], "sleep 1000");
-
-  EXPECT_CALL(sched, statusUpdate(_, _));
-
-  Future<Nothing> _statusUpdateAcknowledgement =
-    FUTURE_DISPATCH(_, &Slave::_statusUpdateAcknowledgement);
-
-  driver.launchTasks(offers.get()[0].id(), {task});
-
-  // Wait for the ACK to be checkpointed.
-  AWAIT_READY(_statusUpdateAcknowledgement);
-
-  Stop(slave.get());
-
-  // Wipe the slave meta directory so that the slave will treat the
-  // above running task as an orphan.
-  ASSERT_SOME(os::rmdir(paths::getMetaRootDir(flags.work_dir)));
-
-  Future<SlaveRegisteredMessage> slaveRegisteredMessage =
-    FUTURE_PROTOBUF(SlaveRegisteredMessage(), _, _);
-
-  Future<Nothing> orphansDestroyed =
-    FUTURE_DISPATCH(_, &MesosContainerizerProcess::___recover);
-
-  // Restart the slave.
-  slave = StartSlave(flags);
-  ASSERT_SOME(slave);
-
-  AWAIT_READY(slaveRegisteredMessage);
-
-  AWAIT_READY(orphansDestroyed);
-
-  // Expect that qdiscs still exist on eth0 and lo but with no filters.
-  Try<bool> hostEth0ExistsQdisc = ingress::exists(eth0);
-  EXPECT_SOME_TRUE(hostEth0ExistsQdisc);
-
-  Try<bool> hostLoExistsQdisc = ingress::exists(lo);
-  EXPECT_SOME_TRUE(hostLoExistsQdisc);
-
-  Result<vector<ip::Classifier> > classifiers =
-    ip::classifiers(eth0, ingress::HANDLE);
-
-  EXPECT_SOME(classifiers);
-  EXPECT_EQ(0u, classifiers.get().size());
-
-  classifiers = ip::classifiers(lo, ingress::HANDLE);
-  EXPECT_SOME(classifiers);
-  EXPECT_EQ(0u, classifiers.get().size());
-
-  // Expect no 'veth' devices.
-  Try<set<string> > links = net::links();
-  ASSERT_SOME(links);
-  foreach (const string& name, links.get()) {
-    EXPECT_FALSE(strings::startsWith(name, slave::PORT_MAPPING_VETH_PREFIX()));
-  }
-
-  // Expect no files in bind mount directory.
-  Try<list<string> > files = os::ls(slave::PORT_MAPPING_BIND_MOUNT_ROOT());
-  ASSERT_SOME(files);
-  EXPECT_EQ(0u, files.get().size());
-
-  driver.stop();
-  driver.join();
-
-  Shutdown();
-}
-
-
-// This test verfies the creation and destroy of the network namespace
-// handle symlink. The symlink was introduced in 0.23.0.
-TEST_F(PortMappingMesosTest, ROOT_NetworkNamespaceHandleSymlink)
-{
-  Try<PID<Master> > master = StartMaster();
-  ASSERT_SOME(master);
-
-  slave::Flags flags = CreateSlaveFlags();
-  flags.isolation = "network/port_mapping";
-
-  Try<MesosContainerizer*> containerizer =
-    MesosContainerizer::create(flags, true, &fetcher);
-
-  ASSERT_SOME(containerizer);
-
-  Try<PID<Slave>> slave = StartSlave(containerizer.get(), flags);
-  ASSERT_SOME(slave);
-
-  MockScheduler sched;
-
-  MesosSchedulerDriver driver(
-      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(sched, registered(_, _, _));
-
-  Future<vector<Offer> > offers;
-  EXPECT_CALL(sched, resourceOffers(_, _))
-    .WillOnce(FutureArg<1>(&offers))
-    .WillRepeatedly(DeclineOffers());      // Ignore subsequent offers.
-
-  driver.start();
-
-  AWAIT_READY(offers);
-  EXPECT_NE(0u, offers.get().size());
-
-  // Start a long running task using network islator.
-  TaskInfo task = createTask(offers.get()[0], "sleep 1000");
-
-  Future<TaskStatus> status1;
-  Future<TaskStatus> status2;
-  EXPECT_CALL(sched, statusUpdate(&driver, _))
-    .WillOnce(FutureArg<1>(&status1))
-    .WillOnce(FutureArg<1>(&status2))
-    .WillRepeatedly(Return());       // Ignore subsequent updates.
-
-  driver.launchTasks(offers.get()[0].id(), {task});
-
-  AWAIT_READY(status1);
-  EXPECT_EQ(task.task_id(), status1.get().task_id());
-  EXPECT_EQ(TASK_RUNNING, status1.get().state());
-
-  Future<hashset<ContainerID>> containers = containerizer.get()->containers();
-  AWAIT_READY(containers);
-  ASSERT_EQ(1u, containers.get().size());
-
-  ContainerID containerId = *(containers.get().begin());
-
-  const string symlink = path::join(
-      slave::PORT_MAPPING_BIND_MOUNT_SYMLINK_ROOT(),
-      stringify(containerId));
-
-  EXPECT_TRUE(os::exists(symlink));
-  EXPECT_TRUE(os::stat::islink(symlink));
-
-  Future<containerizer::Termination> termination =
-    containerizer.get()->wait(containerId);
-
-  driver.killTask(task.task_id());
-
-  AWAIT_READY(status2);
-  EXPECT_EQ(task.task_id(), status2.get().task_id());
-  EXPECT_EQ(TASK_KILLED, status2.get().state());
-
-  AWAIT_READY(termination);
-  EXPECT_FALSE(os::exists(symlink));
-
-  driver.stop();
-  driver.join();
-
-  Shutdown();
-
-  delete containerizer.get();
-}
-
-
-// This test verfies that the isolator is able to recover a mix of
-// known and unkonwn orphans. This is used to capture the regression
-// described in MESOS-2914.
-TEST_F(PortMappingMesosTest, CGROUPS_ROOT_RecoverMixedKnownAndUnKnownOrphans)
-{
-  Try<PID<Master>> master = StartMaster(CreateMasterFlags());
-  ASSERT_SOME(master);
-
-  slave::Flags flags = CreateSlaveFlags();
-  flags.isolation = "network/port_mapping";
-
-  Try<MesosContainerizer*> containerizer =
-    MesosContainerizer::create(flags, true, &fetcher);
-
-  ASSERT_SOME(containerizer);
-
-  Try<PID<Slave> > slave = StartSlave(containerizer.get(), flags);
-  ASSERT_SOME(slave);
-
-  MockScheduler sched;
-
-  FrameworkInfo frameworkInfo = DEFAULT_FRAMEWORK_INFO;
-  frameworkInfo.set_checkpoint(true);
-
-  MesosSchedulerDriver driver(
-      &sched, frameworkInfo, master.get(), DEFAULT_CREDENTIAL);
-
-  EXPECT_CALL(sched, registered(_, _, _));
-
-  Future<vector<Offer> > offers;
-  EXPECT_CALL(sched, resourceOffers(_, _))
-    .WillOnce(FutureArg<1>(&offers))
-    .WillRepeatedly(DeclineOffers());      // Ignore subsequent offers.
-
-  driver.start();
-
-  AWAIT_READY(offers);
-  EXPECT_NE(0u, offers.get().size());
-
-  Offer offer = offers.get()[0];
-
-  TaskInfo task1 = createTask(
-      offer.slave_id(),
-      Resources::parse("cpus:1;mem:64").get(),
-      "sleep 1000");
-
-  TaskInfo task2 = createTask(
-      offer.slave_id(),
-      Resources::parse("cpus:1;mem:64").get(),
-      "sleep 1000");
-
-  Future<TaskStatus> status1;
-  Future<TaskStatus> status2;
-  EXPECT_CALL(sched, statusUpdate(&driver, _))
-    .WillOnce(FutureArg<1>(&status1))
-    .WillOnce(FutureArg<1>(&status2))
-    .WillRepeatedly(Return());       // Ignore subsequent updates.
-
-  driver.launchTasks(offers.get()[0].id(), {task1, task2});
-
-  AWAIT_READY(status1);
-  ASSERT_EQ(TASK_RUNNING, status1.get().state());
-
-  AWAIT_READY(status2);
-  ASSERT_EQ(TASK_RUNNING, status2.get().state());
-
-  // Obtain the container IDs.
-  Future<hashset<ContainerID>> containers = containerizer.get()->containers();
-  AWAIT_READY(containers);
-  ASSERT_EQ(2u, containers.get().size());
-
-  Stop(slave.get());
-  delete containerizer.get();
-
-  // Wipe the slave meta directory so that the slave will treat the
-  // above running tasks as orphans.
-  ASSERT_SOME(os::rmdir(paths::getMetaRootDir(flags.work_dir)));
-
-  // Remove the network namespace symlink for one container so that it
-  // becomes an unknown orphan.
-  const ContainerID containerId = *(containers.get().begin());
-  const string symlink = path::join(
-      slave::PORT_MAPPING_BIND_MOUNT_SYMLINK_ROOT(),
-      stringify(containerId));
-
-  ASSERT_TRUE(os::exists(symlink));
-  ASSERT_TRUE(os::stat::islink(symlink));
-  ASSERT_SOME(os::rm(symlink));
-
-  Future<SlaveRegisteredMessage> slaveRegisteredMessage =
-    FUTURE_PROTOBUF(SlaveRegisteredMessage(), _, _);
-
-  Future<Nothing> knownOrphansDestroyed =
-    FUTURE_DISPATCH(_, &MesosContainerizerProcess::___recover);
-
-  // Restart the slave.
-  slave = StartSlave(flags);
-  ASSERT_SOME(slave);
-
-  AWAIT_READY(slaveRegisteredMessage);
-  AWAIT_READY(knownOrphansDestroyed);
-
-  // We settle the clock here to ensure that the processing of
-  // 'MesosContainerizerProcess::___destroy()' is complete and the
-  // metric is updated.
-  Clock::pause();
-  Clock::settle();
-  Clock::resume();
-
-  JSON::Object metrics = Metrics();
-  EXPECT_EQ(
-      0u,
-      metrics.values["containerizer/mesos/container_destroy_errors"]);
-}
-
-} // namespace tests {
-} // namespace internal {
-} // namespace mesos {


[03/12] mesos git commit: Moved containerizer related tests under src/tests/containerizer.

Posted by ji...@apache.org.
http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/memory_test_helper.cpp
----------------------------------------------------------------------
diff --git a/src/tests/memory_test_helper.cpp b/src/tests/memory_test_helper.cpp
deleted file mode 100644
index 8093e66..0000000
--- a/src/tests/memory_test_helper.cpp
+++ /dev/null
@@ -1,320 +0,0 @@
-/**
- * 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.n
- */
-
-#include <signal.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <sys/mman.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-
-#include <string>
-#include <vector>
-
-#include <stout/bytes.hpp>
-#include <stout/error.hpp>
-#include <stout/hashmap.hpp>
-#include <stout/lambda.hpp>
-#include <stout/os.hpp>
-#include <stout/stringify.hpp>
-#include <stout/strings.hpp>
-#include <stout/try.hpp>
-
-#include "tests/flags.hpp"
-#include "tests/memory_test_helper.hpp"
-
-using process::Subprocess;
-
-using std::cerr;
-using std::cin;
-using std::cout;
-using std::endl;
-using std::flush;
-using std::getline;
-using std::string;
-using std::vector;
-
-namespace mesos {
-namespace internal {
-namespace tests {
-
-// Constants used to sync MemoryTestHelper and its subprocess.
-
-// Used by the subprocess to inform that it has started.
-const char STARTED = 'S';
-
-// Used by the subprocess to inform that the work requested is done.
-const char DONE = 'D';
-
-// Used to signal an increaseRSS request.
-const char INCREASE_RSS[] = "INCREASE_RSS";
-
-// Used to signal an increasePageCache request.
-const char INCREASE_PAGE_CACHE[] = "INCREASE_PAGE_CACHE";
-
-
-// This helper allocates and locks specified anonymous memory (RSS).
-// It uses mlock and memset to make sure allocated memory is mapped.
-static Try<void*> allocateRSS(const Bytes& size, bool lock = true)
-{
-  void* rss = NULL;
-
-  if (posix_memalign(&rss, getpagesize(), size.bytes()) != 0) {
-    return ErrnoError("Failed to increase RSS memory, posix_memalign");
-  }
-
-  // Use memset to actually page in the memory in the kernel.
-  memset(rss, 1, size.bytes());
-
-  // Locking a page makes it unevictable in the kernel.
-  if (lock && mlock(rss, size.bytes()) != 0) {
-    return ErrnoError("Failed to lock memory, mlock");
-  }
-
-  return rss;
-}
-
-
-static Try<Nothing> increaseRSS(const vector<string>& tokens)
-{
-  if (tokens.size() < 2) {
-    return Error("Expect at least one argument");
-  }
-
-  Try<Bytes> size = Bytes::parse(tokens[1]);
-  if (size.isError()) {
-    return Error("The first argument '" + tokens[1] + "' is not a byte size");
-  }
-
-  Try<void*> memory = allocateRSS(size.get());
-  if (memory.isError()) {
-    return Error("Failed to allocate RSS memory: " + memory.error());
-  }
-
-  return Nothing();
-}
-
-
-static Try<Nothing> increasePageCache(const vector<string>& tokens)
-{
-  const Bytes UNIT = Megabytes(1);
-
-  if (tokens.size() < 2) {
-    return Error("Expect at least one argument");
-  }
-
-  Try<Bytes> size = Bytes::parse(tokens[1]);
-  if (size.isError()) {
-    return Error("The first argument '" + tokens[1] + "' is not a byte size");
-  }
-
-  // TODO(chzhcn): Currently, we assume the current working directory
-  // is a temporary directory and will be cleaned up when the test
-  // finishes. Since the child process will inherit the current
-  // working directory from the parent process, that means the test
-  // that uses this helper probably needs to inherit from
-  // TemporaryDirectoryTest. Consider relaxing this constraint.
-  Try<string> path = os::mktemp(path::join(os::getcwd(), "XXXXXX"));
-  if (path.isError()) {
-    return Error("Failed to create a temporary file: " + path.error());
-  }
-
-  Try<int> fd = os::open(path.get(), O_WRONLY);
-  if (fd.isError()) {
-    return Error("Failed to open file: " + fd.error());
-  }
-
-  // NOTE: We are doing round-down here to calculate the number of
-  // writes to do.
-  for (uint64_t i = 0; i < size.get().bytes() / UNIT.bytes(); i++) {
-    // Write UNIT size to disk at a time. The content isn't important.
-    Try<Nothing> write = os::write(fd.get(), string(UNIT.bytes(), 'a'));
-    if (write.isError()) {
-      os::close(fd.get());
-      return Error("Failed to write file: " + write.error());
-    }
-
-    // Use fsync to make sure data is written to disk.
-    if (fsync(fd.get()) == -1) {
-      // Save the error message because os::close below might
-      // overwrite the errno.
-      const string message = strerror(errno);
-
-      os::close(fd.get());
-      return Error("Failed to fsync: " + message);
-    }
-  }
-
-  os::close(fd.get());
-  return Nothing();
-}
-
-
-MemoryTestHelper::~MemoryTestHelper()
-{
-  cleanup();
-}
-
-
-Try<Nothing> MemoryTestHelper::spawn()
-{
-  if (s.isSome()) {
-    return Error("A subprocess has been spawned already");
-  }
-
-  vector<string> argv;
-  argv.push_back("memory-test-helper");
-  argv.push_back(MemoryTestHelperMain::NAME);
-
-  Try<Subprocess> process = subprocess(
-      path::join(flags.build_dir,
-                 "src",
-                 "memory-test-helper"),
-      argv,
-      Subprocess::PIPE(),
-      Subprocess::PIPE(),
-      Subprocess::FD(STDERR_FILENO));
-
-  if (process.isError()) {
-    return Error("Failed to spawn a subprocess: " + process.error());
-  }
-
-  s = process.get();
-
-  // Wait for the child to inform it has started before returning.
-  // Otherwise, the user might set the memory limit too earlier, and
-  // cause the child oom-killed because 'ld' could use a lot of
-  // memory.
-  Result<string> read = os::read(s.get().out().get(), sizeof(STARTED));
-  if (!read.isSome() || read.get() != string(sizeof(STARTED), STARTED)) {
-    cleanup();
-    return Error("Failed to sync with the subprocess");
-  }
-
-  return Nothing();
-}
-
-
-void MemoryTestHelper::cleanup()
-{
-  if (s.isSome()) {
-    // We just want to make sure the subprocess is terminated in case
-    // it's stuck, but we don't care about its status. Any error
-    // should have been logged in the subprocess directly.
-    ::kill(s.get().pid(), SIGKILL);
-    ::waitpid(s.get().pid(), NULL, 0);
-    s = None();
-  }
-}
-
-
-Try<pid_t> MemoryTestHelper::pid()
-{
-  if (s.isNone()) {
-    return Error("The subprocess has not been spawned yet");
-  }
-
-  return s.get().pid();
-}
-
-
-// Send a request to the subprocess and wait for its signal that the
-// work has been done.
-Try<Nothing> MemoryTestHelper::requestAndWait(const string& request)
-{
-  if (s.isNone()) {
-    return Error("The subprocess has not been spawned yet");
-  }
-
-  Try<Nothing> write = os::write(s.get().in().get(), request + "\n");
-  if (write.isError()) {
-    cleanup();
-    return Error("Fail to sync with the subprocess: " + write.error());
-  }
-
-  Result<string> read = os::read(s.get().out().get(), sizeof(DONE));
-  if (!read.isSome() || read.get() != string(sizeof(DONE), DONE)) {
-    cleanup();
-    return Error("Failed to sync with the subprocess");
-  }
-
-  return Nothing();
-}
-
-
-Try<Nothing> MemoryTestHelper::increaseRSS(const Bytes& size)
-{
-  return requestAndWait(string(INCREASE_RSS) + " " + stringify(size));
-}
-
-
-Try<Nothing> MemoryTestHelper::increasePageCache(const Bytes& size)
-{
-  return requestAndWait(string(INCREASE_PAGE_CACHE) + " " + stringify(size));
-}
-
-
-const char MemoryTestHelperMain::NAME[] = "MemoryTestHelperMain";
-
-
-int MemoryTestHelperMain::execute()
-{
-  hashmap<string, Try<Nothing>(*)(const vector<string>&)> commands;
-  commands[INCREASE_RSS] = &increaseRSS;
-  commands[INCREASE_PAGE_CACHE] = &increasePageCache;
-
-  // Tell the parent that child has started.
-  cout << STARTED << flush;
-
-  string line;
-  while(cin.good()) {
-    getline(cin, line);
-    vector<string> tokens = strings::tokenize(line, " ");
-
-    if (tokens.empty()) {
-      cerr << "No command from the parent" << endl;
-      return 1;
-    }
-
-    if (!commands.contains(tokens[0])) {
-      cerr << "Unknown command from the parent '" << tokens[0] << "'" << endl;
-      return 1;
-    }
-
-    Try<Nothing> result = commands[tokens[0]](tokens);
-    if (result.isError()) {
-      cerr << result.error();
-      return 1;
-    }
-
-    cout << DONE << flush;
-  }
-
-  if (!cin) {
-    cerr << "Failed to sync with the parent" << endl;
-    return 1;
-  }
-
-  return 0;
-}
-
-} // namespace tests {
-} // namespace internal {
-} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/memory_test_helper.hpp
----------------------------------------------------------------------
diff --git a/src/tests/memory_test_helper.hpp b/src/tests/memory_test_helper.hpp
deleted file mode 100644
index 11712d7..0000000
--- a/src/tests/memory_test_helper.hpp
+++ /dev/null
@@ -1,89 +0,0 @@
-/**
- * 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 __MEMORY_TEST_HELPER_HPP__
-#define __MEMORY_TEST_HELPER_HPP__
-
-#include <process/subprocess.hpp>
-
-#include <stout/bytes.hpp>
-#include <stout/option.hpp>
-#include <stout/subcommand.hpp>
-#include <stout/try.hpp>
-
-namespace mesos {
-namespace internal {
-namespace tests {
-
-// The abstraction for controlling the memory usage of a subprocess.
-// TODO(chzhcn): Currently, this helper is only supposed to be used by
-// one thread. Consider making it thread safe.
-class MemoryTestHelper
-{
-public:
-  MemoryTestHelper() {};
-  ~MemoryTestHelper();
-
-  // Spawns a subprocess.
-  // TODO(chzhcn): Consider returning a future instead of blocking.
-  Try<Nothing> spawn();
-
-  // Kill and reap the subprocess if exists.
-  // TODO(chzhcn): Consider returning a future instead of blocking.
-  void cleanup();
-
-  // Returns the pid of the subprocess.
-  Try<pid_t> pid();
-
-  // Allocate and lock specified page-aligned anonymous memory (RSS)
-  // in the subprocess. It uses mlock and memset to make sure
-  // allocated memory is mapped.
-  // TODO(chzhcn): Consider returning a future instead of blocking.
-  Try<Nothing> increaseRSS(const Bytes& size);
-
-  // This function attempts to generate requested size of page cache
-  // in the subprocess by using a small buffer and writing it to disk
-  // multiple times.
-  // TODO(chzhcn): Consider returning a future instead of blocking.
-  Try<Nothing> increasePageCache(const Bytes& size = Megabytes(1));
-
-private:
-  Try<Nothing> requestAndWait(const std::string& request);
-
-  Option<process::Subprocess> s;
-};
-
-
-// The actual subprocess behind MemoryTestHelper. It runs in a loop
-// and executes commands passed from stdin.
-class MemoryTestHelperMain : public Subcommand
-{
-public:
-  static const char NAME[];
-
-  MemoryTestHelperMain() : Subcommand(NAME) {};
-
-protected:
-  virtual int execute();
-};
-
-} // namespace tests {
-} // namespace internal {
-} // namespace mesos {
-
-#endif // __MEMORY_TEST_HELPER_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/memory_test_helper_main.cpp
----------------------------------------------------------------------
diff --git a/src/tests/memory_test_helper_main.cpp b/src/tests/memory_test_helper_main.cpp
deleted file mode 100644
index 362535f..0000000
--- a/src/tests/memory_test_helper_main.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * 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 <stout/subcommand.hpp>
-
-#include "tests/memory_test_helper.hpp"
-
-using mesos::internal::tests::MemoryTestHelperMain;
-
-int main(int argc, char** argv)
-{
-  return Subcommand::dispatch(
-      None(),
-      argc,
-      argv,
-      new MemoryTestHelperMain());
-}

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/ns_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/ns_tests.cpp b/src/tests/ns_tests.cpp
deleted file mode 100644
index bcd0e12..0000000
--- a/src/tests/ns_tests.cpp
+++ /dev/null
@@ -1,301 +0,0 @@
-/**
- * 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 <sys/wait.h>
-
-#include <iostream>
-
-#include <pthread.h>
-#include <unistd.h>
-
-#include <list>
-#include <set>
-#include <vector>
-
-#include <gtest/gtest.h>
-
-#include <stout/gtest.hpp>
-#include <stout/lambda.hpp>
-#include <stout/os.hpp>
-
-#include <process/gtest.hpp>
-#include <process/subprocess.hpp>
-
-#include "linux/ns.hpp"
-
-#include "tests/flags.hpp"
-#include "tests/setns_test_helper.hpp"
-
-using namespace process;
-
-using std::list;
-using std::set;
-using std::string;
-using std::vector;
-
-namespace mesos {
-namespace internal {
-namespace tests {
-
-
-// Helper for cloneChild() which expects an int(void*).
-static int cloneChildHelper(void* _func)
-{
-  const lambda::function<int()>* func =
-    static_cast<const lambda::function<int()>*> (_func);
-
-  return (*func)();
-}
-
-
-static pid_t cloneChild(
-    int flags,
-    const lambda::function<int()>& func)
-
-{
-  // 8 MiB stack for child.
-  static unsigned long long stack[(8*1024*1024)/sizeof(unsigned long long)];
-
-  return ::clone(
-      cloneChildHelper,
-      &stack[sizeof(stack)/sizeof(stack[0]) - 1], // Stack grows down.
-      flags | SIGCHLD,
-      (void*) &func);
-}
-
-
-// Test that a child in different namespace(s) can setns back to the
-// root namespace. We must fork a child to test this because setns
-// doesn't support multi-threaded processes (which gtest is).
-TEST(NsTest, ROOT_setns)
-{
-  // Clone then exec the setns-test-helper into a new namespace for
-  // each available namespace.
-  set<string> namespaces = ns::namespaces();
-  ASSERT_FALSE(namespaces.empty());
-
-  int flags = 0;
-
-  foreach (const string& ns, namespaces) {
-    // Skip 'user' namespace because it causes 'clone' to change us
-    // from being user 'root' to user 'nobody', but these tests
-    // require root. See MESOS-3083.
-    if (ns == "user") {
-      continue;
-    }
-
-    Try<int> nstype = ns::nstype(ns);
-    ASSERT_SOME(nstype);
-
-    flags |= nstype.get();
-  }
-
-  vector<string> argv;
-  argv.push_back("setns-test-helper");
-  argv.push_back(SetnsTestHelper::NAME);
-
-  Try<Subprocess> s = subprocess(
-      path::join(tests::flags.build_dir, "src", "setns-test-helper"),
-      argv,
-      Subprocess::FD(STDIN_FILENO),
-      Subprocess::FD(STDOUT_FILENO),
-      Subprocess::FD(STDERR_FILENO),
-      None(),
-      None(),
-      None(),
-      lambda::bind(&cloneChild, flags, lambda::_1));
-
-  // Continue in parent.
-  ASSERT_SOME(s);
-
-  // The child should exit 0.
-  Future<Option<int>> status = s.get().status();
-  AWAIT_READY(status);
-
-  ASSERT_SOME(status.get());
-  EXPECT_TRUE(WIFEXITED(status.get().get()));
-  EXPECT_EQ(0, status.get().get());
-}
-
-
-static void* childThread(void* arg)
-{
-  // Newly created threads have PTHREAD_CANCEL_ENABLE and
-  // PTHREAD_CANCEL_DEFERRED so they can be cancelled.
-  while (true) { os::sleep(Seconds(1)); }
-
-  return NULL;
-}
-
-
-// Test that setns correctly refuses to re-associate to a namespace if
-// the caller is multi-threaded.
-TEST(NsTest, ROOT_setnsMultipleThreads)
-{
-  set<string> namespaces = ns::namespaces();
-  EXPECT_LT(0u, namespaces.size());
-
-  // Do not allow multi-threaded environment.
-  pthread_t pthread;
-  ASSERT_EQ(0, pthread_create(&pthread, NULL, childThread, NULL));
-
-  foreach (const string& ns, namespaces) {
-    EXPECT_ERROR(ns::setns(::getpid(), ns));
-  }
-
-  // Terminate the threads.
-  EXPECT_EQ(0, pthread_cancel(pthread));
-  EXPECT_EQ(0, pthread_join(pthread, NULL));
-}
-
-
-// Use a different child function for clone because it requires
-// int(*)(void*).
-static int childGetns(void* arg)
-{
-  // Sleep until killed.
-  while (true) { sleep(1); }
-
-  ABORT("Error, child should be killed before reaching here");
-}
-
-
-// Test that we can get the namespace inodes for a forked child.
-TEST(NsTest, ROOT_getns)
-{
-  set<string> namespaces = ns::namespaces();
-
-  // ns::setns() does not support "pid".
-  namespaces.erase("pid");
-
-  // Use the first other namespace available.
-  ASSERT_FALSE(namespaces.empty());
-  string ns = *(namespaces.begin());
-
-  ASSERT_SOME(ns::getns(::getpid(), ns));
-
-  Try<int> nstype = ns::nstype(ns);
-  ASSERT_SOME(nstype);
-
-  // 8 MiB stack for child.
-  static unsigned long long stack[(8*1024*1024)/sizeof(unsigned long long)];
-
-  pid_t pid = clone(
-      childGetns,
-      &stack[sizeof(stack)/sizeof(stack[0]) - 1], // Stack grows down.
-      SIGCHLD | nstype.get(),
-      NULL);
-
-  ASSERT_NE(-1, pid);
-
-  // Continue in parent.
-  Try<ino_t> nsParent = ns::getns(::getpid(), ns);
-  ASSERT_SOME(nsParent);
-
-  Try<ino_t> nsChild = ns::getns(pid, ns);
-  ASSERT_SOME(nsChild);
-
-  // Child should be in a different namespace.
-  EXPECT_NE(nsParent.get(), nsChild.get());
-
-  // Kill the child process.
-  ASSERT_NE(-1, ::kill(pid, SIGKILL));
-
-  // Wait for the child process.
-  int status;
-  EXPECT_NE(-1, ::waitpid((pid_t) -1, &status, 0));
-  ASSERT_TRUE(WIFSIGNALED(status));
-  EXPECT_EQ(SIGKILL, WTERMSIG(status));
-}
-
-
-static int childDestroy(void* arg)
-{
-  // Fork a bunch of children.
-  ::fork();
-  ::fork();
-  ::fork();
-
-  // Parent and all children sleep.
-  while (true) { sleep(1); }
-
-  ABORT("Error, child should be killed before reaching here");
-}
-
-
-// Test we can destroy a pid namespace, i.e., kill all processes.
-TEST(NsTest, ROOT_destroy)
-{
-  set<string> namespaces = ns::namespaces();
-
-  if (namespaces.count("pid") == 0) {
-    // Pid namespace is not available.
-    return;
-  }
-
-  Try<int> nstype = ns::nstype("pid");
-  ASSERT_SOME(nstype);
-
-  // 8 MiB stack for child.
-  static unsigned long long stack[(8*1024*1024)/sizeof(unsigned long long)];
-
-  pid_t pid = clone(
-      childDestroy,
-      &stack[sizeof(stack)/sizeof(stack[0]) - 1], // Stack grows down.
-      SIGCHLD | nstype.get(),
-      NULL);
-
-  ASSERT_NE(-1, pid);
-
-  Future<Option<int>> status = process::reap(pid);
-
-  // Ensure the child is in a different pid namespace.
-  Try<ino_t> childNs = ns::getns(pid, "pid");
-  ASSERT_SOME(childNs);
-
-  Try<ino_t> ourNs = ns::getns(::getpid(), "pid");
-  ASSERT_SOME(ourNs);
-
-  ASSERT_NE(ourNs.get(), childNs.get());
-
-  // Kill the child.
-  AWAIT_READY(ns::pid::destroy(childNs.get()));
-
-  AWAIT_READY(status);
-  ASSERT_SOME(status.get());
-  ASSERT_TRUE(WIFSIGNALED(status.get().get()));
-  EXPECT_EQ(SIGKILL, WTERMSIG(status.get().get()));
-
-  // Finally, verify that no processes are in the child's pid
-  // namespace, i.e., destroy() also killed all descendants.
-  Try<set<pid_t>> pids = os::pids();
-  ASSERT_SOME(pids);
-
-  foreach (pid_t pid, pids.get()) {
-    Try<ino_t> otherNs = ns::getns(pid, "pid");
-    // pid may have exited since getting the snapshot of pids so
-    // ignore any error.
-    if (otherNs.isSome()) {
-      ASSERT_SOME_NE(childNs.get(), otherNs);
-    }
-  }
-}
-
-} // namespace tests {
-} // namespace internal {
-} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/perf_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/perf_tests.cpp b/src/tests/perf_tests.cpp
deleted file mode 100644
index 6b3d70f..0000000
--- a/src/tests/perf_tests.cpp
+++ /dev/null
@@ -1,183 +0,0 @@
-/**
- * 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 <sys/prctl.h>
-
-#include <set>
-
-#include <gmock/gmock.h>
-
-#include <process/clock.hpp>
-#include <process/gtest.hpp>
-#include <process/reap.hpp>
-
-#include <stout/gtest.hpp>
-#include <stout/stringify.hpp>
-
-#include "linux/perf.hpp"
-
-using std::set;
-using std::string;
-
-using namespace process;
-
-namespace mesos {
-namespace internal {
-namespace tests {
-
-class PerfTest : public ::testing::Test {};
-
-
-TEST_F(PerfTest, ROOT_Events)
-{
-  set<string> events;
-  // Valid events.
-  events.insert("cycles");
-  events.insert("task-clock");
-  EXPECT_TRUE(perf::valid(events));
-
-  // Add an invalid event.
-  events.insert("this-is-an-invalid-event");
-  EXPECT_FALSE(perf::valid(events));
-}
-
-
-TEST_F(PerfTest, Parse)
-{
-  // uint64 and floats should be parsed.
-  Try<hashmap<string, mesos::PerfStatistics> > parse =
-    perf::parse("123,cycles\n0.123,task-clock");
-  CHECK_SOME(parse);
-
-  ASSERT_TRUE(parse.get().contains(""));
-  mesos::PerfStatistics statistics = parse.get().get("").get();
-
-  ASSERT_TRUE(statistics.has_cycles());
-  EXPECT_EQ(123u, statistics.cycles());
-  ASSERT_TRUE(statistics.has_task_clock());
-  EXPECT_EQ(0.123, statistics.task_clock());
-
-  // Parse multiple cgroups.
-  parse = perf::parse("123,cycles,cgroup1\n"
-                      "456,cycles,cgroup2\n"
-                      "0.456,task-clock,cgroup2\n"
-                      "0.123,task-clock,cgroup1");
-  CHECK_SOME(parse);
-  EXPECT_FALSE(parse.get().contains(""));
-
-  ASSERT_TRUE(parse.get().contains("cgroup1"));
-  statistics = parse.get().get("cgroup1").get();
-
-  ASSERT_TRUE(statistics.has_cycles());
-  EXPECT_EQ(123u, statistics.cycles());
-  ASSERT_TRUE(statistics.has_task_clock());
-  EXPECT_EQ(0.123, statistics.task_clock());
-
-  ASSERT_TRUE(parse.get().contains("cgroup2"));
-  statistics = parse.get().get("cgroup2").get();
-
-  ASSERT_TRUE(statistics.has_cycles());
-  EXPECT_EQ(456u, statistics.cycles());
-  EXPECT_TRUE(statistics.has_task_clock());
-  EXPECT_EQ(0.456, statistics.task_clock());
-
-  // Statistics reporting <not supported> should not appear.
-  parse = perf::parse("<not supported>,cycles");
-  CHECK_SOME(parse);
-
-  ASSERT_TRUE(parse.get().contains(""));
-  statistics = parse.get().get("").get();
-  EXPECT_FALSE(statistics.has_cycles());
-
-  // Statistics reporting <not counted> should be zero.
-  parse = perf::parse("<not counted>,cycles\n<not counted>,task-clock");
-  CHECK_SOME(parse);
-
-  ASSERT_TRUE(parse.get().contains(""));
-  statistics = parse.get().get("").get();
-
-  EXPECT_TRUE(statistics.has_cycles());
-  EXPECT_EQ(0u, statistics.cycles());
-  EXPECT_TRUE(statistics.has_task_clock());
-  EXPECT_EQ(0.0, statistics.task_clock());
-
-  // Check parsing fails.
-  parse = perf::parse("1,cycles\ngarbage");
-  EXPECT_ERROR(parse);
-
-  parse = perf::parse("1,unknown-field");
-  EXPECT_ERROR(parse);
-}
-
-
-TEST_F(PerfTest, ROOT_SamplePid)
-{
-  // TODO(idownes): Replace this with a Subprocess when it supports
-  // DEATHSIG.
-  // Fork a child which we'll run perf against.
-  pid_t pid = fork();
-  ASSERT_GE(pid, 0);
-
-  if (pid == 0) {
-    // Kill ourself if the parent dies to prevent leaking the child.
-    prctl(PR_SET_PDEATHSIG, SIGKILL);
-
-    // Spin child to consume cpu cycles.
-    while (true);
-  }
-
-  // Continue in parent.
-  set<string> events;
-  // Hardware event.
-  events.insert("cycles");
-  // Software event.
-  events.insert("task-clock");
-
-  // Sample the child.
-  Duration duration = Milliseconds(100);
-  Future<mesos::PerfStatistics> statistics =
-    perf::sample(events, pid, duration);
-  AWAIT_READY(statistics);
-
-  // Kill the child and reap it.
-  Future<Option<int>> status = reap(pid);
-  kill(pid, SIGKILL);
-  AWAIT_READY(status);
-
-  // Check the sample timestamp is within the last 5 seconds. This is generous
-  // because there's the process reap delay in addition to the sampling
-  // duration.
-  ASSERT_TRUE(statistics.get().has_timestamp());
-  EXPECT_GT(
-      Seconds(5).secs(), Clock::now().secs() - statistics.get().timestamp());
-  EXPECT_EQ(duration.secs(), statistics.get().duration());
-
-  ASSERT_TRUE(statistics.get().has_cycles());
-
-  // TODO(benh): Some Linux distributions (Ubuntu 14.04) fail to
-  // properly sample 'cycles' with 'perf', so we don't explicitly
-  // check the value here. See MESOS-3082.
-  // EXPECT_LT(0u, statistics.get().cycles());
-
-  ASSERT_TRUE(statistics.get().has_task_clock());
-  EXPECT_LT(0.0, statistics.get().task_clock());
-}
-
-} // namespace tests {
-} // namespace internal {
-} // namespace mesos {


[11/12] mesos git commit: Moved containerizer related tests under src/tests/containerizer.

Posted by ji...@apache.org.
http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer/cgroups_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/cgroups_tests.cpp b/src/tests/containerizer/cgroups_tests.cpp
new file mode 100644
index 0000000..caecd5d
--- /dev/null
+++ b/src/tests/containerizer/cgroups_tests.cpp
@@ -0,0 +1,1235 @@
+/**
+ * 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 <assert.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <gmock/gmock.h>
+
+#include <process/gtest.hpp>
+#include <process/owned.hpp>
+
+#include <stout/gtest.hpp>
+#include <stout/hashmap.hpp>
+#include <stout/numify.hpp>
+#include <stout/option.hpp>
+#include <stout/os.hpp>
+#include <stout/path.hpp>
+#include <stout/proc.hpp>
+#include <stout/stringify.hpp>
+#include <stout/strings.hpp>
+
+#include "linux/cgroups.hpp"
+#include "linux/perf.hpp"
+
+#include "tests/mesos.hpp" // For TEST_CGROUPS_(HIERARCHY|ROOT).
+#include "tests/utils.hpp"
+
+#include "tests/containerizer/memory_test_helper.hpp"
+
+using namespace process;
+
+using cgroups::memory::pressure::Level;
+using cgroups::memory::pressure::Counter;
+
+using std::set;
+
+namespace mesos {
+namespace internal {
+namespace tests {
+
+
+class CgroupsTest : public TemporaryDirectoryTest
+{
+public:
+  static void SetUpTestCase()
+  {
+    // Clean up the testing hierarchy, in case it wasn't cleaned up
+    // properly from previous tests.
+    AWAIT_READY(cgroups::cleanup(TEST_CGROUPS_HIERARCHY));
+  }
+
+  static void TearDownTestCase()
+  {
+    AWAIT_READY(cgroups::cleanup(TEST_CGROUPS_HIERARCHY));
+  }
+};
+
+
+// A fixture which is used to name tests that expect NO hierarchy to
+// exist in order to test the ability to create a hierarchy (since
+// most likely existing hierarchies will have all or most subsystems
+// attached rendering our ability to create a hierarchy fruitless).
+class CgroupsNoHierarchyTest : public CgroupsTest
+{
+public:
+  static void SetUpTestCase()
+  {
+    CgroupsTest::SetUpTestCase();
+
+    Try<std::set<std::string> > hierarchies = cgroups::hierarchies();
+    ASSERT_SOME(hierarchies);
+    ASSERT_TRUE(hierarchies.get().empty())
+      << "-------------------------------------------------------------\n"
+      << "We cannot run any cgroups tests that require mounting\n"
+      << "hierarchies because you have the following hierarchies mounted:\n"
+      << strings::trim(stringify(hierarchies.get()), " {},") << "\n"
+      << "You can either unmount those hierarchies, or disable\n"
+      << "this test case (i.e., --gtest_filter=-CgroupsNoHierarchyTest.*).\n"
+      << "-------------------------------------------------------------";
+  }
+};
+
+
+// A fixture that assumes ANY hierarchy is acceptable for use provided
+// it has the subsystems attached that were specified in the
+// constructor. If no hierarchy could be found that has all the
+// required subsystems then we attempt to create a new hierarchy.
+class CgroupsAnyHierarchyTest : public CgroupsTest
+{
+public:
+  CgroupsAnyHierarchyTest(const std::string& _subsystems = "cpu")
+    : subsystems(_subsystems) {}
+
+protected:
+  virtual void SetUp()
+  {
+    CgroupsTest::SetUp();
+
+    foreach (const std::string& subsystem, strings::tokenize(subsystems, ",")) {
+      // Establish the base hierarchy if this is the first subsystem checked.
+      if (baseHierarchy.empty()) {
+        Result<std::string> hierarchy = cgroups::hierarchy(subsystem);
+        ASSERT_FALSE(hierarchy.isError());
+
+        if (hierarchy.isNone()) {
+          baseHierarchy = TEST_CGROUPS_HIERARCHY;
+        } else {
+          // Strip the subsystem to get the base hierarchy.
+          Try<std::string> baseDirname = Path(hierarchy.get()).dirname();
+          ASSERT_SOME(baseDirname);
+          baseHierarchy = baseDirname.get();
+        }
+      }
+
+      // Mount the subsystem if necessary.
+      std::string hierarchy = path::join(baseHierarchy, subsystem);
+      Try<bool> mounted = cgroups::mounted(hierarchy, subsystem);
+      ASSERT_SOME(mounted);
+      if (!mounted.get()) {
+        ASSERT_SOME(cgroups::mount(hierarchy, subsystem))
+          << "-------------------------------------------------------------\n"
+          << "We cannot run any cgroups tests that require\n"
+          << "a hierarchy with subsystem '" << subsystem << "'\n"
+          << "because we failed to find an existing hierarchy\n"
+          << "or create a new one (tried '" << hierarchy << "').\n"
+          << "You can either remove all existing\n"
+          << "hierarchies, or disable this test case\n"
+          << "(i.e., --gtest_filter=-"
+          << ::testing::UnitTest::GetInstance()
+              ->current_test_info()
+              ->test_case_name() << ".*).\n"
+          << "-------------------------------------------------------------";
+      }
+
+      Try<std::vector<std::string> > cgroups = cgroups::get(hierarchy);
+      CHECK_SOME(cgroups);
+
+      foreach (const std::string& cgroup, cgroups.get()) {
+        // Remove any cgroups that start with TEST_CGROUPS_ROOT.
+        if (cgroup == TEST_CGROUPS_ROOT) {
+          AWAIT_READY(cgroups::destroy(hierarchy, cgroup));
+        }
+      }
+    }
+  }
+
+  virtual void TearDown()
+  {
+    // Remove all *our* cgroups.
+    foreach (const std::string& subsystem, strings::tokenize(subsystems, ",")) {
+      std::string hierarchy = path::join(baseHierarchy, subsystem);
+
+      Try<std::vector<std::string> > cgroups = cgroups::get(hierarchy);
+      CHECK_SOME(cgroups);
+
+      foreach (const std::string& cgroup, cgroups.get()) {
+        // Remove any cgroups that start with TEST_CGROUPS_ROOT.
+        if (cgroup == TEST_CGROUPS_ROOT) {
+          AWAIT_READY(cgroups::destroy(hierarchy, cgroup));
+        }
+      }
+    }
+
+    CgroupsTest::TearDown();
+  }
+
+  const std::string subsystems; // Subsystems required to run tests.
+  std::string baseHierarchy; // Path to the hierarchy being used.
+};
+
+
+class CgroupsAnyHierarchyWithCpuMemoryTest
+  : public CgroupsAnyHierarchyTest
+{
+public:
+  CgroupsAnyHierarchyWithCpuMemoryTest()
+    : CgroupsAnyHierarchyTest("cpu,memory") {}
+};
+
+
+class CgroupsAnyHierarchyWithFreezerTest
+  : public CgroupsAnyHierarchyTest
+{
+public:
+  CgroupsAnyHierarchyWithFreezerTest()
+    : CgroupsAnyHierarchyTest("freezer") {}
+};
+
+
+TEST_F(CgroupsAnyHierarchyTest, ROOT_CGROUPS_Enabled)
+{
+  EXPECT_SOME_TRUE(cgroups::enabled(""));
+  EXPECT_SOME_TRUE(cgroups::enabled(","));
+  EXPECT_SOME_TRUE(cgroups::enabled("cpu"));
+  EXPECT_SOME_TRUE(cgroups::enabled(",cpu"));
+  EXPECT_SOME_TRUE(cgroups::enabled("cpu,memory"));
+  EXPECT_SOME_TRUE(cgroups::enabled("cpu,memory,"));
+  EXPECT_ERROR(cgroups::enabled("invalid"));
+  EXPECT_ERROR(cgroups::enabled("cpu,invalid"));
+}
+
+
+TEST_F(CgroupsAnyHierarchyWithCpuMemoryTest, ROOT_CGROUPS_Busy)
+{
+  EXPECT_SOME_FALSE(cgroups::busy(""));
+  EXPECT_SOME_FALSE(cgroups::busy(","));
+  EXPECT_SOME_TRUE(cgroups::busy("cpu"));
+  EXPECT_SOME_TRUE(cgroups::busy(",cpu"));
+  EXPECT_SOME_TRUE(cgroups::busy("cpu,memory"));
+  EXPECT_SOME_TRUE(cgroups::busy("cpu,memory,"));
+  EXPECT_ERROR(cgroups::busy("invalid"));
+  EXPECT_ERROR(cgroups::busy("cpu,invalid"));
+}
+
+
+TEST_F(CgroupsAnyHierarchyTest, ROOT_CGROUPS_Subsystems)
+{
+  Try<std::set<std::string> > names = cgroups::subsystems();
+  ASSERT_SOME(names);
+
+  Option<std::string> cpu;
+  Option<std::string> memory;
+  foreach (const std::string& name, names.get()) {
+    if (name == "cpu") {
+      cpu = name;
+    } else if (name == "memory") {
+      memory = name;
+    }
+  }
+
+  EXPECT_SOME(cpu);
+  EXPECT_SOME(memory);
+}
+
+
+TEST_F(CgroupsAnyHierarchyWithCpuMemoryTest, ROOT_CGROUPS_SubsystemsHierarchy)
+{
+  std::string cpuHierarchy = path::join(baseHierarchy, "cpu");
+
+  Try<std::set<std::string> > names = cgroups::subsystems(cpuHierarchy);
+  ASSERT_SOME(names);
+
+  Option<std::string> cpu;
+  Option<std::string> memory;
+  foreach (const std::string& name, names.get()) {
+    if (name == "cpu") {
+      cpu = name;
+    } else if (name == "memory") {
+      memory = name;
+    }
+  }
+
+  EXPECT_SOME(cpu);
+  EXPECT_NONE(memory);
+
+  std::string memoryHierarchy = path::join(baseHierarchy, "memory");
+  names = cgroups::subsystems(memoryHierarchy);
+  ASSERT_SOME(names);
+
+  cpu = None();
+  memory = None();
+  foreach (const std::string& name, names.get()) {
+    if (name == "cpu") {
+      cpu = name;
+    } else if (name == "memory") {
+      memory = name;
+    }
+  }
+  EXPECT_NONE(cpu);
+  EXPECT_SOME(memory);
+}
+
+
+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"));
+  EXPECT_ERROR(cgroups::mount(TEST_CGROUPS_HIERARCHY, "invalid"));
+
+  // Try to mount a valid hierarchy, retrying as necessary since the
+  // previous unmount might not have taken effect yet due to a bug in
+  // Ubuntu 12.04.
+  ASSERT_SOME(cgroups::mount(TEST_CGROUPS_HIERARCHY, "cpu,memory", 10));
+  EXPECT_ERROR(cgroups::mount(TEST_CGROUPS_HIERARCHY, "cpuset"));
+  EXPECT_ERROR(cgroups::unmount("/tmp"));
+  ASSERT_SOME(cgroups::unmount(TEST_CGROUPS_HIERARCHY));
+}
+
+
+TEST_F(CgroupsAnyHierarchyTest, ROOT_CGROUPS_Mounted)
+{
+  EXPECT_SOME_FALSE(cgroups::mounted("/tmp-nonexist"));
+  EXPECT_SOME_FALSE(cgroups::mounted("/tmp"));
+  EXPECT_SOME_FALSE(cgroups::mounted(baseHierarchy + "/not_expected"));
+  EXPECT_SOME_TRUE(cgroups::mounted(baseHierarchy + "/cpu"));
+}
+
+
+TEST_F(CgroupsAnyHierarchyWithCpuMemoryTest, ROOT_CGROUPS_MountedSubsystems)
+{
+  EXPECT_SOME_FALSE(cgroups::mounted("/tmp-nonexist", "cpu"));
+  EXPECT_SOME_FALSE(cgroups::mounted("/tmp", "cpu,memory"));
+  EXPECT_SOME_FALSE(cgroups::mounted("/tmp", "cpu"));
+  EXPECT_SOME_FALSE(cgroups::mounted("/tmp", "invalid"));
+  EXPECT_SOME_TRUE(cgroups::mounted(path::join(baseHierarchy, "cpu"), "cpu"));
+  EXPECT_SOME_TRUE(cgroups::mounted(
+        path::join(baseHierarchy, "memory"), "memory"));
+  EXPECT_SOME_FALSE(cgroups::mounted(baseHierarchy, "invalid"));
+  EXPECT_SOME_FALSE(cgroups::mounted(baseHierarchy + "/not_expected", "cpu"));
+}
+
+
+TEST_F(CgroupsAnyHierarchyWithCpuMemoryTest, ROOT_CGROUPS_CreateRemove)
+{
+  EXPECT_ERROR(cgroups::create("/tmp", "test"));
+  EXPECT_ERROR(cgroups::create(baseHierarchy, "mesos_test_missing/1"));
+  ASSERT_SOME(cgroups::create(
+        path::join(baseHierarchy, "cpu"), "mesos_test_missing"));
+  EXPECT_ERROR(cgroups::remove(baseHierarchy, "invalid"));
+  ASSERT_SOME(cgroups::remove(
+        path::join(baseHierarchy, "cpu"), "mesos_test_missing"));
+}
+
+
+TEST_F(CgroupsAnyHierarchyTest, ROOT_CGROUPS_Get)
+{
+  std::string hierarchy = path::join(baseHierarchy, "cpu");
+
+  ASSERT_SOME(cgroups::create(hierarchy, "mesos_test1"));
+  ASSERT_SOME(cgroups::create(hierarchy, "mesos_test2"));
+
+  Try<std::vector<std::string>> cgroups = cgroups::get(hierarchy);
+  ASSERT_SOME(cgroups);
+
+  EXPECT_NE(cgroups.get().end(),
+            find(cgroups.get().begin(), cgroups.get().end(), "mesos_test2"));
+  EXPECT_NE(cgroups.get().end(),
+            find(cgroups.get().begin(), cgroups.get().end(), "mesos_test1"));
+
+  ASSERT_SOME(cgroups::remove(hierarchy, "mesos_test1"));
+  ASSERT_SOME(cgroups::remove(hierarchy, "mesos_test2"));
+}
+
+
+TEST_F(CgroupsAnyHierarchyTest, ROOT_CGROUPS_NestedCgroups)
+{
+  std::string hierarchy = path::join(baseHierarchy, "cpu");
+  ASSERT_SOME(cgroups::create(hierarchy, TEST_CGROUPS_ROOT));
+  std::string cgroup1 = path::join(TEST_CGROUPS_ROOT, "1");
+  std::string cgroup2 = path::join(TEST_CGROUPS_ROOT, "2");
+
+  ASSERT_SOME(cgroups::create(hierarchy, cgroup1))
+    << "-------------------------------------------------------------\n"
+    << "We cannot run this test because it appears you do not have\n"
+    << "a modern enough version of the Linux kernel. You won't be\n"
+    << "able to use the cgroups isolator, but feel free to disable\n"
+    << "this test.\n"
+    << "-------------------------------------------------------------";
+
+  ASSERT_SOME(cgroups::create(hierarchy, cgroup2));
+
+  Try<std::vector<std::string>> cgroups =
+    cgroups::get(hierarchy, TEST_CGROUPS_ROOT);
+  ASSERT_SOME(cgroups);
+
+  ASSERT_EQ(2u, cgroups.get().size());
+
+  EXPECT_NE(cgroups.get().end(),
+            find(cgroups.get().begin(), cgroups.get().end(), cgroup2));
+  EXPECT_NE(cgroups.get().end(),
+            find(cgroups.get().begin(), cgroups.get().end(), cgroup1));
+
+  ASSERT_SOME(cgroups::remove(hierarchy, cgroup1));
+  ASSERT_SOME(cgroups::remove(hierarchy, cgroup2));
+}
+
+
+TEST_F(CgroupsAnyHierarchyTest, ROOT_CGROUPS_Tasks)
+{
+  pid_t pid = ::getpid();
+
+  Result<std::string> cgroup = cgroups::cpu::cgroup(pid);
+  ASSERT_SOME(cgroup);
+
+  std::string hierarchy = path::join(baseHierarchy, "cpu");
+
+  Try<std::set<pid_t>> pids = cgroups::processes(hierarchy, cgroup.get());
+  ASSERT_SOME(pids);
+
+  EXPECT_NE(0u, pids.get().count(pid));
+}
+
+
+TEST_F(CgroupsAnyHierarchyTest, ROOT_CGROUPS_Read)
+{
+  std::string hierarchy = path::join(baseHierarchy, "cpu");
+
+  EXPECT_ERROR(cgroups::read(hierarchy, TEST_CGROUPS_ROOT, "invalid42"));
+
+  pid_t pid = ::getpid();
+
+  Result<std::string> cgroup = cgroups::cpu::cgroup(pid);
+  ASSERT_SOME(cgroup);
+
+  Try<std::string> read = cgroups::read(hierarchy, cgroup.get(), "tasks");
+  ASSERT_SOME(read);
+
+  EXPECT_TRUE(strings::contains(read.get(), stringify(pid)));
+}
+
+
+TEST_F(CgroupsAnyHierarchyTest, ROOT_CGROUPS_Write)
+{
+  std::string hierarchy = path::join(baseHierarchy, "cpu");
+  EXPECT_ERROR(
+      cgroups::write(hierarchy, TEST_CGROUPS_ROOT, "invalid", "invalid"));
+
+  ASSERT_SOME(cgroups::create(hierarchy, TEST_CGROUPS_ROOT));
+
+  pid_t pid = ::fork();
+  ASSERT_NE(-1, pid);
+
+  if (pid == 0) {
+    // In child process, wait for kill signal.
+    while (true) { sleep(1); }
+
+    // Should not reach here.
+    const char* message = "Error, child should be killed before reaching here";
+    while (write(STDERR_FILENO, message, strlen(message)) == -1 &&
+           errno == EINTR);
+
+    _exit(1);
+  }
+
+  // In parent process.
+  ASSERT_SOME(
+      cgroups::write(hierarchy,
+                     TEST_CGROUPS_ROOT,
+                     "cgroup.procs",
+                     stringify(pid)));
+
+  Try<std::set<pid_t> > pids = cgroups::processes(hierarchy, TEST_CGROUPS_ROOT);
+  ASSERT_SOME(pids);
+
+  EXPECT_NE(0u, pids.get().count(pid));
+
+  // Kill the child process.
+  ASSERT_NE(-1, ::kill(pid, SIGKILL));
+
+  // Wait for the child process.
+  int status;
+  EXPECT_NE(-1, ::waitpid((pid_t) -1, &status, 0));
+  ASSERT_TRUE(WIFSIGNALED(status));
+  EXPECT_EQ(SIGKILL, WTERMSIG(status));
+}
+
+
+TEST_F(CgroupsAnyHierarchyTest, ROOT_CGROUPS_Cfs_Big_Quota)
+{
+  std::string hierarchy = path::join(baseHierarchy, "cpu");
+  ASSERT_SOME(cgroups::create(hierarchy, TEST_CGROUPS_ROOT));
+
+  Duration quota = Seconds(100); // Big quota.
+  ASSERT_SOME(cgroups::cpu::cfs_quota_us(hierarchy, TEST_CGROUPS_ROOT, quota));
+
+  // Ensure we can read back the correct quota.
+  ASSERT_SOME_EQ(
+      quota,
+      cgroups::cpu::cfs_quota_us(hierarchy, TEST_CGROUPS_ROOT));
+}
+
+
+class CgroupsAnyHierarchyWithCpuAcctMemoryTest
+  : public CgroupsAnyHierarchyTest
+{
+public:
+  CgroupsAnyHierarchyWithCpuAcctMemoryTest()
+    : CgroupsAnyHierarchyTest("cpuacct,memory") {}
+};
+
+
+TEST_F(CgroupsAnyHierarchyWithCpuAcctMemoryTest, ROOT_CGROUPS_Stat)
+{
+  EXPECT_ERROR(cgroups::stat(baseHierarchy, TEST_CGROUPS_ROOT, "invalid"));
+
+  Try<hashmap<std::string, uint64_t> > result =
+    cgroups::stat(
+        path::join(baseHierarchy, "cpuacct"), "/", "cpuacct.stat");
+  ASSERT_SOME(result);
+  EXPECT_TRUE(result.get().contains("user"));
+  EXPECT_TRUE(result.get().contains("system"));
+  EXPECT_GT(result.get().get("user").get(), 0llu);
+  EXPECT_GT(result.get().get("system").get(), 0llu);
+
+  result = cgroups::stat(
+      path::join(baseHierarchy, "memory"), "/", "memory.stat");
+  ASSERT_SOME(result);
+  EXPECT_TRUE(result.get().contains("rss"));
+  EXPECT_GT(result.get().get("rss").get(), 0llu);
+}
+
+
+TEST_F(CgroupsAnyHierarchyWithCpuMemoryTest, ROOT_CGROUPS_Listen)
+{
+  std::string hierarchy = path::join(baseHierarchy, "memory");
+  ASSERT_SOME(cgroups::create(hierarchy, TEST_CGROUPS_ROOT));
+  ASSERT_SOME(
+      cgroups::memory::oom::killer::enabled(hierarchy, TEST_CGROUPS_ROOT))
+    << "-------------------------------------------------------------\n"
+    << "We cannot run this test because it appears you do not have\n"
+    << "a modern enough version of the Linux kernel. You won't be\n"
+    << "able to use the cgroups isolator, but feel free to disable\n"
+    << "this test.\n"
+    << "-------------------------------------------------------------";
+
+  const Bytes limit =  Megabytes(64);
+
+  ASSERT_SOME(cgroups::memory::limit_in_bytes(
+      hierarchy, TEST_CGROUPS_ROOT, limit));
+
+  // Listen on oom events for test cgroup.
+  Future<Nothing> future =
+    cgroups::memory::oom::listen(hierarchy, TEST_CGROUPS_ROOT);
+
+  ASSERT_FALSE(future.isFailed());
+
+  // Test the cancellation.
+  future.discard();
+
+  // Test the normal operation below.
+  future = cgroups::memory::oom::listen(hierarchy, TEST_CGROUPS_ROOT);
+  ASSERT_FALSE(future.isFailed());
+
+  MemoryTestHelper helper;
+  ASSERT_SOME(helper.spawn());
+  ASSERT_SOME(helper.pid());
+
+  EXPECT_SOME(cgroups::assign(
+      hierarchy, TEST_CGROUPS_ROOT, helper.pid().get()));
+
+  // Request more RSS memory in the subprocess than the limit.
+  // NOTE: We enable the kernel oom killer in this test. If it were
+  // disabled, the subprocess might hang and the following call won't
+  // return. By enabling the oom killer, we let the subprocess get
+  // killed and expect that an error is returned.
+  EXPECT_ERROR(helper.increaseRSS(limit * 2));
+
+  AWAIT_READY(future);
+}
+
+
+TEST_F(CgroupsAnyHierarchyWithFreezerTest, ROOT_CGROUPS_Freeze)
+{
+  int pipes[2];
+  int dummy;
+  ASSERT_NE(-1, ::pipe(pipes));
+
+  std::string hierarchy = path::join(baseHierarchy, "freezer");
+  ASSERT_SOME(cgroups::create(hierarchy, TEST_CGROUPS_ROOT));
+
+  pid_t pid = ::fork();
+  ASSERT_NE(-1, pid);
+
+  if (pid == 0) {
+    // In child process.
+    ::close(pipes[0]);
+
+    // Put self into the test cgroup.
+    Try<Nothing> assign =
+      cgroups::assign(hierarchy, TEST_CGROUPS_ROOT, ::getpid());
+
+    if (assign.isError()) {
+      std::cerr << "Failed to assign cgroup: " << assign.error() << std::endl;
+      abort();
+    }
+
+    // Notify the parent.
+    if (::write(pipes[1], &dummy, sizeof(dummy)) != sizeof(dummy)) {
+      perror("Failed to notify the parent");
+      abort();
+    }
+    ::close(pipes[1]);
+
+    // Infinite loop here.
+    while (true);
+
+    // Should not reach here.
+    std::cerr << "Reach an unreachable statement!" << std::endl;
+    abort();
+  }
+
+  // In parent process.
+  ::close(pipes[1]);
+
+  // Wait until child has assigned the cgroup.
+  ASSERT_LT(0, ::read(pipes[0], &dummy, sizeof(dummy)));
+  ::close(pipes[0]);
+
+  // Freeze the test cgroup.
+  AWAIT_EXPECT_READY(cgroups::freezer::freeze(hierarchy, TEST_CGROUPS_ROOT));
+
+  // Thaw the test cgroup.
+  AWAIT_EXPECT_READY(cgroups::freezer::thaw(hierarchy, TEST_CGROUPS_ROOT));
+
+  // Kill the child process.
+  ASSERT_NE(-1, ::kill(pid, SIGKILL));
+
+  // Wait for the child process.
+  int status;
+  EXPECT_NE(-1, ::waitpid((pid_t) -1, &status, 0));
+  ASSERT_TRUE(WIFSIGNALED(status));
+  EXPECT_EQ(SIGKILL, WTERMSIG(status));
+}
+
+
+TEST_F(CgroupsAnyHierarchyWithCpuMemoryTest, ROOT_CGROUPS_FreezeNonFreezer)
+{
+  std::string hierarchy = path::join(baseHierarchy, "cpu");
+  ASSERT_SOME(cgroups::create(hierarchy, TEST_CGROUPS_ROOT));
+
+  AWAIT_EXPECT_FAILED(cgroups::freezer::freeze(hierarchy, TEST_CGROUPS_ROOT));
+  AWAIT_EXPECT_FAILED(cgroups::freezer::thaw(hierarchy, TEST_CGROUPS_ROOT));
+
+  // The cgroup is empty so we should still be able to destroy it.
+  AWAIT_READY(cgroups::destroy(hierarchy, TEST_CGROUPS_ROOT));
+}
+
+
+TEST_F(CgroupsAnyHierarchyWithFreezerTest, ROOT_CGROUPS_Kill)
+{
+  int pipes[2];
+  int dummy;
+  ASSERT_NE(-1, ::pipe(pipes));
+
+  std::string hierarchy = path::join(baseHierarchy, "freezer");
+  ASSERT_SOME(cgroups::create(hierarchy, TEST_CGROUPS_ROOT));
+
+  pid_t pid = ::fork();
+  ASSERT_NE(-1, pid);
+
+  if (pid > 0) {
+    // In parent process.
+    ::close(pipes[1]);
+
+    // Wait until all children have assigned the cgroup.
+    ASSERT_LT(0, ::read(pipes[0], &dummy, sizeof(dummy)));
+    ASSERT_LT(0, ::read(pipes[0], &dummy, sizeof(dummy)));
+    ASSERT_LT(0, ::read(pipes[0], &dummy, sizeof(dummy)));
+    ASSERT_LT(0, ::read(pipes[0], &dummy, sizeof(dummy)));
+    ::close(pipes[0]);
+
+    Try<Nothing> kill = cgroups::kill(hierarchy, TEST_CGROUPS_ROOT, SIGKILL);
+    EXPECT_SOME(kill);
+
+    int status;
+    EXPECT_NE(-1, ::waitpid((pid_t) -1, &status, 0));
+    ASSERT_TRUE(WIFSIGNALED(status));
+    EXPECT_EQ(SIGKILL, WTERMSIG(status));
+  } else {
+    // In child process.
+
+    // We create 4 child processes here using two forks to test the case in
+    // which there are multiple active processes in the given cgroup.
+    ::fork();
+    ::fork();
+
+    // Put self into the test cgroup.
+    Try<Nothing> assign =
+      cgroups::assign(hierarchy, TEST_CGROUPS_ROOT, ::getpid());
+
+    if (assign.isError()) {
+      std::cerr << "Failed to assign cgroup: " << assign.error() << std::endl;
+      abort();
+    }
+
+    // Notify the parent.
+    ::close(pipes[0]); // TODO(benh): Close after first fork?
+    if (::write(pipes[1], &dummy, sizeof(dummy)) != sizeof(dummy)) {
+      perror("Failed to notify the parent");
+      abort();
+    }
+    ::close(pipes[1]);
+
+    // Wait kill signal from parent.
+    while (true);
+
+    // Should not reach here.
+    std::cerr << "Reach an unreachable statement!" << std::endl;
+    abort();
+  }
+}
+
+
+// TODO(benh): Write a version of this test with nested cgroups.
+TEST_F(CgroupsAnyHierarchyWithFreezerTest, ROOT_CGROUPS_Destroy)
+{
+  int pipes[2];
+  int dummy;
+  ASSERT_NE(-1, ::pipe(pipes));
+
+  std::string hierarchy = path::join(baseHierarchy, "freezer");
+  ASSERT_SOME(cgroups::create(hierarchy, TEST_CGROUPS_ROOT));
+
+  pid_t pid = ::fork();
+  ASSERT_NE(-1, pid);
+
+  if (pid > 0) {
+    // In parent process.
+    ::close(pipes[1]);
+
+    // Wait until all children have assigned the cgroup.
+    ASSERT_LT(0, ::read(pipes[0], &dummy, sizeof(dummy)));
+    ASSERT_LT(0, ::read(pipes[0], &dummy, sizeof(dummy)));
+    ASSERT_LT(0, ::read(pipes[0], &dummy, sizeof(dummy)));
+    ASSERT_LT(0, ::read(pipes[0], &dummy, sizeof(dummy)));
+    ::close(pipes[0]);
+
+    AWAIT_READY(cgroups::destroy(hierarchy, TEST_CGROUPS_ROOT));
+
+    // cgroups::destroy will reap all processes in the cgroup so we should
+    // *not* be able to reap it now.
+    int status;
+    EXPECT_EQ(-1, ::waitpid(pid, &status, 0));
+    EXPECT_EQ(ECHILD, errno);
+  } else {
+    // In child process.
+
+    // We create 4 child processes here using two forks to test the case in
+    // which there are multiple active processes in the given cgroup.
+    ::fork();
+    ::fork();
+
+    // Put self into the test cgroup.
+    Try<Nothing> assign =
+      cgroups::assign(hierarchy, TEST_CGROUPS_ROOT, ::getpid());
+
+    if (assign.isError()) {
+      std::cerr << "Failed to assign cgroup: " << assign.error() << std::endl;
+      abort();
+    }
+
+    // Notify the parent.
+    ::close(pipes[0]); // TODO(benh): Close after first fork?
+    if (::write(pipes[1], &dummy, sizeof(dummy)) != sizeof(dummy)) {
+      perror("Failed to notify the parent");
+      abort();
+    }
+    ::close(pipes[1]);
+
+    // Wait kill signal from parent.
+    while (true) {}
+
+    // Should not reach here.
+    std::cerr << "Reach an unreachable statement!" << std::endl;
+    abort();
+  }
+}
+
+
+void* threadFunction(void*)
+{
+  // Newly created threads have PTHREAD_CANCEL_ENABLE and
+  // PTHREAD_CANCEL_DEFERRED so they can be cancelled from the main thread.
+  while (true) { sleep(1); }
+
+  return NULL;
+}
+
+
+TEST_F(CgroupsAnyHierarchyWithFreezerTest, ROOT_CGROUPS_AssignThreads)
+{
+  size_t numThreads = 5;
+
+  pthread_t pthreads[numThreads];
+
+  // Create additional threads.
+  for (size_t i = 0; i < numThreads; i++)
+  {
+    EXPECT_EQ(0, pthread_create(&pthreads[i], NULL, threadFunction, NULL));
+  }
+
+  std::string hierarchy = path::join(baseHierarchy, "freezer");
+  ASSERT_SOME(cgroups::create(hierarchy, TEST_CGROUPS_ROOT));
+
+  // Check the test cgroup is initially empty.
+  Try<set<pid_t> > cgroupThreads =
+    cgroups::threads(hierarchy, TEST_CGROUPS_ROOT);
+  EXPECT_SOME(cgroupThreads);
+  EXPECT_EQ(0u, cgroupThreads.get().size());
+
+  // Assign ourselves to the test cgroup.
+  CHECK_SOME(cgroups::assign(hierarchy, TEST_CGROUPS_ROOT, ::getpid()));
+
+  // Get our threads (may be more than the numThreads we created if
+  // other threads are running).
+  Try<set<pid_t> > threads = proc::threads(::getpid());
+  ASSERT_SOME(threads);
+
+  // Check the test cgroup now only contains all child threads.
+  cgroupThreads = cgroups::threads(hierarchy, TEST_CGROUPS_ROOT);
+  EXPECT_SOME(cgroupThreads);
+  EXPECT_SOME_EQ(threads.get(), cgroupThreads);
+
+  // Terminate the additional threads.
+  for (size_t i = 0; i < numThreads; i++)
+  {
+    EXPECT_EQ(0, pthread_cancel(pthreads[i]));
+    EXPECT_EQ(0, pthread_join(pthreads[i], NULL));
+  }
+
+  // Move ourselves to the root cgroup.
+  CHECK_SOME(cgroups::assign(hierarchy, "", ::getpid()));
+
+  // Destroy the cgroup.
+  AWAIT_READY(cgroups::destroy(hierarchy, TEST_CGROUPS_ROOT));
+}
+
+
+TEST_F(CgroupsAnyHierarchyWithFreezerTest, ROOT_CGROUPS_DestroyStoppedProcess)
+{
+  std::string hierarchy = path::join(baseHierarchy, "freezer");
+  ASSERT_SOME(cgroups::create(hierarchy, TEST_CGROUPS_ROOT));
+
+  pid_t pid = ::fork();
+  ASSERT_NE(-1, pid);
+
+  if (pid == 0) {
+    // In child process.
+    while (true) { sleep(1); }
+
+    ABORT("Child should not reach this statement");
+  }
+
+  // In parent process.
+
+  // Put child into the freezer cgroup.
+  Try<Nothing> assign = cgroups::assign(hierarchy, TEST_CGROUPS_ROOT, pid);
+
+  // Stop the child process.
+  EXPECT_EQ(0, kill(pid, SIGSTOP));
+
+  AWAIT_READY(cgroups::destroy(hierarchy, TEST_CGROUPS_ROOT));
+
+  // cgroups::destroy will reap all processes in the cgroup so we should
+  // *not* be able to reap it now.
+  int status;
+  EXPECT_EQ(-1, ::waitpid(pid, &status, 0));
+  EXPECT_EQ(ECHILD, errno);
+}
+
+
+TEST_F(CgroupsAnyHierarchyWithFreezerTest, ROOT_CGROUPS_DestroyTracedProcess)
+{
+  std::string hierarchy = path::join(baseHierarchy, "freezer");
+  ASSERT_SOME(cgroups::create(hierarchy, TEST_CGROUPS_ROOT));
+
+  pid_t pid = ::fork();
+  ASSERT_NE(-1, pid);
+
+  if (pid == 0) {
+    // In child process.
+    while (true) { sleep(1); }
+
+    ABORT("Child should not reach this statement");
+  }
+
+  // In parent process.
+  Try<Nothing> assign = cgroups::assign(hierarchy, TEST_CGROUPS_ROOT, pid);
+  ASSERT_SOME(assign);
+
+  // Attach to the child process.
+  ASSERT_EQ(0, ptrace(PT_ATTACH, pid, NULL, NULL));
+
+  // Wait until the process is in traced state ('t' or 'T').
+  Duration elapsed = Duration::zero();
+  while (true) {
+    Result<proc::ProcessStatus> process = proc::status(pid);
+    ASSERT_SOME(process);
+
+    if (process.get().state == 'T' || process.get().state == 't') {
+      break;
+    }
+
+    if (elapsed > Seconds(1)) {
+      FAIL() << "Failed to wait for process to be traced";
+    }
+
+    os::sleep(Milliseconds(5));
+    elapsed += Milliseconds(5);
+  }
+
+  // Now destroy the cgroup.
+  AWAIT_READY(cgroups::destroy(hierarchy, TEST_CGROUPS_ROOT));
+
+  // cgroups::destroy will reap all processes in the cgroup so we should
+  // *not* be able to reap it now.
+  int status;
+  EXPECT_EQ(-1, ::waitpid(pid, &status, 0));
+  EXPECT_EQ(ECHILD, errno);
+}
+
+
+class CgroupsAnyHierarchyWithPerfEventTest
+  : public CgroupsAnyHierarchyTest
+{
+public:
+  CgroupsAnyHierarchyWithPerfEventTest()
+    : CgroupsAnyHierarchyTest("perf_event") {}
+};
+
+
+TEST_F(CgroupsAnyHierarchyWithPerfEventTest, ROOT_CGROUPS_Perf)
+{
+  int pipes[2];
+  int dummy;
+  ASSERT_NE(-1, ::pipe(pipes));
+
+  std::string hierarchy = path::join(baseHierarchy, "perf_event");
+  ASSERT_SOME(cgroups::create(hierarchy, TEST_CGROUPS_ROOT));
+
+  pid_t pid = ::fork();
+  ASSERT_NE(-1, pid);
+
+  if (pid == 0) {
+    // In child process.
+    ::close(pipes[1]);
+
+    // Wait until parent has assigned us to the cgroup.
+    ssize_t len;
+    while ((len = ::read(pipes[0], &dummy, sizeof(dummy))) == -1 &&
+           errno == EINTR);
+    ASSERT_EQ((ssize_t) sizeof(dummy), len);
+    ::close(pipes[0]);
+
+    while (true) {
+      // Don't sleep so 'perf' can actually sample something.
+    }
+
+    ABORT("Child should not reach here");
+  }
+
+  // In parent.
+  ::close(pipes[0]);
+
+  // Put child into the test cgroup.
+  ASSERT_SOME(cgroups::assign(hierarchy, TEST_CGROUPS_ROOT, pid));
+
+  ssize_t len;
+  while ((len = ::write(pipes[1], &dummy, sizeof(dummy))) == -1 &&
+         errno == EINTR);
+  ASSERT_EQ((ssize_t) sizeof(dummy), len);
+  ::close(pipes[1]);
+
+  std::set<std::string> events;
+  // Hardware event.
+  events.insert("cycles");
+  // Software event.
+  events.insert("task-clock");
+
+  // NOTE: Wait at least 2 seconds as we've seen some variance in how
+  // well 'perf' does across Linux distributions (e.g., Ubuntu 14.04)
+  // and we want to make sure that we collect some non-zero values.
+  Future<mesos::PerfStatistics> statistics =
+    perf::sample(events, TEST_CGROUPS_ROOT, Seconds(2));
+  AWAIT_READY(statistics);
+
+  ASSERT_TRUE(statistics.get().has_cycles());
+
+  // TODO(benh): Some Linux distributions (Ubuntu 14.04) fail to
+  // properly sample 'cycles' with 'perf', so we don't explicitly
+  // check the value here. See MESOS-3082.
+  // EXPECT_LT(0u, statistics.get().cycles());
+
+  ASSERT_TRUE(statistics.get().has_task_clock());
+  EXPECT_LT(0.0, statistics.get().task_clock());
+
+  // Kill the child process.
+  ASSERT_NE(-1, ::kill(pid, SIGKILL));
+
+  // Wait for the child process.
+  int status;
+  EXPECT_NE(-1, ::waitpid((pid_t) -1, &status, 0));
+  ASSERT_TRUE(WIFSIGNALED(status));
+  EXPECT_EQ(SIGKILL, WTERMSIG(status));
+
+  // Destroy the cgroup.
+  Future<Nothing> destroy = cgroups::destroy(hierarchy, TEST_CGROUPS_ROOT);
+  AWAIT_READY(destroy);
+}
+
+
+class CgroupsAnyHierarchyMemoryPressureTest
+  : public CgroupsAnyHierarchyTest
+{
+public:
+  CgroupsAnyHierarchyMemoryPressureTest()
+    : CgroupsAnyHierarchyTest("memory"),
+      cgroup(TEST_CGROUPS_ROOT) {}
+
+protected:
+  virtual void SetUp()
+  {
+    CgroupsAnyHierarchyTest::SetUp();
+
+    hierarchy = path::join(baseHierarchy, "memory");
+
+    ASSERT_SOME(cgroups::create(hierarchy, cgroup));
+  }
+
+  void listen()
+  {
+    const std::vector<Level> levels = {
+      Level::LOW,
+      Level::MEDIUM,
+      Level::CRITICAL
+    };
+
+    foreach (Level level, levels) {
+      Try<Owned<Counter>> counter = Counter::create(hierarchy, cgroup, level);
+      EXPECT_SOME(counter);
+
+      counters[level] = counter.get();
+    }
+  }
+
+  std::string hierarchy;
+  const std::string cgroup;
+
+  hashmap<Level, Owned<Counter>> counters;
+};
+
+
+TEST_F(CgroupsAnyHierarchyMemoryPressureTest, ROOT_IncreaseUnlockedRSS)
+{
+  MemoryTestHelper helper;
+  ASSERT_SOME(helper.spawn());
+  ASSERT_SOME(helper.pid());
+
+  const Bytes limit = Megabytes(16);
+
+  // Move the memory test helper into a cgroup and set the limit.
+  EXPECT_SOME(cgroups::memory::limit_in_bytes(hierarchy, cgroup, limit));
+  EXPECT_SOME(cgroups::assign(hierarchy, cgroup, helper.pid().get()));
+
+  listen();
+
+  // Used to save the counter readings from last iteration.
+  uint64_t previousLow = 0;
+  uint64_t previousMedium = 0;
+  uint64_t previousCritical = 0;
+
+  // Used to save the counter readings from this iteration.
+  uint64_t low;
+  uint64_t medium;
+  uint64_t critical;
+
+  // Use a guard to error out if it's been too long.
+  // TODO(chzhcn): Use a better way to set testing time limit.
+  uint64_t iterationLimit = limit.bytes() / getpagesize() * 10;
+
+  for (uint64_t i = 0; i < iterationLimit; i++) {
+    EXPECT_SOME(helper.increaseRSS(getpagesize()));
+
+    Future<uint64_t> _low = counters[Level::LOW]->value();
+    Future<uint64_t> _medium = counters[Level::MEDIUM]->value();
+    Future<uint64_t> _critical = counters[Level::CRITICAL]->value();
+
+    AWAIT_READY(_low);
+    AWAIT_READY(_medium);
+    AWAIT_READY(_critical);
+
+    low = _low.get();
+    medium = _medium.get();
+    critical = _critical.get();
+
+    // We need to know the readings are the same as last time to be
+    // sure they are stable, because the reading is not atomic. For
+    // example, the medium could turn positive after we read low to be
+    // 0, but this should be fixed by the next read immediately.
+    if ((low == previousLow &&
+         medium == previousMedium &&
+         critical == previousCritical)) {
+      if (low != 0) {
+        EXPECT_LE(medium, low);
+        EXPECT_LE(critical, medium);
+
+        // When child's RSS is full, it will be OOM-kill'ed if we
+        // don't stop it right away.
+        break;
+      } else {
+        EXPECT_EQ(0u, medium);
+        EXPECT_EQ(0u, critical);
+      }
+    }
+
+    previousLow = low;
+    previousMedium = medium;
+    previousCritical = critical;
+  }
+}
+
+
+TEST_F(CgroupsAnyHierarchyMemoryPressureTest, ROOT_IncreasePageCache)
+{
+  MemoryTestHelper helper;
+  ASSERT_SOME(helper.spawn());
+  ASSERT_SOME(helper.pid());
+
+  const Bytes limit = Megabytes(16);
+
+  // Move the memory test helper into a cgroup and set the limit.
+  EXPECT_SOME(cgroups::memory::limit_in_bytes(hierarchy, cgroup, limit));
+  EXPECT_SOME(cgroups::assign(hierarchy, cgroup, helper.pid().get()));
+
+  listen();
+
+  // Used to save the counter readings from last iteration.
+  uint64_t previousLow = 0;
+  uint64_t previousMedium = 0;
+  uint64_t previousCritical = 0;
+
+  // Used to save the counter readings from this iteration.
+  uint64_t low;
+  uint64_t medium;
+  uint64_t critical;
+
+  // Use a guard to error out if it's been too long.
+  // TODO(chzhcn): Use a better way to set testing time limit.
+  uint64_t iterationLimit = limit.bytes() / Megabytes(1).bytes() * 2;
+
+  for (uint64_t i = 0; i < iterationLimit; i++) {
+    EXPECT_SOME(helper.increasePageCache(Megabytes(1)));
+
+    Future<uint64_t> _low = counters[Level::LOW]->value();
+    Future<uint64_t> _medium = counters[Level::MEDIUM]->value();
+    Future<uint64_t> _critical = counters[Level::CRITICAL]->value();
+
+    AWAIT_READY(_low);
+    AWAIT_READY(_medium);
+    AWAIT_READY(_critical);
+
+    low = _low.get();
+    medium = _medium.get();
+    critical = _critical.get();
+
+    // We need to know the readings are the same as last time to be
+    // sure they are stable, because the reading is not atomic. For
+    // example, the medium could turn positive after we read low to be
+    // 0, but this should be fixed by the next read immediately.
+    if ((low == previousLow &&
+         medium == previousMedium &&
+         critical == previousCritical)) {
+      if (low != 0) {
+        EXPECT_LE(medium, low);
+        EXPECT_LE(critical, medium);
+
+        // Different from the RSS test, since the child is only
+        // consuming at a slow rate the page cache, which is evictable
+        // and reclaimable, we could therefore be in this state
+        // forever. Our guard will let us out shortly.
+      } else {
+        EXPECT_EQ(0u, medium);
+        EXPECT_EQ(0u, critical);
+      }
+    }
+
+    previousLow = low;
+    previousMedium = medium;
+    previousCritical = critical;
+  }
+
+  EXPECT_LT(0u, low);
+}
+
+// Tests the cpuacct::stat API. This test just tests for ANY value returned by
+// the API.
+TEST_F(CgroupsAnyHierarchyWithCpuAcctMemoryTest, ROOT_CGROUPS_CpuAcctsStats)
+{
+  const std::string hierarchy = path::join(baseHierarchy, "cpuacct");
+  ASSERT_SOME(cgroups::create(hierarchy, TEST_CGROUPS_ROOT));
+
+  CHECK_SOME(cgroups::assign(hierarchy, TEST_CGROUPS_ROOT, ::getpid()));
+
+  ASSERT_SOME(cgroups::cpuacct::stat(hierarchy, TEST_CGROUPS_ROOT));
+
+  // Move ourselves to the root cgroup.
+  CHECK_SOME(cgroups::assign(hierarchy, "", ::getpid()));
+
+  AWAIT_READY(cgroups::destroy(hierarchy, TEST_CGROUPS_ROOT));
+}
+
+} // namespace tests {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer/composing_containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/composing_containerizer_tests.cpp b/src/tests/containerizer/composing_containerizer_tests.cpp
new file mode 100644
index 0000000..d66f519
--- /dev/null
+++ b/src/tests/containerizer/composing_containerizer_tests.cpp
@@ -0,0 +1,171 @@
+/**
+ * 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 <vector>
+
+#include <process/future.hpp>
+#include <process/gmock.hpp>
+
+#include <stout/option.hpp>
+
+#include "messages/messages.hpp"
+
+#include "slave/containerizer/containerizer.hpp"
+#include "slave/containerizer/composing.hpp"
+
+#include "tests/mesos.hpp"
+
+using namespace mesos::internal::slave;
+
+using namespace process;
+
+using std::vector;
+
+using testing::_;
+using testing::Return;
+
+namespace mesos {
+namespace internal {
+namespace tests {
+
+
+class ComposingContainerizerTest : public MesosTest {};
+
+class MockContainerizer : public slave::Containerizer
+{
+public:
+  MOCK_METHOD1(
+      recover,
+      process::Future<Nothing>(
+          const Option<slave::state::SlaveState>&));
+
+  MOCK_METHOD7(
+      launch,
+      process::Future<bool>(
+          const ContainerID&,
+          const ExecutorInfo&,
+          const std::string&,
+          const Option<std::string>&,
+          const SlaveID&,
+          const process::PID<Slave>&,
+          bool));
+
+  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>&,
+          bool));
+
+  MOCK_METHOD2(
+      update,
+      process::Future<Nothing>(
+          const ContainerID&,
+          const Resources&));
+
+  MOCK_METHOD1(
+      usage,
+      process::Future<ResourceStatistics>(
+          const ContainerID&));
+
+  MOCK_METHOD1(
+      wait,
+      process::Future<containerizer::Termination>(
+          const ContainerID&));
+
+  MOCK_METHOD1(
+      destroy,
+      void(const ContainerID&));
+
+  MOCK_METHOD0(
+      containers,
+      process::Future<hashset<ContainerID> >());
+};
+
+
+// This test checks if destroy is called while container is being
+// launched, the composing containerizer still calls the underlying
+// containerizer's destroy and skip calling the rest of the
+// containerizers.
+TEST_F(ComposingContainerizerTest, DestroyWhileLaunching)
+{
+  vector<Containerizer*> containerizers;
+
+  MockContainerizer* mockContainerizer = new MockContainerizer();
+  MockContainerizer* mockContainerizer2 = new MockContainerizer();
+
+  containerizers.push_back(mockContainerizer);
+  containerizers.push_back(mockContainerizer2);
+
+  ComposingContainerizer containerizer(containerizers);
+  ContainerID containerId;
+  containerId.set_value("container");
+  TaskInfo taskInfo;
+  ExecutorInfo executorInfo;
+  SlaveID slaveId;
+  PID<Slave> slavePid;
+
+  Promise<bool> launchPromise;
+
+  EXPECT_CALL(*mockContainerizer, launch(_, _, _, _, _, _, _, _))
+    .WillOnce(Return(launchPromise.future()));
+
+  Future<Nothing> destroy;
+
+  EXPECT_CALL(*mockContainerizer, destroy(_))
+    .WillOnce(FutureSatisfy(&destroy));
+
+  Future<bool> launch = containerizer.launch(
+      containerId,
+      taskInfo,
+      executorInfo,
+      "dir",
+      "user",
+      slaveId,
+      slavePid,
+      false);
+
+  Resources resources = Resources::parse("cpus:1;mem:256").get();
+
+  EXPECT_TRUE(launch.isPending());
+
+  containerizer.destroy(containerId);
+
+  EXPECT_CALL(*mockContainerizer2, launch(_, _, _, _, _, _, _, _))
+    .Times(0);
+
+  // We make sure the destroy is being called on the first containerizer.
+  // The second containerizer shouldn't be called as well since the
+  // container is already destroyed.
+  AWAIT_READY(destroy);
+
+  launchPromise.set(false);
+  AWAIT_FAILED(launch);
+}
+
+} // namespace tests {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer/containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/containerizer_tests.cpp b/src/tests/containerizer/containerizer_tests.cpp
new file mode 100644
index 0000000..a44b6e8
--- /dev/null
+++ b/src/tests/containerizer/containerizer_tests.cpp
@@ -0,0 +1,732 @@
+/**
+ * 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 <vector>
+
+#include <gmock/gmock.h>
+
+#include <mesos/mesos.hpp>
+
+#include <mesos/slave/isolator.hpp>
+
+#include <process/future.hpp>
+#include <process/owned.hpp>
+
+#include <stout/strings.hpp>
+
+#include "slave/flags.hpp"
+
+#include "slave/containerizer/fetcher.hpp"
+#include "slave/containerizer/launcher.hpp"
+
+#include "slave/containerizer/mesos/containerizer.hpp"
+
+#include "tests/flags.hpp"
+#include "tests/mesos.hpp"
+#include "tests/utils.hpp"
+
+#include "tests/containerizer/isolator.hpp"
+#include "tests/containerizer/launcher.hpp"
+
+using namespace process;
+
+using mesos::internal::master::Master;
+
+using mesos::internal::slave::Fetcher;
+using mesos::internal::slave::Launcher;
+using mesos::internal::slave::MesosContainerizer;
+using mesos::internal::slave::MesosContainerizerProcess;
+using mesos::internal::slave::PosixLauncher;
+using mesos::internal::slave::Provisioner;
+using mesos::internal::slave::Slave;
+
+using mesos::internal::slave::state::ExecutorState;
+using mesos::internal::slave::state::FrameworkState;
+using mesos::internal::slave::state::RunState;
+using mesos::internal::slave::state::SlaveState;
+
+using mesos::slave::ExecutorLimitation;
+using mesos::slave::ExecutorRunState;
+using mesos::slave::Isolator;
+using mesos::slave::IsolatorProcess;
+
+using std::list;
+using std::map;
+using std::string;
+using std::vector;
+
+using testing::_;
+using testing::DoAll;
+using testing::Invoke;
+using testing::Return;
+
+namespace mesos {
+namespace internal {
+namespace tests {
+
+class MesosContainerizerIsolatorPreparationTest :
+  public TemporaryDirectoryTest
+{
+public:
+  // Construct a MesosContainerizer with TestIsolator(s) which use the provided
+  // 'prepare' command(s).
+  Try<MesosContainerizer*> CreateContainerizer(
+      Fetcher* fetcher,
+      const vector<Option<CommandInfo>>& prepares)
+  {
+    vector<Owned<Isolator>> isolators;
+
+    foreach (const Option<CommandInfo>& prepare, prepares) {
+      Try<Isolator*> isolator = TestIsolatorProcess::create(prepare);
+      if (isolator.isError()) {
+        return Error(isolator.error());
+      }
+
+      isolators.push_back(Owned<Isolator>(isolator.get()));
+    }
+
+    slave::Flags flags;
+    flags.launcher_dir = path::join(tests::flags.build_dir, "src");
+
+    Try<Launcher*> launcher = PosixLauncher::create(flags);
+    if (launcher.isError()) {
+      return Error(launcher.error());
+    }
+
+    return new MesosContainerizer(
+        flags,
+        false,
+        fetcher,
+        Owned<Launcher>(launcher.get()),
+        isolators,
+        hashmap<ContainerInfo::Image::Type, Owned<Provisioner>>());
+  }
+
+  Try<MesosContainerizer*> CreateContainerizer(
+      Fetcher* fetcher,
+      const Option<CommandInfo>& prepare)
+  {
+    vector<Option<CommandInfo>> prepares;
+    prepares.push_back(prepare);
+
+    return CreateContainerizer(fetcher, prepares);
+  }
+};
+
+
+// The isolator has a prepare command that succeeds.
+TEST_F(MesosContainerizerIsolatorPreparationTest, ScriptSucceeds)
+{
+  string directory = os::getcwd(); // We're inside a temporary sandbox.
+  string file = path::join(directory, "child.script.executed");
+
+  Fetcher fetcher;
+
+  Try<MesosContainerizer*> containerizer = CreateContainerizer(
+      &fetcher,
+      CREATE_COMMAND_INFO("touch " + file));
+
+  CHECK_SOME(containerizer);
+
+  ContainerID containerId;
+  containerId.set_value("test_container");
+
+  Future<bool> launch = containerizer.get()->launch(
+      containerId,
+      CREATE_EXECUTOR_INFO("executor", "exit 0"),
+      directory,
+      None(),
+      SlaveID(),
+      PID<Slave>(),
+      false);
+
+  // Wait until the launch completes.
+  AWAIT_READY(launch);
+
+  // Wait for the child (preparation script + executor) to complete.
+  Future<containerizer::Termination> wait =
+    containerizer.get()->wait(containerId);
+
+  AWAIT_READY(wait);
+
+  // Check the child exited correctly.
+  EXPECT_TRUE(wait.get().has_status());
+  EXPECT_EQ(0, wait.get().status());
+
+  // Check the preparation script actually ran.
+  EXPECT_TRUE(os::exists(file));
+
+  // Destroy the container.
+  containerizer.get()->destroy(containerId);
+
+  delete containerizer.get();
+}
+
+
+// The isolator has a prepare command that fails.
+TEST_F(MesosContainerizerIsolatorPreparationTest, ScriptFails)
+{
+  string directory = os::getcwd(); // We're inside a temporary sandbox.
+  string file = path::join(directory, "child.script.executed");
+
+  Fetcher fetcher;
+
+  Try<MesosContainerizer*> containerizer = CreateContainerizer(
+      &fetcher,
+      CREATE_COMMAND_INFO("touch " + file + " && exit 1"));
+
+  CHECK_SOME(containerizer);
+
+  ContainerID containerId;
+  containerId.set_value("test_container");
+
+  Future<bool> launch = containerizer.get()->launch(
+      containerId,
+      CREATE_EXECUTOR_INFO("executor", "exit 0"),
+      directory,
+      None(),
+      SlaveID(),
+      PID<Slave>(),
+      false);
+
+  // Wait until the launch completes.
+  AWAIT_READY(launch);
+
+  // Wait for the child (preparation script + executor) to complete.
+  Future<containerizer::Termination> wait =
+    containerizer.get()->wait(containerId);
+
+  AWAIT_READY(wait);
+
+  // Check the child failed to exit correctly.
+  EXPECT_TRUE(wait.get().has_status());
+  EXPECT_NE(0, wait.get().status());
+
+  // Check the preparation script actually ran.
+  EXPECT_TRUE(os::exists(file));
+
+  // Destroy the container.
+  containerizer.get()->destroy(containerId);
+
+  delete containerizer.get();
+}
+
+
+// There are two isolators, one with a prepare command that succeeds
+// and another that fails. The execution order is not defined but the
+// launch should fail from the failing prepare command.
+TEST_F(MesosContainerizerIsolatorPreparationTest, MultipleScripts)
+{
+  string directory = os::getcwd(); // We're inside a temporary sandbox.
+  string file1 = path::join(directory, "child.script.executed.1");
+  string file2 = path::join(directory, "child.script.executed.2");
+
+  vector<Option<CommandInfo>> prepares;
+
+  // This isolator prepare command one will succeed if called first,
+  // otherwise it won't get run.
+  prepares.push_back(CREATE_COMMAND_INFO("touch " + file1 + " && exit 0"));
+
+  // This will fail, either first or after the successful command.
+  prepares.push_back(CREATE_COMMAND_INFO("touch " + file2 + " && exit 1"));
+
+  Fetcher fetcher;
+
+  Try<MesosContainerizer*> containerizer =
+    CreateContainerizer(&fetcher, prepares);
+
+  CHECK_SOME(containerizer);
+
+  ContainerID containerId;
+  containerId.set_value("test_container");
+
+  Future<bool> launch = containerizer.get()->launch(
+      containerId,
+      CREATE_EXECUTOR_INFO("executor", "exit 0"),
+      directory,
+      None(),
+      SlaveID(),
+      PID<Slave>(),
+      false);
+
+  // Wait until the launch completes.
+  AWAIT_READY(launch);
+
+  // Wait for the child (preparation script(s) + executor) to complete.
+  Future<containerizer::Termination> wait =
+    containerizer.get()->wait(containerId);
+  AWAIT_READY(wait);
+
+  // Check the child failed to exit correctly.
+  EXPECT_TRUE(wait.get().has_status());
+  EXPECT_NE(0, wait.get().status());
+
+  // Check the failing preparation script has actually ran.
+  EXPECT_TRUE(os::exists(file2));
+
+  // Destroy the container.
+  containerizer.get()->destroy(containerId);
+
+  delete containerizer.get();
+}
+
+
+class MesosContainerizerExecuteTest : public TemporaryDirectoryTest {};
+
+
+TEST_F(MesosContainerizerExecuteTest, IoRedirection)
+{
+  string directory = os::getcwd(); // We're inside a temporary sandbox.
+
+  slave::Flags flags;
+  flags.launcher_dir = path::join(tests::flags.build_dir, "src");
+
+  Fetcher fetcher;
+
+  // Use local=false so std{err,out} are redirected to files.
+  Try<MesosContainerizer*> containerizer =
+    MesosContainerizer::create(flags, false, &fetcher);
+
+  ASSERT_SOME(containerizer);
+
+  ContainerID containerId;
+  containerId.set_value("test_container");
+
+  string errMsg = "this is stderr";
+  string outMsg = "this is stdout";
+  string command =
+    "(echo '" + errMsg + "' 1>&2) && echo '" + outMsg + "'";
+
+  Future<bool> launch = containerizer.get()->launch(
+      containerId,
+      CREATE_EXECUTOR_INFO("executor", command),
+      directory,
+      None(),
+      SlaveID(),
+      PID<Slave>(),
+      false);
+
+  // Wait for the launch to complete.
+  AWAIT_READY(launch);
+
+  // Wait on the container.
+  Future<containerizer::Termination> wait =
+    containerizer.get()->wait(containerId);
+
+  AWAIT_READY(wait);
+
+  // Check the executor exited correctly.
+  EXPECT_TRUE(wait.get().has_status());
+  EXPECT_EQ(0, wait.get().status());
+
+  // Check that std{err, out} was redirected.
+  // NOTE: Fetcher uses GLOG, which outputs extra information to
+  // stderr.
+  Try<string> stderr = os::read(path::join(directory, "stderr"));
+  ASSERT_SOME(stderr);
+  EXPECT_TRUE(strings::contains(stderr.get(), errMsg));
+
+  EXPECT_SOME_EQ(outMsg + "\n", os::read(path::join(directory, "stdout")));
+
+  delete containerizer.get();
+}
+
+
+class MesosContainerizerDestroyTest : public MesosTest {};
+
+
+class MockMesosContainerizerProcess : public MesosContainerizerProcess
+{
+public:
+  MockMesosContainerizerProcess(
+      const slave::Flags& flags,
+      bool local,
+      Fetcher* fetcher,
+      const Owned<Launcher>& launcher,
+      const vector<Owned<Isolator>>& isolators,
+      const hashmap<ContainerInfo::Image::Type,
+                    Owned<Provisioner>>& provisioners)
+    : MesosContainerizerProcess(
+          flags,
+          local,
+          fetcher,
+          launcher,
+          isolators,
+          provisioners)
+  {
+    // NOTE: See TestContainerizer::setup for why we use
+    // 'EXPECT_CALL' and 'WillRepeatedly' here instead of
+    // 'ON_CALL' and 'WillByDefault'.
+    EXPECT_CALL(*this, exec(_, _))
+      .WillRepeatedly(Invoke(this, &MockMesosContainerizerProcess::_exec));
+  }
+
+  MOCK_METHOD2(
+      exec,
+      Future<bool>(
+          const ContainerID& containerId,
+          int pipeWrite));
+
+  Future<bool> _exec(
+      const ContainerID& containerId,
+      int pipeWrite)
+  {
+    return MesosContainerizerProcess::exec(
+        containerId,
+        pipeWrite);
+  }
+};
+
+
+class MockIsolator : public mesos::slave::Isolator
+{
+public:
+  MockIsolator()
+  {
+    EXPECT_CALL(*this, watch(_))
+      .WillRepeatedly(Return(watchPromise.future()));
+
+    EXPECT_CALL(*this, isolate(_, _))
+      .WillRepeatedly(Return(Nothing()));
+
+    EXPECT_CALL(*this, cleanup(_))
+      .WillRepeatedly(Return(Nothing()));
+
+    EXPECT_CALL(*this, prepare(_, _, _, _, _))
+      .WillRepeatedly(Invoke(this, &MockIsolator::_prepare));
+  }
+
+  MOCK_METHOD2(
+      recover,
+      Future<Nothing>(
+          const list<ExecutorRunState>&,
+          const hashset<ContainerID>&));
+
+  MOCK_METHOD5(
+      prepare,
+      Future<Option<CommandInfo>>(
+          const ContainerID&,
+          const ExecutorInfo&,
+          const string&,
+          const Option<string>&,
+          const Option<string>&));
+
+  virtual Future<Option<CommandInfo>> _prepare(
+      const ContainerID& containerId,
+      const ExecutorInfo& executorInfo,
+      const string& directory,
+      const Option<string>& rootfs,
+      const Option<string>& user)
+  {
+    return None();
+  }
+
+  MOCK_METHOD2(
+      isolate,
+      Future<Nothing>(const ContainerID&, pid_t));
+
+  MOCK_METHOD1(
+      watch,
+      Future<mesos::slave::ExecutorLimitation>(const ContainerID&));
+
+  MOCK_METHOD2(
+      update,
+      Future<Nothing>(const ContainerID&, const Resources&));
+
+  MOCK_METHOD1(
+      usage,
+      Future<ResourceStatistics>(const ContainerID&));
+
+  MOCK_METHOD1(
+      cleanup,
+      Future<Nothing>(const ContainerID&));
+
+  Promise<mesos::slave::ExecutorLimitation> watchPromise;
+};
+
+
+// Destroying a mesos containerizer while it is fetching should
+// complete without waiting for the fetching to finish.
+TEST_F(MesosContainerizerDestroyTest, DestroyWhileFetching)
+{
+  slave::Flags flags = CreateSlaveFlags();
+
+  Try<Launcher*> launcher = PosixLauncher::create(flags);
+  ASSERT_SOME(launcher);
+
+  Fetcher fetcher;
+
+  MockMesosContainerizerProcess* process = new MockMesosContainerizerProcess(
+      flags,
+      true,
+      &fetcher,
+      Owned<Launcher>(launcher.get()),
+      vector<Owned<Isolator>>(),
+      hashmap<ContainerInfo::Image::Type, Owned<Provisioner>>());
+
+  Future<Nothing> exec;
+  Promise<bool> promise;
+
+  // Letting exec hang to simulate a long fetch.
+  EXPECT_CALL(*process, exec(_, _))
+    .WillOnce(DoAll(FutureSatisfy(&exec),
+                    Return(promise.future())));
+
+  MesosContainerizer containerizer((Owned<MesosContainerizerProcess>(process)));
+
+  ContainerID containerId;
+  containerId.set_value("test_container");
+
+  TaskInfo taskInfo;
+  CommandInfo commandInfo;
+  taskInfo.mutable_command()->MergeFrom(commandInfo);
+
+  containerizer.launch(
+      containerId,
+      taskInfo,
+      CREATE_EXECUTOR_INFO("executor", "exit 0"),
+      os::getcwd(),
+      None(),
+      SlaveID(),
+      PID<Slave>(),
+      false);
+
+  Future<containerizer::Termination> wait = containerizer.wait(containerId);
+
+  AWAIT_READY(exec);
+
+  containerizer.destroy(containerId);
+
+  // The container should still exit even if fetch didn't complete.
+  AWAIT_READY(wait);
+}
+
+
+// Destroying a mesos containerizer while it is preparing should wait
+// until isolators are finished preparing before destroying.
+TEST_F(MesosContainerizerDestroyTest, DestroyWhilePreparing)
+{
+  slave::Flags flags = CreateSlaveFlags();
+
+  Try<Launcher*> launcher = PosixLauncher::create(flags);
+  ASSERT_SOME(launcher);
+
+  MockIsolator* isolator = new MockIsolator();
+
+  Future<Nothing> prepare;
+  Promise<Option<CommandInfo>> promise;
+
+  // Simulate a long prepare from the isolator.
+  EXPECT_CALL(*isolator, prepare(_, _, _, _, _))
+    .WillOnce(DoAll(FutureSatisfy(&prepare),
+                    Return(promise.future())));
+
+  Fetcher fetcher;
+
+  MockMesosContainerizerProcess* process = new MockMesosContainerizerProcess(
+      flags,
+      true,
+      &fetcher,
+      Owned<Launcher>(launcher.get()),
+      {Owned<Isolator>(isolator)},
+      hashmap<ContainerInfo::Image::Type, Owned<Provisioner>>());
+
+  MesosContainerizer containerizer((Owned<MesosContainerizerProcess>(process)));
+
+  ContainerID containerId;
+  containerId.set_value("test_container");
+
+  TaskInfo taskInfo;
+  CommandInfo commandInfo;
+  taskInfo.mutable_command()->MergeFrom(commandInfo);
+
+  containerizer.launch(
+      containerId,
+      taskInfo,
+      CREATE_EXECUTOR_INFO("executor", "exit 0"),
+      os::getcwd(),
+      None(),
+      SlaveID(),
+      PID<Slave>(),
+      false);
+
+  Future<containerizer::Termination> wait = containerizer.wait(containerId);
+
+  AWAIT_READY(prepare);
+
+  containerizer.destroy(containerId);
+
+  // The container should not exit until prepare is complete.
+  ASSERT_TRUE(wait.isPending());
+
+  // Need to help the compiler to disambiguate between overloads.
+  Option<CommandInfo> option = commandInfo;
+  promise.set(option);
+
+  AWAIT_READY(wait);
+
+  containerizer::Termination termination = wait.get();
+
+  EXPECT_EQ(
+      "Container destroyed while preparing isolators",
+      termination.message());
+
+  EXPECT_TRUE(termination.killed());
+  EXPECT_FALSE(termination.has_status());
+}
+
+
+// This action destroys the container using the real launcher and
+// waits until the destroy is complete.
+ACTION_P(InvokeDestroyAndWait, launcher)
+{
+  Future<Nothing> destroy = launcher->real->destroy(arg0);
+  AWAIT_READY(destroy);
+}
+
+
+// This test verifies that when a container destruction fails the
+// 'container_destroy_errors' metric is updated.
+TEST_F(MesosContainerizerDestroyTest, LauncherDestroyFailure)
+{
+  // Create a TestLauncher backed by PosixLauncher.
+  slave::Flags flags = CreateSlaveFlags();
+
+  Try<Launcher*> launcher_ = PosixLauncher::create(flags);
+  ASSERT_SOME(launcher_);
+
+  TestLauncher* launcher = new TestLauncher(Owned<Launcher>(launcher_.get()));
+
+  Fetcher fetcher;
+
+  MesosContainerizerProcess* process = new MesosContainerizerProcess(
+      flags,
+      true,
+      &fetcher,
+      Owned<Launcher>(launcher),
+      vector<Owned<Isolator>>(),
+      hashmap<ContainerInfo::Image::Type, Owned<Provisioner>>());
+
+  MesosContainerizer containerizer((Owned<MesosContainerizerProcess>(process)));
+
+  ContainerID containerId;
+  containerId.set_value("test_container");
+
+  TaskInfo taskInfo;
+  CommandInfo commandInfo;
+  taskInfo.mutable_command()->MergeFrom(commandInfo);
+
+  // Destroy the container using the PosixLauncher but return a failed
+  // future to the containerizer.
+  EXPECT_CALL(*launcher, destroy(_))
+    .WillOnce(DoAll(InvokeDestroyAndWait(launcher),
+                    Return(Failure("Destroy failure"))));
+
+  Future<bool> launch = containerizer.launch(
+      containerId,
+      taskInfo,
+      CREATE_EXECUTOR_INFO("executor", "sleep 1000"),
+      os::getcwd(),
+      None(),
+      SlaveID(),
+      PID<Slave>(),
+      false);
+
+  AWAIT_READY(launch);
+
+  Future<containerizer::Termination> wait = containerizer.wait(containerId);
+
+  containerizer.destroy(containerId);
+
+  // The container destroy should fail.
+  AWAIT_FAILED(wait);
+
+  // We settle the clock here to ensure that the processing of
+  // 'MesosContainerizerProcess::__destroy()' is complete and the
+  // metric is updated.
+  Clock::pause();
+  Clock::settle();
+  Clock::resume();
+
+  // Ensure that the metric is updated.
+  JSON::Object metrics = Metrics();
+  ASSERT_EQ(
+      1u,
+      metrics.values.count("containerizer/mesos/container_destroy_errors"));
+  ASSERT_EQ(
+      1u,
+      metrics.values["containerizer/mesos/container_destroy_errors"]);
+}
+
+
+class MesosContainerizerRecoverTest : public MesosTest {};
+
+
+// This test checks that MesosContainerizer doesn't recover executors
+// that were started by another containerizer (e.g: Docker).
+TEST_F(MesosContainerizerRecoverTest, SkipRecoverNonMesosContainers)
+{
+  slave::Flags flags = CreateSlaveFlags();
+  Fetcher fetcher;
+
+  Try<MesosContainerizer*> containerizer =
+    MesosContainerizer::create(flags, true, &fetcher);
+
+  ASSERT_SOME(containerizer);
+
+  ExecutorID executorId;
+  executorId.set_value(UUID::random().toString());
+
+  ContainerID containerId;
+  containerId.set_value(UUID::random().toString());
+
+  ExecutorInfo executorInfo;
+  executorInfo.mutable_container()->set_type(ContainerInfo::DOCKER);
+
+  ExecutorState executorState;
+  executorState.info = executorInfo;
+  executorState.latest = containerId;
+
+  RunState runState;
+  runState.id = containerId;
+  executorState.runs.put(containerId, runState);
+
+  FrameworkState frameworkState;
+  frameworkState.executors.put(executorId, executorState);
+
+  SlaveState slaveState;
+  FrameworkID frameworkId;
+  frameworkId.set_value(UUID::random().toString());
+  slaveState.frameworks.put(frameworkId, frameworkState);
+
+  Future<Nothing> recover = containerizer.get()->recover(slaveState);
+  AWAIT_READY(recover);
+
+  Future<hashset<ContainerID>> containers = containerizer.get()->containers();
+  AWAIT_READY(containers);
+  EXPECT_EQ(0u, containers.get().size());
+
+  delete containerizer.get();
+}
+
+} // namespace tests {
+} // namespace internal {
+} // namespace mesos {


[09/12] mesos git commit: Moved containerizer related tests under src/tests/containerizer.

Posted by ji...@apache.org.
http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer/docker_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/docker_tests.cpp b/src/tests/containerizer/docker_tests.cpp
new file mode 100644
index 0000000..a4a2725
--- /dev/null
+++ b/src/tests/containerizer/docker_tests.cpp
@@ -0,0 +1,421 @@
+/**
+ * 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 <process/owned.hpp>
+#include <process/subprocess.hpp>
+
+#include <stout/duration.hpp>
+#include <stout/option.hpp>
+#include <stout/gtest.hpp>
+
+#include "docker/docker.hpp"
+
+#include "mesos/resources.hpp"
+
+#include "tests/environment.hpp"
+#include "tests/flags.hpp"
+#include "tests/mesos.hpp"
+
+using namespace process;
+
+using std::list;
+using std::string;
+
+namespace mesos {
+namespace internal {
+namespace tests {
+
+
+static const string NAME_PREFIX="mesos-docker";
+
+
+class DockerTest : public MesosTest
+{
+  virtual void TearDown()
+  {
+    Try<Docker*> docker = Docker::create(tests::flags.docker, false);
+    ASSERT_SOME(docker);
+
+    Future<list<Docker::Container>> containers =
+      docker.get()->ps(true, NAME_PREFIX);
+
+    AWAIT_READY(containers);
+
+    // Cleanup all mesos launched containers.
+    foreach (const Docker::Container& container, containers.get()) {
+      AWAIT_READY_FOR(docker.get()->rm(container.id, true), Seconds(30));
+    }
+
+    delete docker.get();
+  }
+};
+
+// This test tests the functionality of the docker's interfaces.
+TEST_F(DockerTest, ROOT_DOCKER_interface)
+{
+  const string containerName = NAME_PREFIX + "-test";
+  Resources resources = Resources::parse("cpus:1;mem:512").get();
+
+  Owned<Docker> docker(Docker::create(tests::flags.docker, false).get());
+
+  // Verify that we do not see the container.
+  Future<list<Docker::Container> > containers = docker->ps(true, containerName);
+  AWAIT_READY(containers);
+  foreach (const Docker::Container& container, containers.get()) {
+    EXPECT_NE("/" + containerName, container.name);
+  }
+
+  Try<string> directory = environment->mkdtemp();
+  CHECK_SOME(directory) << "Failed to create temporary directory";
+
+  ContainerInfo containerInfo;
+  containerInfo.set_type(ContainerInfo::DOCKER);
+
+  ContainerInfo::DockerInfo dockerInfo;
+  dockerInfo.set_image("busybox");
+  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
+
+  CommandInfo commandInfo;
+  commandInfo.set_value("sleep 120");
+
+  // Start the container.
+  Future<Nothing> status = docker->run(
+      containerInfo,
+      commandInfo,
+      containerName,
+      directory.get(),
+      "/mnt/mesos/sandbox",
+      resources);
+
+  Future<Docker::Container> inspect =
+    docker->inspect(containerName, Seconds(1));
+  AWAIT_READY(inspect);
+
+  // 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);
+
+  // Test some fields of the container.
+  EXPECT_NE("", inspect.get().id);
+  EXPECT_EQ("/" + containerName, inspect.get().name);
+  EXPECT_SOME(inspect.get().pid);
+
+  // Stop the container.
+  status = docker->stop(containerName);
+  AWAIT_READY(status);
+
+  // 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, containerName);
+  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.
+  inspect = docker->inspect(containerName);
+  AWAIT_READY(inspect);
+
+  EXPECT_NE("", inspect.get().id);
+  EXPECT_EQ("/" + containerName, inspect.get().name);
+  EXPECT_NONE(inspect.get().pid);
+
+  // Remove the container.
+  status = docker->rm(containerName);
+  AWAIT_READY(status);
+
+  // Should not be able to inspect the container.
+  inspect = docker->inspect(containerName);
+  AWAIT_FAILED(inspect);
+
+  // 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);
+  }
+
+  // Start the container again, this time we will do a "rm -f"
+  // directly, instead of stopping and rm.
+  status = docker->run(
+      containerInfo,
+      commandInfo,
+      containerName,
+      directory.get(),
+      "/mnt/mesos/sandbox",
+      resources);
+
+  inspect = docker->inspect(containerName, Seconds(1));
+  AWAIT_READY(inspect);
+
+  // 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);
+
+  // 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, containerName);
+  AWAIT_READY(containers);
+  foreach (const Docker::Container& container, containers.get()) {
+    EXPECT_NE("/" + containerName, container.name);
+  }
+}
+
+
+TEST_F(DockerTest, ROOT_DOCKER_CheckCommandWithShell)
+{
+  Owned<Docker> docker(Docker::create(tests::flags.docker, false).get());
+
+  ContainerInfo containerInfo;
+  containerInfo.set_type(ContainerInfo::DOCKER);
+
+  ContainerInfo::DockerInfo dockerInfo;
+  dockerInfo.set_image("busybox");
+  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
+
+  CommandInfo commandInfo;
+  commandInfo.set_shell(true);
+
+  Future<Nothing> run = docker->run(
+      containerInfo,
+      commandInfo,
+      "testContainer",
+      "dir",
+      "/mnt/mesos/sandbox");
+
+  ASSERT_TRUE(run.isFailed());
+}
+
+
+TEST_F(DockerTest, ROOT_DOCKER_CheckPortResource)
+{
+  const string containerName = NAME_PREFIX + "-port-resource-test";
+  Owned<Docker> docker(Docker::create(tests::flags.docker, false).get());
+
+  // Make sure the container is removed.
+  Future<Nothing> remove = docker->rm(containerName, true);
+
+  ASSERT_TRUE(process::internal::await(remove, Seconds(10)));
+
+  ContainerInfo containerInfo;
+  containerInfo.set_type(ContainerInfo::DOCKER);
+
+  ContainerInfo::DockerInfo dockerInfo;
+  dockerInfo.set_image("busybox");
+  dockerInfo.set_network(ContainerInfo::DockerInfo::BRIDGE);
+
+  ContainerInfo::DockerInfo::PortMapping portMapping;
+  portMapping.set_host_port(10000);
+  portMapping.set_container_port(80);
+
+  dockerInfo.add_port_mappings()->CopyFrom(portMapping);
+  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
+
+  CommandInfo commandInfo;
+  commandInfo.set_shell(false);
+  commandInfo.set_value("true");
+
+  Resources resources =
+    Resources::parse("ports:[9998-9999];ports:[10001-11000]").get();
+
+  Future<Nothing> run = docker->run(
+      containerInfo,
+      commandInfo,
+      containerName,
+      "dir",
+      "/mnt/mesos/sandbox",
+      resources);
+
+  // Port should be out side of the provided ranges.
+  AWAIT_EXPECT_FAILED(run);
+
+  resources = Resources::parse("ports:[9998-9999];ports:[10000-11000]").get();
+
+  Try<string> directory = environment->mkdtemp();
+  CHECK_SOME(directory) << "Failed to create temporary directory";
+
+  run = docker->run(
+      containerInfo,
+      commandInfo,
+      containerName,
+      directory.get(),
+      "/mnt/mesos/sandbox",
+      resources);
+
+  AWAIT_READY(run);
+}
+
+
+TEST_F(DockerTest, ROOT_DOCKER_CancelPull)
+{
+  // Delete the test image if it exists.
+
+  Try<Subprocess> s = process::subprocess(
+      tests::flags.docker + " rmi lingmann/1gb",
+      Subprocess::PATH("/dev/null"),
+      Subprocess::PATH("/dev/null"),
+      Subprocess::PATH("/dev/null"));
+
+  ASSERT_SOME(s);
+
+  AWAIT_READY_FOR(s.get().status(), Seconds(30));
+
+  Owned<Docker> docker(Docker::create(tests::flags.docker, false).get());
+
+  Try<string> directory = environment->mkdtemp();
+
+  CHECK_SOME(directory) << "Failed to create temporary directory";
+
+  // Assume that pulling the very large image 'lingmann/1gb' will take
+  // sufficiently long that we can start it and discard (i.e., cancel
+  // it) right away and the future will indeed get discarded.
+  Future<Docker::Image> future =
+    docker->pull(directory.get(), "lingmann/1gb");
+
+  future.discard();
+
+  AWAIT_DISCARDED(future);
+}
+
+
+// This test verifies mounting in a relative path when running a
+// docker container works.
+TEST_F(DockerTest, ROOT_DOCKER_MountRelative)
+{
+  Owned<Docker> docker(Docker::create(tests::flags.docker, false).get());
+
+  ContainerInfo containerInfo;
+  containerInfo.set_type(ContainerInfo::DOCKER);
+
+  Volume* volume = containerInfo.add_volumes();
+  volume->set_host_path("test_file");
+  volume->set_container_path("/tmp/test_file");
+  volume->set_mode(Volume::RO);
+
+  ContainerInfo::DockerInfo dockerInfo;
+  dockerInfo.set_image("busybox");
+
+  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
+
+  CommandInfo commandInfo;
+  commandInfo.set_shell(true);
+  commandInfo.set_value("ls /tmp/test_file");
+
+  Try<string> directory = environment->mkdtemp();
+  CHECK_SOME(directory) << "Failed to create temporary directory";
+
+  const string testFile = path::join(directory.get(), "test_file");
+  EXPECT_SOME(os::write(testFile, "data"));
+
+  Future<Nothing> run = docker->run(
+      containerInfo,
+      commandInfo,
+      NAME_PREFIX + "-mount-relative-test",
+      directory.get(),
+      directory.get());
+
+  AWAIT_READY(run);
+}
+
+
+// This test verifies mounting in a absolute path when running a
+// docker container works.
+TEST_F(DockerTest, ROOT_DOCKER_MountAbsolute)
+{
+  Owned<Docker> docker(Docker::create(tests::flags.docker, false).get());
+
+  ContainerInfo containerInfo;
+  containerInfo.set_type(ContainerInfo::DOCKER);
+
+  Try<string> directory = environment->mkdtemp();
+  CHECK_SOME(directory) << "Failed to create temporary directory";
+
+  const string testFile = path::join(directory.get(), "test_file");
+  EXPECT_SOME(os::write(testFile, "data"));
+
+  Volume* volume = containerInfo.add_volumes();
+  volume->set_host_path(testFile);
+  volume->set_container_path("/tmp/test_file");
+  volume->set_mode(Volume::RO);
+
+  ContainerInfo::DockerInfo dockerInfo;
+  dockerInfo.set_image("busybox");
+
+  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
+
+  CommandInfo commandInfo;
+  commandInfo.set_shell(true);
+  commandInfo.set_value("ls /tmp/test_file");
+
+  Future<Nothing> run = docker->run(
+      containerInfo,
+      commandInfo,
+      NAME_PREFIX + "-mount-absolute-test",
+      directory.get(),
+      directory.get());
+
+  AWAIT_READY(run);
+}
+
+
+} // namespace tests {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer/external_containerizer_test.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/external_containerizer_test.cpp b/src/tests/containerizer/external_containerizer_test.cpp
new file mode 100644
index 0000000..4f152a4
--- /dev/null
+++ b/src/tests/containerizer/external_containerizer_test.cpp
@@ -0,0 +1,267 @@
+/**
+ * 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 <unistd.h>
+
+#include <gmock/gmock.h>
+
+#include <string>
+#include <vector>
+#include <map>
+
+#include <mesos/resources.hpp>
+
+#include <process/future.hpp>
+
+#include <stout/os.hpp>
+#include <stout/path.hpp>
+
+#include "master/master.hpp"
+#include "master/detector.hpp"
+
+#include "slave/flags.hpp"
+#include "slave/slave.hpp"
+
+#include "slave/containerizer/containerizer.hpp"
+#include "slave/containerizer/external_containerizer.hpp"
+
+#include "tests/mesos.hpp"
+#include "tests/flags.hpp"
+
+using namespace process;
+
+using mesos::internal::master::Master;
+using mesos::internal::slave::Containerizer;
+using mesos::internal::slave::Slave;
+
+using std::string;
+using std::vector;
+
+using testing::_;
+using testing::DoAll;
+using testing::Return;
+using testing::SaveArg;
+using testing::Invoke;
+
+namespace mesos {
+namespace internal {
+namespace tests {
+
+// The external containerizer tests currently rely on a Python script
+// which needs the Mesos Python egg being built.
+// TODO(tillt): Consider providing tests that do not rely on Python.
+#ifdef MESOS_HAS_PYTHON
+
+// TODO(tillt): Update and enhance the ExternalContainerizer tests,
+// possibly following some of the patterns used within the
+// IsolatorTests or even entirely reusing the Containerizer tests.
+class ExternalContainerizerTest : public MesosTest {};
+
+
+class MockExternalContainerizer : public slave::ExternalContainerizer
+{
+public:
+  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));
+
+  MockExternalContainerizer(const slave::Flags& flags)
+    : ExternalContainerizer(flags)
+  {
+    // Set up defaults for mocked methods.
+    // NOTE: See TestContainerizer::setup for why we use
+    // 'EXPECT_CALL' and 'WillRepeatedly' here instead of
+    // 'ON_CALL' and 'WillByDefault'.
+    EXPECT_CALL(*this, launch(_, _, _, _, _, _, _, _))
+      .WillRepeatedly(Invoke(this, &MockExternalContainerizer::_launch));
+  }
+
+  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 slave::ExternalContainerizer::launch(
+        containerId,
+        taskInfo,
+        executorInfo,
+        directory,
+        user,
+        slaveId,
+        slavePid,
+        checkpoint);
+  }
+};
+
+
+// This test has been temporarily disabled due to MESOS-1257.
+TEST_F(ExternalContainerizerTest, DISABLED_Launch)
+{
+  Try<PID<Master> > master = this->StartMaster();
+  ASSERT_SOME(master);
+
+  Flags testFlags;
+
+  slave::Flags flags = this->CreateSlaveFlags();
+
+  flags.isolation = "external";
+  flags.containerizer_path =
+    testFlags.build_dir + "/src/examples/python/test-containerizer";
+
+  MockExternalContainerizer containerizer(flags);
+
+  Try<PID<Slave> > slave = this->StartSlave(&containerizer, flags);
+  ASSERT_SOME(slave);
+
+  MockScheduler sched;
+  MesosSchedulerDriver driver(
+      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+  Future<FrameworkID> frameworkId;
+  EXPECT_CALL(sched, registered(&driver, _, _))
+    .WillOnce(FutureArg<1>(&frameworkId));
+
+  Future<vector<Offer> > offers;
+  EXPECT_CALL(sched, resourceOffers(&driver, _))
+    .WillOnce(FutureArg<1>(&offers))
+    .WillRepeatedly(Return()); // Ignore subsequent offers.
+
+  driver.start();
+
+  AWAIT_READY(frameworkId);
+  AWAIT_READY(offers);
+
+  EXPECT_NE(0u, offers.get().size());
+
+  TaskInfo task;
+  task.set_name("isolator_test");
+  task.mutable_task_id()->set_value("1");
+  task.mutable_slave_id()->CopyFrom(offers.get()[0].slave_id());
+  task.mutable_resources()->CopyFrom(offers.get()[0].resources());
+
+  Resources resources(offers.get()[0].resources());
+  Option<Bytes> mem = resources.mem();
+  ASSERT_SOME(mem);
+  Option<double> cpus = resources.cpus();
+  ASSERT_SOME(cpus);
+
+  const std::string& file = path::join(flags.work_dir, "ready");
+
+  // This task induces user/system load in a child process by
+  // running top in a child process for ten seconds.
+  task.mutable_command()->set_value(
+#ifdef __APPLE__
+      // Use logging mode with 30,000 samples with no interval.
+      "top -l 30000 -s 0 2>&1 > /dev/null & "
+#else
+      // Batch mode, with 30,000 samples with no interval.
+      "top -b -d 0 -n 30000 2>&1 > /dev/null & "
+#endif
+      "touch " + file +  "; " // Signals that the top command is running.
+      "sleep 60");
+
+  Future<TaskStatus> status;
+  EXPECT_CALL(sched, statusUpdate(&driver, _))
+    .WillOnce(FutureArg<1>(&status))
+    .WillRepeatedly(Return()); // Ignore rest for now.
+
+  Future<ContainerID> containerId;
+  EXPECT_CALL(containerizer, launch(_, _, _, _, _, _, _, _))
+    .WillOnce(DoAll(FutureArg<0>(&containerId),
+                    Invoke(&containerizer,
+                           &MockExternalContainerizer::_launch)));
+
+  driver.launchTasks(offers.get()[0].id(), {task});
+
+  AWAIT_READY(containerId);
+
+  AWAIT_READY(status);
+
+  EXPECT_EQ(TASK_RUNNING, status.get().state());
+
+  // Wait for the task to begin inducing cpu time.
+  while (!os::exists(file));
+
+  ExecutorID executorId;
+  executorId.set_value(task.task_id().value());
+
+  // We'll wait up to 10 seconds for the child process to induce
+  // 1/8 of a second of user and system cpu time in total.
+  // TODO(bmahler): Also induce rss memory consumption, by re-using
+  // the balloon framework.
+  ResourceStatistics statistics;
+  Duration waited = Duration::zero();
+  do {
+    Future<ResourceStatistics> usage = containerizer.usage(containerId.get());
+    AWAIT_READY(usage);
+
+    statistics = usage.get();
+
+    // If we meet our usage expectations, we're done!
+    // NOTE: We are currently getting dummy-data from the test-
+    // containerizer python script matching these expectations.
+    // TODO(tillt): Consider working with real data.
+    if (statistics.cpus_user_time_secs() >= 0.120 &&
+        statistics.cpus_system_time_secs() >= 0.05 &&
+        statistics.mem_rss_bytes() >= 1024u) {
+      break;
+    }
+
+    os::sleep(Milliseconds(100));
+    waited += Milliseconds(100);
+  } while (waited < Seconds(10));
+
+  EXPECT_GE(statistics.cpus_user_time_secs(), 0.120);
+  EXPECT_GE(statistics.cpus_system_time_secs(), 0.05);
+  EXPECT_EQ(statistics.cpus_limit(), cpus.get());
+  EXPECT_GE(statistics.mem_rss_bytes(), 1024u);
+  EXPECT_EQ(statistics.mem_limit_bytes(), mem.get().bytes());
+
+  EXPECT_CALL(sched, statusUpdate(&driver, _))
+    .WillOnce(FutureArg<1>(&status));
+
+  driver.killTask(task.task_id());
+
+  AWAIT_READY(status);
+
+  EXPECT_EQ(TASK_KILLED, status.get().state());
+
+  driver.stop();
+  driver.join();
+
+  this->Shutdown();
+}
+
+#endif // MESOS_HAS_PYTHON
+
+} // namespace tests {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer/fs_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/fs_tests.cpp b/src/tests/containerizer/fs_tests.cpp
new file mode 100644
index 0000000..34d3c41
--- /dev/null
+++ b/src/tests/containerizer/fs_tests.cpp
@@ -0,0 +1,170 @@
+/**
+ * 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 <paths.h>
+
+#include <gmock/gmock.h>
+
+#include <stout/foreach.hpp>
+#include <stout/gtest.hpp>
+#include <stout/none.hpp>
+#include <stout/option.hpp>
+#include <stout/try.hpp>
+
+#include "linux/fs.hpp"
+
+namespace mesos {
+namespace internal {
+namespace tests {
+
+using fs::MountTable;
+using fs::FileSystemTable;
+using fs::MountInfoTable;
+
+
+TEST(FsTest, MountTableRead)
+{
+  Try<MountTable> table = MountTable::read(_PATH_MOUNTED);
+
+  ASSERT_SOME(table);
+
+  Option<MountTable::Entry> root = None();
+  Option<MountTable::Entry> proc = None();
+  foreach (const MountTable::Entry& entry, table.get().entries) {
+    if (entry.dir == "/") {
+      root = entry;
+    } else if (entry.dir == "/proc") {
+      proc = entry;
+    }
+  }
+
+  EXPECT_SOME(root);
+  ASSERT_SOME(proc);
+  EXPECT_EQ(proc.get().type, "proc");
+}
+
+
+TEST(FsTest, MountTableHasOption)
+{
+  Try<MountTable> table = MountTable::read(_PATH_MOUNTED);
+
+  ASSERT_SOME(table);
+
+  Option<MountTable::Entry> proc = None();
+  foreach (const MountTable::Entry& entry, table.get().entries) {
+    if (entry.dir == "/proc") {
+      proc = entry;
+    }
+  }
+
+  ASSERT_SOME(proc);
+  EXPECT_TRUE(proc.get().hasOption(MNTOPT_RW));
+}
+
+
+TEST(FsTest, FileSystemTableRead)
+{
+  Try<FileSystemTable> table = FileSystemTable::read();
+
+  ASSERT_SOME(table);
+
+  // NOTE: We do not check for /proc because, it is not always present in
+  // /etc/fstab.
+  Option<FileSystemTable::Entry> root = None();
+  foreach (const FileSystemTable::Entry& entry, table.get().entries) {
+    if (entry.file == "/") {
+      root = entry;
+    }
+  }
+
+  EXPECT_SOME(root);
+}
+
+
+TEST(FsTest, MountInfoTableParse)
+{
+  // Parse a private mount (no optional fields).
+  const std::string privateMount =
+    "19 1 8:1 / / rw,relatime - ext4 /dev/sda1 rw,seclabel,data=ordered";
+  Try<MountInfoTable::Entry> entry = MountInfoTable::Entry::parse(privateMount);
+
+  ASSERT_SOME(entry);
+  EXPECT_EQ(19, entry.get().id);
+  EXPECT_EQ(1, entry.get().parent);
+  EXPECT_EQ(makedev(8, 1), entry.get().devno);
+  EXPECT_EQ("/", entry.get().root);
+  EXPECT_EQ("/", entry.get().target);
+  EXPECT_EQ("rw,relatime", entry.get().vfsOptions);
+  EXPECT_EQ("rw,seclabel,data=ordered", entry.get().fsOptions);
+  EXPECT_EQ("", entry.get().optionalFields);
+  EXPECT_EQ("ext4", entry.get().type);
+  EXPECT_EQ("/dev/sda1", entry.get().source);
+
+  // Parse a shared mount (includes one optional field).
+  const std::string sharedMount =
+    "19 1 8:1 / / rw,relatime shared:2 - ext4 /dev/sda1 rw,seclabel";
+  entry = MountInfoTable::Entry::parse(sharedMount);
+
+  ASSERT_SOME(entry);
+  EXPECT_EQ(19, entry.get().id);
+  EXPECT_EQ(1, entry.get().parent);
+  EXPECT_EQ(makedev(8, 1), entry.get().devno);
+  EXPECT_EQ("/", entry.get().root);
+  EXPECT_EQ("/", entry.get().target);
+  EXPECT_EQ("rw,relatime", entry.get().vfsOptions);
+  EXPECT_EQ("rw,seclabel", entry.get().fsOptions);
+  EXPECT_EQ("shared:2", entry.get().optionalFields);
+  EXPECT_EQ("ext4", entry.get().type);
+  EXPECT_EQ("/dev/sda1", entry.get().source);
+}
+
+
+TEST(FsTest, DISABLED_MountInfoTableRead)
+{
+  // Examine the calling process's mountinfo table.
+  Try<fs::MountInfoTable> table = fs::MountInfoTable::read();
+  ASSERT_SOME(table);
+
+  // Every system should have at least a rootfs mounted.
+  Option<MountInfoTable::Entry> root = None();
+  foreach (const MountInfoTable::Entry& entry, table.get().entries) {
+    if (entry.target == "/") {
+      root = entry;
+    }
+  }
+
+  EXPECT_SOME(root);
+
+  // Repeat for pid 1.
+  table = fs::MountInfoTable::read(1);
+  ASSERT_SOME(table);
+
+  // Every system should have at least a rootfs mounted.
+  root = None();
+  foreach (const MountInfoTable::Entry& entry, table.get().entries) {
+    if (entry.target == "/") {
+      root = entry;
+    }
+  }
+
+  EXPECT_SOME(root);
+}
+
+} // namespace tests {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer/isolator.hpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/isolator.hpp b/src/tests/containerizer/isolator.hpp
new file mode 100644
index 0000000..8aaf88c
--- /dev/null
+++ b/src/tests/containerizer/isolator.hpp
@@ -0,0 +1,101 @@
+/**
+ * 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 __TEST_ISOLATOR_HPP__
+#define __TEST_ISOLATOR_HPP__
+
+#include <gmock/gmock.h>
+
+#include "slave/containerizer/isolator.hpp"
+
+namespace mesos {
+namespace internal {
+namespace tests {
+
+class TestIsolatorProcess : public slave::MesosIsolatorProcess
+{
+public:
+  static Try<mesos::slave::Isolator*> create(
+      const Option<CommandInfo>& commandInfo)
+  {
+    process::Owned<MesosIsolatorProcess> process(
+        new TestIsolatorProcess(commandInfo));
+
+    return new slave::MesosIsolator(process);
+  }
+
+  MOCK_METHOD2(
+      recover,
+      process::Future<Nothing>(
+          const std::list<mesos::slave::ExecutorRunState>&,
+          const hashset<ContainerID>&));
+
+  virtual process::Future<Option<CommandInfo>> prepare(
+      const ContainerID& containerId,
+      const ExecutorInfo& executorInfo,
+      const std::string& directory,
+      const Option<std::string>& rootfs,
+      const Option<std::string>& user)
+  {
+    return commandInfo;
+  }
+
+  MOCK_METHOD2(
+      isolate,
+      process::Future<Nothing>(const ContainerID&, pid_t));
+
+  MOCK_METHOD1(
+      watch,
+      process::Future<mesos::slave::ExecutorLimitation>(const ContainerID&));
+
+  MOCK_METHOD2(
+      update,
+      process::Future<Nothing>(const ContainerID&, const Resources&));
+
+  MOCK_METHOD1(
+      usage,
+      process::Future<ResourceStatistics>(const ContainerID&));
+
+  MOCK_METHOD1(
+      cleanup,
+      process::Future<Nothing>(const ContainerID&));
+
+private:
+  TestIsolatorProcess(const Option<CommandInfo>& _commandInfo)
+    : commandInfo(_commandInfo)
+  {
+    EXPECT_CALL(*this, watch(testing::_))
+      .WillRepeatedly(testing::Return(promise.future()));
+
+    EXPECT_CALL(*this, isolate(testing::_, testing::_))
+      .WillRepeatedly(testing::Return(Nothing()));
+
+    EXPECT_CALL(*this, cleanup(testing::_))
+      .WillRepeatedly(testing::Return(Nothing()));
+  }
+
+  const Option<CommandInfo> commandInfo;
+
+  process::Promise<mesos::slave::ExecutorLimitation> promise;
+};
+
+} // namespace tests {
+} // namespace internal {
+} // namespace mesos {
+
+#endif // __TEST_ISOLATOR_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer/isolator_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/isolator_tests.cpp b/src/tests/containerizer/isolator_tests.cpp
new file mode 100644
index 0000000..59f08c0
--- /dev/null
+++ b/src/tests/containerizer/isolator_tests.cpp
@@ -0,0 +1,1317 @@
+/**
+ * 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 <unistd.h>
+
+#include <gmock/gmock.h>
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <mesos/resources.hpp>
+
+#include <mesos/module/isolator.hpp>
+
+#include <mesos/slave/isolator.hpp>
+
+#include <process/future.hpp>
+#include <process/owned.hpp>
+#include <process/reap.hpp>
+
+#include <stout/abort.hpp>
+#include <stout/gtest.hpp>
+#include <stout/os.hpp>
+#include <stout/path.hpp>
+
+#ifdef __linux__
+#include "linux/ns.hpp"
+#endif // __linux__
+
+#include "master/master.hpp"
+#include "master/detector.hpp"
+
+#include "slave/flags.hpp"
+#include "slave/slave.hpp"
+
+#ifdef __linux__
+#include "slave/containerizer/isolators/cgroups/constants.hpp"
+#include "slave/containerizer/isolators/cgroups/cpushare.hpp"
+#include "slave/containerizer/isolators/cgroups/mem.hpp"
+#include "slave/containerizer/isolators/cgroups/perf_event.hpp"
+#include "slave/containerizer/isolators/filesystem/shared.hpp"
+#endif // __linux__
+#include "slave/containerizer/isolators/posix.hpp"
+
+#include "slave/containerizer/launcher.hpp"
+#ifdef __linux__
+#include "slave/containerizer/fetcher.hpp"
+#include "slave/containerizer/linux_launcher.hpp"
+
+#include "slave/containerizer/mesos/containerizer.hpp"
+#include "slave/containerizer/mesos/launch.hpp"
+#endif // __linux__
+
+#include "tests/flags.hpp"
+#include "tests/mesos.hpp"
+#include "tests/module.hpp"
+#include "tests/utils.hpp"
+
+#include "tests/containerizer/memory_test_helper.hpp"
+
+using namespace process;
+
+using mesos::internal::master::Master;
+#ifdef __linux__
+using mesos::internal::slave::CgroupsCpushareIsolatorProcess;
+using mesos::internal::slave::CgroupsMemIsolatorProcess;
+using mesos::internal::slave::CgroupsPerfEventIsolatorProcess;
+using mesos::internal::slave::CPU_SHARES_PER_CPU_REVOCABLE;
+using mesos::internal::slave::Fetcher;
+using mesos::internal::slave::LinuxLauncher;
+using mesos::internal::slave::SharedFilesystemIsolatorProcess;
+#endif // __linux__
+using mesos::internal::slave::Launcher;
+using mesos::internal::slave::MesosContainerizer;
+using mesos::internal::slave::PosixLauncher;
+using mesos::internal::slave::PosixCpuIsolatorProcess;
+using mesos::internal::slave::PosixMemIsolatorProcess;
+using mesos::internal::slave::Slave;
+
+using mesos::slave::Isolator;
+using mesos::slave::IsolatorProcess;
+
+using std::ostringstream;
+using std::set;
+using std::string;
+using std::vector;
+
+using testing::_;
+using testing::DoAll;
+using testing::Return;
+using testing::SaveArg;
+
+namespace mesos {
+namespace internal {
+namespace tests {
+
+static int childSetup(int pipes[2])
+{
+  // In child process.
+  while (::close(pipes[1]) == -1 && errno == EINTR);
+
+  // Wait until the parent signals us to continue.
+  char dummy;
+  ssize_t length;
+  while ((length = ::read(pipes[0], &dummy, sizeof(dummy))) == -1 &&
+         errno == EINTR);
+
+  if (length != sizeof(dummy)) {
+    ABORT("Failed to synchronize with parent");
+  }
+
+  while (::close(pipes[0]) == -1 && errno == EINTR);
+
+  return 0;
+}
+
+
+template <typename T>
+class CpuIsolatorTest : public MesosTest {};
+
+
+typedef ::testing::Types<
+    PosixCpuIsolatorProcess,
+#ifdef __linux__
+    CgroupsCpushareIsolatorProcess,
+#endif // __linux__
+    tests::Module<Isolator, TestCpuIsolator>> CpuIsolatorTypes;
+
+
+TYPED_TEST_CASE(CpuIsolatorTest, CpuIsolatorTypes);
+
+
+TYPED_TEST(CpuIsolatorTest, UserCpuUsage)
+{
+  slave::Flags flags;
+
+  Try<Isolator*> isolator = TypeParam::create(flags);
+  CHECK_SOME(isolator);
+
+  // A PosixLauncher is sufficient even when testing a cgroups isolator.
+  Try<Launcher*> launcher = PosixLauncher::create(flags);
+
+  ExecutorInfo executorInfo;
+  executorInfo.mutable_resources()->CopyFrom(
+      Resources::parse("cpus:1.0").get());
+
+  ContainerID containerId;
+  containerId.set_value(UUID::random().toString());
+
+  // Use a relative temporary directory so it gets cleaned up
+  // automatically with the test.
+  Try<string> dir = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
+  ASSERT_SOME(dir);
+
+  AWAIT_READY(isolator.get()->prepare(
+      containerId,
+      executorInfo,
+      dir.get(),
+      None(),
+      None()));
+
+  const string& file = path::join(dir.get(), "mesos_isolator_test_ready");
+
+  // Max out a single core in userspace. This will run for at most one second.
+  string command = "while true ; do true ; done &"
+    "touch " + file + "; " // Signals the command is running.
+    "sleep 60";
+
+  int pipes[2];
+  ASSERT_NE(-1, ::pipe(pipes));
+
+  vector<string> argv(3);
+  argv[0] = "sh";
+  argv[1] = "-c";
+  argv[2] = command;
+
+  Try<pid_t> pid = launcher.get()->fork(
+      containerId,
+      "/bin/sh",
+      argv,
+      Subprocess::FD(STDIN_FILENO),
+      Subprocess::FD(STDOUT_FILENO),
+      Subprocess::FD(STDERR_FILENO),
+      None(),
+      None(),
+      lambda::bind(&childSetup, pipes));
+
+  ASSERT_SOME(pid);
+
+  // Reap the forked child.
+  Future<Option<int> > status = process::reap(pid.get());
+
+  // Continue in the parent.
+  ASSERT_SOME(os::close(pipes[0]));
+
+  // Isolate the forked child.
+  AWAIT_READY(isolator.get()->isolate(containerId, pid.get()));
+
+  // Now signal the child to continue.
+  char dummy;
+  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
+
+  ASSERT_SOME(os::close(pipes[1]));
+
+  // Wait for the command to start.
+  while (!os::exists(file));
+
+  // Wait up to 1 second for the child process to induce 1/8 of a second of
+  // user cpu time.
+  ResourceStatistics statistics;
+  Duration waited = Duration::zero();
+  do {
+    Future<ResourceStatistics> usage = isolator.get()->usage(containerId);
+    AWAIT_READY(usage);
+
+    statistics = usage.get();
+
+    // If we meet our usage expectations, we're done!
+    if (statistics.cpus_user_time_secs() >= 0.125) {
+      break;
+    }
+
+    os::sleep(Milliseconds(200));
+    waited += Milliseconds(200);
+  } while (waited < Seconds(1));
+
+  EXPECT_LE(0.125, statistics.cpus_user_time_secs());
+
+  // Ensure all processes are killed.
+  AWAIT_READY(launcher.get()->destroy(containerId));
+
+  // Make sure the child was reaped.
+  AWAIT_READY(status);
+
+  // Let the isolator clean up.
+  AWAIT_READY(isolator.get()->cleanup(containerId));
+
+  delete isolator.get();
+  delete launcher.get();
+}
+
+
+TYPED_TEST(CpuIsolatorTest, SystemCpuUsage)
+{
+  slave::Flags flags;
+
+  Try<Isolator*> isolator = TypeParam::create(flags);
+  CHECK_SOME(isolator);
+
+  // A PosixLauncher is sufficient even when testing a cgroups isolator.
+  Try<Launcher*> launcher = PosixLauncher::create(flags);
+
+  ExecutorInfo executorInfo;
+  executorInfo.mutable_resources()->CopyFrom(
+      Resources::parse("cpus:1.0").get());
+
+  ContainerID containerId;
+  containerId.set_value(UUID::random().toString());
+
+  // Use a relative temporary directory so it gets cleaned up
+  // automatically with the test.
+  Try<string> dir = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
+  ASSERT_SOME(dir);
+
+  AWAIT_READY(isolator.get()->prepare(
+      containerId,
+      executorInfo,
+      dir.get(),
+      None(),
+      None()));
+
+  const string& file = path::join(dir.get(), "mesos_isolator_test_ready");
+
+  // Generating random numbers is done by the kernel and will max out a single
+  // core and run almost exclusively in the kernel, i.e., system time.
+  string command = "cat /dev/urandom > /dev/null & "
+    "touch " + file + "; " // Signals the command is running.
+    "sleep 60";
+
+  int pipes[2];
+  ASSERT_NE(-1, ::pipe(pipes));
+
+  vector<string> argv(3);
+  argv[0] = "sh";
+  argv[1] = "-c";
+  argv[2] = command;
+
+  Try<pid_t> pid = launcher.get()->fork(
+      containerId,
+      "/bin/sh",
+      argv,
+      Subprocess::FD(STDIN_FILENO),
+      Subprocess::FD(STDOUT_FILENO),
+      Subprocess::FD(STDERR_FILENO),
+      None(),
+      None(),
+      lambda::bind(&childSetup, pipes));
+
+  ASSERT_SOME(pid);
+
+  // Reap the forked child.
+  Future<Option<int> > status = process::reap(pid.get());
+
+  // Continue in the parent.
+  ASSERT_SOME(os::close(pipes[0]));
+
+  // Isolate the forked child.
+  AWAIT_READY(isolator.get()->isolate(containerId, pid.get()));
+
+  // Now signal the child to continue.
+  char dummy;
+  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
+
+  ASSERT_SOME(os::close(pipes[1]));
+
+  // Wait for the command to start.
+  while (!os::exists(file));
+
+  // Wait up to 1 second for the child process to induce 1/8 of a second of
+  // system cpu time.
+  ResourceStatistics statistics;
+  Duration waited = Duration::zero();
+  do {
+    Future<ResourceStatistics> usage = isolator.get()->usage(containerId);
+    AWAIT_READY(usage);
+
+    statistics = usage.get();
+
+    // If we meet our usage expectations, we're done!
+    if (statistics.cpus_system_time_secs() >= 0.125) {
+      break;
+    }
+
+    os::sleep(Milliseconds(200));
+    waited += Milliseconds(200);
+  } while (waited < Seconds(1));
+
+  EXPECT_LE(0.125, statistics.cpus_system_time_secs());
+
+  // Ensure all processes are killed.
+  AWAIT_READY(launcher.get()->destroy(containerId));
+
+  // Make sure the child was reaped.
+  AWAIT_READY(status);
+
+  // Let the isolator clean up.
+  AWAIT_READY(isolator.get()->cleanup(containerId));
+
+  delete isolator.get();
+  delete launcher.get();
+}
+
+
+#ifdef __linux__
+class RevocableCpuIsolatorTest : public MesosTest {};
+
+
+TEST_F(RevocableCpuIsolatorTest, ROOT_CGROUPS_RevocableCpu)
+{
+  slave::Flags flags;
+
+  Try<Isolator*> isolator = CgroupsCpushareIsolatorProcess::create(flags);
+  CHECK_SOME(isolator);
+
+  Try<Launcher*> launcher = PosixLauncher::create(flags);
+
+  // Include revocable CPU in the executor's resources.
+  Resource cpu = Resources::parse("cpus", "1", "*").get();
+  cpu.mutable_revocable();
+
+  ExecutorInfo executorInfo;
+  executorInfo.add_resources()->CopyFrom(cpu);
+
+  ContainerID containerId;
+  containerId.set_value(UUID::random().toString());
+
+  AWAIT_READY(isolator.get()->prepare(
+        containerId,
+        executorInfo,
+        os::getcwd(),
+        None(),
+        None()));
+
+  vector<string> argv{"sleep", "100"};
+
+  Try<pid_t> pid = launcher.get()->fork(
+      containerId,
+      "/bin/sleep",
+      argv,
+      Subprocess::PATH("/dev/null"),
+      Subprocess::PATH("/dev/null"),
+      Subprocess::PATH("/dev/null"),
+      None(),
+      None(),
+      None());
+
+  ASSERT_SOME(pid);
+
+  AWAIT_READY(isolator.get()->isolate(containerId, pid.get()));
+
+  // Executor should have proper cpu.shares for revocable containers.
+  Result<string> cpuHierarchy = cgroups::hierarchy("cpu");
+  ASSERT_SOME(cpuHierarchy);
+
+  Result<string> cpuCgroup = cgroups::cpu::cgroup(pid.get());
+  ASSERT_SOME(cpuCgroup);
+
+  EXPECT_SOME_EQ(
+      CPU_SHARES_PER_CPU_REVOCABLE,
+      cgroups::cpu::shares(cpuHierarchy.get(), cpuCgroup.get()));
+
+  // Kill the container and clean up.
+  Future<Option<int>> status = process::reap(pid.get());
+
+  AWAIT_READY(launcher.get()->destroy(containerId));
+
+  AWAIT_READY(status);
+
+  AWAIT_READY(isolator.get()->cleanup(containerId));
+
+  delete isolator.get();
+  delete launcher.get();
+}
+#endif // __linux__
+
+
+#ifdef __linux__
+class LimitedCpuIsolatorTest : public MesosTest {};
+
+
+TEST_F(LimitedCpuIsolatorTest, ROOT_CGROUPS_Cfs)
+{
+  slave::Flags flags;
+
+  // Enable CFS to cap CPU utilization.
+  flags.cgroups_enable_cfs = true;
+
+  Try<Isolator*> isolator = CgroupsCpushareIsolatorProcess::create(flags);
+  CHECK_SOME(isolator);
+
+  Try<Launcher*> launcher =
+    LinuxLauncher::create(flags, isolator.get()->namespaces().get());
+  CHECK_SOME(launcher);
+
+  // Set the executor's resources to 0.5 cpu.
+  ExecutorInfo executorInfo;
+  executorInfo.mutable_resources()->CopyFrom(
+      Resources::parse("cpus:0.5").get());
+
+  ContainerID containerId;
+  containerId.set_value(UUID::random().toString());
+
+  // Use a relative temporary directory so it gets cleaned up
+  // automatically with the test.
+  Try<string> dir = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
+  ASSERT_SOME(dir);
+
+  AWAIT_READY(isolator.get()->prepare(
+      containerId,
+      executorInfo,
+      dir.get(),
+      None(),
+      None()));
+
+  // Generate random numbers to max out a single core. We'll run this for 0.5
+  // seconds of wall time so it should consume approximately 250 ms of total
+  // cpu time when limited to 0.5 cpu. We use /dev/urandom to prevent blocking
+  // on Linux when there's insufficient entropy.
+  string command = "cat /dev/urandom > /dev/null & "
+    "export MESOS_TEST_PID=$! && "
+    "sleep 0.5 && "
+    "kill $MESOS_TEST_PID";
+
+  int pipes[2];
+  ASSERT_NE(-1, ::pipe(pipes));
+
+  vector<string> argv(3);
+  argv[0] = "sh";
+  argv[1] = "-c";
+  argv[2] = command;
+
+  Try<pid_t> pid = launcher.get()->fork(
+      containerId,
+      "/bin/sh",
+      argv,
+      Subprocess::FD(STDIN_FILENO),
+      Subprocess::FD(STDOUT_FILENO),
+      Subprocess::FD(STDERR_FILENO),
+      None(),
+      None(),
+      lambda::bind(&childSetup, pipes));
+
+  ASSERT_SOME(pid);
+
+  // Reap the forked child.
+  Future<Option<int> > status = process::reap(pid.get());
+
+  // Continue in the parent.
+  ASSERT_SOME(os::close(pipes[0]));
+
+  // Isolate the forked child.
+  AWAIT_READY(isolator.get()->isolate(containerId, pid.get()));
+
+  // Now signal the child to continue.
+  char dummy;
+  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
+
+  ASSERT_SOME(os::close(pipes[1]));
+
+  // Wait for the command to complete.
+  AWAIT_READY(status);
+
+  Future<ResourceStatistics> usage = isolator.get()->usage(containerId);
+  AWAIT_READY(usage);
+
+  // Expect that no more than 300 ms of cpu time has been consumed. We also
+  // check that at least 50 ms of cpu time has been consumed so this test will
+  // fail if the host system is very heavily loaded. This behavior is correct
+  // because under such conditions we aren't actually testing the CFS cpu
+  // limiter.
+  double cpuTime = usage.get().cpus_system_time_secs() +
+                   usage.get().cpus_user_time_secs();
+
+  EXPECT_GE(0.30, cpuTime);
+  EXPECT_LE(0.05, cpuTime);
+
+  // Ensure all processes are killed.
+  AWAIT_READY(launcher.get()->destroy(containerId));
+
+  // Let the isolator clean up.
+  AWAIT_READY(isolator.get()->cleanup(containerId));
+
+  delete isolator.get();
+  delete launcher.get();
+}
+
+
+// This test verifies that we can successfully launch a container with
+// a big (>= 10 cpus) cpu quota. This is to catch the regression
+// observed in MESOS-1049.
+// TODO(vinod): Revisit this if/when the isolator restricts the number
+// of cpus that an executor can use based on the slave cpus.
+TEST_F(LimitedCpuIsolatorTest, ROOT_CGROUPS_Cfs_Big_Quota)
+{
+  slave::Flags flags;
+
+  // Enable CFS to cap CPU utilization.
+  flags.cgroups_enable_cfs = true;
+
+  Try<Isolator*> isolator = CgroupsCpushareIsolatorProcess::create(flags);
+  CHECK_SOME(isolator);
+
+  Try<Launcher*> launcher =
+    LinuxLauncher::create(flags, isolator.get()->namespaces().get());
+  CHECK_SOME(launcher);
+
+  // Set the executor's resources to 100.5 cpu.
+  ExecutorInfo executorInfo;
+  executorInfo.mutable_resources()->CopyFrom(
+      Resources::parse("cpus:100.5").get());
+
+  ContainerID containerId;
+  containerId.set_value(UUID::random().toString());
+
+  // Use a relative temporary directory so it gets cleaned up
+  // automatically with the test.
+  Try<string> dir = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
+  ASSERT_SOME(dir);
+
+  AWAIT_READY(isolator.get()->prepare(
+      containerId,
+      executorInfo,
+      dir.get(),
+      None(),
+      None()));
+
+  int pipes[2];
+  ASSERT_NE(-1, ::pipe(pipes));
+
+  vector<string> argv(3);
+  argv[0] = "sh";
+  argv[1] = "-c";
+  argv[2] = "exit 0";
+
+  Try<pid_t> pid = launcher.get()->fork(
+      containerId,
+      "/bin/sh",
+      argv,
+      Subprocess::FD(STDIN_FILENO),
+      Subprocess::FD(STDOUT_FILENO),
+      Subprocess::FD(STDERR_FILENO),
+      None(),
+      None(),
+      lambda::bind(&childSetup, pipes));
+
+  ASSERT_SOME(pid);
+
+  // Reap the forked child.
+  Future<Option<int> > status = process::reap(pid.get());
+
+  // Continue in the parent.
+  ASSERT_SOME(os::close(pipes[0]));
+
+  // Isolate the forked child.
+  AWAIT_READY(isolator.get()->isolate(containerId, pid.get()));
+
+  // Now signal the child to continue.
+  char dummy;
+  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
+
+  ASSERT_SOME(os::close(pipes[1]));
+
+  // Wait for the command to complete successfully.
+  AWAIT_READY(status);
+  ASSERT_SOME_EQ(0, status.get());
+
+  // Ensure all processes are killed.
+  AWAIT_READY(launcher.get()->destroy(containerId));
+
+  // Let the isolator clean up.
+  AWAIT_READY(isolator.get()->cleanup(containerId));
+
+  delete isolator.get();
+  delete launcher.get();
+}
+
+
+// A test to verify the number of processes and threads in a
+// container.
+TEST_F(LimitedCpuIsolatorTest, ROOT_CGROUPS_Pids_and_Tids)
+{
+  slave::Flags flags;
+  flags.cgroups_cpu_enable_pids_and_tids_count = true;
+
+  Try<Isolator*> isolator = CgroupsCpushareIsolatorProcess::create(flags);
+  CHECK_SOME(isolator);
+
+  Try<Launcher*> launcher =
+    LinuxLauncher::create(flags, isolator.get()->namespaces().get());
+  CHECK_SOME(launcher);
+
+  ExecutorInfo executorInfo;
+  executorInfo.mutable_resources()->CopyFrom(
+      Resources::parse("cpus:0.5;mem:512").get());
+
+  ContainerID containerId;
+  containerId.set_value(UUID::random().toString());
+
+  // Use a relative temporary directory so it gets cleaned up
+  // automatically with the test.
+  Try<string> dir = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
+  ASSERT_SOME(dir);
+
+  AWAIT_READY(isolator.get()->prepare(
+      containerId,
+      executorInfo,
+      dir.get(),
+      None(),
+      None()));
+
+  // Right after the creation of the cgroup, which happens in
+  // 'prepare', we check that it is empty.
+  Future<ResourceStatistics> usage = isolator.get()->usage(containerId);
+  AWAIT_READY(usage);
+  EXPECT_EQ(0U, usage.get().processes());
+  EXPECT_EQ(0U, usage.get().threads());
+
+  int pipes[2];
+  ASSERT_NE(-1, ::pipe(pipes));
+
+  vector<string> argv(3);
+  argv[0] = "sh";
+  argv[1] = "-c";
+  argv[2] = "while true; do sleep 1; done;";
+
+  Try<pid_t> pid = launcher.get()->fork(
+      containerId,
+      "/bin/sh",
+      argv,
+      Subprocess::FD(STDIN_FILENO),
+      Subprocess::FD(STDOUT_FILENO),
+      Subprocess::FD(STDERR_FILENO),
+      None(),
+      None(),
+      lambda::bind(&childSetup, pipes));
+
+  ASSERT_SOME(pid);
+
+  // Reap the forked child.
+  Future<Option<int>> status = process::reap(pid.get());
+
+  // Continue in the parent.
+  ASSERT_SOME(os::close(pipes[0]));
+
+  // Before isolation, the cgroup is empty.
+  usage = isolator.get()->usage(containerId);
+  AWAIT_READY(usage);
+  EXPECT_EQ(0U, usage.get().processes());
+  EXPECT_EQ(0U, usage.get().threads());
+
+  // Isolate the forked child.
+  AWAIT_READY(isolator.get()->isolate(containerId, pid.get()));
+
+  // After the isolation, the cgroup is not empty, even though the
+  // process hasn't exec'd yet.
+  usage = isolator.get()->usage(containerId);
+  AWAIT_READY(usage);
+  EXPECT_EQ(1U, usage.get().processes());
+  EXPECT_EQ(1U, usage.get().threads());
+
+  // Now signal the child to continue.
+  char dummy;
+  ASSERT_LT(0, ::write(pipes[1], &dummy, sizeof(dummy)));
+
+  ASSERT_SOME(os::close(pipes[1]));
+
+  // Process count should be 1 since 'sleep' is still sleeping.
+  usage = isolator.get()->usage(containerId);
+  AWAIT_READY(usage);
+  EXPECT_EQ(1U, usage.get().processes());
+  EXPECT_EQ(1U, usage.get().threads());
+
+  // Ensure all processes are killed.
+  AWAIT_READY(launcher.get()->destroy(containerId));
+
+  // Wait for the command to complete.
+  AWAIT_READY(status);
+
+  // After the process is killed, the cgroup should be empty again.
+  usage = isolator.get()->usage(containerId);
+  AWAIT_READY(usage);
+  EXPECT_EQ(0U, usage.get().processes());
+  EXPECT_EQ(0U, usage.get().threads());
+
+  // Let the isolator clean up.
+  AWAIT_READY(isolator.get()->cleanup(containerId));
+
+  delete isolator.get();
+  delete launcher.get();
+}
+#endif // __linux__
+
+
+template <typename T>
+class MemIsolatorTest : public MesosTest {};
+
+
+typedef ::testing::Types<
+    PosixMemIsolatorProcess,
+#ifdef __linux__
+    CgroupsMemIsolatorProcess,
+#endif // __linux__
+    tests::Module<Isolator, TestMemIsolator>> MemIsolatorTypes;
+
+
+TYPED_TEST_CASE(MemIsolatorTest, MemIsolatorTypes);
+
+
+TYPED_TEST(MemIsolatorTest, MemUsage)
+{
+  slave::Flags flags;
+
+  Try<Isolator*> isolator = TypeParam::create(flags);
+  CHECK_SOME(isolator);
+
+  ExecutorInfo executorInfo;
+  executorInfo.mutable_resources()->CopyFrom(
+      Resources::parse("mem:1024").get());
+
+  ContainerID containerId;
+  containerId.set_value(UUID::random().toString());
+
+  // Use a relative temporary directory so it gets cleaned up
+  // automatically with the test.
+  Try<string> dir = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
+  ASSERT_SOME(dir);
+
+  AWAIT_READY(isolator.get()->prepare(
+      containerId,
+      executorInfo,
+      dir.get(),
+      None(),
+      None()));
+
+  MemoryTestHelper helper;
+  ASSERT_SOME(helper.spawn());
+  ASSERT_SOME(helper.pid());
+
+  // Set up the reaper to wait on the subprocess.
+  Future<Option<int>> status = process::reap(helper.pid().get());
+
+  // Isolate the subprocess.
+  AWAIT_READY(isolator.get()->isolate(containerId, helper.pid().get()));
+
+  const Bytes allocation = Megabytes(128);
+  EXPECT_SOME(helper.increaseRSS(allocation));
+
+  Future<ResourceStatistics> usage = isolator.get()->usage(containerId);
+  AWAIT_READY(usage);
+
+  EXPECT_GE(usage.get().mem_rss_bytes(), allocation.bytes());
+
+  // Ensure the process is killed.
+  helper.cleanup();
+
+  // Make sure the subprocess was reaped.
+  AWAIT_READY(status);
+
+  // Let the isolator clean up.
+  AWAIT_READY(isolator.get()->cleanup(containerId));
+
+  delete isolator.get();
+}
+
+
+#ifdef __linux__
+class PerfEventIsolatorTest : public MesosTest {};
+
+
+TEST_F(PerfEventIsolatorTest, ROOT_CGROUPS_Sample)
+{
+  slave::Flags flags;
+
+  flags.perf_events = "cycles,task-clock";
+  flags.perf_duration = Milliseconds(250);
+  flags.perf_interval = Milliseconds(500);
+
+  Try<Isolator*> isolator = CgroupsPerfEventIsolatorProcess::create(flags);
+  ASSERT_SOME(isolator);
+
+  ExecutorInfo executorInfo;
+
+  ContainerID containerId;
+  containerId.set_value(UUID::random().toString());
+
+  // Use a relative temporary directory so it gets cleaned up
+  // automatically with the test.
+  Try<string> dir = os::mkdtemp(path::join(os::getcwd(), "XXXXXX"));
+  ASSERT_SOME(dir);
+
+  AWAIT_READY(isolator.get()->prepare(
+      containerId,
+      executorInfo,
+      dir.get(),
+      None(),
+      None()));
+
+  // This first sample is likely to be empty because perf hasn't
+  // completed yet but we should still have the required fields.
+  Future<ResourceStatistics> statistics1 = isolator.get()->usage(containerId);
+  AWAIT_READY(statistics1);
+  ASSERT_TRUE(statistics1.get().has_perf());
+  EXPECT_TRUE(statistics1.get().perf().has_timestamp());
+  EXPECT_TRUE(statistics1.get().perf().has_duration());
+
+  // Wait until we get the next sample. We use a generous timeout of
+  // two seconds because we currently have a one second reap interval;
+  // when running perf with perf_duration of 250ms we won't notice the
+  // exit for up to one second.
+  ResourceStatistics statistics2;
+  Duration waited = Duration::zero();
+  do {
+    Future<ResourceStatistics> statistics = isolator.get()->usage(containerId);
+    AWAIT_READY(statistics);
+
+    statistics2 = statistics.get();
+
+    ASSERT_TRUE(statistics2.has_perf());
+
+    if (statistics1.get().perf().timestamp() !=
+        statistics2.perf().timestamp()) {
+      break;
+    }
+
+    os::sleep(Milliseconds(250));
+    waited += Milliseconds(250);
+  } while (waited < Seconds(2));
+
+  sleep(2);
+
+  EXPECT_NE(statistics1.get().perf().timestamp(),
+            statistics2.perf().timestamp());
+
+  EXPECT_TRUE(statistics2.perf().has_cycles());
+  EXPECT_LE(0u, statistics2.perf().cycles());
+
+  EXPECT_TRUE(statistics2.perf().has_task_clock());
+  EXPECT_LE(0.0, statistics2.perf().task_clock());
+
+  AWAIT_READY(isolator.get()->cleanup(containerId));
+
+  delete isolator.get();
+}
+
+
+class SharedFilesystemIsolatorTest : public MesosTest {};
+
+
+// Test that a container can create a private view of a system
+// directory (/var/tmp). Check that a file written by a process inside
+// the container doesn't appear on the host filesystem but does appear
+// under the container's work directory.
+// This test is disabled since we're planning to remove the shared
+// filesystem isolator and this test is not working on other distros
+// such as CentOS 7.1
+// TODO(tnachen): Remove this test when shared filesystem isolator
+// is removed.
+TEST_F(SharedFilesystemIsolatorTest, DISABLED_ROOT_RelativeVolume)
+{
+  slave::Flags flags = CreateSlaveFlags();
+  flags.isolation = "filesystem/shared";
+
+  Try<Isolator*> isolator = SharedFilesystemIsolatorProcess::create(flags);
+  CHECK_SOME(isolator);
+
+  Try<Launcher*> launcher =
+    LinuxLauncher::create(flags, isolator.get()->namespaces().get());
+  CHECK_SOME(launcher);
+
+  // Use /var/tmp so we don't mask the work directory (under /tmp).
+  const string containerPath = "/var/tmp";
+  ASSERT_TRUE(os::stat::isdir(containerPath));
+
+  // Use a host path relative to the container work directory.
+  const string hostPath = strings::remove(containerPath, "/", strings::PREFIX);
+
+  ContainerInfo containerInfo;
+  containerInfo.set_type(ContainerInfo::MESOS);
+  containerInfo.add_volumes()->CopyFrom(
+      CREATE_VOLUME(containerPath, hostPath, Volume::RW));
+
+  ExecutorInfo executorInfo;
+  executorInfo.mutable_container()->CopyFrom(containerInfo);
+
+  ContainerID containerId;
+  containerId.set_value(UUID::random().toString());
+
+  Future<Option<CommandInfo> > prepare =
+    isolator.get()->prepare(
+        containerId,
+        executorInfo,
+        flags.work_dir,
+        None(),
+        None());
+
+  AWAIT_READY(prepare);
+  ASSERT_SOME(prepare.get());
+
+  // The test will touch a file in container path.
+  const string file = path::join(containerPath, UUID::random().toString());
+  ASSERT_FALSE(os::exists(file));
+
+  // Manually run the isolator's preparation command first, then touch
+  // the file.
+  vector<string> args;
+  args.push_back("/bin/sh");
+  args.push_back("-x");
+  args.push_back("-c");
+  args.push_back(prepare.get().get().value() + " && touch " + file);
+
+  Try<pid_t> pid = launcher.get()->fork(
+      containerId,
+      "/bin/sh",
+      args,
+      Subprocess::FD(STDIN_FILENO),
+      Subprocess::FD(STDOUT_FILENO),
+      Subprocess::FD(STDERR_FILENO),
+      None(),
+      None(),
+      None());
+  ASSERT_SOME(pid);
+
+  // Set up the reaper to wait on the forked child.
+  Future<Option<int> > status = process::reap(pid.get());
+
+  AWAIT_READY(status);
+  EXPECT_SOME_EQ(0, status.get());
+
+  // Check the correct hierarchy was created under the container work
+  // directory.
+  string dir = "/";
+  foreach (const string& subdir, strings::tokenize(containerPath, "/")) {
+    dir = path::join(dir, subdir);
+
+    struct stat hostStat;
+    EXPECT_EQ(0, ::stat(dir.c_str(), &hostStat));
+
+    struct stat containerStat;
+    EXPECT_EQ(0,
+              ::stat(path::join(flags.work_dir, dir).c_str(), &containerStat));
+
+    EXPECT_EQ(hostStat.st_mode, containerStat.st_mode);
+    EXPECT_EQ(hostStat.st_uid, containerStat.st_uid);
+    EXPECT_EQ(hostStat.st_gid, containerStat.st_gid);
+  }
+
+  // Check it did *not* create a file in the host namespace.
+  EXPECT_FALSE(os::exists(file));
+
+  // Check it did create the file under the container's work directory
+  // on the host.
+  EXPECT_TRUE(os::exists(path::join(flags.work_dir, file)));
+
+  delete launcher.get();
+  delete isolator.get();
+}
+
+
+// This test is disabled since we're planning to remove the shared
+// filesystem isolator and this test is not working on other distros
+// such as CentOS 7.1
+// TODO(tnachen): Remove this test when shared filesystem isolator
+// is removed.
+TEST_F(SharedFilesystemIsolatorTest, DISABLED_ROOT_AbsoluteVolume)
+{
+  slave::Flags flags = CreateSlaveFlags();
+  flags.isolation = "filesystem/shared";
+
+  Try<Isolator*> isolator = SharedFilesystemIsolatorProcess::create(flags);
+  CHECK_SOME(isolator);
+
+  Try<Launcher*> launcher =
+    LinuxLauncher::create(flags, isolator.get()->namespaces().get());
+  CHECK_SOME(launcher);
+
+  // We'll mount the absolute test work directory as /var/tmp in the
+  // container.
+  const string hostPath = flags.work_dir;
+  const string containerPath = "/var/tmp";
+
+  ContainerInfo containerInfo;
+  containerInfo.set_type(ContainerInfo::MESOS);
+  containerInfo.add_volumes()->CopyFrom(
+      CREATE_VOLUME(containerPath, hostPath, Volume::RW));
+
+  ExecutorInfo executorInfo;
+  executorInfo.mutable_container()->CopyFrom(containerInfo);
+
+  ContainerID containerId;
+  containerId.set_value(UUID::random().toString());
+
+  Future<Option<CommandInfo> > prepare =
+    isolator.get()->prepare(
+        containerId,
+        executorInfo,
+        flags.work_dir,
+        None(),
+        None());
+
+  AWAIT_READY(prepare);
+  ASSERT_SOME(prepare.get());
+
+  // Test the volume mounting by touching a file in the container's
+  // /tmp, which should then be in flags.work_dir.
+  const string filename = UUID::random().toString();
+  ASSERT_FALSE(os::exists(path::join(containerPath, filename)));
+
+  vector<string> args;
+  args.push_back("/bin/sh");
+  args.push_back("-x");
+  args.push_back("-c");
+  args.push_back(prepare.get().get().value() +
+                 " && touch " +
+                 path::join(containerPath, filename));
+
+  Try<pid_t> pid = launcher.get()->fork(
+      containerId,
+      "/bin/sh",
+      args,
+      Subprocess::FD(STDIN_FILENO),
+      Subprocess::FD(STDOUT_FILENO),
+      Subprocess::FD(STDERR_FILENO),
+      None(),
+      None(),
+      None());
+  ASSERT_SOME(pid);
+
+  // Set up the reaper to wait on the forked child.
+  Future<Option<int> > status = process::reap(pid.get());
+
+  AWAIT_READY(status);
+  EXPECT_SOME_EQ(0, status.get());
+
+  // Check the file was created in flags.work_dir.
+  EXPECT_TRUE(os::exists(path::join(hostPath, filename)));
+
+  // Check it didn't get created in the host's view of containerPath.
+  EXPECT_FALSE(os::exists(path::join(containerPath, filename)));
+
+  delete launcher.get();
+  delete isolator.get();
+}
+
+
+class NamespacesPidIsolatorTest : public MesosTest {};
+
+
+TEST_F(NamespacesPidIsolatorTest, ROOT_PidNamespace)
+{
+  slave::Flags flags = CreateSlaveFlags();
+  flags.isolation = "namespaces/pid";
+
+  string directory = os::getcwd(); // We're inside a temporary sandbox.
+
+  Fetcher fetcher;
+
+  Try<MesosContainerizer*> containerizer =
+    MesosContainerizer::create(flags, false, &fetcher);
+  ASSERT_SOME(containerizer);
+
+  ContainerID containerId;
+  containerId.set_value(UUID::random().toString());
+
+  // Write the command's pid namespace inode and init name to files.
+  const string command =
+    "stat -c %i /proc/self/ns/pid > ns && (cat /proc/1/comm > init)";
+
+  process::Future<bool> launch = containerizer.get()->launch(
+      containerId,
+      CREATE_EXECUTOR_INFO("executor", command),
+      directory,
+      None(),
+      SlaveID(),
+      process::PID<Slave>(),
+      false);
+  AWAIT_READY(launch);
+  ASSERT_TRUE(launch.get());
+
+  // Wait on the container.
+  process::Future<containerizer::Termination> wait =
+    containerizer.get()->wait(containerId);
+  AWAIT_READY(wait);
+
+  // Check the executor exited correctly.
+  EXPECT_TRUE(wait.get().has_status());
+  EXPECT_EQ(0, wait.get().status());
+
+  // Check that the command was run in a different pid namespace.
+  Try<ino_t> testPidNamespace = ns::getns(::getpid(), "pid");
+  ASSERT_SOME(testPidNamespace);
+
+  Try<string> containerPidNamespace = os::read(path::join(directory, "ns"));
+  ASSERT_SOME(containerPidNamespace);
+
+  EXPECT_NE(stringify(testPidNamespace.get()),
+            strings::trim(containerPidNamespace.get()));
+
+  // Check that 'sh' is the container's 'init' process.
+  // This verifies that /proc has been correctly mounted for the container.
+  Try<string> init = os::read(path::join(directory, "init"));
+  ASSERT_SOME(init);
+
+  EXPECT_EQ("sh", strings::trim(init.get()));
+
+  delete containerizer.get();
+}
+
+
+// Username for the unprivileged user that will be created to test
+// unprivileged cgroup creation. It will be removed after the tests.
+// It is presumed this user does not normally exist.
+const string UNPRIVILEGED_USERNAME = "mesos.test.unprivileged.user";
+
+
+template <typename T>
+class UserCgroupIsolatorTest : public MesosTest
+{
+public:
+  static void SetUpTestCase()
+  {
+    // Remove the user in case it wasn't cleaned up from a previous
+    // test.
+    os::system("userdel -r " + UNPRIVILEGED_USERNAME + " > /dev/null");
+
+    ASSERT_EQ(0, os::system("useradd " + UNPRIVILEGED_USERNAME));
+  }
+
+
+  static void TearDownTestCase()
+  {
+    ASSERT_EQ(0, os::system("userdel -r " + UNPRIVILEGED_USERNAME));
+  }
+};
+
+
+// Test all isolators that use cgroups.
+typedef ::testing::Types<
+  CgroupsMemIsolatorProcess,
+  CgroupsCpushareIsolatorProcess,
+  CgroupsPerfEventIsolatorProcess> CgroupsIsolatorTypes;
+
+
+TYPED_TEST_CASE(UserCgroupIsolatorTest, CgroupsIsolatorTypes);
+
+
+TYPED_TEST(UserCgroupIsolatorTest, ROOT_CGROUPS_UserCgroup)
+{
+  slave::Flags flags;
+  flags.perf_events = "cpu-cycles"; // Needed for CgroupsPerfEventIsolator.
+
+  Try<Isolator*> isolator = TypeParam::create(flags);
+  ASSERT_SOME(isolator);
+
+  ExecutorInfo executorInfo;
+  executorInfo.mutable_resources()->CopyFrom(
+      Resources::parse("mem:1024;cpus:1").get()); // For cpu/mem isolators.
+
+  ContainerID containerId;
+  containerId.set_value(UUID::random().toString());
+
+  AWAIT_READY(isolator.get()->prepare(
+      containerId,
+      executorInfo,
+      os::getcwd(),
+      None(),
+      UNPRIVILEGED_USERNAME));
+
+  // Isolators don't provide a way to determine the cgroups they use
+  // so we'll inspect the cgroups for an isolated dummy process.
+  pid_t pid = fork();
+  if (pid == 0) {
+    // Child just sleeps.
+    ::sleep(100);
+
+    ABORT("Child process should not reach here");
+  }
+  ASSERT_GT(pid, 0);
+
+  AWAIT_READY(isolator.get()->isolate(containerId, pid));
+
+  // Get the container's cgroups from /proc/$PID/cgroup. We're only
+  // interested in the cgroups that this isolator has created which we
+  // can do explicitly by selecting those that have the path that
+  // corresponds to the 'cgroups_root' slave flag. For example:
+  //
+  //   $ cat /proc/pid/cgroup
+  //   6:blkio:/
+  //   5:perf_event:/
+  //   4:memory:/mesos/b7410ed8-c85b-445e-b50e-3a1698d0e18c
+  //   3:freezer:/
+  //   2:cpuacct:/
+  //   1:cpu:/
+  //
+  // Our 'grep' will only select the 'memory' line and then 'awk' will
+  // output 'memory/mesos/b7410ed8-c85b-445e-b50e-3a1698d0e18c'.
+  ostringstream output;
+  Try<int> status = os::shell(
+      &output,
+      "grep '" + path::join("/", flags.cgroups_root) + "' /proc/" +
+      stringify(pid) + "/cgroup | awk -F ':' '{print $2$3}'");
+
+  ASSERT_SOME(status);
+
+  // Kill the dummy child process.
+  ::kill(pid, SIGKILL);
+  int exitStatus;
+  EXPECT_NE(-1, ::waitpid(pid, &exitStatus, 0));
+
+  vector<string> cgroups = strings::tokenize(output.str(), "\n");
+  ASSERT_FALSE(cgroups.empty());
+
+  foreach (const string& cgroup, cgroups) {
+    // Check the user cannot manipulate the container's cgroup control
+    // files.
+    EXPECT_NE(0, os::system(
+          "su - " + UNPRIVILEGED_USERNAME +
+          " -c 'echo $$ >" +
+          path::join(flags.cgroups_hierarchy, cgroup, "cgroup.procs") +
+          "'"));
+
+    // Check the user can create a cgroup under the container's
+    // cgroup.
+    string userCgroup = path::join(cgroup, "user");
+
+    EXPECT_EQ(0, os::system(
+          "su - " +
+          UNPRIVILEGED_USERNAME +
+          " -c 'mkdir " +
+          path::join(flags.cgroups_hierarchy, userCgroup) +
+          "'"));
+
+    // Check the user can manipulate control files in the created
+    // cgroup.
+    EXPECT_EQ(0, os::system(
+          "su - " +
+          UNPRIVILEGED_USERNAME +
+          " -c 'echo $$ >" +
+          path::join(flags.cgroups_hierarchy, userCgroup, "cgroup.procs") +
+          "'"));
+  }
+
+  // Clean up the container. This will also remove the nested cgroups.
+  AWAIT_READY(isolator.get()->cleanup(containerId));
+
+  delete isolator.get();
+}
+#endif // __linux__
+
+} // namespace tests {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer/launch_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/launch_tests.cpp b/src/tests/containerizer/launch_tests.cpp
new file mode 100644
index 0000000..73c8c5f
--- /dev/null
+++ b/src/tests/containerizer/launch_tests.cpp
@@ -0,0 +1,238 @@
+/**
+ * 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 <string>
+#include <vector>
+
+#include <gmock/gmock.h>
+
+#include <stout/foreach.hpp>
+#include <stout/gtest.hpp>
+#include <stout/os.hpp>
+#include <stout/try.hpp>
+
+#include <process/gtest.hpp>
+#include <process/io.hpp>
+#include <process/reap.hpp>
+#include <process/subprocess.hpp>
+
+#include "mesos/resources.hpp"
+
+#include "slave/containerizer/mesos/launch.hpp"
+
+#include "linux/fs.hpp"
+
+#include "tests/flags.hpp"
+#include "tests/utils.hpp"
+
+using namespace process;
+
+using std::string;
+using std::vector;
+
+namespace mesos {
+namespace internal {
+namespace tests {
+
+class Chroot
+{
+public:
+  Chroot(const string& _rootfs)
+    : rootfs(_rootfs) {}
+
+  virtual ~Chroot() {}
+
+  virtual Try<Subprocess> run(const string& command) = 0;
+
+  const string rootfs;
+};
+
+
+class BasicLinuxChroot : public Chroot
+{
+public:
+  static Try<Owned<Chroot>> create(const string& rootfs)
+  {
+    if (!os::exists(rootfs)) {
+      return Error("rootfs does not exist");
+    }
+
+    if (os::system("cp -r /bin " + rootfs + "/") != 0) {
+      return ErrnoError("Failed to copy /bin to chroot");
+    }
+
+    if (os::system("cp -r /lib " + rootfs + "/") != 0) {
+      return ErrnoError("Failed to copy /lib to chroot");
+    }
+
+    if (os::system("cp -r /lib64 " + rootfs + "/") != 0) {
+      return ErrnoError("Failed to copy /lib64 to chroot");
+    }
+
+    vector<string> directories = {"proc", "sys", "dev", "tmp"};
+    foreach (const string& directory, directories) {
+      Try<Nothing> mkdir = os::mkdir(path::join(rootfs, directory));
+      if (mkdir.isError()) {
+        return Error("Failed to create /" + directory + " in chroot: " +
+                     mkdir.error());
+      }
+    }
+
+    // We need to bind mount the rootfs so we can pivot on it.
+    Try<Nothing> mount =
+      fs::mount(rootfs, rootfs, None(), MS_BIND | MS_SLAVE, NULL);
+
+    if (mount.isError()) {
+      return Error("Failed to bind mount chroot rootfs: " + mount.error());
+    }
+
+    return Owned<Chroot>(new BasicLinuxChroot(rootfs));
+  }
+
+  virtual Try<Subprocess> run(const string& _command)
+  {
+    slave::MesosContainerizerLaunch::Flags launchFlags;
+
+    CommandInfo command;
+    command.set_value(_command);
+
+    launchFlags.command = JSON::Protobuf(command);
+    launchFlags.directory = "/tmp";
+    launchFlags.pipe_read = open("/dev/zero", O_RDONLY);
+    launchFlags.pipe_write = open("/dev/null", O_WRONLY);
+    launchFlags.rootfs = rootfs;
+
+    vector<string> argv(2);
+    argv[0] = "mesos-containerizer";
+    argv[1] = slave::MesosContainerizerLaunch::NAME;
+
+    Try<Subprocess> s = subprocess(
+        path::join(tests::flags.build_dir, "src", "mesos-containerizer"),
+        argv,
+        Subprocess::PATH("/dev/null"),
+        Subprocess::PIPE(),
+        Subprocess::FD(STDERR_FILENO),
+        launchFlags,
+        None(),
+        None(),
+        lambda::bind(&clone, lambda::_1));
+
+    if (s.isError()) {
+      close(launchFlags.pipe_read.get());
+      close(launchFlags.pipe_write.get());
+    } else {
+      s.get().status().onAny([=]() {
+        // Close when the subprocess terminates.
+        close(launchFlags.pipe_read.get());
+        close(launchFlags.pipe_write.get());
+      });
+    }
+
+    return s;
+  }
+
+private:
+  static pid_t clone(const lambda::function<int()>& f)
+  {
+    static unsigned long long stack[(8*1024*1024)/sizeof(unsigned long long)];
+
+    return ::clone(
+        _clone,
+        &stack[sizeof(stack)/sizeof(stack[0]) - 1],  // Stack grows down.
+        CLONE_NEWNS | SIGCHLD,   // Specify SIGCHLD as child termination signal.
+        (void*) &f);
+  }
+
+  static int _clone(void* f)
+  {
+    const lambda::function<int()>* _f =
+      static_cast<const lambda::function<int()>*> (f);
+
+    return (*_f)();
+  }
+
+  BasicLinuxChroot(const string& rootfs) : Chroot(rootfs) {}
+
+  ~BasicLinuxChroot()
+  {
+    // Because the test process has the rootfs as its cwd the umount
+    // won't actually happen until the
+    // TemporaryDirectoryTest::TearDown() changes back to the original
+    // directory.
+    fs::unmount(rootfs, MNT_DETACH);
+  }
+};
+
+
+template <typename T>
+class LaunchChrootTest : public TemporaryDirectoryTest {};
+
+
+// TODO(idownes): Add tests for OSX chroots.
+typedef ::testing::Types<BasicLinuxChroot> ChrootTypes;
+
+
+TYPED_TEST_CASE(LaunchChrootTest, ChrootTypes);
+
+
+TYPED_TEST(LaunchChrootTest, ROOT_DifferentRoot)
+{
+  Try<Owned<Chroot>> chroot = TypeParam::create(os::getcwd());
+  ASSERT_SOME(chroot);
+
+  // Add /usr/bin/stat into the chroot.
+  const string usrbin = path::join(chroot.get()->rootfs, "usr", "bin");
+  ASSERT_SOME(os::mkdir(usrbin));
+  ASSERT_EQ(0, os::system("cp /usr/bin/stat " + path::join(usrbin, "stat")));
+
+  Clock::pause();
+
+  Try<Subprocess> s = chroot.get()->run(
+      "/usr/bin/stat -c %i / >" + path::join("/", "stat.output"));
+
+  CHECK_SOME(s);
+
+  // Advance time until the internal reaper reaps the subprocess.
+  while (s.get().status().isPending()) {
+    Clock::advance(Seconds(1));
+    Clock::settle();
+  }
+
+  AWAIT_ASSERT_READY(s.get().status());
+  ASSERT_SOME(s.get().status().get());
+
+  int status = s.get().status().get().get();
+  ASSERT_TRUE(WIFEXITED(status));
+  ASSERT_EQ(0, WEXITSTATUS(status));
+
+  // Check the chroot has a different root by comparing the inodes.
+  Try<ino_t> self = os::stat::inode("/");
+  ASSERT_SOME(self);
+
+  Try<string> read = os::read(path::join(chroot.get()->rootfs, "stat.output"));
+  CHECK_SOME(read);
+
+  Try<ino_t> other = numify<ino_t>(strings::trim(read.get()));
+  ASSERT_SOME(other);
+
+  EXPECT_NE(self.get(), other.get());
+}
+
+} // namespace tests {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer/launcher.hpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/launcher.hpp b/src/tests/containerizer/launcher.hpp
new file mode 100644
index 0000000..78216e0
--- /dev/null
+++ b/src/tests/containerizer/launcher.hpp
@@ -0,0 +1,119 @@
+/**
+ * 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 <vector>
+
+#include <gmock/gmock.h>
+
+#include <mesos/mesos.hpp>
+
+#include <mesos/slave/isolator.hpp>
+
+#include <process/future.hpp>
+#include <process/owned.hpp>
+#include <process/subprocess.hpp>
+
+#include <stout/flags.hpp>
+#include <stout/lambda.hpp>
+#include <stout/nothing.hpp>
+#include <stout/option.hpp>
+
+#include "slave/containerizer/launcher.hpp"
+
+namespace mesos {
+namespace internal {
+namespace tests {
+
+
+ACTION_P(InvokeRecover, launcher)
+{
+  return launcher->real->recover(arg0);
+}
+
+
+ACTION_P(InvokeFork, launcher)
+{
+  return launcher->real->fork(
+      arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
+}
+
+
+ACTION_P(InvokeDestroy, launcher)
+{
+  return launcher->real->destroy(arg0);
+}
+
+
+class TestLauncher : public slave::Launcher
+{
+public:
+  TestLauncher(const process::Owned<slave::Launcher>& _real)
+    : real(_real)
+  {
+    using testing::_;
+    using testing::DoDefault;
+
+    ON_CALL(*this, recover(_))
+      .WillByDefault(InvokeRecover(this));
+    EXPECT_CALL(*this, recover(_))
+      .WillRepeatedly(DoDefault());
+
+    ON_CALL(*this, fork(_, _, _, _, _, _, _, _, _))
+      .WillByDefault(InvokeFork(this));
+    EXPECT_CALL(*this, fork(_, _, _, _, _, _, _, _, _))
+      .WillRepeatedly(DoDefault());
+
+    ON_CALL(*this, destroy(_))
+      .WillByDefault(InvokeDestroy(this));
+    EXPECT_CALL(*this, destroy(_))
+      .WillRepeatedly(DoDefault());
+  }
+
+  ~TestLauncher() {}
+
+  MOCK_METHOD1(
+      recover,
+      process::Future<hashset<ContainerID>>(
+          const std::list<mesos::slave::ExecutorRunState>& states));
+
+  MOCK_METHOD9(
+      fork,
+      Try<pid_t>(
+          const ContainerID& containerId,
+          const std::string& path,
+          const std::vector<std::string>& argv,
+          const process::Subprocess::IO& in,
+          const process::Subprocess::IO& out,
+          const process::Subprocess::IO& err,
+          const Option<flags::FlagsBase>& flags,
+          const Option<std::map<std::string, std::string> >& env,
+          const Option<lambda::function<int()> >& setup));
+
+  MOCK_METHOD1(
+      destroy,
+      process::Future<Nothing>(const ContainerID& containerId));
+
+  process::Owned<slave::Launcher> real;
+};
+
+} // namespace tests {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer/memory_pressure_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/memory_pressure_tests.cpp b/src/tests/containerizer/memory_pressure_tests.cpp
new file mode 100644
index 0000000..8089879
--- /dev/null
+++ b/src/tests/containerizer/memory_pressure_tests.cpp
@@ -0,0 +1,293 @@
+/**
+ * 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 <vector>
+
+#include <mesos/resources.hpp>
+#include <mesos/scheduler.hpp>
+
+#include <process/gtest.hpp>
+
+#include <stout/gtest.hpp>
+#include <stout/os.hpp>
+
+#include "master/master.hpp"
+
+#include "slave/slave.hpp"
+
+#include "slave/containerizer/containerizer.hpp"
+#include "slave/containerizer/fetcher.hpp"
+
+#include "messages/messages.hpp"
+
+#include "tests/mesos.hpp"
+
+using namespace process;
+
+using mesos::internal::master::Master;
+
+using mesos::internal::slave::Fetcher;
+using mesos::internal::slave::MesosContainerizer;
+using mesos::internal::slave::Slave;
+
+using std::vector;
+
+using testing::_;
+using testing::Eq;
+using testing::Return;
+
+namespace mesos {
+namespace internal {
+namespace tests {
+
+class MemoryPressureMesosTest : public ContainerizerTest<MesosContainerizer>
+{
+public:
+  static void SetUpTestCase()
+  {
+    // Verify that the dd command and its flags used in a bit are valid
+    // on this system.
+    ASSERT_EQ(0, os::system("dd count=1 bs=1M if=/dev/zero of=/dev/null"))
+      << "Cannot find a compatible 'dd' command";
+  }
+};
+
+
+TEST_F(MemoryPressureMesosTest, CGROUPS_ROOT_Statistics)
+{
+  Try<PID<Master>> master = StartMaster();
+  ASSERT_SOME(master);
+
+  slave::Flags flags = CreateSlaveFlags();
+
+  // We only care about memory cgroup for this test.
+  flags.isolation = "cgroups/mem";
+  flags.slave_subsystems = None();
+
+  Fetcher fetcher;
+
+  Try<MesosContainerizer*> containerizer =
+    MesosContainerizer::create(flags, true, &fetcher);
+
+  ASSERT_SOME(containerizer);
+
+  Try<PID<Slave>> slave = StartSlave(containerizer.get(), flags);
+  ASSERT_SOME(slave);
+
+  MockScheduler sched;
+
+  MesosSchedulerDriver driver(
+      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+  EXPECT_CALL(sched, registered(_, _, _));
+
+  Future<vector<Offer>> offers;
+  EXPECT_CALL(sched, resourceOffers(_, _))
+    .WillOnce(FutureArg<1>(&offers))
+    .WillRepeatedly(Return());      // Ignore subsequent offers.
+
+  driver.start();
+
+  AWAIT_READY(offers);
+  EXPECT_NE(0u, offers.get().size());
+
+  Offer offer = offers.get()[0];
+
+  // Run a task that triggers memory pressure event. We request 1G
+  // disk because we are going to write a 512 MB file repeatedly.
+  TaskInfo task = createTask(
+      offer.slave_id(),
+      Resources::parse("cpus:1;mem:256;disk:1024").get(),
+      "while true; do dd count=512 bs=1M if=/dev/zero of=./temp; done");
+
+  Future<TaskStatus> status;
+  EXPECT_CALL(sched, statusUpdate(&driver, _))
+    .WillOnce(FutureArg<1>(&status))
+    .WillRepeatedly(Return());       // Ignore subsequent updates.
+
+  driver.launchTasks(offer.id(), {task});
+
+  AWAIT_READY(status);
+  EXPECT_EQ(task.task_id(), status.get().task_id());
+  EXPECT_EQ(TASK_RUNNING, status.get().state());
+
+  Future<hashset<ContainerID>> containers = containerizer.get()->containers();
+  AWAIT_READY(containers);
+  ASSERT_EQ(1u, containers.get().size());
+
+  ContainerID containerId = *(containers.get().begin());
+
+  Duration waited = Duration::zero();
+  do {
+    Future<ResourceStatistics> usage = containerizer.get()->usage(containerId);
+    AWAIT_READY(usage);
+
+    if (usage.get().mem_low_pressure_counter() > 0) {
+      EXPECT_GE(usage.get().mem_low_pressure_counter(),
+                usage.get().mem_medium_pressure_counter());
+      EXPECT_GE(usage.get().mem_medium_pressure_counter(),
+                usage.get().mem_critical_pressure_counter());
+      break;
+    }
+
+    os::sleep(Milliseconds(100));
+    waited += Milliseconds(100);
+  } while (waited < Seconds(5));
+
+  EXPECT_LE(waited, Seconds(5));
+
+  driver.stop();
+  driver.join();
+
+  Shutdown();
+  delete containerizer.get();
+}
+
+
+// Test that memory pressure listening is restarted after recovery.
+TEST_F(MemoryPressureMesosTest, CGROUPS_ROOT_SlaveRecovery)
+{
+  Try<PID<Master>> master = StartMaster();
+  ASSERT_SOME(master);
+
+  slave::Flags flags = CreateSlaveFlags();
+
+  // We only care about memory cgroup for this test.
+  flags.isolation = "cgroups/mem";
+  flags.slave_subsystems = None();
+
+  Fetcher fetcher;
+
+  Try<MesosContainerizer*> containerizer1 =
+    MesosContainerizer::create(flags, true, &fetcher);
+
+  ASSERT_SOME(containerizer1);
+
+  Try<PID<Slave>> slave = StartSlave(containerizer1.get(), flags);
+  ASSERT_SOME(slave);
+
+  MockScheduler sched;
+
+  // Enable checkpointing for the framework.
+  FrameworkInfo frameworkInfo = DEFAULT_FRAMEWORK_INFO;
+  frameworkInfo.set_checkpoint(true);
+
+  MesosSchedulerDriver driver(
+      &sched, frameworkInfo, master.get(), DEFAULT_CREDENTIAL);
+
+  EXPECT_CALL(sched, registered(_, _, _));
+
+  Future<vector<Offer>> offers;
+  EXPECT_CALL(sched, resourceOffers(_, _))
+    .WillOnce(FutureArg<1>(&offers))
+    .WillRepeatedly(Return());      // Ignore subsequent offers.
+
+  driver.start();
+
+  AWAIT_READY(offers);
+  EXPECT_NE(0u, offers.get().size());
+
+  Offer offer = offers.get()[0];
+
+  // Run a task that triggers memory pressure event. We request 1G
+  // disk because we are going to write a 512 MB file repeatedly.
+  TaskInfo task = createTask(
+      offer.slave_id(),
+      Resources::parse("cpus:1;mem:256;disk:1024").get(),
+      "while true; do dd count=512 bs=1M if=/dev/zero of=./temp; done");
+
+  Future<TaskStatus> status;
+  EXPECT_CALL(sched, statusUpdate(&driver, _))
+    .WillOnce(FutureArg<1>(&status))
+    .WillRepeatedly(Return());       // Ignore subsequent updates.
+
+  driver.launchTasks(offers.get()[0].id(), {task});
+
+  AWAIT_READY(status);
+  EXPECT_EQ(task.task_id(), status.get().task_id());
+  EXPECT_EQ(TASK_RUNNING, status.get().state());
+
+  // We restart the slave to let it recover.
+  Stop(slave.get());
+  delete containerizer1.get();
+
+  Future<Nothing> _recover = FUTURE_DISPATCH(_, &Slave::_recover);
+
+  Future<SlaveReregisteredMessage> slaveReregisteredMessage =
+    FUTURE_PROTOBUF(SlaveReregisteredMessage(), _, _);
+
+  // Use the same flags.
+  Try<MesosContainerizer*> containerizer2 =
+    MesosContainerizer::create(flags, true, &fetcher);
+
+  ASSERT_SOME(containerizer2);
+
+  slave = StartSlave(containerizer2.get(), flags);
+  ASSERT_SOME(slave);
+
+  Clock::pause();
+
+  AWAIT_READY(_recover);
+
+  // Wait for slave to schedule reregister timeout.
+  Clock::settle();
+
+  // Ensure the slave considers itself recovered.
+  Clock::advance(slave::EXECUTOR_REREGISTER_TIMEOUT);
+
+  Clock::resume();
+
+  // Wait for the slave to re-register.
+  AWAIT_READY(slaveReregisteredMessage);
+
+  Future<hashset<ContainerID>> containers = containerizer2.get()->containers();
+  AWAIT_READY(containers);
+  ASSERT_EQ(1u, containers.get().size());
+
+  ContainerID containerId = *(containers.get().begin());
+
+  Duration waited = Duration::zero();
+  do {
+    Future<ResourceStatistics> usage = containerizer2.get()->usage(containerId);
+    AWAIT_READY(usage);
+
+    if (usage.get().mem_low_pressure_counter() > 0) {
+      EXPECT_GE(usage.get().mem_low_pressure_counter(),
+                usage.get().mem_medium_pressure_counter());
+      EXPECT_GE(usage.get().mem_medium_pressure_counter(),
+                usage.get().mem_critical_pressure_counter());
+      break;
+    }
+
+    os::sleep(Milliseconds(100));
+    waited += Milliseconds(100);
+  } while (waited < Seconds(5));
+
+  EXPECT_LE(waited, Seconds(5));
+
+  driver.stop();
+  driver.join();
+
+  Shutdown();
+  delete containerizer2.get();
+}
+
+} // namespace tests {
+} // namespace internal {
+} // namespace mesos {


[10/12] mesos git commit: Moved containerizer related tests under src/tests/containerizer.

Posted by ji...@apache.org.
http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer/docker_containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/docker_containerizer_tests.cpp b/src/tests/containerizer/docker_containerizer_tests.cpp
new file mode 100644
index 0000000..80ed60e
--- /dev/null
+++ b/src/tests/containerizer/docker_containerizer_tests.cpp
@@ -0,0 +1,2955 @@
+/**
+ * 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 <process/gmock.hpp>
+#include <process/owned.hpp>
+#include <process/subprocess.hpp>
+
+#include <stout/duration.hpp>
+
+#include "linux/cgroups.hpp"
+
+#include "messages/messages.hpp"
+
+#include "tests/flags.hpp"
+#include "tests/mesos.hpp"
+
+#include "slave/containerizer/docker.hpp"
+#include "slave/containerizer/fetcher.hpp"
+
+#include "slave/paths.hpp"
+#include "slave/slave.hpp"
+#include "slave/state.hpp"
+
+using namespace mesos::internal::slave::paths;
+using namespace mesos::internal::slave::state;
+
+using namespace process;
+
+using mesos::internal::master::Master;
+
+using mesos::internal::slave::DockerContainerizer;
+using mesos::internal::slave::DockerContainerizerProcess;
+using mesos::internal::slave::Fetcher;
+using mesos::internal::slave::Slave;
+
+using process::Future;
+using process::Message;
+using process::PID;
+using process::UPID;
+
+using std::vector;
+using std::list;
+using std::string;
+
+using testing::_;
+using testing::DoAll;
+using testing::DoDefault;
+using testing::Eq;
+using testing::Invoke;
+using testing::Return;
+
+namespace mesos {
+namespace internal {
+namespace tests {
+
+
+class MockDocker : public Docker
+{
+public:
+  MockDocker(const string& path) : Docker(path)
+  {
+    EXPECT_CALL(*this, pull(_, _, _))
+      .WillRepeatedly(Invoke(this, &MockDocker::_pull));
+
+    EXPECT_CALL(*this, stop(_, _, _))
+      .WillRepeatedly(Invoke(this, &MockDocker::_stop));
+
+    EXPECT_CALL(*this, run(_, _, _, _, _, _, _, _, _))
+      .WillRepeatedly(Invoke(this, &MockDocker::_run));
+
+    EXPECT_CALL(*this, inspect(_, _))
+      .WillRepeatedly(Invoke(this, &MockDocker::_inspect));
+  }
+
+  MOCK_CONST_METHOD9(
+      run,
+      process::Future<Nothing>(
+          const mesos::ContainerInfo&,
+          const mesos::CommandInfo&,
+          const std::string&,
+          const std::string&,
+          const std::string&,
+          const Option<mesos::Resources>&,
+          const Option<std::map<std::string, std::string>>&,
+          const Option<std::string>&,
+          const Option<std::string>&));
+
+  MOCK_CONST_METHOD3(
+      pull,
+      process::Future<Docker::Image>(
+          const string&,
+          const string&,
+          bool));
+
+  MOCK_CONST_METHOD3(
+      stop,
+      process::Future<Nothing>(
+          const string&,
+          const Duration&,
+          bool));
+
+  MOCK_CONST_METHOD2(
+      inspect,
+      process::Future<Docker::Container>(
+          const string&,
+          const Option<Duration>&));
+
+  process::Future<Nothing> _run(
+      const mesos::ContainerInfo& containerInfo,
+      const mesos::CommandInfo& commandInfo,
+      const std::string& name,
+      const std::string& sandboxDirectory,
+      const std::string& mappedDirectory,
+      const Option<mesos::Resources>& resources,
+      const Option<std::map<std::string, std::string>>& env,
+      const Option<std::string>& stdoutPath,
+      const Option<std::string>& stderrPath) const
+  {
+    return Docker::run(
+        containerInfo,
+        commandInfo,
+        name,
+        sandboxDirectory,
+        mappedDirectory,
+        resources,
+        env,
+        stdoutPath,
+        stderrPath);
+  }
+
+  process::Future<Docker::Image> _pull(
+      const string& directory,
+      const string& image,
+      bool force) const
+  {
+    return Docker::pull(directory, image, force);
+  }
+
+  process::Future<Nothing> _stop(
+      const string& containerName,
+      const Duration& timeout,
+      bool remove) const
+  {
+    return Docker::stop(containerName, timeout, remove);
+  }
+
+  process::Future<Docker::Container> _inspect(
+      const string& containerName,
+      const Option<Duration>& retryInterval)
+  {
+    return Docker::inspect(containerName, retryInterval);
+  }
+};
+
+
+class DockerContainerizerTest : public MesosTest
+{
+public:
+  static string containerName(
+      const SlaveID& slaveId,
+      const ContainerID& containerId)
+  {
+    return slave::DOCKER_NAME_PREFIX + slaveId.value() +
+      slave::DOCKER_NAME_SEPERATOR + containerId.value();
+  }
+
+  enum ContainerState {
+    EXISTS,
+    RUNNING
+  };
+
+  static bool exists(
+      const process::Shared<Docker>& docker,
+      const SlaveID& slaveId,
+      const ContainerID& containerId,
+      ContainerState state = ContainerState::EXISTS)
+  {
+    Duration waited = Duration::zero();
+    string expectedName = containerName(slaveId, containerId);
+
+    do {
+      Future<Docker::Container> inspect = docker->inspect(expectedName);
+
+      if (!inspect.await(Seconds(3))) {
+        return false;
+      }
+
+      if (inspect.isReady()) {
+        switch (state) {
+          case ContainerState::RUNNING:
+            if (inspect.get().pid.isSome()) {
+              return true;
+            }
+            // Retry looking for running pid until timeout.
+            break;
+          case ContainerState::EXISTS:
+            return true;
+        }
+      }
+
+      os::sleep(Milliseconds(200));
+      waited += Milliseconds(200);
+    } while (waited < Seconds(5));
+
+    return false;
+  }
+
+  static bool containsLine(
+    const vector<string>& lines,
+    const string& expectedLine)
+  {
+    foreach (const string& line, lines) {
+      if (line == expectedLine) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  virtual void TearDown()
+  {
+    Try<Docker*> docker = Docker::create(tests::flags.docker, false);
+    ASSERT_SOME(docker);
+    Future<list<Docker::Container>> containers =
+      docker.get()->ps(true, slave::DOCKER_NAME_PREFIX);
+
+    AWAIT_READY(containers);
+
+    // Cleanup all mesos launched containers.
+    foreach (const Docker::Container& container, containers.get()) {
+      AWAIT_READY_FOR(docker.get()->rm(container.id, true), Seconds(30));
+    }
+
+    delete docker.get();
+  }
+};
+
+
+class MockDockerContainerizer : public DockerContainerizer {
+public:
+  MockDockerContainerizer(
+      const slave::Flags& flags,
+      Fetcher* fetcher,
+      Shared<Docker> docker)
+    : DockerContainerizer(flags, fetcher, docker)
+  {
+    initialize();
+  }
+
+  MockDockerContainerizer(const Owned<DockerContainerizerProcess>& process)
+    : DockerContainerizer(process)
+  {
+    initialize();
+  }
+
+  void initialize()
+  {
+    // NOTE: See TestContainerizer::setup for why we use
+    // 'EXPECT_CALL' and 'WillRepeatedly' here instead of
+    // 'ON_CALL' and 'WillByDefault'.
+    EXPECT_CALL(*this, launch(_, _, _, _, _, _, _))
+      .WillRepeatedly(Invoke(this, &MockDockerContainerizer::_launchExecutor));
+
+    EXPECT_CALL(*this, launch(_, _, _, _, _, _, _, _))
+      .WillRepeatedly(Invoke(this, &MockDockerContainerizer::_launch));
+
+    EXPECT_CALL(*this, update(_, _))
+      .WillRepeatedly(Invoke(this, &MockDockerContainerizer::_update));
+  }
+
+  MOCK_METHOD7(
+      launch,
+      process::Future<bool>(
+          const ContainerID&,
+          const ExecutorInfo&,
+          const std::string&,
+          const Option<std::string>&,
+          const SlaveID&,
+          const process::PID<slave::Slave>&,
+          bool checkpoint));
+
+  MOCK_METHOD8(
+      launch,
+      process::Future<bool>(
+          const ContainerID&,
+          const TaskInfo&,
+          const ExecutorInfo&,
+          const std::string&,
+          const Option<std::string>&,
+          const SlaveID&,
+          const process::PID<slave::Slave>&,
+          bool checkpoint));
+
+  MOCK_METHOD2(
+      update,
+      process::Future<Nothing>(
+          const ContainerID&,
+          const Resources&));
+
+  // Default 'launch' implementation (necessary because we can't just
+  // use &DockerContainerizer::launch with 'Invoke').
+  process::Future<bool> _launch(
+      const ContainerID& containerId,
+      const TaskInfo& taskInfo,
+      const ExecutorInfo& executorInfo,
+      const string& directory,
+      const Option<string>& user,
+      const SlaveID& slaveId,
+      const PID<Slave>& slavePid,
+      bool checkpoint)
+  {
+    return DockerContainerizer::launch(
+        containerId,
+        taskInfo,
+        executorInfo,
+        directory,
+        user,
+        slaveId,
+        slavePid,
+        checkpoint);
+  }
+
+  process::Future<bool> _launchExecutor(
+      const ContainerID& containerId,
+      const ExecutorInfo& executorInfo,
+      const string& directory,
+      const Option<string>& user,
+      const SlaveID& slaveId,
+      const PID<Slave>& slavePid,
+      bool checkpoint)
+  {
+    return DockerContainerizer::launch(
+        containerId,
+        executorInfo,
+        directory,
+        user,
+        slaveId,
+        slavePid,
+        checkpoint);
+  }
+
+  process::Future<Nothing> _update(
+      const ContainerID& containerId,
+      const Resources& resources)
+  {
+    return DockerContainerizer::update(
+        containerId,
+        resources);
+  }
+};
+
+
+class MockDockerContainerizerProcess : public DockerContainerizerProcess
+{
+public:
+  MockDockerContainerizerProcess(
+      const slave::Flags& flags,
+      Fetcher* fetcher,
+      const Shared<Docker>& docker)
+    : DockerContainerizerProcess(flags, fetcher, docker)
+  {
+    EXPECT_CALL(*this, fetch(_, _))
+      .WillRepeatedly(Invoke(this, &MockDockerContainerizerProcess::_fetch));
+
+    EXPECT_CALL(*this, pull(_))
+      .WillRepeatedly(Invoke(this, &MockDockerContainerizerProcess::_pull));
+  }
+
+  MOCK_METHOD2(
+      fetch,
+      process::Future<Nothing>(
+          const ContainerID& containerId,
+          const SlaveID& slaveId));
+
+  MOCK_METHOD1(
+      pull,
+      process::Future<Nothing>(const ContainerID& containerId));
+
+  process::Future<Nothing> _fetch(
+      const ContainerID& containerId,
+      const SlaveID& slaveId)
+  {
+    return DockerContainerizerProcess::fetch(containerId, slaveId);
+  }
+
+  process::Future<Nothing> _pull(const ContainerID& containerId)
+  {
+    return DockerContainerizerProcess::pull(containerId);
+  }
+};
+
+
+// Only enable executor launch on linux as other platforms
+// requires running linux VM and need special port forwarding
+// to get host networking to work.
+#ifdef __linux__
+TEST_F(DockerContainerizerTest, ROOT_DOCKER_Launch_Executor)
+{
+  Try<PID<Master> > master = StartMaster();
+  ASSERT_SOME(master);
+
+  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
+  Shared<Docker> docker(mockDocker);
+
+  slave::Flags flags = CreateSlaveFlags();
+
+  Fetcher fetcher;
+
+  MockDockerContainerizer dockerContainerizer(flags, &fetcher, docker);
+
+  Try<PID<Slave> > slave = StartSlave(&dockerContainerizer, flags);
+  ASSERT_SOME(slave);
+
+  MockScheduler sched;
+  MesosSchedulerDriver driver(
+    &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+  Future<FrameworkID> frameworkId;
+  EXPECT_CALL(sched, registered(&driver, _, _))
+    .WillOnce(FutureArg<1>(&frameworkId));
+
+  Future<vector<Offer> > offers;
+  EXPECT_CALL(sched, resourceOffers(&driver, _))
+    .WillOnce(FutureArg<1>(&offers))
+    .WillRepeatedly(Return()); // Ignore subsequent offers.
+
+  driver.start();
+
+  AWAIT_READY(frameworkId);
+
+  AWAIT_READY(offers);
+  ASSERT_NE(0u, offers.get().size());
+
+  const Offer& offer = offers.get()[0];
+
+  SlaveID slaveId = offer.slave_id();
+
+  TaskInfo task;
+  task.set_name("");
+  task.mutable_task_id()->set_value("1");
+  task.mutable_slave_id()->CopyFrom(offer.slave_id());
+  task.mutable_resources()->CopyFrom(offer.resources());
+
+  ExecutorInfo executorInfo;
+  ExecutorID executorId;
+  executorId.set_value("e1");
+  executorInfo.mutable_executor_id()->CopyFrom(executorId);
+
+  CommandInfo command;
+  command.set_value("/bin/test-executor");
+  executorInfo.mutable_command()->CopyFrom(command);
+
+  ContainerInfo containerInfo;
+  containerInfo.set_type(ContainerInfo::DOCKER);
+
+  // TODO(tnachen): Use local image to test if possible.
+  ContainerInfo::DockerInfo dockerInfo;
+  dockerInfo.set_image("tnachen/test-executor");
+
+  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
+  executorInfo.mutable_container()->CopyFrom(containerInfo);
+
+  task.mutable_executor()->CopyFrom(executorInfo);
+
+  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(), {task});
+
+  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());
+
+  ASSERT_TRUE(exists(docker, slaveId, containerId.get()));
+
+  Future<containerizer::Termination> termination =
+    dockerContainerizer.wait(containerId.get());
+
+  driver.stop();
+  driver.join();
+
+  AWAIT_READY(termination);
+
+  ASSERT_FALSE(
+    exists(docker, slaveId, containerId.get(), ContainerState::RUNNING));
+
+  Shutdown();
+}
+
+
+// This test verifies that a custom executor can be launched and
+// registered with the slave with docker bridge network enabled.
+// We're assuming that the custom executor is registering it's public
+// ip instead of 0.0.0.0 or equivelent to the slave as that's the
+// default behavior for libprocess.
+TEST_F(DockerContainerizerTest, ROOT_DOCKER_Launch_Executor_Bridged)
+{
+  Try<PID<Master> > master = StartMaster();
+  ASSERT_SOME(master);
+
+  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
+  Shared<Docker> docker(mockDocker);
+
+  slave::Flags flags = CreateSlaveFlags();
+
+  Fetcher fetcher;
+
+  MockDockerContainerizer dockerContainerizer(flags, &fetcher, docker);
+
+  Try<PID<Slave> > slave = StartSlave(&dockerContainerizer, flags);
+  ASSERT_SOME(slave);
+
+  MockScheduler sched;
+  MesosSchedulerDriver driver(
+    &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+  Future<FrameworkID> frameworkId;
+  EXPECT_CALL(sched, registered(&driver, _, _))
+    .WillOnce(FutureArg<1>(&frameworkId));
+
+  Future<vector<Offer> > offers;
+  EXPECT_CALL(sched, resourceOffers(&driver, _))
+    .WillOnce(FutureArg<1>(&offers))
+    .WillRepeatedly(Return()); // Ignore subsequent offers.
+
+  driver.start();
+
+  AWAIT_READY(frameworkId);
+
+  AWAIT_READY(offers);
+  ASSERT_NE(0u, offers.get().size());
+
+  const Offer& offer = offers.get()[0];
+
+  SlaveID slaveId = offer.slave_id();
+
+  TaskInfo task;
+  task.set_name("");
+  task.mutable_task_id()->set_value("1");
+  task.mutable_slave_id()->CopyFrom(offer.slave_id());
+  task.mutable_resources()->CopyFrom(offer.resources());
+
+  ExecutorInfo executorInfo;
+  ExecutorID executorId;
+  executorId.set_value("e1");
+  executorInfo.mutable_executor_id()->CopyFrom(executorId);
+
+  CommandInfo command;
+  command.set_value("/bin/test-executor");
+  executorInfo.mutable_command()->CopyFrom(command);
+
+  ContainerInfo containerInfo;
+  containerInfo.set_type(ContainerInfo::DOCKER);
+
+  // TODO(tnachen): Use local image to test if possible.
+  ContainerInfo::DockerInfo dockerInfo;
+  dockerInfo.set_image("tnachen/test-executor");
+  dockerInfo.set_network(ContainerInfo::DockerInfo::BRIDGE);
+
+  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
+  executorInfo.mutable_container()->CopyFrom(containerInfo);
+
+  task.mutable_executor()->CopyFrom(executorInfo);
+
+  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(), {task});
+
+  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());
+
+  ASSERT_TRUE(exists(docker, slaveId, containerId.get()));
+
+  Future<containerizer::Termination> termination =
+    dockerContainerizer.wait(containerId.get());
+
+  driver.stop();
+  driver.join();
+
+  AWAIT_READY(termination);
+
+  ASSERT_FALSE(
+    exists(docker, slaveId, containerId.get(), ContainerState::RUNNING));
+
+  Shutdown();
+}
+#endif // __linux__
+
+
+TEST_F(DockerContainerizerTest, ROOT_DOCKER_Launch)
+{
+  Try<PID<Master> > master = StartMaster();
+  ASSERT_SOME(master);
+
+  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
+  Shared<Docker> docker(mockDocker);
+
+  slave::Flags flags = CreateSlaveFlags();
+
+  Fetcher fetcher;
+
+  MockDockerContainerizer dockerContainerizer(flags, &fetcher, docker);
+
+  Try<PID<Slave> > slave = StartSlave(&dockerContainerizer, flags);
+  ASSERT_SOME(slave);
+
+  MockScheduler sched;
+  MesosSchedulerDriver driver(
+      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+  Future<FrameworkID> frameworkId;
+  EXPECT_CALL(sched, registered(&driver, _, _))
+    .WillOnce(FutureArg<1>(&frameworkId));
+
+  Future<vector<Offer> > offers;
+  EXPECT_CALL(sched, resourceOffers(&driver, _))
+    .WillOnce(FutureArg<1>(&offers))
+    .WillRepeatedly(Return()); // Ignore subsequent offers.
+
+  driver.start();
+
+  AWAIT_READY(frameworkId);
+
+  AWAIT_READY(offers);
+  ASSERT_NE(0u, offers.get().size());
+
+  const Offer& offer = offers.get()[0];
+
+  SlaveID slaveId = offer.slave_id();
+
+  TaskInfo task;
+  task.set_name("");
+  task.mutable_task_id()->set_value("1");
+  task.mutable_slave_id()->CopyFrom(offer.slave_id());
+  task.mutable_resources()->CopyFrom(offer.resources());
+
+  CommandInfo command;
+  command.set_value("sleep 1000");
+
+  ContainerInfo containerInfo;
+  containerInfo.set_type(ContainerInfo::DOCKER);
+
+  // TODO(tnachen): Use local image to test if possible.
+  ContainerInfo::DockerInfo dockerInfo;
+  dockerInfo.set_image("busybox");
+  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
+
+  task.mutable_command()->CopyFrom(command);
+  task.mutable_container()->CopyFrom(containerInfo);
+
+  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))
+    .WillRepeatedly(DoDefault());
+
+  driver.launchTasks(offers.get()[0].id(), {task});
+
+  AWAIT_READY_FOR(containerId, Seconds(60));
+  AWAIT_READY_FOR(statusRunning, Seconds(60));
+  EXPECT_EQ(TASK_RUNNING, statusRunning.get().state());
+  ASSERT_TRUE(statusRunning.get().has_data());
+
+  Try<JSON::Array> parse = JSON::parse<JSON::Array>(statusRunning.get().data());
+  ASSERT_SOME(parse);
+
+  // Now verify that the Docker.NetworkSettings.IPAddress label is
+  // present.
+  ASSERT_TRUE(statusRunning.get().has_labels());
+  EXPECT_EQ(1, statusRunning.get().labels().labels().size());
+  EXPECT_EQ("Docker.NetworkSettings.IPAddress",
+            statusRunning.get().labels().labels(0).key());
+
+  ASSERT_TRUE(exists(docker, slaveId, containerId.get()));
+
+  Future<containerizer::Termination> termination =
+    dockerContainerizer.wait(containerId.get());
+
+  driver.stop();
+  driver.join();
+
+  AWAIT_READY(termination);
+
+  ASSERT_FALSE(
+    exists(docker, slaveId, containerId.get(), ContainerState::RUNNING));
+
+  Shutdown();
+}
+
+
+TEST_F(DockerContainerizerTest, ROOT_DOCKER_Kill)
+{
+  Try<PID<Master> > master = StartMaster();
+  ASSERT_SOME(master);
+
+  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
+  Shared<Docker> docker(mockDocker);
+
+  slave::Flags flags = CreateSlaveFlags();
+
+  Fetcher fetcher;
+
+  MockDockerContainerizer dockerContainerizer(flags, &fetcher, docker);
+
+  Try<PID<Slave> > slave = StartSlave(&dockerContainerizer, flags);
+  ASSERT_SOME(slave);
+
+  MockScheduler sched;
+  MesosSchedulerDriver driver(
+      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+  Future<FrameworkID> frameworkId;
+  EXPECT_CALL(sched, registered(&driver, _, _))
+    .WillOnce(FutureArg<1>(&frameworkId));
+
+  Future<vector<Offer> > offers;
+  EXPECT_CALL(sched, resourceOffers(&driver, _))
+    .WillOnce(FutureArg<1>(&offers))
+    .WillRepeatedly(Return()); // Ignore subsequent offers.
+
+  driver.start();
+
+  AWAIT_READY(frameworkId);
+
+  AWAIT_READY(offers);
+  ASSERT_NE(0u, offers.get().size());
+
+  const Offer& offer = offers.get()[0];
+
+  SlaveID slaveId = offer.slave_id();
+
+  TaskInfo task;
+  task.set_name("");
+  task.mutable_task_id()->set_value("1");
+  task.mutable_slave_id()->CopyFrom(offer.slave_id());
+  task.mutable_resources()->CopyFrom(offer.resources());
+
+  CommandInfo command;
+  command.set_value("sleep 1000");
+
+  ContainerInfo containerInfo;
+  containerInfo.set_type(ContainerInfo::DOCKER);
+
+  // TODO(tnachen): Use local image to test if possible.
+  ContainerInfo::DockerInfo dockerInfo;
+  dockerInfo.set_image("busybox");
+  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
+
+  task.mutable_command()->CopyFrom(command);
+  task.mutable_container()->CopyFrom(containerInfo);
+
+  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(), {task});
+
+  AWAIT_READY_FOR(containerId, Seconds(60));
+  AWAIT_READY_FOR(statusRunning, Seconds(60));
+  EXPECT_EQ(TASK_RUNNING, statusRunning.get().state());
+
+  ASSERT_TRUE(
+    exists(docker, slaveId, containerId.get(), ContainerState::RUNNING));
+
+  Future<TaskStatus> statusKilled;
+  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);
+
+  ASSERT_FALSE(
+    exists(docker, slaveId, containerId.get(), ContainerState::RUNNING));
+
+  driver.stop();
+  driver.join();
+
+  Shutdown();
+}
+
+
+// This test tests DockerContainerizer::usage().
+TEST_F(DockerContainerizerTest, ROOT_DOCKER_Usage)
+{
+  Try<PID<Master> > master = StartMaster();
+  ASSERT_SOME(master);
+
+  slave::Flags flags = CreateSlaveFlags();
+  flags.resources = Option<string>("cpus:2;mem:1024");
+
+  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
+  Shared<Docker> docker(mockDocker);
+
+  Fetcher fetcher;
+
+  MockDockerContainerizer dockerContainerizer(flags, &fetcher, docker);
+
+  Try<PID<Slave> > slave = StartSlave(&dockerContainerizer, flags);
+  ASSERT_SOME(slave);
+
+  MockScheduler sched;
+  MesosSchedulerDriver driver(
+      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+  Future<FrameworkID> frameworkId;
+  EXPECT_CALL(sched, registered(&driver, _, _))
+    .WillOnce(FutureArg<1>(&frameworkId));
+
+  Future<vector<Offer> > offers;
+  EXPECT_CALL(sched, resourceOffers(&driver, _))
+    .WillOnce(FutureArg<1>(&offers))
+    .WillRepeatedly(Return()); // Ignore subsequent offers.
+
+  driver.start();
+
+  AWAIT_READY(frameworkId);
+
+  AWAIT_READY(offers);
+  ASSERT_NE(0u, offers.get().size());
+
+  const Offer& offer = offers.get()[0];
+
+  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;
+  // Run a CPU intensive command, so we can measure utime and stime later.
+  command.set_value("dd if=/dev/zero of=/dev/null");
+
+  ContainerInfo containerInfo;
+  containerInfo.set_type(ContainerInfo::DOCKER);
+
+  // TODO(tnachen): Use local image to test if possible.
+  ContainerInfo::DockerInfo dockerInfo;
+  dockerInfo.set_image("busybox");
+  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
+
+  task.mutable_command()->CopyFrom(command);
+  task.mutable_container()->CopyFrom(containerInfo);
+
+  Future<ContainerID> containerId;
+  EXPECT_CALL(dockerContainerizer, launch(_, _, _, _, _, _, _, _))
+    .WillOnce(DoAll(FutureArg<0>(&containerId),
+                    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))
+    .WillRepeatedly(DoDefault());
+
+  driver.launchTasks(offers.get()[0].id(), {task});
+
+  AWAIT_READY_FOR(containerId, Seconds(60));
+  AWAIT_READY_FOR(statusRunning, Seconds(60));
+  EXPECT_EQ(TASK_RUNNING, statusRunning.get().state());
+
+  // Verify the usage.
+  ResourceStatistics statistics;
+  Duration waited = Duration::zero();
+  do {
+    Future<ResourceStatistics> usage =
+      dockerContainerizer.usage(containerId.get());
+    // TODO(tnachen): Replace await with AWAIT_COMPLETED once
+    // implemented.
+    ASSERT_TRUE(usage.await(Seconds(3)));
+
+    if (usage.isReady()) {
+      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));
+
+  // Usage includes the executor resources.
+  EXPECT_EQ(2.0 + slave::DEFAULT_EXECUTOR_CPUS, statistics.cpus_limit());
+  EXPECT_EQ((Gigabytes(1) + slave::DEFAULT_EXECUTOR_MEM).bytes(),
+            statistics.mem_limit_bytes());
+  EXPECT_LT(0, statistics.cpus_user_time_secs());
+  EXPECT_LT(0, statistics.cpus_system_time_secs());
+  EXPECT_GT(statistics.mem_rss_bytes(), 0u);
+
+  Future<containerizer::Termination> termination =
+    dockerContainerizer.wait(containerId.get());
+
+  dockerContainerizer.destroy(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();
+  driver.join();
+
+  Shutdown();
+}
+
+
+#ifdef __linux__
+TEST_F(DockerContainerizerTest, ROOT_DOCKER_Update)
+{
+  Try<PID<Master> > master = StartMaster();
+  ASSERT_SOME(master);
+
+  slave::Flags flags = CreateSlaveFlags();
+
+  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
+  Shared<Docker> docker(mockDocker);
+
+  Fetcher fetcher;
+
+  MockDockerContainerizer dockerContainerizer(flags, &fetcher, docker);
+
+  Try<PID<Slave> > slave = StartSlave(&dockerContainerizer, flags);
+  ASSERT_SOME(slave);
+
+  MockScheduler sched;
+  MesosSchedulerDriver driver(
+      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+  Future<FrameworkID> frameworkId;
+  EXPECT_CALL(sched, registered(&driver, _, _))
+    .WillOnce(FutureArg<1>(&frameworkId));
+
+  Future<vector<Offer> > offers;
+  EXPECT_CALL(sched, resourceOffers(&driver, _))
+    .WillOnce(FutureArg<1>(&offers))
+    .WillRepeatedly(Return()); // Ignore subsequent offers.
+
+  driver.start();
+
+  AWAIT_READY(frameworkId);
+
+  AWAIT_READY(offers);
+  ASSERT_NE(0u, offers.get().size());
+
+  const Offer& offer = offers.get()[0];
+
+  SlaveID slaveId = offer.slave_id();
+
+  TaskInfo task;
+  task.set_name("");
+  task.mutable_task_id()->set_value("1");
+  task.mutable_slave_id()->CopyFrom(offer.slave_id());
+  task.mutable_resources()->CopyFrom(offer.resources());
+
+  CommandInfo command;
+  command.set_value("sleep 1000");
+
+  ContainerInfo containerInfo;
+  containerInfo.set_type(ContainerInfo::DOCKER);
+
+  // TODO(tnachen): Use local image to test if possible.
+  ContainerInfo::DockerInfo dockerInfo;
+  dockerInfo.set_image("busybox");
+  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
+
+  task.mutable_command()->CopyFrom(command);
+  task.mutable_container()->CopyFrom(containerInfo);
+
+  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))
+    .WillRepeatedly(DoDefault());
+
+  driver.launchTasks(offers.get()[0].id(), {task});
+
+  AWAIT_READY(containerId);
+
+  AWAIT_READY_FOR(statusRunning, Seconds(60));
+  EXPECT_EQ(TASK_RUNNING, statusRunning.get().state());
+
+  ASSERT_TRUE(
+    exists(docker, slaveId, containerId.get(), ContainerState::RUNNING));
+
+  string name = containerName(slaveId, containerId.get());
+
+  Future<Docker::Container> inspect = docker->inspect(name);
+
+  AWAIT_READY(inspect);
+
+  Try<Resources> newResources = Resources::parse("cpus:1;mem:128");
+
+  ASSERT_SOME(newResources);
+
+  Future<Nothing> update =
+    dockerContainerizer.update(containerId.get(), newResources.get());
+
+  AWAIT_READY(update);
+
+  Result<string> cpuHierarchy = cgroups::hierarchy("cpu");
+  Result<string> memoryHierarchy = cgroups::hierarchy("memory");
+
+  ASSERT_SOME(cpuHierarchy);
+  ASSERT_SOME(memoryHierarchy);
+
+  Option<pid_t> pid = inspect.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(
+      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());
+
+  newResources = Resources::parse("cpus:1;mem:144");
+
+  // Issue second update that uses the cached pid instead of inspect.
+  update = dockerContainerizer.update(containerId.get(), newResources.get());
+
+  AWAIT_READY(update);
+
+  cpu = cgroups::cpu::shares(cpuHierarchy.get(), cpuCgroup.get());
+
+  ASSERT_SOME(cpu);
+
+  mem = cgroups::memory::soft_limit_in_bytes(
+      memoryHierarchy.get(),
+      memoryCgroup.get());
+
+  ASSERT_SOME(mem);
+
+  EXPECT_EQ(1024u, cpu.get());
+  EXPECT_EQ(144u, mem.get().megabytes());
+
+  driver.stop();
+  driver.join();
+
+  Shutdown();
+}
+#endif //__linux__
+
+
+TEST_F(DockerContainerizerTest, ROOT_DOCKER_Recover)
+{
+  slave::Flags flags = CreateSlaveFlags();
+
+  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
+  Shared<Docker> docker(mockDocker);
+
+  Future<string> stoppedContainer;
+  EXPECT_CALL(*mockDocker, stop(_, _, _))
+    .WillOnce(DoAll(FutureArg<0>(&stoppedContainer),
+                    Return(Nothing())));
+
+  Fetcher fetcher;
+
+  MockDockerContainerizer dockerContainerizer(flags, &fetcher, docker);
+
+  SlaveID slaveId;
+  slaveId.set_value("s1");
+  ContainerID containerId;
+  containerId.set_value("c1");
+  ContainerID reapedContainerId;
+  reapedContainerId.set_value("c2");
+
+  string container1 = containerName(slaveId, containerId);
+  string container2 = containerName(slaveId, reapedContainerId);
+
+  // Clean up artifacts if containers still exists.
+  ASSERT_TRUE(docker->rm(container1, true).await(Seconds(30)));
+  ASSERT_TRUE(docker->rm(container2, true).await(Seconds(30)));
+
+  Resources resources = Resources::parse("cpus:1;mem:512").get();
+
+  ContainerInfo containerInfo;
+  containerInfo.set_type(ContainerInfo::DOCKER);
+
+  // TODO(tnachen): Use local image to test if possible.
+  ContainerInfo::DockerInfo dockerInfo;
+  dockerInfo.set_image("busybox");
+  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
+
+  CommandInfo commandInfo;
+  commandInfo.set_value("sleep 1000");
+
+  Future<Nothing> d1 =
+    docker->run(
+        containerInfo,
+        commandInfo,
+        container1,
+        flags.work_dir,
+        flags.sandbox_directory,
+        resources);
+
+  Future<Nothing> d2 =
+    docker->run(
+        containerInfo,
+        commandInfo,
+        container2,
+        flags.work_dir,
+        flags.sandbox_directory,
+        resources);
+
+  ASSERT_TRUE(
+    exists(docker, slaveId, containerId, ContainerState::RUNNING));
+  ASSERT_TRUE(
+    exists(docker, slaveId, reapedContainerId, ContainerState::RUNNING));
+
+  Future<Docker::Container> inspect = docker->inspect(container2);
+  AWAIT_READY(inspect);
+
+  SlaveState slaveState;
+  slaveState.id = slaveId;
+  FrameworkState frameworkState;
+
+  ExecutorID execId;
+  execId.set_value("e1");
+
+  ExecutorState execState;
+  ExecutorInfo execInfo;
+  execState.info = execInfo;
+  execState.latest = containerId;
+
+  Try<process::Subprocess> wait =
+    process::subprocess(tests::flags.docker + " wait " + container1);
+
+  ASSERT_SOME(wait);
+
+  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));
+
+  AWAIT_EQ(inspect.get().id, stoppedContainer);
+
+  Shutdown();
+}
+
+
+// This test checks the docker containerizer doesn't recover executors
+// that were started by another containerizer (e.g: mesos).
+TEST_F(DockerContainerizerTest, ROOT_DOCKER_SkipRecoverNonDocker)
+{
+  slave::Flags flags = CreateSlaveFlags();
+
+  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
+  Shared<Docker> docker(mockDocker);
+
+  Fetcher fetcher;
+
+  MockDockerContainerizer dockerContainerizer(flags, &fetcher, docker);
+
+  ContainerID containerId;
+  containerId.set_value("c1");
+  ContainerID reapedContainerId;
+  reapedContainerId.set_value("c2");
+
+  ExecutorID executorId;
+  executorId.set_value(UUID::random().toString());
+
+  ExecutorInfo executorInfo;
+  executorInfo.mutable_container()->set_type(ContainerInfo::MESOS);
+
+  ExecutorState executorState;
+  executorState.info = executorInfo;
+  executorState.latest = containerId;
+
+  RunState runState;
+  runState.id = containerId;
+  executorState.runs.put(containerId, runState);
+
+  FrameworkState frameworkState;
+  frameworkState.executors.put(executorId, executorState);
+
+  SlaveState slaveState;
+  FrameworkID frameworkId;
+  frameworkId.set_value(UUID::random().toString());
+  slaveState.frameworks.put(frameworkId, frameworkState);
+
+  Future<Nothing> recover = dockerContainerizer.recover(slaveState);
+  AWAIT_READY(recover);
+
+  Future<hashset<ContainerID>> containers = dockerContainerizer.containers();
+  AWAIT_READY(containers);
+
+  // A MesosContainerizer task shouldn't be recovered by
+  // DockerContainerizer.
+  EXPECT_EQ(0u, containers.get().size());
+}
+
+
+TEST_F(DockerContainerizerTest, ROOT_DOCKER_Logs)
+{
+  Try<PID<Master> > master = StartMaster();
+  ASSERT_SOME(master);
+
+  slave::Flags flags = CreateSlaveFlags();
+
+  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
+  Shared<Docker> docker(mockDocker);
+
+  // We skip stopping the docker container because stopping a container
+  // even when it terminated might not flush the logs and we end up
+  // not getting stdout/stderr in our tests.
+  EXPECT_CALL(*mockDocker, stop(_, _, _))
+    .WillRepeatedly(Return(Nothing()));
+
+  Fetcher fetcher;
+
+  MockDockerContainerizer dockerContainerizer(flags, &fetcher, docker);
+
+  Try<PID<Slave> > slave = StartSlave(&dockerContainerizer, flags);
+  ASSERT_SOME(slave);
+
+  MockScheduler sched;
+  MesosSchedulerDriver driver(
+      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+  Future<FrameworkID> frameworkId;
+  EXPECT_CALL(sched, registered(&driver, _, _))
+    .WillOnce(FutureArg<1>(&frameworkId));
+
+  Future<vector<Offer> > offers;
+  EXPECT_CALL(sched, resourceOffers(&driver, _))
+    .WillOnce(FutureArg<1>(&offers))
+    .WillRepeatedly(Return()); // Ignore subsequent offers.
+
+  driver.start();
+
+  AWAIT_READY(frameworkId);
+
+  AWAIT_READY(offers);
+  ASSERT_NE(0u, offers.get().size());
+
+  const Offer& offer = offers.get()[0];
+
+  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());
+
+  string uuid = UUID::random().toString();
+
+  CommandInfo command;
+  command.set_value("echo out" + uuid + " ; echo err" + uuid + " 1>&2");
+
+  ContainerInfo containerInfo;
+  containerInfo.set_type(ContainerInfo::DOCKER);
+
+  // TODO(tnachen): Use local image to test if possible.
+  ContainerInfo::DockerInfo dockerInfo;
+  dockerInfo.set_image("busybox");
+  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
+
+  task.mutable_command()->CopyFrom(command);
+  task.mutable_container()->CopyFrom(containerInfo);
+
+  Future<ContainerID> containerId;
+  Future<string> directory;
+  EXPECT_CALL(dockerContainerizer, launch(_, _, _, _, _, _, _, _))
+    .WillOnce(DoAll(FutureArg<0>(&containerId),
+                    FutureArg<3>(&directory),
+                    Invoke(&dockerContainerizer,
+                           &MockDockerContainerizer::_launch)));
+
+  Future<TaskStatus> statusRunning;
+  Future<TaskStatus> statusFinished;
+  EXPECT_CALL(sched, statusUpdate(&driver, _))
+    .WillOnce(FutureArg<1>(&statusRunning))
+    .WillOnce(FutureArg<1>(&statusFinished))
+    .WillRepeatedly(DoDefault());
+
+  driver.launchTasks(offers.get()[0].id(), {task});
+
+  AWAIT_READY_FOR(containerId, Seconds(60));
+  AWAIT_READY(directory);
+  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());
+
+  // Now check that the proper output is in stderr and stdout (which
+  // might also contain other things, hence the use of a UUID).
+  Try<string> read = os::read(path::join(directory.get(), "stderr"));
+  ASSERT_SOME(read);
+
+  vector<string> lines = strings::split(read.get(), "\n");
+
+  EXPECT_TRUE(containsLine(lines, "err" + uuid));
+  EXPECT_FALSE(containsLine(lines, "out" + uuid));
+
+  read = os::read(path::join(directory.get(), "stdout"));
+  ASSERT_SOME(read);
+
+  lines = strings::split(read.get(), "\n");
+
+  EXPECT_TRUE(containsLine(lines, "out" + uuid));
+  EXPECT_FALSE(containsLine(lines, "err" + uuid));
+
+  driver.stop();
+  driver.join();
+
+  Shutdown();
+}
+
+
+// The following test uses a Docker image (mesosphere/inky) that has
+// an entrypoint "echo" and a default command "inky".
+TEST_F(DockerContainerizerTest, ROOT_DOCKER_Default_CMD)
+{
+  Try<PID<Master> > master = StartMaster();
+  ASSERT_SOME(master);
+
+  slave::Flags flags = CreateSlaveFlags();
+
+  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
+  Shared<Docker> docker(mockDocker);
+
+  // We skip stopping the docker container because stopping a container
+  // even when it terminated might not flush the logs and we end up
+  // not getting stdout/stderr in our tests.
+  EXPECT_CALL(*mockDocker, stop(_, _, _))
+    .WillRepeatedly(Return(Nothing()));
+
+  Fetcher fetcher;
+
+  MockDockerContainerizer dockerContainerizer(flags, &fetcher, docker);
+
+  Try<PID<Slave> > slave = StartSlave(&dockerContainerizer, flags);
+  ASSERT_SOME(slave);
+
+  MockScheduler sched;
+  MesosSchedulerDriver driver(
+      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+  Future<FrameworkID> frameworkId;
+  EXPECT_CALL(sched, registered(&driver, _, _))
+    .WillOnce(FutureArg<1>(&frameworkId));
+
+  Future<vector<Offer> > offers;
+  EXPECT_CALL(sched, resourceOffers(&driver, _))
+    .WillOnce(FutureArg<1>(&offers))
+    .WillRepeatedly(Return()); // Ignore subsequent offers.
+
+  driver.start();
+
+  AWAIT_READY(frameworkId);
+
+  AWAIT_READY(offers);
+  ASSERT_NE(0u, offers.get().size());
+
+  const Offer& offer = offers.get()[0];
+
+  SlaveID slaveId = offer.slave_id();
+
+  TaskInfo task;
+  task.set_name("");
+  task.mutable_task_id()->set_value("1");
+  task.mutable_slave_id()->CopyFrom(offer.slave_id());
+  task.mutable_resources()->CopyFrom(offer.resources());
+
+  CommandInfo command;
+  command.set_shell(false);
+
+  // NOTE: By not setting CommandInfo::value we're testing that we
+  // will still be able to run the container because it has a default
+  // entrypoint!
+
+  ContainerInfo containerInfo;
+  containerInfo.set_type(ContainerInfo::DOCKER);
+
+  // TODO(tnachen): Use local image to test if possible.
+  ContainerInfo::DockerInfo dockerInfo;
+  dockerInfo.set_image("mesosphere/inky");
+  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
+
+  task.mutable_command()->CopyFrom(command);
+  task.mutable_container()->CopyFrom(containerInfo);
+
+  Future<ContainerID> containerId;
+  Future<string> directory;
+  EXPECT_CALL(dockerContainerizer, launch(_, _, _, _, _, _, _, _))
+    .WillOnce(DoAll(FutureArg<0>(&containerId),
+                    FutureArg<3>(&directory),
+                    Invoke(&dockerContainerizer,
+                           &MockDockerContainerizer::_launch)));
+
+  Future<TaskStatus> statusRunning;
+  Future<TaskStatus> statusFinished;
+  EXPECT_CALL(sched, statusUpdate(&driver, _))
+    .WillOnce(FutureArg<1>(&statusRunning))
+    .WillOnce(FutureArg<1>(&statusFinished))
+    .WillRepeatedly(DoDefault());
+
+  driver.launchTasks(offers.get()[0].id(), {task});
+
+  AWAIT_READY_FOR(containerId, Seconds(60));
+  AWAIT_READY(directory);
+  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());
+
+  Try<string> read = os::read(path::join(directory.get(), "stdout"));
+  ASSERT_SOME(read);
+
+  vector<string> lines = strings::split(read.get(), "\n");
+
+  // Since we're not passing any command value, we're expecting the
+  // default entry point to be run which is 'echo' with the default
+  // command from the image which is 'inky'.
+  EXPECT_TRUE(containsLine(lines, "inky"));
+
+  read = os::read(path::join(directory.get(), "stderr"));
+  ASSERT_SOME(read);
+
+  lines = strings::split(read.get(), "\n");
+
+  EXPECT_FALSE(containsLine(lines, "inky"));
+
+  driver.stop();
+  driver.join();
+
+  Shutdown();
+}
+
+
+// The following test uses a Docker image (mesosphere/inky) that has
+// an entrypoint "echo" and a default command "inky".
+TEST_F(DockerContainerizerTest, ROOT_DOCKER_Default_CMD_Override)
+{
+  Try<PID<Master> > master = StartMaster();
+  ASSERT_SOME(master);
+
+  slave::Flags flags = CreateSlaveFlags();
+
+  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
+  Shared<Docker> docker(mockDocker);
+
+  // We skip stopping the docker container because stopping  a container
+  // even when it terminated might not flush the logs and we end up
+  // not getting stdout/stderr in our tests.
+  EXPECT_CALL(*mockDocker, stop(_, _, _))
+    .WillRepeatedly(Return(Nothing()));
+
+  Fetcher fetcher;
+
+  MockDockerContainerizer dockerContainerizer(flags, &fetcher, docker);
+
+  Try<PID<Slave> > slave = StartSlave(&dockerContainerizer, flags);
+  ASSERT_SOME(slave);
+
+  MockScheduler sched;
+  MesosSchedulerDriver driver(
+      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+  Future<FrameworkID> frameworkId;
+  EXPECT_CALL(sched, registered(&driver, _, _))
+    .WillOnce(FutureArg<1>(&frameworkId));
+
+  Future<vector<Offer> > offers;
+  EXPECT_CALL(sched, resourceOffers(&driver, _))
+    .WillOnce(FutureArg<1>(&offers))
+    .WillRepeatedly(Return()); // Ignore subsequent offers.
+
+  driver.start();
+
+  AWAIT_READY(frameworkId);
+
+  AWAIT_READY(offers);
+  ASSERT_NE(0u, offers.get().size());
+
+  const Offer& offer = offers.get()[0];
+
+  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());
+
+  string uuid = UUID::random().toString();
+
+  CommandInfo command;
+  command.set_shell(false);
+
+  // We can set the value to just the 'uuid' since it should get
+  // passed as an argument to the entrypoint, i.e., 'echo uuid'.
+  command.set_value(uuid);
+
+  ContainerInfo containerInfo;
+  containerInfo.set_type(ContainerInfo::DOCKER);
+
+  // TODO(tnachen): Use local image to test if possible.
+  ContainerInfo::DockerInfo dockerInfo;
+  dockerInfo.set_image("mesosphere/inky");
+  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
+
+  task.mutable_command()->CopyFrom(command);
+  task.mutable_container()->CopyFrom(containerInfo);
+
+  Future<ContainerID> containerId;
+  Future<string> directory;
+  EXPECT_CALL(dockerContainerizer, launch(_, _, _, _, _, _, _, _))
+    .WillOnce(DoAll(FutureArg<0>(&containerId),
+                    FutureArg<3>(&directory),
+                    Invoke(&dockerContainerizer,
+                           &MockDockerContainerizer::_launch)));
+
+  Future<TaskStatus> statusRunning;
+  Future<TaskStatus> statusFinished;
+  EXPECT_CALL(sched, statusUpdate(&driver, _))
+    .WillOnce(FutureArg<1>(&statusRunning))
+    .WillOnce(FutureArg<1>(&statusFinished))
+    .WillRepeatedly(DoDefault());
+
+  driver.launchTasks(offers.get()[0].id(), {task});
+
+  AWAIT_READY_FOR(containerId, Seconds(60));
+  AWAIT_READY(directory);
+  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());
+
+  // Now check that the proper output is in stderr and stdout.
+  Try<string> read = os::read(path::join(directory.get(), "stdout"));
+  ASSERT_SOME(read);
+
+  vector<string> lines = strings::split(read.get(), "\n");
+
+  // We expect the passed in command value to override the image's
+  // default command, thus we should see the value of 'uuid' in the
+  // output instead of the default command which is 'inky'.
+  EXPECT_TRUE(containsLine(lines, uuid));
+  EXPECT_FALSE(containsLine(lines, "inky"));
+
+  read = os::read(path::join(directory.get(), "stderr"));
+  ASSERT_SOME(read);
+
+  lines = strings::split(read.get(), "\n");
+
+  EXPECT_FALSE(containsLine(lines, "inky"));
+  EXPECT_FALSE(containsLine(lines, uuid));
+
+  driver.stop();
+  driver.join();
+
+  Shutdown();
+}
+
+
+// The following test uses a Docker image (mesosphere/inky) that has
+// an entrypoint "echo" and a default command "inky".
+TEST_F(DockerContainerizerTest, ROOT_DOCKER_Default_CMD_Args)
+{
+  Try<PID<Master> > master = StartMaster();
+  ASSERT_SOME(master);
+
+  slave::Flags flags = CreateSlaveFlags();
+
+  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
+  Shared<Docker> docker(mockDocker);
+
+  // We skip stopping the docker container because stopping a container
+  // even when it terminated might not flush the logs and we end up
+  // not getting stdout/stderr in our tests.
+  EXPECT_CALL(*mockDocker, stop(_, _, _))
+    .WillRepeatedly(Return(Nothing()));
+
+  Fetcher fetcher;
+
+  MockDockerContainerizer dockerContainerizer(flags, &fetcher, docker);
+
+  Try<PID<Slave> > slave = StartSlave(&dockerContainerizer, flags);
+  ASSERT_SOME(slave);
+
+  MockScheduler sched;
+  MesosSchedulerDriver driver(
+      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+  Future<FrameworkID> frameworkId;
+  EXPECT_CALL(sched, registered(&driver, _, _))
+    .WillOnce(FutureArg<1>(&frameworkId));
+
+  Future<vector<Offer> > offers;
+  EXPECT_CALL(sched, resourceOffers(&driver, _))
+    .WillOnce(FutureArg<1>(&offers))
+    .WillRepeatedly(Return()); // Ignore subsequent offers.
+
+  driver.start();
+
+  AWAIT_READY(frameworkId);
+
+  AWAIT_READY(offers);
+  ASSERT_NE(0u, offers.get().size());
+
+  const Offer& offer = offers.get()[0];
+
+  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());
+
+  string uuid = UUID::random().toString();
+
+  CommandInfo command;
+  command.set_shell(false);
+
+  // We should also be able to skip setting the comamnd value and just
+  // set the arguments and those should also get passed through to the
+  // entrypoint!
+  command.add_arguments(uuid);
+
+  ContainerInfo containerInfo;
+  containerInfo.set_type(ContainerInfo::DOCKER);
+
+  // TODO(tnachen): Use local image to test if possible.
+  ContainerInfo::DockerInfo dockerInfo;
+  dockerInfo.set_image("mesosphere/inky");
+  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
+
+  task.mutable_command()->CopyFrom(command);
+  task.mutable_container()->CopyFrom(containerInfo);
+
+  Future<ContainerID> containerId;
+  Future<string> directory;
+  EXPECT_CALL(dockerContainerizer, launch(_, _, _, _, _, _, _, _))
+    .WillOnce(DoAll(FutureArg<0>(&containerId),
+                    FutureArg<3>(&directory),
+                    Invoke(&dockerContainerizer,
+                           &MockDockerContainerizer::_launch)));
+
+  Future<TaskStatus> statusRunning;
+  Future<TaskStatus> statusFinished;
+  EXPECT_CALL(sched, statusUpdate(&driver, _))
+    .WillOnce(FutureArg<1>(&statusRunning))
+    .WillOnce(FutureArg<1>(&statusFinished))
+    .WillRepeatedly(DoDefault());
+
+  driver.launchTasks(offers.get()[0].id(), {task});
+
+  AWAIT_READY_FOR(containerId, Seconds(60));
+  AWAIT_READY(directory);
+  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());
+
+  // Now check that the proper output is in stderr and stdout.
+  Try<string> read = os::read(path::join(directory.get(), "stdout"));
+  ASSERT_SOME(read);
+
+  vector<string> lines = strings::split(read.get(), "\n");
+
+  // We expect the passed in command arguments to override the image's
+  // default command, thus we should see the value of 'uuid' in the
+  // output instead of the default command which is 'inky'.
+  EXPECT_TRUE(containsLine(lines, uuid));
+  EXPECT_FALSE(containsLine(lines, "inky"));
+
+  read = os::read(path::join(directory.get(), "stderr"));
+  ASSERT_SOME(read);
+
+  lines = strings::split(read.get(), "\n");
+
+  EXPECT_FALSE(containsLine(lines, "inky"));
+  EXPECT_FALSE(containsLine(lines, uuid));
+
+  driver.stop();
+  driver.join();
+
+  Shutdown();
+}
+
+
+// The slave is stopped before the first update for a task is received
+// from the executor. When it comes back up we make sure the executor
+// re-registers and the slave properly sends the update.
+TEST_F(DockerContainerizerTest, ROOT_DOCKER_SlaveRecoveryTaskContainer)
+{
+  Try<PID<Master> > master = StartMaster();
+  ASSERT_SOME(master);
+
+  slave::Flags flags = CreateSlaveFlags();
+
+  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
+  Shared<Docker> docker(mockDocker);
+
+  Fetcher fetcher;
+
+  // We put the containerizer on the heap so we can more easily
+  // control it's lifetime, i.e., when we invoke the destructor.
+  MockDockerContainerizer* dockerContainerizer1 =
+    new MockDockerContainerizer(flags, &fetcher, docker);
+
+  Try<PID<Slave> > slave1 = StartSlave(dockerContainerizer1, flags);
+  ASSERT_SOME(slave1);
+
+  // Enable checkpointing for the framework.
+  FrameworkInfo frameworkInfo = DEFAULT_FRAMEWORK_INFO;
+  frameworkInfo.set_checkpoint(true);
+
+  MockScheduler sched;
+  MesosSchedulerDriver driver(
+      &sched, frameworkInfo, master.get(), DEFAULT_CREDENTIAL);
+
+  Future<FrameworkID> frameworkId;
+  EXPECT_CALL(sched, registered(&driver, _, _))
+    .WillOnce(FutureArg<1>(&frameworkId));
+
+  Future<vector<Offer> > offers;
+  EXPECT_CALL(sched, resourceOffers(&driver, _))
+    .WillOnce(FutureArg<1>(&offers))
+    .WillRepeatedly(Return()); // Ignore subsequent offers.
+
+  driver.start();
+
+  AWAIT_READY(frameworkId);
+
+  AWAIT_READY(offers);
+  ASSERT_NE(0u, offers.get().size());
+
+  const Offer& offer = offers.get()[0];
+
+  SlaveID slaveId = offer.slave_id();
+
+  TaskInfo task;
+  task.set_name("");
+  task.mutable_task_id()->set_value("1");
+  task.mutable_slave_id()->CopyFrom(offer.slave_id());
+  task.mutable_resources()->CopyFrom(offer.resources());
+
+  CommandInfo command;
+  command.set_value("sleep 1000");
+
+  ContainerInfo containerInfo;
+  containerInfo.set_type(ContainerInfo::DOCKER);
+
+  // TODO(tnachen): Use local image to test if possible.
+  ContainerInfo::DockerInfo dockerInfo;
+  dockerInfo.set_image("busybox");
+  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
+
+  task.mutable_command()->CopyFrom(command);
+  task.mutable_container()->CopyFrom(containerInfo);
+
+  Future<ContainerID> containerId;
+  EXPECT_CALL(*dockerContainerizer1, launch(_, _, _, _, _, _, _, _))
+    .WillOnce(DoAll(FutureArg<0>(&containerId),
+                    Invoke(dockerContainerizer1,
+                           &MockDockerContainerizer::_launch)));
+
+  // Drop the first update from the executor.
+  Future<StatusUpdateMessage> statusUpdateMessage =
+    DROP_PROTOBUF(StatusUpdateMessage(), _, _);
+
+  driver.launchTasks(offers.get()[0].id(), {task});
+
+  AWAIT_READY(containerId);
+
+  // Stop the slave before the status update is received.
+  AWAIT_READY(statusUpdateMessage);
+
+  Stop(slave1.get());
+
+  delete dockerContainerizer1;
+
+  Future<Message> reregisterExecutorMessage =
+    FUTURE_MESSAGE(Eq(ReregisterExecutorMessage().GetTypeName()), _, _);
+
+  Future<TaskStatus> status;
+  EXPECT_CALL(sched, statusUpdate(_, _))
+    .WillOnce(FutureArg<1>(&status))
+    .WillRepeatedly(Return());       // Ignore subsequent updates.
+
+  MockDockerContainerizer* dockerContainerizer2 =
+    new MockDockerContainerizer(flags, &fetcher, docker);
+
+  Try<PID<Slave> > slave2 = StartSlave(dockerContainerizer2, flags);
+  ASSERT_SOME(slave2);
+
+  // Ensure the executor re-registers.
+  AWAIT_READY(reregisterExecutorMessage);
+  UPID executorPid = reregisterExecutorMessage.get().from;
+
+  ReregisterExecutorMessage reregister;
+  reregister.ParseFromString(reregisterExecutorMessage.get().body);
+
+  // Executor should inform about the unacknowledged update.
+  ASSERT_EQ(1, reregister.updates_size());
+  const StatusUpdate& update = reregister.updates(0);
+  ASSERT_EQ(task.task_id(), update.status().task_id());
+  ASSERT_EQ(TASK_RUNNING, update.status().state());
+
+  // Scheduler should receive the recovered update.
+  AWAIT_READY(status);
+  ASSERT_EQ(TASK_RUNNING, status.get().state());
+
+  ASSERT_TRUE(exists(docker, slaveId, containerId.get()));
+
+  Future<containerizer::Termination> termination =
+    dockerContainerizer2->wait(containerId.get());
+
+  driver.stop();
+  driver.join();
+
+  AWAIT_READY(termination);
+
+  Shutdown();
+
+  delete dockerContainerizer2;
+}
+
+
+// The slave is stopped before the first update for a task is received
+// from the executor. When it comes back up we make sure the executor
+// re-registers and the slave properly sends the update.
+//
+// TODO(benh): This test is currently disabled because the executor
+// inside the image mesosphere/test-executor does not properly set the
+// executor PID that is uses during registration, so when the new
+// slave recovers it can't reconnect and instead destroys that
+// container. In particular, it uses '0' for it's IP which we properly
+// parse and can even properly use for sending other messages, but the
+// current implementation of 'UPID::operator bool ()' fails if the IP
+// component of a PID is '0'.
+TEST_F(DockerContainerizerTest,
+       DISABLED_ROOT_DOCKER_SlaveRecoveryExecutorContainer)
+{
+  Try<PID<Master> > master = StartMaster();
+  ASSERT_SOME(master);
+
+  slave::Flags flags = CreateSlaveFlags();
+
+  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
+  Shared<Docker> docker(mockDocker);
+
+  Fetcher fetcher;
+
+  MockDockerContainerizer* dockerContainerizer1 =
+    new MockDockerContainerizer(flags, &fetcher, docker);
+
+  Try<PID<Slave> > slave1 = StartSlave(dockerContainerizer1, flags);
+  ASSERT_SOME(slave1);
+
+  // Enable checkpointing for the framework.
+  FrameworkInfo frameworkInfo = DEFAULT_FRAMEWORK_INFO;
+  frameworkInfo.set_checkpoint(true);
+
+  MockScheduler sched;
+  MesosSchedulerDriver driver(
+      &sched, frameworkInfo, master.get(), DEFAULT_CREDENTIAL);
+
+  Future<FrameworkID> frameworkId;
+  EXPECT_CALL(sched, registered(&driver, _, _))
+    .WillOnce(FutureArg<1>(&frameworkId));
+
+  Future<vector<Offer> > offers;
+  EXPECT_CALL(sched, resourceOffers(&driver, _))
+    .WillOnce(FutureArg<1>(&offers))
+    .WillRepeatedly(Return()); // Ignore subsequent offers.
+
+  driver.start();
+
+  AWAIT_READY(frameworkId);
+
+  AWAIT_READY(offers);
+  ASSERT_NE(0u, offers.get().size());
+
+  const Offer& offer = offers.get()[0];
+
+  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());
+
+  ExecutorInfo executorInfo;
+  ExecutorID executorId;
+  executorId.set_value("e1");
+  executorInfo.mutable_executor_id()->CopyFrom(executorId);
+
+  CommandInfo command;
+  command.set_value("test-executor");
+  executorInfo.mutable_command()->CopyFrom(command);
+
+  ContainerInfo containerInfo;
+  containerInfo.set_type(ContainerInfo::DOCKER);
+
+  // TODO(tnachen): Use local image to test if possible.
+  ContainerInfo::DockerInfo dockerInfo;
+  dockerInfo.set_image("mesosphere/test-executor");
+
+  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
+  executorInfo.mutable_container()->CopyFrom(containerInfo);
+
+  task.mutable_executor()->CopyFrom(executorInfo);
+
+  Future<ContainerID> containerId;
+  Future<SlaveID> slaveId;
+  EXPECT_CALL(*dockerContainerizer1, launch(_, _, _, _, _, _, _))
+    .WillOnce(DoAll(FutureArg<0>(&containerId),
+                    FutureArg<4>(&slaveId),
+                    Invoke(dockerContainerizer1,
+                           &MockDockerContainerizer::_launchExecutor)));
+
+  // We need to wait until the container's pid has been been
+  // checkpointed so that when the next slave recovers it won't treat
+  // the executor as having gone lost! We know this has completed
+  // after Containerizer::launch returns and the
+  // Slave::executorLaunched gets dispatched.
+  Future<Nothing> executorLaunched =
+    FUTURE_DISPATCH(_, &Slave::executorLaunched);
+
+  // The test-executor in the image immediately sends a TASK_RUNNING
+  // followed by TASK_FINISHED (no sleep/delay in between) so we need
+  // to drop the first TWO updates that come from the executor rather
+  // than only the first update like above where we can control how
+  // the length of the task.
+  Future<StatusUpdateMessage> statusUpdateMessage1 =
+    DROP_PROTOBUF(StatusUpdateMessage(), _, _);
+
+  // Drop the first update from the executor.
+  Future<StatusUpdateMessage> statusUpdateMessage2 =
+    DROP_PROTOBUF(StatusUpdateMessage(), _, _);
+
+  driver.launchTasks(offers.get()[0].id(), {task});
+
+  AWAIT_READY(containerId);
+  AWAIT_READY(slaveId);
+
+  AWAIT_READY(executorLaunched);
+  AWAIT_READY(statusUpdateMessage1);
+  AWAIT_READY(statusUpdateMessage2);
+
+  Stop(slave1.get());
+
+  delete dockerContainerizer1;
+
+  Future<Message> reregisterExecutorMessage =
+    FUTURE_MESSAGE(Eq(ReregisterExecutorMessage().GetTypeName()), _, _);
+
+  Future<TaskStatus> status;
+  EXPECT_CALL(sched, statusUpdate(_, _))
+    .WillOnce(FutureArg<1>(&status))
+    .WillRepeatedly(Return());       // Ignore subsequent updates.
+
+  MockDockerContainerizer* dockerContainerizer2 =
+    new MockDockerContainerizer(flags, &fetcher, docker);
+
+  Try<PID<Slave> > slave2 = StartSlave(dockerContainerizer2, flags);
+  ASSERT_SOME(slave2);
+
+  // Ensure the executor re-registers.
+  AWAIT_READY(reregisterExecutorMessage);
+  UPID executorPid = reregisterExecutorMessage.get().from;
+
+  ReregisterExecutorMessage reregister;
+  reregister.ParseFromString(reregisterExecutorMessage.get().body);
+
+  // Executor should inform about the unacknowledged update.
+  ASSERT_EQ(1, reregister.updates_size());
+  const StatusUpdate& update = reregister.updates(0);
+  ASSERT_EQ(task.task_id(), update.status().task_id());
+  ASSERT_EQ(TASK_RUNNING, update.status().state());
+
+  // Scheduler should receive the recovered update.
+  AWAIT_READY(status);
+  ASSERT_EQ(TASK_RUNNING, status.get().state());
+
+  ASSERT_TRUE(exists(docker, slaveId.get(), containerId.get()));
+
+  driver.stop();
+  driver.join();
+
+  delete dockerContainerizer2;
+}
+
+
+// This test verifies that port mapping with bridge network is
+// exposing the host port to the container port, by sending data
+// to the host port and receiving it in the container by listening
+// to the mapped container port.
+TEST_F(DockerContainerizerTest, ROOT_DOCKER_NC_PortMapping)
+{
+  Try<PID<Master> > master = StartMaster();
+  ASSERT_SOME(master);
+
+  slave::Flags flags = CreateSlaveFlags();
+
+  flags.resources = "cpus:1;mem:1024;ports:[10000-10000]";
+
+  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
+  Shared<Docker> docker(mockDocker);
+
+  // We skip stopping the docker container because stopping a container
+  // even when it terminated might not flush the logs and we end up
+  // not getting stdout/stderr in our tests.
+  EXPECT_CALL(*mockDocker, stop(_, _, _))
+    .WillRepeatedly(Return(Nothing()));
+
+  Fetcher fetcher;
+
+  MockDockerContainerizer dockerContainerizer(flags, &fetcher, docker);
+
+  Try<PID<Slave> > slave = StartSlave(&dockerContainerizer, flags);
+  ASSERT_SOME(slave);
+
+  MockScheduler sched;
+  MesosSchedulerDriver driver(
+      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+  Future<FrameworkID> frameworkId;
+  EXPECT_CALL(sched, registered(&driver, _, _))
+    .WillOnce(FutureArg<1>(&frameworkId));
+
+  Future<vector<Offer> > offers;
+  EXPECT_CALL(sched, resourceOffers(&driver, _))
+    .WillOnce(FutureArg<1>(&offers))
+    .WillRepeatedly(Return()); // Ignore subsequent offers.
+
+  driver.start();
+
+  AWAIT_READY(frameworkId);
+
+  AWAIT_READY(offers);
+  ASSERT_NE(0u, offers.get().size());
+
+  const Offer& offer = offers.get()[0];
+
+  SlaveID slaveId = offer.slave_id();
+
+  TaskInfo task;
+  task.set_name("");
+  task.mutable_task_id()->set_value("1");
+  task.mutable_slave_id()->CopyFrom(offer.slave_id());
+  task.mutable_resources()->CopyFrom(offer.resources());
+
+  CommandInfo command;
+  command.set_shell(false);
+  command.set_value("nc");
+  command.add_arguments("-l");
+  command.add_arguments("-p");
+  command.add_arguments("1000");
+
+  ContainerInfo containerInfo;
+  containerInfo.set_type(ContainerInfo::DOCKER);
+
+  // TODO(tnachen): Use local image to test if possible.
+  ContainerInfo::DockerInfo dockerInfo;
+  dockerInfo.set_image("busybox");
+  dockerInfo.set_network(ContainerInfo::DockerInfo::BRIDGE);
+
+  ContainerInfo::DockerInfo::PortMapping portMapping;
+  portMapping.set_host_port(10000);
+  portMapping.set_container_port(1000);
+
+  dockerInfo.add_port_mappings()->CopyFrom(portMapping);
+  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
+
+  task.mutable_command()->CopyFrom(command);
+  task.mutable_container()->CopyFrom(containerInfo);
+
+  Future<ContainerID> containerId;
+  Future<string> directory;
+  EXPECT_CALL(dockerContainerizer, launch(_, _, _, _, _, _, _, _))
+    .WillOnce(DoAll(FutureArg<0>(&containerId),
+                    FutureArg<3>(&directory),
+                    Invoke(&dockerContainerizer,
+                           &MockDockerContainerizer::_launch)));
+
+  Future<TaskStatus> statusRunning;
+  Future<TaskStatus> statusFinished;
+  EXPECT_CALL(sched, statusUpdate(&driver, _))
+    .WillOnce(FutureArg<1>(&statusRunning))
+    .WillOnce(FutureArg<1>(&statusFinished))
+    .WillRepeatedly(DoDefault());
+
+  driver.launchTasks(offers.get()[0].id(), {task});
+
+  AWAIT_READY_FOR(containerId, Seconds(60));
+  AWAIT_READY(directory);
+  AWAIT_READY_FOR(statusRunning, Seconds(60));
+  EXPECT_EQ(TASK_RUNNING, statusRunning.get().state());
+
+  ASSERT_TRUE(
+    exists(docker, slaveId, containerId.get(), ContainerState::RUNNING));
+
+  string uuid = UUID::random().toString();
+
+  // Write uuid to docker mapped host port.
+  Try<process::Subprocess> s = process::subprocess(
+      "echo " + uuid + " | nc localhost 10000");
+
+  ASSERT_SOME(s);
+  AWAIT_READY_FOR(s.get().status(), Seconds(60));
+
+  AWAIT_READY_FOR(statusFinished, Seconds(60));
+  EXPECT_EQ(TASK_FINISHED, statusFinished.get().state());
+
+  // Now check that the proper output is in stdout.
+  Try<string> read = os::read(path::join(directory.get(), "stdout"));
+  ASSERT_SOME(read);
+
+  const vector<string> lines = strings::split(read.get(), "\n");
+
+  // We expect the uuid that is sent to host port to be written
+  // to stdout by the docker container running nc -l.
+  EXPECT_TRUE(containsLine(lines, uuid));
+
+  Future<containerizer::Termination> termination =
+    dockerContainerizer.wait(containerId.get());
+
+  driver.stop();
+  driver.join();
+
+  AWAIT_READY(termination);
+
+  Shutdown();
+}
+
+
+// This test verifies that sandbox with ':' in the path can still
+// run successfully. This a limitation of the Docker CLI where
+// the volume map parameter treats colons (:) as seperators,
+// and incorrectly seperates the sandbox directory.
+TEST_F(DockerContainerizerTest, ROOT_DOCKER_LaunchSandboxWithColon)
+{
+  Try<PID<Master>> master = StartMaster();
+  ASSERT_SOME(master);
+
+  slave::Flags flags = CreateSlaveFlags();
+
+  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
+  Shared<Docker> docker(mockDocker);
+
+  Fetcher fetcher;
+
+  MockDockerContainerizer dockerContainerizer(flags, &fetcher, docker);
+
+  Try<PID<Slave> > slave = StartSlave(&dockerContainerizer, flags);
+  ASSERT_SOME(slave);
+
+  MockScheduler sched;
+  MesosSchedulerDriver driver(
+      &sched, DEFAULT_FRAMEWORK_INFO, master.get(), DEFAULT_CREDENTIAL);
+
+  Future<FrameworkID> frameworkId;
+  EXPECT_CALL(sched, registered(&driver, _, _))
+    .WillOnce(FutureArg<1>(&frameworkId));
+
+  Future<vector<Offer> > offers;
+  EXPECT_CALL(sched, resourceOffers(&driver, _))
+    .WillOnce(FutureArg<1>(&offers))
+    .WillRepeatedly(Return()); // Ignore subsequent offers.
+
+  driver.start();
+
+  AWAIT_READY(frameworkId);
+
+  AWAIT_READY(offers);
+  ASSERT_NE(0u, offers.get().size());
+
+  const Offer& offer = offers.get()[0];
+
+  SlaveID slaveId = offer.slave_id();
+
+  TaskInfo task;
+  task.set_name("");
+  task.mutable_task_id()->set_value("test:colon");
+  task.mutable_slave_id()->CopyFrom(offer.slave_id());
+  task.mutable_resources()->CopyFrom(offer.resources());
+
+  CommandInfo command;
+  command.set_value("sleep 1000");
+
+  ContainerInfo containerInfo;
+  containerInfo.set_type(ContainerInfo::DOCKER);
+
+  // TODO(tnachen): Use local image to test if possible.
+  ContainerInfo::DockerInfo dockerInfo;
+  dockerInfo.set_image("busybox");
+  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
+
+  task.mutable_command()->CopyFrom(command);
+  task.mutable_container()->CopyFrom(containerInfo);
+
+  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))
+    .WillRepeatedly(DoDefault());
+
+  driver.launchTasks(offers.get()[0].id(), {task});
+
+  AWAIT_READY_FOR(containerId, Seconds(60));
+  AWAIT_READY_FOR(statusRunning, Seconds(60));
+  EXPECT_EQ(TASK_RUNNING, statusRunning.get().state());
+
+  ASSERT_TRUE(exists(docker, slaveId, containerId.get()));
+
+  Future<containerizer::Termination> termination =
+    dockerContainerizer.wait(containerId.get());
+
+  driver.stop();
+  driver.join();
+
+  AWAIT_READY(termination);
+
+  Shutdown();
+}
+
+
+TEST_F(DockerContainerizerTest, ROOT_DOCKER_DestroyWhileFetching)
+{
+  Try<PID<Master> > master = StartMaster();
+  ASSERT_SOME(master);
+
+  slave::Flags flags = CreateSlaveFlags();
+
+  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
+  Shared<Docker> docker(mockDocker);
+
+  Fetcher fetcher;
+
+  // The docker containerizer will free the process, so we must
+  // allocate on the heap.
+  MockDockerContainerizerProcess* process =
+    new MockDockerContainerizerProcess(flags, &fetcher, docker);
+
+  MockDockerContainerizer dockerContainerizer(
+      (Owned<DockerContainerizerProcess>(process)));
+
+  Promise<Nothing> promise;
+  Future<Nothing> fetch;
+
+  // We want to pause the fetch call to simulate a long fetch time.
+  EXPECT_CALL(*process, fetch(_, _))
+    .WillOnce(DoAll(FutureSatisfy(&fetch),
+                    Return(promise.future())));
+
+  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);
+  ASSERT_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;
+  command.set_value("sleep 1000");
+
+  ContainerInfo containerInfo;
+  containerInfo.set_type(ContainerInfo::DOCKER);
+
+  // TODO(tnachen): Use local image to test if possible.
+  ContainerInfo::DockerInfo dockerInfo;
+  dockerInfo.set_image("busybox");
+  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
+
+  task.mutable_command()->CopyFrom(command);
+  task.mutable_container()->CopyFrom(containerInfo);
+
+  Future<TaskStatus> statusFailed;
+  EXPECT_CALL(sched, statusUpdate(&driver, _))
+    .WillOnce(FutureArg<1>(&statusFailed));
+
+  Future<ContainerID> containerId;
+  EXPECT_CALL(dockerContainerizer, launch(_, _, _, _, _, _, _, _))
+    .WillOnce(DoAll(FutureArg<0>(&containerId),
+                    Invoke(&dockerContainerizer,
+                           &MockDockerContainerizer::_launch)));
+
+  driver.launchTasks(offers.get()[0].id(), {task});
+
+  AWAIT_READY_FOR(containerId, Seconds(60));
+
+  AWAIT_READY(fetch);
+
+  dockerContainerizer.destroy(containerId.get());
+
+  // Resume docker launch.
+  promise.set(Nothing());
+
+  AWAIT_READY(statusFailed);
+
+  EXPECT_EQ(TASK_FAILED, statusFailed.get().state());
+
+  driver.stop();
+  driver.join();
+
+  Shutdown();
+}
+
+
+TEST_F(DockerContainerizerTest, ROOT_DOCKER_DestroyWhilePulling)
+{
+  Try<PID<Master> > master = StartMaster();
+  ASSERT_SOME(master);
+
+  slave::Flags flags = CreateSlaveFlags();
+
+  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
+  Shared<Docker> docker(mockDocker);
+
+  Fetcher fetcher;
+
+  // The docker containerizer will free the process, so we must
+  // allocate on the heap.
+  MockDockerContainerizerProcess* process =
+    new MockDockerContainerizerProcess(flags, &fetcher, docker);
+
+  MockDockerContainerizer dockerContainerizer(
+      (Owned<DockerContainerizerProcess>(process)));
+
+  Future<Nothing> fetch;
+  EXPECT_CALL(*process, fetch(_, _))
+    .WillOnce(DoAll(FutureSatisfy(&fetch),
+                    Return(Nothing())));
+
+  Promise<Nothing> promise;
+
+  // We want to pause the fetch call to simulate a long fetch time.
+  EXPECT_CALL(*process, pull(_))
+    .WillOnce(Return(promise.future()));
+
+  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);
+  ASSERT_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;
+  command.set_value("sleep 1000");
+
+  ContainerInfo containerInfo;
+  containerInfo.set_type(ContainerInfo::DOCKER);
+
+  // TODO(tnachen): Use local image to test if possible.
+  ContainerInfo::DockerInfo dockerInfo;
+  dockerInfo.set_image("busybox");
+  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
+
+  task.mutable_command()->CopyFrom(command);
+  task.mutable_container()->CopyFrom(containerInfo);
+
+  Future<TaskStatus> statusFailed;
+  EXPECT_CALL(sched, statusUpdate(&driver, _))
+    .WillOnce(FutureArg<1>(&statusFailed));
+
+  Future<ContainerID> containerId;
+  EXPECT_CALL(dockerContainerizer, launch(_, _, _, _, _, _, _, _))
+    .WillOnce(DoAll(FutureArg<0>(&containerId),
+                    Invoke(&dockerContainerizer,
+                           &MockDockerContainerizer::_launch)));
+
+  driver.launchTasks(offers.get()[0].id(), {task});
+
+  AWAIT_READY_FOR(containerId, Seconds(60));
+
+  // Wait until fetch is finished.
+  AWAIT_READY(fetch);
+
+  dockerContainerizer.destroy(containerId.get());
+
+  // Resume docker launch.
+  promise.set(Nothing());
+
+  AWAIT_READY(statusFailed);
+
+  EXPECT_EQ(TASK_FAILED, statusFailed.get().state());
+
+  driver.stop();
+  driver.join();
+
+  Shutdown();
+}
+
+
+// This test checks that when a docker containerizer update failed
+// and the container failed before the executor started, the executor
+// is properly killed and cleaned up.
+TEST_F(DockerContainerizerTest, ROOT_DOCKER_ExecutorCleanupWhenLaunchFailed)
+{
+  Try<PID<Master>> master = StartMaster();
+  ASSERT_SOME(master);
+
+  slave::Flags flags = CreateSlaveFlags();
+
+  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
+  Shared<Docker> docker(mockDocker);
+
+  Fetcher fetcher;
+
+  // The docker containerizer will free the process, so we must
+  // allocate on the heap.
+  MockDockerContainerizerProcess* process =
+    new MockDockerContainerizerProcess(flags, &fetcher, docker);
+
+  MockDockerContainerizer dockerContainerizer(
+      (Owned<DockerContainerizerProcess>(process)));
+
+  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);
+  ASSERT_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;
+  command.set_value("ls");
+
+  ContainerInfo containerInfo;
+  containerInfo.set_type(ContainerInfo::DOCKER);
+
+  ContainerInfo::DockerInfo dockerInfo;
+  dockerInfo.set_image("busybox");
+  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
+
+  task.mutable_command()->CopyFrom(command);
+  task.mutable_container()->CopyFrom(containerInfo);
+
+  Future<TaskStatus> statusFailed;
+  EXPECT_CALL(sched, statusUpdate(&driver, _))
+    .WillOnce(FutureArg<1>(&statusFailed));
+
+  Future<ContainerID> containerId;
+  EXPECT_CALL(dockerContainerizer, launch(_, _, _, _, _, _, _, _))
+    .WillOnce(DoAll(FutureArg<0>(&containerId),
+                    Invoke(&dockerContainerizer,
+                           &MockDockerContainerizer::_launch)));
+
+  // Fail the update so we don't proceed to send run task to the executor.
+  EXPECT_CALL(dockerContainerizer, update(_, _))
+    .WillRepeatedly(Return(Failure("Fail resource update")));
+
+  driver.launchTasks(offers.get()[0].id(), {task});
+
+  AWAIT_READY_FOR(containerId, Seconds(60));
+
+  AWAIT_READY(statusFailed);
+
+  EXPECT_EQ(TASK_FAILED, statusFailed.get().state());
+
+  driver.stop();
+  driver.join();
+
+  // We expect the executor to have exited, and if not in Shutdown
+  // the test will fail because of the executor process still running.
+  Shutdown();
+}
+
+
+// When the fetch fails we should send the scheduler a status
+// update with message the shows the actual error.
+TEST_F(DockerContainerizerTest, ROOT_DOCKER_FetchFailure)
+{
+  Try<PID<Master>> master = StartMaster();
+  ASSERT_SOME(master);
+
+  slave::Flags flags = CreateSlaveFlags();
+
+  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
+  Shared<Docker> docker(mockDocker);
+
+  Fetcher fetcher;
+
+  // The docker containerizer will free the process, so we must
+  // allocate on the heap.
+  MockDockerContainerizerProcess* process =
+    new MockDockerContainerizerProcess(flags, &fetcher, docker);
+
+  MockDockerContainerizer dockerContainerizer(
+      (Owned<DockerContainerizerProcess>(process)));
+
+  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);
+  ASSERT_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;
+  command.set_value("ls");
+
+  ContainerInfo containerInfo;
+  containerInfo.set_type(ContainerInfo::DOCKER);
+
+  ContainerInfo::DockerInfo dockerInfo;
+  dockerInfo.set_image("busybox");
+  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
+
+  task.mutable_command()->CopyFrom(command);
+  task.mutable_container()->CopyFrom(containerInfo);
+
+  Future<TaskStatus> statusFailed;
+  EXPECT_CALL(sched, statusUpdate(&driver, _))
+    .WillOnce(FutureArg<1>(&statusFailed));
+
+  Future<ContainerID> containerId;
+  EXPECT_CALL(dockerContainerizer, launch(_, _, _, _, _, _, _, _))
+    .WillOnce(DoAll(FutureArg<0>(&containerId),
+                    Invoke(&dockerContainerizer,
+                           &MockDockerContainerizer::_launch)));
+
+  EXPECT_CALL(*process, fetch(_, _))
+    .WillOnce(Return(Failure("some error from fetch")));
+
+  driver.launchTasks(offers.get()[0].id(), {task});
+
+  AWAIT_READY_FOR(containerId, Seconds(60));
+
+  AWAIT_READY(statusFailed);
+
+  EXPECT_EQ(TASK_FAILED, statusFailed.get().state());
+  EXPECT_EQ("Failed to launch container: some error from fetch",
+             statusFailed.get().message());
+
+  // TODO(jaybuff): When MESOS-2035 is addressed we should validate
+  // that statusFailed.get().reason() is correctly set here.
+
+  driver.stop();
+  driver.join();
+
+  // We expect the executor to have exited, and if not in Shutdown
+  // the test will fail because of the executor process still running.
+  Shutdown();
+}
+
+
+// When the docker pull fails we should send the scheduler a status
+// update with message the shows the actual error.
+TEST_F(DockerContainerizerTest, ROOT_DOCKER_DockerPullFailure)
+{
+  Try<PID<Master>> master = StartMaster();
+  ASSERT_SOME(master);
+
+  slave::Flags flags = CreateSlaveFlags();
+
+  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
+  Shared<Docker> docker(mockDocker);
+
+  Fetcher fetcher;
+
+  // The docker containerizer will free the process, so we must
+  // allocate on the heap.
+  MockDockerContainerizerProcess* process =
+    new MockDockerContainerizerProcess(flags, &fetcher, docker);
+
+  MockDockerContainerizer dockerContainerizer(
+      (Owned<DockerContainerizerProcess>(process)));
+
+  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);
+  ASSERT_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;
+  command.set_value("ls");
+
+  ContainerInfo containerInfo;
+  containerInfo.set_type(ContainerInfo::DOCKER);
+
+  ContainerInfo::DockerInfo dockerInfo;
+  dockerInfo.set_image("busybox");
+  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
+
+  task.mutable_command()->CopyFrom(command);
+  task.mutable_container()->CopyFrom(containerInfo);
+
+  Future<TaskStatus> statusFailed;
+  EXPECT_CALL(sched, statusUpdate(&driver, _))
+    .WillOnce(FutureArg<1>(&statusFailed));
+
+  Future<ContainerID> containerId;
+  EXPECT_CALL(dockerContainerizer, launch(_, _, _, _, _, _, _, _))
+    .WillOnce(DoAll(FutureArg<0>(&containerId),
+                    Invoke(&dockerContainerizer,
+                           &MockDockerContainerizer::_launch)));
+
+  EXPECT_CALL(*mockDocker, pull(_, _, _))
+    .WillOnce(Return(Failure("some error from docker pull")));
+
+  driver.launchTasks(offers.get()[0].id(), {task});
+
+  AWAIT_READY_FOR(containerId, Seconds(60));
+
+  AWAIT_READY(statusFailed);
+
+  EXPECT_EQ(TASK_FAILED, statusFailed.get().state());
+  EXPECT_EQ("Failed to launch container: some error from docker pull",
+             statusFailed.get().message());
+
+  // TODO(jaybuff): When MESOS-2035 is addressed we should validate
+  // that statusFailed.get().reason() is correctly set here.
+
+  driver.stop();
+  driver.join();
+
+  // We expect the executor to have exited, and if not in Shutdown
+  // the test will fail because of the executor process still running.
+  Shutdown();
+}
+
+
+// When the docker executor container fails to launch, docker inspect
+// future that is in a retry loop should be discarded.
+TEST_F(DockerContainerizerTest, ROOT_DOCKER_DockerInspectDiscard)
+{
+  Try<PID<Master>> master = StartMaster();
+  ASSERT_SOME(master);
+
+  slave::Flags flags = CreateSlaveFlags();
+
+  MockDocker* mockDocker = new MockDocker(tests::flags.docker);
+  Shared<Docker> docker(mockDocker);
+
+  Future<Docker::Container> inspect;
+  EXPECT_CALL(*mockDocker, inspect(_, _))
+    .WillOnce(FutureResult(&inspect,
+                           Invoke((MockDocker*) docker.get(),
+                                  &MockDocker::_inspect)));
+
+  EXPECT_CALL(*mockDocker, run(_, _, _, _, _, _, _, _, _))
+    .WillOnce(Return(Failure("Run failed")));
+
+  Fetcher fetcher;
+
+  // The docker containerizer will free the process, so we must
+  // allocate on the heap.
+  MockDockerContainerizerProcess* process =
+    new MockDockerContainerizerProcess(flags, &fetcher, docker);
+
+  MockDockerContainerizer dockerContainerizer(
+      (Owned<DockerContainerizerProcess>(process)));
+
+  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);
+  ASSERT_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());
+
+  ExecutorInfo executorInfo;
+  ExecutorID executorId;
+  executorId.set_value("e1");
+  executorInfo.mutable_executor_id()->CopyFrom(executorId);
+
+  CommandInfo command;
+  command.set_value("/bin/test-executor");
+  executorInfo.mutable_command()->CopyFrom(command);
+
+  ContainerInfo containerInfo;
+  containerInfo.set_type(ContainerInfo::DOCKER);
+
+  // TODO(tnachen): Use local image to test if possible.
+  ContainerInfo::DockerInfo dockerInfo;
+  dockerInfo.set_image("tnachen/test-executor");
+
+  containerInfo.mutable_docker()->CopyFrom(dockerInfo);
+  executorInfo.mutable_container()->CopyFrom(containerInfo);
+
+  task.mutable_executor()->CopyFrom(executorInfo);
+
+  Future<TaskStatus> statusFailed;
+  EXPECT_CALL(sched, statusUpdate(&driver, _))
+    .WillOnce(FutureArg<1>(&statusFaile

<TRUNCATED>

[12/12] mesos git commit: Moved containerizer related tests under src/tests/containerizer.

Posted by ji...@apache.org.
Moved containerizer related tests under src/tests/containerizer.

Review: https://reviews.apache.org/r/36801


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

Branch: refs/heads/master
Commit: 963513722ecf121cc9ec8c1d4b63922398fc0658
Parents: 50696fa
Author: Jie Yu <yu...@gmail.com>
Authored: Fri Jul 24 15:42:43 2015 -0700
Committer: Jie Yu <yu...@gmail.com>
Committed: Fri Jul 24 16:37:23 2015 -0700

----------------------------------------------------------------------
 src/Makefile.am                                 |  174 +-
 src/tests/cgroups_isolator_tests.cpp            |   46 -
 src/tests/cgroups_tests.cpp                     | 1234 --------
 src/tests/composing_containerizer_tests.cpp     |  171 -
 .../containerizer/cgroups_isolator_tests.cpp    |   46 +
 src/tests/containerizer/cgroups_tests.cpp       | 1235 ++++++++
 .../composing_containerizer_tests.cpp           |  171 +
 src/tests/containerizer/containerizer_tests.cpp |  732 +++++
 .../docker_containerizer_tests.cpp              | 2955 ++++++++++++++++++
 src/tests/containerizer/docker_tests.cpp        |  421 +++
 .../external_containerizer_test.cpp             |  267 ++
 src/tests/containerizer/fs_tests.cpp            |  170 +
 src/tests/containerizer/isolator.hpp            |  101 +
 src/tests/containerizer/isolator_tests.cpp      | 1317 ++++++++
 src/tests/containerizer/launch_tests.cpp        |  238 ++
 src/tests/containerizer/launcher.hpp            |  119 +
 .../containerizer/memory_pressure_tests.cpp     |  293 ++
 src/tests/containerizer/memory_test_helper.cpp  |  321 ++
 src/tests/containerizer/memory_test_helper.hpp  |   89 +
 .../containerizer/memory_test_helper_main.cpp   |   32 +
 src/tests/containerizer/ns_tests.cpp            |  302 ++
 src/tests/containerizer/perf_tests.cpp          |  183 ++
 src/tests/containerizer/port_mapping_tests.cpp  | 2296 ++++++++++++++
 src/tests/containerizer/routing_tests.cpp       | 1416 +++++++++
 src/tests/containerizer/sched_tests.cpp         |   96 +
 src/tests/containerizer/setns_test_helper.cpp   |   63 +
 src/tests/containerizer/setns_test_helper.hpp   |   35 +
 .../containerizer/setns_test_helper_main.cpp    |   30 +
 src/tests/containerizer_tests.cpp               |  731 -----
 src/tests/docker_containerizer_tests.cpp        | 2955 ------------------
 src/tests/docker_tests.cpp                      |  421 ---
 src/tests/external_containerizer_test.cpp       |  266 --
 src/tests/fs_tests.cpp                          |  170 -
 src/tests/isolator.hpp                          |  101 -
 src/tests/isolator_tests.cpp                    | 1316 --------
 src/tests/launch_tests.cpp                      |  238 --
 src/tests/launcher.hpp                          |  119 -
 src/tests/memory_pressure_tests.cpp             |  293 --
 src/tests/memory_test_helper.cpp                |  320 --
 src/tests/memory_test_helper.hpp                |   89 -
 src/tests/memory_test_helper_main.cpp           |   32 -
 src/tests/ns_tests.cpp                          |  301 --
 src/tests/perf_tests.cpp                        |  183 --
 src/tests/port_mapping_tests.cpp                | 2296 --------------
 src/tests/routing_tests.cpp                     | 1416 ---------
 src/tests/sched_tests.cpp                       |   96 -
 src/tests/setns_test_helper.cpp                 |   63 -
 src/tests/setns_test_helper.hpp                 |   35 -
 src/tests/setns_test_helper_main.cpp            |   30 -
 49 files changed, 13015 insertions(+), 13009 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index 93a6a7a..a7104bb 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -658,17 +658,17 @@ libmesos_no_3rdparty_la_SOURCES +=					\
 	tests/containerizer.hpp						\
 	tests/environment.hpp						\
 	tests/flags.hpp							\
-	tests/isolator.hpp						\
-	tests/launcher.hpp						\
 	tests/limiter.hpp						\
-	tests/memory_test_helper.hpp					\
 	tests/mesos.hpp							\
 	tests/module.hpp						\
 	tests/script.hpp						\
-	tests/setns_test_helper.hpp					\
 	tests/utils.hpp							\
 	tests/zookeeper.hpp						\
 	tests/zookeeper_test_server.hpp					\
+	tests/containerizer/isolator.hpp				\
+	tests/containerizer/launcher.hpp				\
+	tests/containerizer/memory_test_helper.hpp			\
+	tests/containerizer/setns_test_helper.hpp			\
 	usage/usage.hpp							\
 	watcher/whitelist_watcher.hpp					\
 	zookeeper/authentication.hpp					\
@@ -1383,8 +1383,8 @@ persistent_volume_framework_LDADD = libmesos.la $(LDADD)
 if OS_LINUX
   check_PROGRAMS += setns-test-helper
   setns_test_helper_SOURCES =					\
-    tests/setns_test_helper_main.cpp				\
-    tests/setns_test_helper.cpp
+    tests/containerizer/setns_test_helper_main.cpp		\
+    tests/containerizer/setns_test_helper.cpp
   setns_test_helper_CPPFLAGS = $(MESOS_CPPFLAGS)
   setns_test_helper_LDADD = libmesos.la $(LDADD)
 
@@ -1393,8 +1393,8 @@ endif
 check_PROGRAMS += memory-test-helper
 memory_test_helper_SOURCES =					\
   tests/flags.cpp						\
-  tests/memory_test_helper_main.cpp				\
-  tests/memory_test_helper.cpp
+  tests/containerizer/memory_test_helper_main.cpp		\
+  tests/containerizer/memory_test_helper.cpp
 memory_test_helper_CPPFLAGS = $(mesos_tests_CPPFLAGS)
 memory_test_helper_LDADD = libmesos.la $(LDADD)
 
@@ -1474,74 +1474,74 @@ libtestqos_controller_la_SOURCES =		\
 libtestqos_controller_la_CPPFLAGS = $(MESOS_CPPFLAGS)
 libtestqos_controller_la_LDFLAGS = $(MESOS_TEST_MODULE_LDFLAGS)
 
-mesos_tests_SOURCES =				\
-  tests/anonymous_tests.cpp			\
-  tests/attributes_tests.cpp			\
-  tests/authentication_tests.cpp		\
-  tests/authorization_tests.cpp		        \
-  tests/common/http_tests.cpp			\
-  tests/composing_containerizer_tests.cpp       \
-  tests/containerizer.cpp			\
-  tests/containerizer_tests.cpp			\
-  tests/cram_md5_authentication_tests.cpp	\
-  tests/credentials_tests.cpp			\
-  tests/disk_quota_tests.cpp			\
-  tests/docker_containerizer_tests.cpp          \
-  tests/docker_tests.cpp			\
-  tests/environment.cpp				\
-  tests/examples_tests.cpp			\
-  tests/exception_tests.cpp			\
-  tests/external_containerizer_test.cpp		\
-  tests/health_check_tests.cpp                  \
-  tests/fault_tolerance_tests.cpp		\
-  tests/fetcher_cache_tests.cpp			\
-  tests/fetcher_tests.cpp                       \
-  tests/files_tests.cpp				\
-  tests/flags.cpp				\
-  tests/gc_tests.cpp				\
-  tests/hierarchical_allocator_tests.cpp	\
-  tests/hook_tests.cpp				\
-  tests/http_api_tests.cpp			\
-  tests/isolator_tests.cpp			\
-  tests/log_tests.cpp				\
-  tests/logging_tests.cpp			\
-  tests/main.cpp				\
-  tests/master_allocator_tests.cpp		\
-  tests/master_authorization_tests.cpp		\
-  tests/master_contender_detector_tests.cpp	\
-  tests/master_slave_reconciliation_tests.cpp	\
-  tests/master_tests.cpp			\
-  tests/master_validation_tests.cpp		\
-  tests/memory_test_helper.cpp			\
-  tests/mesos.cpp				\
-  tests/metrics_tests.cpp			\
-  tests/module.cpp				\
-  tests/module_tests.cpp			\
-  tests/monitor_tests.cpp			\
-  tests/oversubscription_tests.cpp		\
-  tests/partition_tests.cpp			\
-  tests/paths_tests.cpp				\
-  tests/persistent_volume_tests.cpp		\
-  tests/protobuf_io_tests.cpp			\
-  tests/rate_limiting_tests.cpp			\
-  tests/reconciliation_tests.cpp		\
-  tests/registrar_tests.cpp			\
-  tests/repair_tests.cpp			\
-  tests/reservation_tests.cpp			\
-  tests/resource_offers_tests.cpp		\
-  tests/resources_tests.cpp			\
-  tests/scheduler_tests.cpp			\
-  tests/scheduler_event_call_tests.cpp		\
-  tests/script.cpp				\
-  tests/slave_recovery_tests.cpp		\
-  tests/slave_tests.cpp				\
-  tests/sorter_tests.cpp			\
-  tests/state_tests.cpp				\
-  tests/status_update_manager_tests.cpp		\
-  tests/teardown_tests.cpp			\
-  tests/utils.cpp				\
-  tests/values_tests.cpp			\
-  tests/zookeeper_url_tests.cpp
+mesos_tests_SOURCES =						\
+  tests/anonymous_tests.cpp					\
+  tests/attributes_tests.cpp					\
+  tests/authentication_tests.cpp				\
+  tests/authorization_tests.cpp					\
+  tests/containerizer.cpp					\
+  tests/cram_md5_authentication_tests.cpp			\
+  tests/credentials_tests.cpp					\
+  tests/disk_quota_tests.cpp					\
+  tests/environment.cpp						\
+  tests/examples_tests.cpp					\
+  tests/exception_tests.cpp					\
+  tests/health_check_tests.cpp					\
+  tests/fault_tolerance_tests.cpp				\
+  tests/fetcher_cache_tests.cpp					\
+  tests/fetcher_tests.cpp					\
+  tests/files_tests.cpp						\
+  tests/flags.cpp						\
+  tests/gc_tests.cpp						\
+  tests/hierarchical_allocator_tests.cpp			\
+  tests/hook_tests.cpp						\
+  tests/http_api_tests.cpp					\
+  tests/log_tests.cpp						\
+  tests/logging_tests.cpp					\
+  tests/main.cpp						\
+  tests/master_allocator_tests.cpp				\
+  tests/master_authorization_tests.cpp				\
+  tests/master_contender_detector_tests.cpp			\
+  tests/master_slave_reconciliation_tests.cpp			\
+  tests/master_tests.cpp					\
+  tests/master_validation_tests.cpp				\
+  tests/mesos.cpp						\
+  tests/metrics_tests.cpp					\
+  tests/module.cpp						\
+  tests/module_tests.cpp					\
+  tests/monitor_tests.cpp					\
+  tests/oversubscription_tests.cpp				\
+  tests/partition_tests.cpp					\
+  tests/paths_tests.cpp						\
+  tests/persistent_volume_tests.cpp				\
+  tests/protobuf_io_tests.cpp					\
+  tests/rate_limiting_tests.cpp					\
+  tests/reconciliation_tests.cpp				\
+  tests/registrar_tests.cpp					\
+  tests/repair_tests.cpp					\
+  tests/reservation_tests.cpp					\
+  tests/resource_offers_tests.cpp				\
+  tests/resources_tests.cpp					\
+  tests/scheduler_tests.cpp					\
+  tests/scheduler_event_call_tests.cpp				\
+  tests/script.cpp						\
+  tests/slave_recovery_tests.cpp				\
+  tests/slave_tests.cpp						\
+  tests/sorter_tests.cpp					\
+  tests/state_tests.cpp						\
+  tests/status_update_manager_tests.cpp				\
+  tests/teardown_tests.cpp					\
+  tests/utils.cpp						\
+  tests/values_tests.cpp					\
+  tests/zookeeper_url_tests.cpp					\
+  tests/common/http_tests.cpp					\
+  tests/containerizer/composing_containerizer_tests.cpp		\
+  tests/containerizer/containerizer_tests.cpp			\
+  tests/containerizer/docker_containerizer_tests.cpp		\
+  tests/containerizer/docker_tests.cpp				\
+  tests/containerizer/external_containerizer_test.cpp		\
+  tests/containerizer/isolator_tests.cpp			\
+  tests/containerizer/memory_test_helper.cpp
 
 mesos_tests_CPPFLAGS = $(MESOS_CPPFLAGS)
 mesos_tests_CPPFLAGS += -DSOURCE_DIR=\"$(abs_top_srcdir)\"
@@ -1554,20 +1554,20 @@ mesos_tests_LDADD = ../$(LIBPROCESS)/3rdparty/libgmock.la libmesos.la -ldl $(LDA
 mesos_tests_DEPENDENCIES = # Initialized to allow += below.
 
 if OS_LINUX
-  mesos_tests_SOURCES += tests/cgroups_isolator_tests.cpp
-  mesos_tests_SOURCES += tests/cgroups_tests.cpp
-  mesos_tests_SOURCES += tests/fs_tests.cpp
-  mesos_tests_SOURCES += tests/launch_tests.cpp
-  mesos_tests_SOURCES += tests/memory_pressure_tests.cpp
-  mesos_tests_SOURCES += tests/ns_tests.cpp
-  mesos_tests_SOURCES += tests/perf_tests.cpp
-  mesos_tests_SOURCES += tests/sched_tests.cpp
-  mesos_tests_SOURCES += tests/setns_test_helper.cpp
+  mesos_tests_SOURCES += tests/containerizer/cgroups_isolator_tests.cpp
+  mesos_tests_SOURCES += tests/containerizer/cgroups_tests.cpp
+  mesos_tests_SOURCES += tests/containerizer/fs_tests.cpp
+  mesos_tests_SOURCES += tests/containerizer/launch_tests.cpp
+  mesos_tests_SOURCES += tests/containerizer/memory_pressure_tests.cpp
+  mesos_tests_SOURCES += tests/containerizer/ns_tests.cpp
+  mesos_tests_SOURCES += tests/containerizer/perf_tests.cpp
+  mesos_tests_SOURCES += tests/containerizer/sched_tests.cpp
+  mesos_tests_SOURCES += tests/containerizer/setns_test_helper.cpp
 endif
 
 if WITH_NETWORK_ISOLATOR
-  mesos_tests_SOURCES += tests/routing_tests.cpp
-  mesos_tests_SOURCES += tests/port_mapping_tests.cpp
+  mesos_tests_SOURCES += tests/containerizer/routing_tests.cpp
+  mesos_tests_SOURCES += tests/containerizer/port_mapping_tests.cpp
 endif
 
 if HAS_JAVA

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/cgroups_isolator_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/cgroups_isolator_tests.cpp b/src/tests/cgroups_isolator_tests.cpp
deleted file mode 100644
index a4ccc8e..0000000
--- a/src/tests/cgroups_isolator_tests.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/**
- * 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 <utility>
-
-#include <gtest/gtest.h>
-
-#include <stout/foreach.hpp>
-#include <stout/proc.hpp>
-#include <stout/stringify.hpp>
-
-#include "slave/containerizer/mesos/containerizer.hpp"
-
-#include "tests/script.hpp"
-
-using std::map;
-
-namespace mesos {
-namespace internal {
-namespace tests {
-
-
-// Run the balloon framework under a mesos containerizer.
-TEST_SCRIPT(ContainerizerTest,
-            ROOT_CGROUPS_BalloonFramework,
-            "balloon_framework_test.sh")
-
-} // namespace tests {
-} // namespace internal {
-} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/cgroups_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/cgroups_tests.cpp b/src/tests/cgroups_tests.cpp
deleted file mode 100644
index b63d956..0000000
--- a/src/tests/cgroups_tests.cpp
+++ /dev/null
@@ -1,1234 +0,0 @@
-/**
- * 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 <assert.h>
-#include <errno.h>
-#include <signal.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <set>
-#include <string>
-#include <vector>
-
-#include <sys/mman.h>
-#include <sys/ptrace.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-
-#include <gmock/gmock.h>
-
-#include <process/gtest.hpp>
-#include <process/owned.hpp>
-
-#include <stout/gtest.hpp>
-#include <stout/hashmap.hpp>
-#include <stout/numify.hpp>
-#include <stout/option.hpp>
-#include <stout/os.hpp>
-#include <stout/path.hpp>
-#include <stout/proc.hpp>
-#include <stout/stringify.hpp>
-#include <stout/strings.hpp>
-
-#include "linux/cgroups.hpp"
-#include "linux/perf.hpp"
-
-#include "tests/memory_test_helper.hpp"
-#include "tests/mesos.hpp" // For TEST_CGROUPS_(HIERARCHY|ROOT).
-#include "tests/utils.hpp"
-
-using namespace process;
-
-using cgroups::memory::pressure::Level;
-using cgroups::memory::pressure::Counter;
-
-using std::set;
-
-namespace mesos {
-namespace internal {
-namespace tests {
-
-
-class CgroupsTest : public TemporaryDirectoryTest
-{
-public:
-  static void SetUpTestCase()
-  {
-    // Clean up the testing hierarchy, in case it wasn't cleaned up
-    // properly from previous tests.
-    AWAIT_READY(cgroups::cleanup(TEST_CGROUPS_HIERARCHY));
-  }
-
-  static void TearDownTestCase()
-  {
-    AWAIT_READY(cgroups::cleanup(TEST_CGROUPS_HIERARCHY));
-  }
-};
-
-
-// A fixture which is used to name tests that expect NO hierarchy to
-// exist in order to test the ability to create a hierarchy (since
-// most likely existing hierarchies will have all or most subsystems
-// attached rendering our ability to create a hierarchy fruitless).
-class CgroupsNoHierarchyTest : public CgroupsTest
-{
-public:
-  static void SetUpTestCase()
-  {
-    CgroupsTest::SetUpTestCase();
-
-    Try<std::set<std::string> > hierarchies = cgroups::hierarchies();
-    ASSERT_SOME(hierarchies);
-    ASSERT_TRUE(hierarchies.get().empty())
-      << "-------------------------------------------------------------\n"
-      << "We cannot run any cgroups tests that require mounting\n"
-      << "hierarchies because you have the following hierarchies mounted:\n"
-      << strings::trim(stringify(hierarchies.get()), " {},") << "\n"
-      << "You can either unmount those hierarchies, or disable\n"
-      << "this test case (i.e., --gtest_filter=-CgroupsNoHierarchyTest.*).\n"
-      << "-------------------------------------------------------------";
-  }
-};
-
-
-// A fixture that assumes ANY hierarchy is acceptable for use provided
-// it has the subsystems attached that were specified in the
-// constructor. If no hierarchy could be found that has all the
-// required subsystems then we attempt to create a new hierarchy.
-class CgroupsAnyHierarchyTest : public CgroupsTest
-{
-public:
-  CgroupsAnyHierarchyTest(const std::string& _subsystems = "cpu")
-    : subsystems(_subsystems) {}
-
-protected:
-  virtual void SetUp()
-  {
-    CgroupsTest::SetUp();
-
-    foreach (const std::string& subsystem, strings::tokenize(subsystems, ",")) {
-      // Establish the base hierarchy if this is the first subsystem checked.
-      if (baseHierarchy.empty()) {
-        Result<std::string> hierarchy = cgroups::hierarchy(subsystem);
-        ASSERT_FALSE(hierarchy.isError());
-
-        if (hierarchy.isNone()) {
-          baseHierarchy = TEST_CGROUPS_HIERARCHY;
-        } else {
-          // Strip the subsystem to get the base hierarchy.
-          Try<std::string> baseDirname = Path(hierarchy.get()).dirname();
-          ASSERT_SOME(baseDirname);
-          baseHierarchy = baseDirname.get();
-        }
-      }
-
-      // Mount the subsystem if necessary.
-      std::string hierarchy = path::join(baseHierarchy, subsystem);
-      Try<bool> mounted = cgroups::mounted(hierarchy, subsystem);
-      ASSERT_SOME(mounted);
-      if (!mounted.get()) {
-        ASSERT_SOME(cgroups::mount(hierarchy, subsystem))
-          << "-------------------------------------------------------------\n"
-          << "We cannot run any cgroups tests that require\n"
-          << "a hierarchy with subsystem '" << subsystem << "'\n"
-          << "because we failed to find an existing hierarchy\n"
-          << "or create a new one (tried '" << hierarchy << "').\n"
-          << "You can either remove all existing\n"
-          << "hierarchies, or disable this test case\n"
-          << "(i.e., --gtest_filter=-"
-          << ::testing::UnitTest::GetInstance()
-              ->current_test_info()
-              ->test_case_name() << ".*).\n"
-          << "-------------------------------------------------------------";
-      }
-
-      Try<std::vector<std::string> > cgroups = cgroups::get(hierarchy);
-      CHECK_SOME(cgroups);
-
-      foreach (const std::string& cgroup, cgroups.get()) {
-        // Remove any cgroups that start with TEST_CGROUPS_ROOT.
-        if (cgroup == TEST_CGROUPS_ROOT) {
-          AWAIT_READY(cgroups::destroy(hierarchy, cgroup));
-        }
-      }
-    }
-  }
-
-  virtual void TearDown()
-  {
-    // Remove all *our* cgroups.
-    foreach (const std::string& subsystem, strings::tokenize(subsystems, ",")) {
-      std::string hierarchy = path::join(baseHierarchy, subsystem);
-
-      Try<std::vector<std::string> > cgroups = cgroups::get(hierarchy);
-      CHECK_SOME(cgroups);
-
-      foreach (const std::string& cgroup, cgroups.get()) {
-        // Remove any cgroups that start with TEST_CGROUPS_ROOT.
-        if (cgroup == TEST_CGROUPS_ROOT) {
-          AWAIT_READY(cgroups::destroy(hierarchy, cgroup));
-        }
-      }
-    }
-
-    CgroupsTest::TearDown();
-  }
-
-  const std::string subsystems; // Subsystems required to run tests.
-  std::string baseHierarchy; // Path to the hierarchy being used.
-};
-
-
-class CgroupsAnyHierarchyWithCpuMemoryTest
-  : public CgroupsAnyHierarchyTest
-{
-public:
-  CgroupsAnyHierarchyWithCpuMemoryTest()
-    : CgroupsAnyHierarchyTest("cpu,memory") {}
-};
-
-
-class CgroupsAnyHierarchyWithFreezerTest
-  : public CgroupsAnyHierarchyTest
-{
-public:
-  CgroupsAnyHierarchyWithFreezerTest()
-    : CgroupsAnyHierarchyTest("freezer") {}
-};
-
-
-TEST_F(CgroupsAnyHierarchyTest, ROOT_CGROUPS_Enabled)
-{
-  EXPECT_SOME_TRUE(cgroups::enabled(""));
-  EXPECT_SOME_TRUE(cgroups::enabled(","));
-  EXPECT_SOME_TRUE(cgroups::enabled("cpu"));
-  EXPECT_SOME_TRUE(cgroups::enabled(",cpu"));
-  EXPECT_SOME_TRUE(cgroups::enabled("cpu,memory"));
-  EXPECT_SOME_TRUE(cgroups::enabled("cpu,memory,"));
-  EXPECT_ERROR(cgroups::enabled("invalid"));
-  EXPECT_ERROR(cgroups::enabled("cpu,invalid"));
-}
-
-
-TEST_F(CgroupsAnyHierarchyWithCpuMemoryTest, ROOT_CGROUPS_Busy)
-{
-  EXPECT_SOME_FALSE(cgroups::busy(""));
-  EXPECT_SOME_FALSE(cgroups::busy(","));
-  EXPECT_SOME_TRUE(cgroups::busy("cpu"));
-  EXPECT_SOME_TRUE(cgroups::busy(",cpu"));
-  EXPECT_SOME_TRUE(cgroups::busy("cpu,memory"));
-  EXPECT_SOME_TRUE(cgroups::busy("cpu,memory,"));
-  EXPECT_ERROR(cgroups::busy("invalid"));
-  EXPECT_ERROR(cgroups::busy("cpu,invalid"));
-}
-
-
-TEST_F(CgroupsAnyHierarchyTest, ROOT_CGROUPS_Subsystems)
-{
-  Try<std::set<std::string> > names = cgroups::subsystems();
-  ASSERT_SOME(names);
-
-  Option<std::string> cpu;
-  Option<std::string> memory;
-  foreach (const std::string& name, names.get()) {
-    if (name == "cpu") {
-      cpu = name;
-    } else if (name == "memory") {
-      memory = name;
-    }
-  }
-
-  EXPECT_SOME(cpu);
-  EXPECT_SOME(memory);
-}
-
-
-TEST_F(CgroupsAnyHierarchyWithCpuMemoryTest, ROOT_CGROUPS_SubsystemsHierarchy)
-{
-  std::string cpuHierarchy = path::join(baseHierarchy, "cpu");
-
-  Try<std::set<std::string> > names = cgroups::subsystems(cpuHierarchy);
-  ASSERT_SOME(names);
-
-  Option<std::string> cpu;
-  Option<std::string> memory;
-  foreach (const std::string& name, names.get()) {
-    if (name == "cpu") {
-      cpu = name;
-    } else if (name == "memory") {
-      memory = name;
-    }
-  }
-
-  EXPECT_SOME(cpu);
-  EXPECT_NONE(memory);
-
-  std::string memoryHierarchy = path::join(baseHierarchy, "memory");
-  names = cgroups::subsystems(memoryHierarchy);
-  ASSERT_SOME(names);
-
-  cpu = None();
-  memory = None();
-  foreach (const std::string& name, names.get()) {
-    if (name == "cpu") {
-      cpu = name;
-    } else if (name == "memory") {
-      memory = name;
-    }
-  }
-  EXPECT_NONE(cpu);
-  EXPECT_SOME(memory);
-}
-
-
-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"));
-  EXPECT_ERROR(cgroups::mount(TEST_CGROUPS_HIERARCHY, "invalid"));
-
-  // Try to mount a valid hierarchy, retrying as necessary since the
-  // previous unmount might not have taken effect yet due to a bug in
-  // Ubuntu 12.04.
-  ASSERT_SOME(cgroups::mount(TEST_CGROUPS_HIERARCHY, "cpu,memory", 10));
-  EXPECT_ERROR(cgroups::mount(TEST_CGROUPS_HIERARCHY, "cpuset"));
-  EXPECT_ERROR(cgroups::unmount("/tmp"));
-  ASSERT_SOME(cgroups::unmount(TEST_CGROUPS_HIERARCHY));
-}
-
-
-TEST_F(CgroupsAnyHierarchyTest, ROOT_CGROUPS_Mounted)
-{
-  EXPECT_SOME_FALSE(cgroups::mounted("/tmp-nonexist"));
-  EXPECT_SOME_FALSE(cgroups::mounted("/tmp"));
-  EXPECT_SOME_FALSE(cgroups::mounted(baseHierarchy + "/not_expected"));
-  EXPECT_SOME_TRUE(cgroups::mounted(baseHierarchy + "/cpu"));
-}
-
-
-TEST_F(CgroupsAnyHierarchyWithCpuMemoryTest, ROOT_CGROUPS_MountedSubsystems)
-{
-  EXPECT_SOME_FALSE(cgroups::mounted("/tmp-nonexist", "cpu"));
-  EXPECT_SOME_FALSE(cgroups::mounted("/tmp", "cpu,memory"));
-  EXPECT_SOME_FALSE(cgroups::mounted("/tmp", "cpu"));
-  EXPECT_SOME_FALSE(cgroups::mounted("/tmp", "invalid"));
-  EXPECT_SOME_TRUE(cgroups::mounted(path::join(baseHierarchy, "cpu"), "cpu"));
-  EXPECT_SOME_TRUE(cgroups::mounted(
-        path::join(baseHierarchy, "memory"), "memory"));
-  EXPECT_SOME_FALSE(cgroups::mounted(baseHierarchy, "invalid"));
-  EXPECT_SOME_FALSE(cgroups::mounted(baseHierarchy + "/not_expected", "cpu"));
-}
-
-
-TEST_F(CgroupsAnyHierarchyWithCpuMemoryTest, ROOT_CGROUPS_CreateRemove)
-{
-  EXPECT_ERROR(cgroups::create("/tmp", "test"));
-  EXPECT_ERROR(cgroups::create(baseHierarchy, "mesos_test_missing/1"));
-  ASSERT_SOME(cgroups::create(
-        path::join(baseHierarchy, "cpu"), "mesos_test_missing"));
-  EXPECT_ERROR(cgroups::remove(baseHierarchy, "invalid"));
-  ASSERT_SOME(cgroups::remove(
-        path::join(baseHierarchy, "cpu"), "mesos_test_missing"));
-}
-
-
-TEST_F(CgroupsAnyHierarchyTest, ROOT_CGROUPS_Get)
-{
-  std::string hierarchy = path::join(baseHierarchy, "cpu");
-
-  ASSERT_SOME(cgroups::create(hierarchy, "mesos_test1"));
-  ASSERT_SOME(cgroups::create(hierarchy, "mesos_test2"));
-
-  Try<std::vector<std::string>> cgroups = cgroups::get(hierarchy);
-  ASSERT_SOME(cgroups);
-
-  EXPECT_NE(cgroups.get().end(),
-            find(cgroups.get().begin(), cgroups.get().end(), "mesos_test2"));
-  EXPECT_NE(cgroups.get().end(),
-            find(cgroups.get().begin(), cgroups.get().end(), "mesos_test1"));
-
-  ASSERT_SOME(cgroups::remove(hierarchy, "mesos_test1"));
-  ASSERT_SOME(cgroups::remove(hierarchy, "mesos_test2"));
-}
-
-
-TEST_F(CgroupsAnyHierarchyTest, ROOT_CGROUPS_NestedCgroups)
-{
-  std::string hierarchy = path::join(baseHierarchy, "cpu");
-  ASSERT_SOME(cgroups::create(hierarchy, TEST_CGROUPS_ROOT));
-  std::string cgroup1 = path::join(TEST_CGROUPS_ROOT, "1");
-  std::string cgroup2 = path::join(TEST_CGROUPS_ROOT, "2");
-
-  ASSERT_SOME(cgroups::create(hierarchy, cgroup1))
-    << "-------------------------------------------------------------\n"
-    << "We cannot run this test because it appears you do not have\n"
-    << "a modern enough version of the Linux kernel. You won't be\n"
-    << "able to use the cgroups isolator, but feel free to disable\n"
-    << "this test.\n"
-    << "-------------------------------------------------------------";
-
-  ASSERT_SOME(cgroups::create(hierarchy, cgroup2));
-
-  Try<std::vector<std::string>> cgroups =
-    cgroups::get(hierarchy, TEST_CGROUPS_ROOT);
-  ASSERT_SOME(cgroups);
-
-  ASSERT_EQ(2u, cgroups.get().size());
-
-  EXPECT_NE(cgroups.get().end(),
-            find(cgroups.get().begin(), cgroups.get().end(), cgroup2));
-  EXPECT_NE(cgroups.get().end(),
-            find(cgroups.get().begin(), cgroups.get().end(), cgroup1));
-
-  ASSERT_SOME(cgroups::remove(hierarchy, cgroup1));
-  ASSERT_SOME(cgroups::remove(hierarchy, cgroup2));
-}
-
-
-TEST_F(CgroupsAnyHierarchyTest, ROOT_CGROUPS_Tasks)
-{
-  pid_t pid = ::getpid();
-
-  Result<std::string> cgroup = cgroups::cpu::cgroup(pid);
-  ASSERT_SOME(cgroup);
-
-  std::string hierarchy = path::join(baseHierarchy, "cpu");
-
-  Try<std::set<pid_t>> pids = cgroups::processes(hierarchy, cgroup.get());
-  ASSERT_SOME(pids);
-
-  EXPECT_NE(0u, pids.get().count(pid));
-}
-
-
-TEST_F(CgroupsAnyHierarchyTest, ROOT_CGROUPS_Read)
-{
-  std::string hierarchy = path::join(baseHierarchy, "cpu");
-
-  EXPECT_ERROR(cgroups::read(hierarchy, TEST_CGROUPS_ROOT, "invalid42"));
-
-  pid_t pid = ::getpid();
-
-  Result<std::string> cgroup = cgroups::cpu::cgroup(pid);
-  ASSERT_SOME(cgroup);
-
-  Try<std::string> read = cgroups::read(hierarchy, cgroup.get(), "tasks");
-  ASSERT_SOME(read);
-
-  EXPECT_TRUE(strings::contains(read.get(), stringify(pid)));
-}
-
-
-TEST_F(CgroupsAnyHierarchyTest, ROOT_CGROUPS_Write)
-{
-  std::string hierarchy = path::join(baseHierarchy, "cpu");
-  EXPECT_ERROR(
-      cgroups::write(hierarchy, TEST_CGROUPS_ROOT, "invalid", "invalid"));
-
-  ASSERT_SOME(cgroups::create(hierarchy, TEST_CGROUPS_ROOT));
-
-  pid_t pid = ::fork();
-  ASSERT_NE(-1, pid);
-
-  if (pid == 0) {
-    // In child process, wait for kill signal.
-    while (true) { sleep(1); }
-
-    // Should not reach here.
-    const char* message = "Error, child should be killed before reaching here";
-    while (write(STDERR_FILENO, message, strlen(message)) == -1 &&
-           errno == EINTR);
-
-    _exit(1);
-  }
-
-  // In parent process.
-  ASSERT_SOME(
-      cgroups::write(hierarchy,
-                     TEST_CGROUPS_ROOT,
-                     "cgroup.procs",
-                     stringify(pid)));
-
-  Try<std::set<pid_t> > pids = cgroups::processes(hierarchy, TEST_CGROUPS_ROOT);
-  ASSERT_SOME(pids);
-
-  EXPECT_NE(0u, pids.get().count(pid));
-
-  // Kill the child process.
-  ASSERT_NE(-1, ::kill(pid, SIGKILL));
-
-  // Wait for the child process.
-  int status;
-  EXPECT_NE(-1, ::waitpid((pid_t) -1, &status, 0));
-  ASSERT_TRUE(WIFSIGNALED(status));
-  EXPECT_EQ(SIGKILL, WTERMSIG(status));
-}
-
-
-TEST_F(CgroupsAnyHierarchyTest, ROOT_CGROUPS_Cfs_Big_Quota)
-{
-  std::string hierarchy = path::join(baseHierarchy, "cpu");
-  ASSERT_SOME(cgroups::create(hierarchy, TEST_CGROUPS_ROOT));
-
-  Duration quota = Seconds(100); // Big quota.
-  ASSERT_SOME(cgroups::cpu::cfs_quota_us(hierarchy, TEST_CGROUPS_ROOT, quota));
-
-  // Ensure we can read back the correct quota.
-  ASSERT_SOME_EQ(
-      quota,
-      cgroups::cpu::cfs_quota_us(hierarchy, TEST_CGROUPS_ROOT));
-}
-
-
-class CgroupsAnyHierarchyWithCpuAcctMemoryTest
-  : public CgroupsAnyHierarchyTest
-{
-public:
-  CgroupsAnyHierarchyWithCpuAcctMemoryTest()
-    : CgroupsAnyHierarchyTest("cpuacct,memory") {}
-};
-
-
-TEST_F(CgroupsAnyHierarchyWithCpuAcctMemoryTest, ROOT_CGROUPS_Stat)
-{
-  EXPECT_ERROR(cgroups::stat(baseHierarchy, TEST_CGROUPS_ROOT, "invalid"));
-
-  Try<hashmap<std::string, uint64_t> > result =
-    cgroups::stat(
-        path::join(baseHierarchy, "cpuacct"), "/", "cpuacct.stat");
-  ASSERT_SOME(result);
-  EXPECT_TRUE(result.get().contains("user"));
-  EXPECT_TRUE(result.get().contains("system"));
-  EXPECT_GT(result.get().get("user").get(), 0llu);
-  EXPECT_GT(result.get().get("system").get(), 0llu);
-
-  result = cgroups::stat(
-      path::join(baseHierarchy, "memory"), "/", "memory.stat");
-  ASSERT_SOME(result);
-  EXPECT_TRUE(result.get().contains("rss"));
-  EXPECT_GT(result.get().get("rss").get(), 0llu);
-}
-
-
-TEST_F(CgroupsAnyHierarchyWithCpuMemoryTest, ROOT_CGROUPS_Listen)
-{
-  std::string hierarchy = path::join(baseHierarchy, "memory");
-  ASSERT_SOME(cgroups::create(hierarchy, TEST_CGROUPS_ROOT));
-  ASSERT_SOME(
-      cgroups::memory::oom::killer::enabled(hierarchy, TEST_CGROUPS_ROOT))
-    << "-------------------------------------------------------------\n"
-    << "We cannot run this test because it appears you do not have\n"
-    << "a modern enough version of the Linux kernel. You won't be\n"
-    << "able to use the cgroups isolator, but feel free to disable\n"
-    << "this test.\n"
-    << "-------------------------------------------------------------";
-
-  const Bytes limit =  Megabytes(64);
-
-  ASSERT_SOME(cgroups::memory::limit_in_bytes(
-      hierarchy, TEST_CGROUPS_ROOT, limit));
-
-  // Listen on oom events for test cgroup.
-  Future<Nothing> future =
-    cgroups::memory::oom::listen(hierarchy, TEST_CGROUPS_ROOT);
-
-  ASSERT_FALSE(future.isFailed());
-
-  // Test the cancellation.
-  future.discard();
-
-  // Test the normal operation below.
-  future = cgroups::memory::oom::listen(hierarchy, TEST_CGROUPS_ROOT);
-  ASSERT_FALSE(future.isFailed());
-
-  MemoryTestHelper helper;
-  ASSERT_SOME(helper.spawn());
-  ASSERT_SOME(helper.pid());
-
-  EXPECT_SOME(cgroups::assign(
-      hierarchy, TEST_CGROUPS_ROOT, helper.pid().get()));
-
-  // Request more RSS memory in the subprocess than the limit.
-  // NOTE: We enable the kernel oom killer in this test. If it were
-  // disabled, the subprocess might hang and the following call won't
-  // return. By enabling the oom killer, we let the subprocess get
-  // killed and expect that an error is returned.
-  EXPECT_ERROR(helper.increaseRSS(limit * 2));
-
-  AWAIT_READY(future);
-}
-
-
-TEST_F(CgroupsAnyHierarchyWithFreezerTest, ROOT_CGROUPS_Freeze)
-{
-  int pipes[2];
-  int dummy;
-  ASSERT_NE(-1, ::pipe(pipes));
-
-  std::string hierarchy = path::join(baseHierarchy, "freezer");
-  ASSERT_SOME(cgroups::create(hierarchy, TEST_CGROUPS_ROOT));
-
-  pid_t pid = ::fork();
-  ASSERT_NE(-1, pid);
-
-  if (pid == 0) {
-    // In child process.
-    ::close(pipes[0]);
-
-    // Put self into the test cgroup.
-    Try<Nothing> assign =
-      cgroups::assign(hierarchy, TEST_CGROUPS_ROOT, ::getpid());
-
-    if (assign.isError()) {
-      std::cerr << "Failed to assign cgroup: " << assign.error() << std::endl;
-      abort();
-    }
-
-    // Notify the parent.
-    if (::write(pipes[1], &dummy, sizeof(dummy)) != sizeof(dummy)) {
-      perror("Failed to notify the parent");
-      abort();
-    }
-    ::close(pipes[1]);
-
-    // Infinite loop here.
-    while (true);
-
-    // Should not reach here.
-    std::cerr << "Reach an unreachable statement!" << std::endl;
-    abort();
-  }
-
-  // In parent process.
-  ::close(pipes[1]);
-
-  // Wait until child has assigned the cgroup.
-  ASSERT_LT(0, ::read(pipes[0], &dummy, sizeof(dummy)));
-  ::close(pipes[0]);
-
-  // Freeze the test cgroup.
-  AWAIT_EXPECT_READY(cgroups::freezer::freeze(hierarchy, TEST_CGROUPS_ROOT));
-
-  // Thaw the test cgroup.
-  AWAIT_EXPECT_READY(cgroups::freezer::thaw(hierarchy, TEST_CGROUPS_ROOT));
-
-  // Kill the child process.
-  ASSERT_NE(-1, ::kill(pid, SIGKILL));
-
-  // Wait for the child process.
-  int status;
-  EXPECT_NE(-1, ::waitpid((pid_t) -1, &status, 0));
-  ASSERT_TRUE(WIFSIGNALED(status));
-  EXPECT_EQ(SIGKILL, WTERMSIG(status));
-}
-
-
-TEST_F(CgroupsAnyHierarchyWithCpuMemoryTest, ROOT_CGROUPS_FreezeNonFreezer)
-{
-  std::string hierarchy = path::join(baseHierarchy, "cpu");
-  ASSERT_SOME(cgroups::create(hierarchy, TEST_CGROUPS_ROOT));
-
-  AWAIT_EXPECT_FAILED(cgroups::freezer::freeze(hierarchy, TEST_CGROUPS_ROOT));
-  AWAIT_EXPECT_FAILED(cgroups::freezer::thaw(hierarchy, TEST_CGROUPS_ROOT));
-
-  // The cgroup is empty so we should still be able to destroy it.
-  AWAIT_READY(cgroups::destroy(hierarchy, TEST_CGROUPS_ROOT));
-}
-
-
-TEST_F(CgroupsAnyHierarchyWithFreezerTest, ROOT_CGROUPS_Kill)
-{
-  int pipes[2];
-  int dummy;
-  ASSERT_NE(-1, ::pipe(pipes));
-
-  std::string hierarchy = path::join(baseHierarchy, "freezer");
-  ASSERT_SOME(cgroups::create(hierarchy, TEST_CGROUPS_ROOT));
-
-  pid_t pid = ::fork();
-  ASSERT_NE(-1, pid);
-
-  if (pid > 0) {
-    // In parent process.
-    ::close(pipes[1]);
-
-    // Wait until all children have assigned the cgroup.
-    ASSERT_LT(0, ::read(pipes[0], &dummy, sizeof(dummy)));
-    ASSERT_LT(0, ::read(pipes[0], &dummy, sizeof(dummy)));
-    ASSERT_LT(0, ::read(pipes[0], &dummy, sizeof(dummy)));
-    ASSERT_LT(0, ::read(pipes[0], &dummy, sizeof(dummy)));
-    ::close(pipes[0]);
-
-    Try<Nothing> kill = cgroups::kill(hierarchy, TEST_CGROUPS_ROOT, SIGKILL);
-    EXPECT_SOME(kill);
-
-    int status;
-    EXPECT_NE(-1, ::waitpid((pid_t) -1, &status, 0));
-    ASSERT_TRUE(WIFSIGNALED(status));
-    EXPECT_EQ(SIGKILL, WTERMSIG(status));
-  } else {
-    // In child process.
-
-    // We create 4 child processes here using two forks to test the case in
-    // which there are multiple active processes in the given cgroup.
-    ::fork();
-    ::fork();
-
-    // Put self into the test cgroup.
-    Try<Nothing> assign =
-      cgroups::assign(hierarchy, TEST_CGROUPS_ROOT, ::getpid());
-
-    if (assign.isError()) {
-      std::cerr << "Failed to assign cgroup: " << assign.error() << std::endl;
-      abort();
-    }
-
-    // Notify the parent.
-    ::close(pipes[0]); // TODO(benh): Close after first fork?
-    if (::write(pipes[1], &dummy, sizeof(dummy)) != sizeof(dummy)) {
-      perror("Failed to notify the parent");
-      abort();
-    }
-    ::close(pipes[1]);
-
-    // Wait kill signal from parent.
-    while (true);
-
-    // Should not reach here.
-    std::cerr << "Reach an unreachable statement!" << std::endl;
-    abort();
-  }
-}
-
-
-// TODO(benh): Write a version of this test with nested cgroups.
-TEST_F(CgroupsAnyHierarchyWithFreezerTest, ROOT_CGROUPS_Destroy)
-{
-  int pipes[2];
-  int dummy;
-  ASSERT_NE(-1, ::pipe(pipes));
-
-  std::string hierarchy = path::join(baseHierarchy, "freezer");
-  ASSERT_SOME(cgroups::create(hierarchy, TEST_CGROUPS_ROOT));
-
-  pid_t pid = ::fork();
-  ASSERT_NE(-1, pid);
-
-  if (pid > 0) {
-    // In parent process.
-    ::close(pipes[1]);
-
-    // Wait until all children have assigned the cgroup.
-    ASSERT_LT(0, ::read(pipes[0], &dummy, sizeof(dummy)));
-    ASSERT_LT(0, ::read(pipes[0], &dummy, sizeof(dummy)));
-    ASSERT_LT(0, ::read(pipes[0], &dummy, sizeof(dummy)));
-    ASSERT_LT(0, ::read(pipes[0], &dummy, sizeof(dummy)));
-    ::close(pipes[0]);
-
-    AWAIT_READY(cgroups::destroy(hierarchy, TEST_CGROUPS_ROOT));
-
-    // cgroups::destroy will reap all processes in the cgroup so we should
-    // *not* be able to reap it now.
-    int status;
-    EXPECT_EQ(-1, ::waitpid(pid, &status, 0));
-    EXPECT_EQ(ECHILD, errno);
-  } else {
-    // In child process.
-
-    // We create 4 child processes here using two forks to test the case in
-    // which there are multiple active processes in the given cgroup.
-    ::fork();
-    ::fork();
-
-    // Put self into the test cgroup.
-    Try<Nothing> assign =
-      cgroups::assign(hierarchy, TEST_CGROUPS_ROOT, ::getpid());
-
-    if (assign.isError()) {
-      std::cerr << "Failed to assign cgroup: " << assign.error() << std::endl;
-      abort();
-    }
-
-    // Notify the parent.
-    ::close(pipes[0]); // TODO(benh): Close after first fork?
-    if (::write(pipes[1], &dummy, sizeof(dummy)) != sizeof(dummy)) {
-      perror("Failed to notify the parent");
-      abort();
-    }
-    ::close(pipes[1]);
-
-    // Wait kill signal from parent.
-    while (true) {}
-
-    // Should not reach here.
-    std::cerr << "Reach an unreachable statement!" << std::endl;
-    abort();
-  }
-}
-
-
-void* threadFunction(void*)
-{
-  // Newly created threads have PTHREAD_CANCEL_ENABLE and
-  // PTHREAD_CANCEL_DEFERRED so they can be cancelled from the main thread.
-  while (true) { sleep(1); }
-
-  return NULL;
-}
-
-
-TEST_F(CgroupsAnyHierarchyWithFreezerTest, ROOT_CGROUPS_AssignThreads)
-{
-  size_t numThreads = 5;
-
-  pthread_t pthreads[numThreads];
-
-  // Create additional threads.
-  for (size_t i = 0; i < numThreads; i++)
-  {
-    EXPECT_EQ(0, pthread_create(&pthreads[i], NULL, threadFunction, NULL));
-  }
-
-  std::string hierarchy = path::join(baseHierarchy, "freezer");
-  ASSERT_SOME(cgroups::create(hierarchy, TEST_CGROUPS_ROOT));
-
-  // Check the test cgroup is initially empty.
-  Try<set<pid_t> > cgroupThreads =
-    cgroups::threads(hierarchy, TEST_CGROUPS_ROOT);
-  EXPECT_SOME(cgroupThreads);
-  EXPECT_EQ(0u, cgroupThreads.get().size());
-
-  // Assign ourselves to the test cgroup.
-  CHECK_SOME(cgroups::assign(hierarchy, TEST_CGROUPS_ROOT, ::getpid()));
-
-  // Get our threads (may be more than the numThreads we created if
-  // other threads are running).
-  Try<set<pid_t> > threads = proc::threads(::getpid());
-  ASSERT_SOME(threads);
-
-  // Check the test cgroup now only contains all child threads.
-  cgroupThreads = cgroups::threads(hierarchy, TEST_CGROUPS_ROOT);
-  EXPECT_SOME(cgroupThreads);
-  EXPECT_SOME_EQ(threads.get(), cgroupThreads);
-
-  // Terminate the additional threads.
-  for (size_t i = 0; i < numThreads; i++)
-  {
-    EXPECT_EQ(0, pthread_cancel(pthreads[i]));
-    EXPECT_EQ(0, pthread_join(pthreads[i], NULL));
-  }
-
-  // Move ourselves to the root cgroup.
-  CHECK_SOME(cgroups::assign(hierarchy, "", ::getpid()));
-
-  // Destroy the cgroup.
-  AWAIT_READY(cgroups::destroy(hierarchy, TEST_CGROUPS_ROOT));
-}
-
-
-TEST_F(CgroupsAnyHierarchyWithFreezerTest, ROOT_CGROUPS_DestroyStoppedProcess)
-{
-  std::string hierarchy = path::join(baseHierarchy, "freezer");
-  ASSERT_SOME(cgroups::create(hierarchy, TEST_CGROUPS_ROOT));
-
-  pid_t pid = ::fork();
-  ASSERT_NE(-1, pid);
-
-  if (pid == 0) {
-    // In child process.
-    while (true) { sleep(1); }
-
-    ABORT("Child should not reach this statement");
-  }
-
-  // In parent process.
-
-  // Put child into the freezer cgroup.
-  Try<Nothing> assign = cgroups::assign(hierarchy, TEST_CGROUPS_ROOT, pid);
-
-  // Stop the child process.
-  EXPECT_EQ(0, kill(pid, SIGSTOP));
-
-  AWAIT_READY(cgroups::destroy(hierarchy, TEST_CGROUPS_ROOT));
-
-  // cgroups::destroy will reap all processes in the cgroup so we should
-  // *not* be able to reap it now.
-  int status;
-  EXPECT_EQ(-1, ::waitpid(pid, &status, 0));
-  EXPECT_EQ(ECHILD, errno);
-}
-
-
-TEST_F(CgroupsAnyHierarchyWithFreezerTest, ROOT_CGROUPS_DestroyTracedProcess)
-{
-  std::string hierarchy = path::join(baseHierarchy, "freezer");
-  ASSERT_SOME(cgroups::create(hierarchy, TEST_CGROUPS_ROOT));
-
-  pid_t pid = ::fork();
-  ASSERT_NE(-1, pid);
-
-  if (pid == 0) {
-    // In child process.
-    while (true) { sleep(1); }
-
-    ABORT("Child should not reach this statement");
-  }
-
-  // In parent process.
-  Try<Nothing> assign = cgroups::assign(hierarchy, TEST_CGROUPS_ROOT, pid);
-  ASSERT_SOME(assign);
-
-  // Attach to the child process.
-  ASSERT_EQ(0, ptrace(PT_ATTACH, pid, NULL, NULL));
-
-  // Wait until the process is in traced state ('t' or 'T').
-  Duration elapsed = Duration::zero();
-  while (true) {
-    Result<proc::ProcessStatus> process = proc::status(pid);
-    ASSERT_SOME(process);
-
-    if (process.get().state == 'T' || process.get().state == 't') {
-      break;
-    }
-
-    if (elapsed > Seconds(1)) {
-      FAIL() << "Failed to wait for process to be traced";
-    }
-
-    os::sleep(Milliseconds(5));
-    elapsed += Milliseconds(5);
-  }
-
-  // Now destroy the cgroup.
-  AWAIT_READY(cgroups::destroy(hierarchy, TEST_CGROUPS_ROOT));
-
-  // cgroups::destroy will reap all processes in the cgroup so we should
-  // *not* be able to reap it now.
-  int status;
-  EXPECT_EQ(-1, ::waitpid(pid, &status, 0));
-  EXPECT_EQ(ECHILD, errno);
-}
-
-
-class CgroupsAnyHierarchyWithPerfEventTest
-  : public CgroupsAnyHierarchyTest
-{
-public:
-  CgroupsAnyHierarchyWithPerfEventTest()
-    : CgroupsAnyHierarchyTest("perf_event") {}
-};
-
-
-TEST_F(CgroupsAnyHierarchyWithPerfEventTest, ROOT_CGROUPS_Perf)
-{
-  int pipes[2];
-  int dummy;
-  ASSERT_NE(-1, ::pipe(pipes));
-
-  std::string hierarchy = path::join(baseHierarchy, "perf_event");
-  ASSERT_SOME(cgroups::create(hierarchy, TEST_CGROUPS_ROOT));
-
-  pid_t pid = ::fork();
-  ASSERT_NE(-1, pid);
-
-  if (pid == 0) {
-    // In child process.
-    ::close(pipes[1]);
-
-    // Wait until parent has assigned us to the cgroup.
-    ssize_t len;
-    while ((len = ::read(pipes[0], &dummy, sizeof(dummy))) == -1 &&
-           errno == EINTR);
-    ASSERT_EQ((ssize_t) sizeof(dummy), len);
-    ::close(pipes[0]);
-
-    while (true) {
-      // Don't sleep so 'perf' can actually sample something.
-    }
-
-    ABORT("Child should not reach here");
-  }
-
-  // In parent.
-  ::close(pipes[0]);
-
-  // Put child into the test cgroup.
-  ASSERT_SOME(cgroups::assign(hierarchy, TEST_CGROUPS_ROOT, pid));
-
-  ssize_t len;
-  while ((len = ::write(pipes[1], &dummy, sizeof(dummy))) == -1 &&
-         errno == EINTR);
-  ASSERT_EQ((ssize_t) sizeof(dummy), len);
-  ::close(pipes[1]);
-
-  std::set<std::string> events;
-  // Hardware event.
-  events.insert("cycles");
-  // Software event.
-  events.insert("task-clock");
-
-  // NOTE: Wait at least 2 seconds as we've seen some variance in how
-  // well 'perf' does across Linux distributions (e.g., Ubuntu 14.04)
-  // and we want to make sure that we collect some non-zero values.
-  Future<mesos::PerfStatistics> statistics =
-    perf::sample(events, TEST_CGROUPS_ROOT, Seconds(2));
-  AWAIT_READY(statistics);
-
-  ASSERT_TRUE(statistics.get().has_cycles());
-
-  // TODO(benh): Some Linux distributions (Ubuntu 14.04) fail to
-  // properly sample 'cycles' with 'perf', so we don't explicitly
-  // check the value here. See MESOS-3082.
-  // EXPECT_LT(0u, statistics.get().cycles());
-
-  ASSERT_TRUE(statistics.get().has_task_clock());
-  EXPECT_LT(0.0, statistics.get().task_clock());
-
-  // Kill the child process.
-  ASSERT_NE(-1, ::kill(pid, SIGKILL));
-
-  // Wait for the child process.
-  int status;
-  EXPECT_NE(-1, ::waitpid((pid_t) -1, &status, 0));
-  ASSERT_TRUE(WIFSIGNALED(status));
-  EXPECT_EQ(SIGKILL, WTERMSIG(status));
-
-  // Destroy the cgroup.
-  Future<Nothing> destroy = cgroups::destroy(hierarchy, TEST_CGROUPS_ROOT);
-  AWAIT_READY(destroy);
-}
-
-
-class CgroupsAnyHierarchyMemoryPressureTest
-  : public CgroupsAnyHierarchyTest
-{
-public:
-  CgroupsAnyHierarchyMemoryPressureTest()
-    : CgroupsAnyHierarchyTest("memory"),
-      cgroup(TEST_CGROUPS_ROOT) {}
-
-protected:
-  virtual void SetUp()
-  {
-    CgroupsAnyHierarchyTest::SetUp();
-
-    hierarchy = path::join(baseHierarchy, "memory");
-
-    ASSERT_SOME(cgroups::create(hierarchy, cgroup));
-  }
-
-  void listen()
-  {
-    const std::vector<Level> levels = {
-      Level::LOW,
-      Level::MEDIUM,
-      Level::CRITICAL
-    };
-
-    foreach (Level level, levels) {
-      Try<Owned<Counter>> counter = Counter::create(hierarchy, cgroup, level);
-      EXPECT_SOME(counter);
-
-      counters[level] = counter.get();
-    }
-  }
-
-  std::string hierarchy;
-  const std::string cgroup;
-
-  hashmap<Level, Owned<Counter>> counters;
-};
-
-
-TEST_F(CgroupsAnyHierarchyMemoryPressureTest, ROOT_IncreaseUnlockedRSS)
-{
-  MemoryTestHelper helper;
-  ASSERT_SOME(helper.spawn());
-  ASSERT_SOME(helper.pid());
-
-  const Bytes limit = Megabytes(16);
-
-  // Move the memory test helper into a cgroup and set the limit.
-  EXPECT_SOME(cgroups::memory::limit_in_bytes(hierarchy, cgroup, limit));
-  EXPECT_SOME(cgroups::assign(hierarchy, cgroup, helper.pid().get()));
-
-  listen();
-
-  // Used to save the counter readings from last iteration.
-  uint64_t previousLow = 0;
-  uint64_t previousMedium = 0;
-  uint64_t previousCritical = 0;
-
-  // Used to save the counter readings from this iteration.
-  uint64_t low;
-  uint64_t medium;
-  uint64_t critical;
-
-  // Use a guard to error out if it's been too long.
-  // TODO(chzhcn): Use a better way to set testing time limit.
-  uint64_t iterationLimit = limit.bytes() / getpagesize() * 10;
-
-  for (uint64_t i = 0; i < iterationLimit; i++) {
-    EXPECT_SOME(helper.increaseRSS(getpagesize()));
-
-    Future<uint64_t> _low = counters[Level::LOW]->value();
-    Future<uint64_t> _medium = counters[Level::MEDIUM]->value();
-    Future<uint64_t> _critical = counters[Level::CRITICAL]->value();
-
-    AWAIT_READY(_low);
-    AWAIT_READY(_medium);
-    AWAIT_READY(_critical);
-
-    low = _low.get();
-    medium = _medium.get();
-    critical = _critical.get();
-
-    // We need to know the readings are the same as last time to be
-    // sure they are stable, because the reading is not atomic. For
-    // example, the medium could turn positive after we read low to be
-    // 0, but this should be fixed by the next read immediately.
-    if ((low == previousLow &&
-         medium == previousMedium &&
-         critical == previousCritical)) {
-      if (low != 0) {
-        EXPECT_LE(medium, low);
-        EXPECT_LE(critical, medium);
-
-        // When child's RSS is full, it will be OOM-kill'ed if we
-        // don't stop it right away.
-        break;
-      } else {
-        EXPECT_EQ(0u, medium);
-        EXPECT_EQ(0u, critical);
-      }
-    }
-
-    previousLow = low;
-    previousMedium = medium;
-    previousCritical = critical;
-  }
-}
-
-
-TEST_F(CgroupsAnyHierarchyMemoryPressureTest, ROOT_IncreasePageCache)
-{
-  MemoryTestHelper helper;
-  ASSERT_SOME(helper.spawn());
-  ASSERT_SOME(helper.pid());
-
-  const Bytes limit = Megabytes(16);
-
-  // Move the memory test helper into a cgroup and set the limit.
-  EXPECT_SOME(cgroups::memory::limit_in_bytes(hierarchy, cgroup, limit));
-  EXPECT_SOME(cgroups::assign(hierarchy, cgroup, helper.pid().get()));
-
-  listen();
-
-  // Used to save the counter readings from last iteration.
-  uint64_t previousLow = 0;
-  uint64_t previousMedium = 0;
-  uint64_t previousCritical = 0;
-
-  // Used to save the counter readings from this iteration.
-  uint64_t low;
-  uint64_t medium;
-  uint64_t critical;
-
-  // Use a guard to error out if it's been too long.
-  // TODO(chzhcn): Use a better way to set testing time limit.
-  uint64_t iterationLimit = limit.bytes() / Megabytes(1).bytes() * 2;
-
-  for (uint64_t i = 0; i < iterationLimit; i++) {
-    EXPECT_SOME(helper.increasePageCache(Megabytes(1)));
-
-    Future<uint64_t> _low = counters[Level::LOW]->value();
-    Future<uint64_t> _medium = counters[Level::MEDIUM]->value();
-    Future<uint64_t> _critical = counters[Level::CRITICAL]->value();
-
-    AWAIT_READY(_low);
-    AWAIT_READY(_medium);
-    AWAIT_READY(_critical);
-
-    low = _low.get();
-    medium = _medium.get();
-    critical = _critical.get();
-
-    // We need to know the readings are the same as last time to be
-    // sure they are stable, because the reading is not atomic. For
-    // example, the medium could turn positive after we read low to be
-    // 0, but this should be fixed by the next read immediately.
-    if ((low == previousLow &&
-         medium == previousMedium &&
-         critical == previousCritical)) {
-      if (low != 0) {
-        EXPECT_LE(medium, low);
-        EXPECT_LE(critical, medium);
-
-        // Different from the RSS test, since the child is only
-        // consuming at a slow rate the page cache, which is evictable
-        // and reclaimable, we could therefore be in this state
-        // forever. Our guard will let us out shortly.
-      } else {
-        EXPECT_EQ(0u, medium);
-        EXPECT_EQ(0u, critical);
-      }
-    }
-
-    previousLow = low;
-    previousMedium = medium;
-    previousCritical = critical;
-  }
-
-  EXPECT_LT(0u, low);
-}
-
-// Tests the cpuacct::stat API. This test just tests for ANY value returned by
-// the API.
-TEST_F(CgroupsAnyHierarchyWithCpuAcctMemoryTest, ROOT_CGROUPS_CpuAcctsStats)
-{
-  const std::string hierarchy = path::join(baseHierarchy, "cpuacct");
-  ASSERT_SOME(cgroups::create(hierarchy, TEST_CGROUPS_ROOT));
-
-  CHECK_SOME(cgroups::assign(hierarchy, TEST_CGROUPS_ROOT, ::getpid()));
-
-  ASSERT_SOME(cgroups::cpuacct::stat(hierarchy, TEST_CGROUPS_ROOT));
-
-  // Move ourselves to the root cgroup.
-  CHECK_SOME(cgroups::assign(hierarchy, "", ::getpid()));
-
-  AWAIT_READY(cgroups::destroy(hierarchy, TEST_CGROUPS_ROOT));
-}
-
-} // namespace tests {
-} // namespace internal {
-} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/composing_containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/composing_containerizer_tests.cpp b/src/tests/composing_containerizer_tests.cpp
deleted file mode 100644
index d66f519..0000000
--- a/src/tests/composing_containerizer_tests.cpp
+++ /dev/null
@@ -1,171 +0,0 @@
-/**
- * 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 <vector>
-
-#include <process/future.hpp>
-#include <process/gmock.hpp>
-
-#include <stout/option.hpp>
-
-#include "messages/messages.hpp"
-
-#include "slave/containerizer/containerizer.hpp"
-#include "slave/containerizer/composing.hpp"
-
-#include "tests/mesos.hpp"
-
-using namespace mesos::internal::slave;
-
-using namespace process;
-
-using std::vector;
-
-using testing::_;
-using testing::Return;
-
-namespace mesos {
-namespace internal {
-namespace tests {
-
-
-class ComposingContainerizerTest : public MesosTest {};
-
-class MockContainerizer : public slave::Containerizer
-{
-public:
-  MOCK_METHOD1(
-      recover,
-      process::Future<Nothing>(
-          const Option<slave::state::SlaveState>&));
-
-  MOCK_METHOD7(
-      launch,
-      process::Future<bool>(
-          const ContainerID&,
-          const ExecutorInfo&,
-          const std::string&,
-          const Option<std::string>&,
-          const SlaveID&,
-          const process::PID<Slave>&,
-          bool));
-
-  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>&,
-          bool));
-
-  MOCK_METHOD2(
-      update,
-      process::Future<Nothing>(
-          const ContainerID&,
-          const Resources&));
-
-  MOCK_METHOD1(
-      usage,
-      process::Future<ResourceStatistics>(
-          const ContainerID&));
-
-  MOCK_METHOD1(
-      wait,
-      process::Future<containerizer::Termination>(
-          const ContainerID&));
-
-  MOCK_METHOD1(
-      destroy,
-      void(const ContainerID&));
-
-  MOCK_METHOD0(
-      containers,
-      process::Future<hashset<ContainerID> >());
-};
-
-
-// This test checks if destroy is called while container is being
-// launched, the composing containerizer still calls the underlying
-// containerizer's destroy and skip calling the rest of the
-// containerizers.
-TEST_F(ComposingContainerizerTest, DestroyWhileLaunching)
-{
-  vector<Containerizer*> containerizers;
-
-  MockContainerizer* mockContainerizer = new MockContainerizer();
-  MockContainerizer* mockContainerizer2 = new MockContainerizer();
-
-  containerizers.push_back(mockContainerizer);
-  containerizers.push_back(mockContainerizer2);
-
-  ComposingContainerizer containerizer(containerizers);
-  ContainerID containerId;
-  containerId.set_value("container");
-  TaskInfo taskInfo;
-  ExecutorInfo executorInfo;
-  SlaveID slaveId;
-  PID<Slave> slavePid;
-
-  Promise<bool> launchPromise;
-
-  EXPECT_CALL(*mockContainerizer, launch(_, _, _, _, _, _, _, _))
-    .WillOnce(Return(launchPromise.future()));
-
-  Future<Nothing> destroy;
-
-  EXPECT_CALL(*mockContainerizer, destroy(_))
-    .WillOnce(FutureSatisfy(&destroy));
-
-  Future<bool> launch = containerizer.launch(
-      containerId,
-      taskInfo,
-      executorInfo,
-      "dir",
-      "user",
-      slaveId,
-      slavePid,
-      false);
-
-  Resources resources = Resources::parse("cpus:1;mem:256").get();
-
-  EXPECT_TRUE(launch.isPending());
-
-  containerizer.destroy(containerId);
-
-  EXPECT_CALL(*mockContainerizer2, launch(_, _, _, _, _, _, _, _))
-    .Times(0);
-
-  // We make sure the destroy is being called on the first containerizer.
-  // The second containerizer shouldn't be called as well since the
-  // container is already destroyed.
-  AWAIT_READY(destroy);
-
-  launchPromise.set(false);
-  AWAIT_FAILED(launch);
-}
-
-} // namespace tests {
-} // namespace internal {
-} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer/cgroups_isolator_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/cgroups_isolator_tests.cpp b/src/tests/containerizer/cgroups_isolator_tests.cpp
new file mode 100644
index 0000000..a4ccc8e
--- /dev/null
+++ b/src/tests/containerizer/cgroups_isolator_tests.cpp
@@ -0,0 +1,46 @@
+/**
+ * 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 <utility>
+
+#include <gtest/gtest.h>
+
+#include <stout/foreach.hpp>
+#include <stout/proc.hpp>
+#include <stout/stringify.hpp>
+
+#include "slave/containerizer/mesos/containerizer.hpp"
+
+#include "tests/script.hpp"
+
+using std::map;
+
+namespace mesos {
+namespace internal {
+namespace tests {
+
+
+// Run the balloon framework under a mesos containerizer.
+TEST_SCRIPT(ContainerizerTest,
+            ROOT_CGROUPS_BalloonFramework,
+            "balloon_framework_test.sh")
+
+} // namespace tests {
+} // namespace internal {
+} // namespace mesos {


[06/12] mesos git commit: Moved containerizer related tests under src/tests/containerizer.

Posted by ji...@apache.org.
http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer/routing_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/routing_tests.cpp b/src/tests/containerizer/routing_tests.cpp
new file mode 100644
index 0000000..e4f1bcf
--- /dev/null
+++ b/src/tests/containerizer/routing_tests.cpp
@@ -0,0 +1,1416 @@
+/**
+ * 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 <signal.h>
+#include <unistd.h>
+
+#include <linux/version.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <gtest/gtest.h>
+
+#include <process/clock.hpp>
+#include <process/gtest.hpp>
+
+#include <stout/foreach.hpp>
+#include <stout/gtest.hpp>
+#include <stout/hashmap.hpp>
+#include <stout/ip.hpp>
+#include <stout/mac.hpp>
+#include <stout/net.hpp>
+#include <stout/stringify.hpp>
+
+#include "linux/routing/handle.hpp"
+#include "linux/routing/route.hpp"
+#include "linux/routing/utils.hpp"
+
+#include "linux/routing/diagnosis/diagnosis.hpp"
+
+#include "linux/routing/filter/basic.hpp"
+#include "linux/routing/filter/handle.hpp"
+#include "linux/routing/filter/icmp.hpp"
+#include "linux/routing/filter/ip.hpp"
+
+#include "linux/routing/link/link.hpp"
+
+#include "linux/routing/queueing/fq_codel.hpp"
+#include "linux/routing/queueing/htb.hpp"
+#include "linux/routing/queueing/ingress.hpp"
+#include "linux/routing/queueing/statistics.hpp"
+
+using namespace process;
+
+using namespace routing;
+using namespace routing::filter;
+using namespace routing::queueing;
+
+using std::endl;
+using std::set;
+using std::string;
+using std::vector;
+
+namespace mesos {
+namespace internal {
+namespace tests {
+
+
+static const string TEST_VETH_LINK = "veth-test";
+static const string TEST_PEER_LINK = "veth-peer";
+
+
+class RoutingTest : public ::testing::Test
+{
+protected:
+  virtual void SetUp()
+  {
+    ASSERT_SOME(routing::check())
+      << "-------------------------------------------------------------\n"
+      << "We cannot run any routing tests because either your libnl\n"
+      << "library or kernel is not new enough. You can either upgrade,\n"
+      << "or disable this test case\n"
+      << "-------------------------------------------------------------";
+  }
+};
+
+
+// Tests that require setting up virtual ethernet on host.
+class RoutingVethTest : public RoutingTest
+{
+protected:
+  virtual void SetUp()
+  {
+    RoutingTest::SetUp();
+
+    // Clean up the test links, in case it wasn't cleaned up properly
+    // from previous tests.
+    link::remove(TEST_VETH_LINK);
+
+    ASSERT_SOME_FALSE(link::exists(TEST_VETH_LINK));
+    ASSERT_SOME_FALSE(link::exists(TEST_PEER_LINK));
+  }
+
+  virtual void TearDown()
+  {
+    link::remove(TEST_VETH_LINK);
+  }
+};
+
+
+TEST_F(RoutingTest, PortRange)
+{
+  Try<ip::PortRange> ports = ip::PortRange::fromBeginEnd(1, 0);
+  EXPECT_ERROR(ports);
+
+  ports = ip::PortRange::fromBeginEnd(4, 11);
+  EXPECT_ERROR(ports);
+
+  ports = ip::PortRange::fromBeginEnd(4, 7);
+  ASSERT_SOME(ports);
+  EXPECT_EQ(4u, ports.get().begin());
+  EXPECT_EQ(7u, ports.get().end());
+  EXPECT_EQ(0xfffc, ports.get().mask());
+  EXPECT_EQ("[4,7]", stringify(ports.get()));
+
+  ports = ip::PortRange::fromBeginEnd(10, 10);
+  ASSERT_SOME(ports);
+  EXPECT_EQ(10u, ports.get().begin());
+  EXPECT_EQ(10u, ports.get().end());
+  EXPECT_EQ(0xffff, ports.get().mask());
+  EXPECT_EQ("[10,10]", stringify(ports.get()));
+
+  ports = ip::PortRange::fromBeginMask(20, 0xffff);
+  ASSERT_SOME(ports);
+  EXPECT_EQ(20u, ports.get().begin());
+  EXPECT_EQ(20u, ports.get().end());
+  EXPECT_EQ(0xffff, ports.get().mask());
+  EXPECT_EQ("[20,20]", stringify(ports.get()));
+
+  ports = ip::PortRange::fromBeginMask(1024, 0xfff8);
+  ASSERT_SOME(ports);
+  EXPECT_EQ(1024u, ports.get().begin());
+  EXPECT_EQ(1031u, ports.get().end());
+  EXPECT_EQ(0xfff8, ports.get().mask());
+  EXPECT_EQ("[1024,1031]", stringify(ports.get()));
+}
+
+
+TEST_F(RoutingTest, RouteTable)
+{
+  Try<vector<route::Rule> > table = route::table();
+  EXPECT_SOME(table);
+
+  Result<net::IP> gateway = route::defaultGateway();
+  EXPECT_FALSE(gateway.isError());
+}
+
+
+TEST_F(RoutingTest, LinkIndex)
+{
+  Try<set<string> > links = net::links();
+  ASSERT_SOME(links);
+
+  foreach (const string& link, links.get()) {
+    EXPECT_SOME_NE(0, link::index(link));
+  }
+
+  EXPECT_NONE(link::index("not-exist"));
+}
+
+
+TEST_F(RoutingTest, LinkName)
+{
+  Try<set<string> > links = net::links();
+  ASSERT_SOME(links);
+
+  foreach (const string& link, links.get()) {
+    EXPECT_SOME_NE(0, link::index(link));
+    EXPECT_SOME_EQ(link, link::name(link::index(link).get()));
+  }
+}
+
+
+TEST_F(RoutingTest, LinkStatistics)
+{
+  Try<set<string> > links = net::links();
+  ASSERT_SOME(links);
+
+  foreach (const string& link, links.get()) {
+    Result<hashmap<string, uint64_t> > statistics = link::statistics(link);
+
+    ASSERT_SOME(statistics);
+    EXPECT_TRUE(statistics.get().contains("rx_packets"));
+    EXPECT_TRUE(statistics.get().contains("rx_bytes"));
+    EXPECT_TRUE(statistics.get().contains("tx_packets"));
+    EXPECT_TRUE(statistics.get().contains("tx_bytes"));
+  }
+
+  EXPECT_NONE(link::statistics("not-exist"));
+}
+
+
+TEST_F(RoutingTest, LinkExists)
+{
+  Try<set<string> > links = net::links();
+  ASSERT_SOME(links);
+
+  foreach (const string& link, links.get()) {
+    EXPECT_SOME_TRUE(link::exists(link));
+  }
+
+  EXPECT_SOME_FALSE(link::exists("not-exist"));
+}
+
+
+TEST_F(RoutingTest, Eth0)
+{
+  Result<string> eth0 = link::eth0();
+  EXPECT_FALSE(eth0.isError());
+
+  if (eth0.isSome()) {
+    ASSERT_SOME_TRUE(link::exists(eth0.get()));
+  }
+}
+
+
+TEST_F(RoutingTest, Lo)
+{
+  Result<string> lo = link::lo();
+  EXPECT_FALSE(lo.isError());
+
+  if (lo.isSome()) {
+    ASSERT_SOME_TRUE(link::exists(lo.get()));
+  }
+}
+
+
+TEST_F(RoutingTest, INETSockets)
+{
+  Try<vector<diagnosis::socket::Info> > infos =
+    diagnosis::socket::infos(AF_INET, diagnosis::socket::state::ALL);
+
+  EXPECT_SOME(infos);
+
+  foreach (const diagnosis::socket::Info& info, infos.get()) {
+    // Both source and destination IPs should be present since
+    // 'AF_INET' is asked for.
+    EXPECT_SOME(info.sourceIP);
+    EXPECT_SOME(info.destinationIP);
+  }
+}
+
+
+TEST_F(RoutingVethTest, ROOT_LinkCreate)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  EXPECT_SOME_NE(0, link::index(TEST_VETH_LINK));
+  EXPECT_SOME_NE(0, link::index(TEST_PEER_LINK));
+
+  // Test the case where the veth (with the same name) already exists.
+  EXPECT_SOME_FALSE(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_LinkRemove)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::remove(TEST_VETH_LINK));
+  EXPECT_SOME_FALSE(link::remove(TEST_VETH_LINK));
+  EXPECT_SOME_FALSE(link::remove(TEST_PEER_LINK));
+}
+
+
+// An old glibc might not have this symbol.
+#ifndef CLONE_NEWNET
+#define CLONE_NEWNET 0x40000000
+#endif
+
+
+// Entry point of the child process (used in clone()).
+static int child(void*)
+{
+  // Wait to be killed.
+  while (true) {
+    sleep(1);
+  }
+
+  // Should not reach here.
+  ABORT("Child process should not reach here");
+}
+
+
+TEST_F(RoutingVethTest, ROOT_LinkCreatePid)
+{
+  // Stack used in the child process.
+  unsigned long long stack[32];
+
+  pid_t pid = ::clone(child, &stack[31], CLONE_NEWNET | SIGCHLD, NULL);
+  ASSERT_NE(-1, pid);
+
+  // In parent process.
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, pid));
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+
+  // The peer should not exist in parent network namespace.
+  EXPECT_SOME_FALSE(link::exists(TEST_PEER_LINK));
+
+  // TODO(jieyu): Enter the child network namespace and make sure that
+  // the TEST_PEER_LINK is there.
+
+  EXPECT_SOME_NE(0, link::index(TEST_VETH_LINK));
+
+  // Kill the child process.
+  ASSERT_NE(-1, kill(pid, SIGKILL));
+
+  // Wait for the child process.
+  int status;
+  EXPECT_NE(-1, waitpid((pid_t) -1, &status, 0));
+  ASSERT_TRUE(WIFSIGNALED(status));
+  EXPECT_EQ(SIGKILL, WTERMSIG(status));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_LinkWait)
+{
+  AWAIT_READY(link::removed(TEST_VETH_LINK));
+
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  Future<Nothing> removed = link::removed(TEST_VETH_LINK);
+  EXPECT_TRUE(removed.isPending());
+
+  ASSERT_SOME_TRUE(link::remove(TEST_VETH_LINK));
+  AWAIT_READY(removed);
+}
+
+
+TEST_F(RoutingVethTest, ROOT_LinkSetUp)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  EXPECT_SOME_FALSE(link::isUp(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::setUp(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::isUp(TEST_VETH_LINK));
+
+  EXPECT_SOME_FALSE(link::isUp(TEST_PEER_LINK));
+  EXPECT_SOME_TRUE(link::setUp(TEST_PEER_LINK));
+  EXPECT_SOME_TRUE(link::isUp(TEST_PEER_LINK));
+
+  EXPECT_NONE(link::isUp("non-exist"));
+  EXPECT_SOME_FALSE(link::setUp("non-exist"));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_LinkSetMAC)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  uint8_t bytes[6] = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc};
+
+  EXPECT_SOME_TRUE(link::setMAC(TEST_VETH_LINK, net::MAC(bytes)));
+  EXPECT_SOME_TRUE(link::setMAC(TEST_PEER_LINK, net::MAC(bytes)));
+
+  Result<net::MAC> mac = net::mac(TEST_VETH_LINK);
+
+  ASSERT_SOME(mac);
+  EXPECT_EQ(mac.get(), net::MAC(bytes));
+
+  mac = net::mac(TEST_PEER_LINK);
+
+  ASSERT_SOME(mac);
+  EXPECT_EQ(mac.get(), net::MAC(bytes));
+
+  EXPECT_SOME_FALSE(link::setMAC("non-exist", net::MAC(bytes)));
+
+  // Kernel will reject a multicast MAC address.
+  uint8_t multicast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+  EXPECT_ERROR(link::setMAC(TEST_VETH_LINK, net::MAC(multicast)));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_LinkMTU)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  EXPECT_SOME_TRUE(link::setMTU(TEST_VETH_LINK, 10000));
+
+  Result<unsigned int> mtu = link::mtu(TEST_VETH_LINK);
+  ASSERT_SOME(mtu);
+  EXPECT_EQ(10000u, mtu.get());
+
+  EXPECT_NONE(link::mtu("not-exist"));
+  EXPECT_SOME_FALSE(link::setMTU("not-exist", 1500));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_IngressQdisc)
+{
+  // Test for a qdisc on a nonexistent interface should fail.
+  EXPECT_SOME_FALSE(ingress::exists("noSuchInterface"));
+
+  EXPECT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  // Interface exists but does not have an ingress qdisc.
+  EXPECT_SOME_FALSE(ingress::exists(TEST_VETH_LINK));
+  EXPECT_SOME_FALSE(ingress::exists(TEST_PEER_LINK));
+
+  // Interfaces without qdisc established no data.
+  EXPECT_NONE(ingress::statistics(TEST_VETH_LINK));
+  EXPECT_NONE(ingress::statistics(TEST_PEER_LINK));
+
+  // Try to create an ingress qdisc on a nonexistent interface.
+  EXPECT_ERROR(ingress::create("noSuchInterface"));
+
+  // Create an ingress qdisc on an existing interface.
+  EXPECT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  // Interface exists and has an ingress qdisc.
+  EXPECT_SOME_TRUE(ingress::exists(TEST_VETH_LINK));
+  EXPECT_SOME_FALSE(ingress::exists(TEST_PEER_LINK));
+
+  // Interfaces which exist return at least the core statisitcs.
+  Result<hashmap<string, uint64_t>> stats = ingress::statistics(TEST_VETH_LINK);
+  ASSERT_SOME(stats);
+  EXPECT_TRUE(stats.get().contains(statistics::PACKETS));
+  EXPECT_TRUE(stats.get().contains(statistics::BYTES));
+  EXPECT_TRUE(stats.get().contains(statistics::RATE_BPS));
+  EXPECT_TRUE(stats.get().contains(statistics::RATE_PPS));
+  EXPECT_TRUE(stats.get().contains(statistics::QLEN));
+  EXPECT_TRUE(stats.get().contains(statistics::BACKLOG));
+  EXPECT_TRUE(stats.get().contains(statistics::DROPS));
+  EXPECT_TRUE(stats.get().contains(statistics::REQUEUES));
+  EXPECT_TRUE(stats.get().contains(statistics::OVERLIMITS));
+
+  // Interface without qdisc returns no data.
+  EXPECT_NONE(ingress::statistics(TEST_PEER_LINK));
+
+  // Try to create a second ingress qdisc on an existing interface.
+  EXPECT_SOME_FALSE(ingress::create(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(ingress::exists(TEST_VETH_LINK));
+  EXPECT_SOME_FALSE(ingress::exists(TEST_PEER_LINK));
+
+  // Remove the ingress qdisc.
+  EXPECT_SOME_TRUE(ingress::remove(TEST_VETH_LINK));
+  EXPECT_SOME_FALSE(ingress::exists(TEST_VETH_LINK));
+  EXPECT_SOME_FALSE(ingress::exists(TEST_PEER_LINK));
+
+  // Try to remove it from a nonexistent interface.
+  EXPECT_SOME_FALSE(ingress::remove("noSuchInterface"));
+
+  // Remove the ingress qdisc when it does not exist.
+  EXPECT_SOME_FALSE(ingress::remove(TEST_VETH_LINK));
+  EXPECT_SOME_FALSE(ingress::exists(TEST_VETH_LINK));
+  EXPECT_SOME_FALSE(ingress::exists(TEST_PEER_LINK));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_HTBQdisc)
+{
+  // Test for a qdisc on a nonexistent interface should fail.
+  EXPECT_SOME_FALSE(htb::exists("noSuchInterface", EGRESS_ROOT));
+
+  EXPECT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  // This test uses a common handle throughout
+  const Handle handle = Handle(1, 0);
+
+  // Interface exists but does not have an htb qdisc.
+  EXPECT_SOME_FALSE(htb::exists(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(htb::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+  // Interfaces without qdisc established no data.
+  EXPECT_NONE(htb::statistics(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_NONE(htb::statistics(TEST_PEER_LINK, EGRESS_ROOT));
+
+  // Try to create an htb qdisc on a nonexistent interface.
+  EXPECT_ERROR(htb::create("noSuchInterface", EGRESS_ROOT, handle));
+
+  // Create an htb qdisc on an existing interface.
+  EXPECT_SOME_TRUE(htb::create(TEST_VETH_LINK, EGRESS_ROOT, handle));
+
+  // Interface exists and has an htb qdisc.
+  EXPECT_SOME_TRUE(htb::exists(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(htb::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+  // Interfaces which exist return at least the core statisitcs.
+  Result<hashmap<string, uint64_t>> stats =
+      htb::statistics(TEST_VETH_LINK, EGRESS_ROOT);
+  ASSERT_SOME(stats);
+  EXPECT_TRUE(stats.get().contains(statistics::PACKETS));
+  EXPECT_TRUE(stats.get().contains(statistics::BYTES));
+  EXPECT_TRUE(stats.get().contains(statistics::RATE_BPS));
+  EXPECT_TRUE(stats.get().contains(statistics::RATE_PPS));
+  EXPECT_TRUE(stats.get().contains(statistics::QLEN));
+  EXPECT_TRUE(stats.get().contains(statistics::BACKLOG));
+  EXPECT_TRUE(stats.get().contains(statistics::DROPS));
+  EXPECT_TRUE(stats.get().contains(statistics::REQUEUES));
+  EXPECT_TRUE(stats.get().contains(statistics::OVERLIMITS));
+
+  // Interface without htb qdisc returns no data.
+  EXPECT_NONE(htb::statistics(TEST_PEER_LINK, EGRESS_ROOT));
+
+  // Try to create a second htb qdisc on an existing interface.
+  EXPECT_SOME_FALSE(htb::create(TEST_VETH_LINK, EGRESS_ROOT, handle));
+  EXPECT_SOME_TRUE(htb::exists(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(htb::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+  // Remove the htb qdisc.
+  EXPECT_SOME_TRUE(htb::remove(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(htb::exists(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(htb::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+  // Try to remove it from a nonexistent interface.
+  EXPECT_SOME_FALSE(htb::remove("noSuchInterface", EGRESS_ROOT));
+
+  // Remove the htb qdisc when it does not exist.
+  EXPECT_SOME_FALSE(htb::remove(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(htb::exists(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(htb::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+  // Try to create an htb qdisc on a nonexistent interface and
+  // default handle.
+  EXPECT_ERROR(htb::create("noSuchInterface", EGRESS_ROOT, None()));
+
+  // Create an htb qdisc on an existing interface.
+  EXPECT_SOME_TRUE(htb::create(TEST_VETH_LINK, EGRESS_ROOT, None()));
+
+  // Interface exists and has an htb qdisc.
+  EXPECT_SOME_TRUE(htb::exists(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(htb::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+  // Remove the htb qdisc.
+  EXPECT_SOME_TRUE(htb::remove(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(htb::exists(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(htb::exists(TEST_PEER_LINK, EGRESS_ROOT));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_FqCodeQdisc)
+{
+  // Test for a qdisc on a nonexistent interface should fail.
+  EXPECT_SOME_FALSE(fq_codel::exists("noSuchInterface", EGRESS_ROOT));
+
+  EXPECT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  // This test uses a common handle throughout
+  const Handle handle = Handle(1, 0);
+
+  // Interface exists but does not have an fq_codel qdisc.
+  EXPECT_SOME_FALSE(fq_codel::exists(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(fq_codel::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+  // Interfaces without qdisc established no data.
+  EXPECT_NONE(fq_codel::statistics(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_NONE(fq_codel::statistics(TEST_PEER_LINK, EGRESS_ROOT));
+
+  // Try to create an fq_codel qdisc on a nonexistent interface.
+  EXPECT_ERROR(fq_codel::create("noSuchInterface", EGRESS_ROOT, handle));
+
+  // Create an fq_codel qdisc on an existing interface.
+  EXPECT_SOME_TRUE(fq_codel::create(TEST_VETH_LINK, EGRESS_ROOT, handle));
+
+  // Interface exists and has an fq_codel qdisc.
+  EXPECT_SOME_TRUE(fq_codel::exists(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(fq_codel::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+  // Interfaces which exist return at least the core statisitcs.
+  Result<hashmap<string, uint64_t>> stats =
+      fq_codel::statistics(TEST_VETH_LINK, EGRESS_ROOT);
+  ASSERT_SOME(stats);
+  EXPECT_TRUE(stats.get().contains(statistics::PACKETS));
+  EXPECT_TRUE(stats.get().contains(statistics::BYTES));
+  EXPECT_TRUE(stats.get().contains(statistics::RATE_BPS));
+  EXPECT_TRUE(stats.get().contains(statistics::RATE_PPS));
+  EXPECT_TRUE(stats.get().contains(statistics::QLEN));
+  EXPECT_TRUE(stats.get().contains(statistics::BACKLOG));
+  EXPECT_TRUE(stats.get().contains(statistics::DROPS));
+  EXPECT_TRUE(stats.get().contains(statistics::REQUEUES));
+  EXPECT_TRUE(stats.get().contains(statistics::OVERLIMITS));
+
+  // Interface without fq_codel qdisc returns no data.
+  EXPECT_NONE(fq_codel::statistics(TEST_PEER_LINK, EGRESS_ROOT));
+
+  // Try to create a second fq_codel qdisc on an existing interface.
+  EXPECT_SOME_FALSE(fq_codel::create(TEST_VETH_LINK, EGRESS_ROOT, handle));
+  EXPECT_SOME_TRUE(fq_codel::exists(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(fq_codel::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+  // Remove the fq_codel qdisc.
+  EXPECT_SOME_TRUE(fq_codel::remove(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(fq_codel::exists(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(fq_codel::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+  // Try to remove it from a nonexistent interface.
+  EXPECT_SOME_FALSE(fq_codel::remove("noSuchInterface", EGRESS_ROOT));
+
+  // Remove the fq_codel qdisc when it does not exist.
+  EXPECT_SOME_FALSE(fq_codel::remove(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(fq_codel::exists(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(fq_codel::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+  // Try to create an fq_codel qdisc on a nonexistent interface and
+  // default handle.
+  EXPECT_ERROR(fq_codel::create("noSuchInterface", EGRESS_ROOT, None()));
+
+  // Create an fq_codel qdisc on an existing interface.
+  EXPECT_SOME_TRUE(fq_codel::create(TEST_VETH_LINK, EGRESS_ROOT, None()));
+
+  // Interface exists and has an fq_codel qdisc.
+  EXPECT_SOME_TRUE(fq_codel::exists(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(fq_codel::exists(TEST_PEER_LINK, EGRESS_ROOT));
+
+  // Remove the fq_codel qdisc.
+  EXPECT_SOME_TRUE(fq_codel::remove(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(fq_codel::exists(TEST_VETH_LINK, EGRESS_ROOT));
+  EXPECT_SOME_FALSE(fq_codel::exists(TEST_PEER_LINK, EGRESS_ROOT));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_FqCodelClassifier)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  const Handle handle = Handle(1, 0);
+  ASSERT_SOME_TRUE(fq_codel::create(TEST_VETH_LINK, EGRESS_ROOT, handle));
+
+  EXPECT_SOME_TRUE(basic::create(
+      TEST_VETH_LINK,
+      handle,
+      ETH_P_ALL,
+      None(),
+      Handle(handle, 0)));
+
+  EXPECT_SOME_TRUE(basic::exists(TEST_VETH_LINK, handle, ETH_P_ALL));
+
+  EXPECT_SOME_TRUE(basic::create(
+      TEST_VETH_LINK,
+      handle,
+      ETH_P_ARP,
+      None(),
+      Handle(handle, 0)));
+
+  // There is a kernel bug which could cause this test fail. Please
+  // make sure your kernel, if newer than 3.14, has commit:
+  // b057df24a7536cce6c372efe9d0e3d1558afedf4
+  // (https://git.kernel.org/cgit/linux/kernel/git/davem/net.git).
+  // Please fix your kernel if you see this failure.
+  EXPECT_SOME_TRUE(basic::exists(TEST_VETH_LINK, handle, ETH_P_ARP));
+
+  EXPECT_SOME_TRUE(icmp::create(
+      TEST_VETH_LINK,
+      handle,
+      icmp::Classifier(None()),
+      None(),
+      Handle(handle, 0)));
+
+  EXPECT_SOME_TRUE(icmp::exists(
+      TEST_VETH_LINK,
+      handle,
+      icmp::Classifier(None())));
+
+  Result<net::MAC> mac = net::mac(TEST_VETH_LINK);
+  ASSERT_SOME(mac);
+
+  net::IP ip = net::IP(0x01020304); // 1.2.3.4
+
+  Try<ip::PortRange> sourcePorts =
+    ip::PortRange::fromBeginEnd(1024, 1027);
+  ASSERT_SOME(sourcePorts);
+
+  Try<ip::PortRange> destinationPorts =
+    ip::PortRange::fromBeginEnd(2000, 2000);
+  ASSERT_SOME(destinationPorts);
+
+  ip::Classifier classifier =
+    ip::Classifier(
+        mac.get(),
+        ip,
+        sourcePorts.get(),
+        destinationPorts.get());
+
+  EXPECT_SOME_TRUE(ip::create(
+      TEST_VETH_LINK,
+      handle,
+      classifier,
+      None(),
+      Handle(handle, 1)));
+
+  EXPECT_SOME_TRUE(ip::exists(TEST_VETH_LINK, handle, classifier));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_ARPFilterCreate)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  EXPECT_SOME_TRUE(basic::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      ETH_P_ARP,
+      None(),
+      action::Redirect(TEST_PEER_LINK)));
+
+  EXPECT_SOME_TRUE(basic::exists(TEST_VETH_LINK, ingress::HANDLE, ETH_P_ARP));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_ARPFilterCreateDuplicated)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  set<string> links;
+  links.insert(TEST_PEER_LINK);
+
+  EXPECT_SOME_TRUE(basic::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      ETH_P_ARP,
+      None(),
+      action::Mirror(links)));
+
+  EXPECT_SOME_TRUE(basic::exists(TEST_VETH_LINK, ingress::HANDLE, ETH_P_ARP));
+
+  EXPECT_SOME_FALSE(basic::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      ETH_P_ARP,
+      None(),
+      action::Redirect(TEST_PEER_LINK)));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_ARPFilterRemove)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  set<string> links;
+  links.insert(TEST_PEER_LINK);
+
+  EXPECT_SOME_TRUE(basic::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      ETH_P_ARP,
+      None(),
+      action::Mirror(links)));
+
+  EXPECT_SOME_TRUE(basic::exists(TEST_VETH_LINK, ingress::HANDLE, ETH_P_ARP));
+  EXPECT_SOME_TRUE(basic::remove(TEST_VETH_LINK, ingress::HANDLE, ETH_P_ARP));
+  EXPECT_SOME_FALSE(basic::exists(TEST_VETH_LINK, ingress::HANDLE, ETH_P_ARP));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_ARPFilterUpdate)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  set<string> links;
+  links.insert(TEST_PEER_LINK);
+
+  EXPECT_SOME_FALSE(basic::update(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      ETH_P_ARP,
+      action::Mirror(links)));
+
+  EXPECT_SOME_TRUE(basic::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      ETH_P_ARP,
+      None(),
+      action::Redirect(TEST_PEER_LINK)));
+
+  EXPECT_SOME_TRUE(basic::exists(TEST_VETH_LINK, ingress::HANDLE, ETH_P_ARP));
+
+  EXPECT_SOME_TRUE(basic::update(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      ETH_P_ARP,
+      action::Mirror(links)));
+
+  EXPECT_SOME_TRUE(basic::exists(TEST_VETH_LINK, ingress::HANDLE, ETH_P_ARP));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_ICMPFilterCreate)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  net::IP ip = net::IP(0x01020304); // 1.2.3.4
+
+  EXPECT_SOME_TRUE(icmp::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(ip),
+      None(),
+      action::Redirect(TEST_PEER_LINK)));
+
+  EXPECT_SOME_TRUE(icmp::exists(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(ip)));
+
+  Result<vector<icmp::Classifier> > classifiers =
+    icmp::classifiers(TEST_VETH_LINK, ingress::HANDLE);
+
+  ASSERT_SOME(classifiers);
+  ASSERT_EQ(1u, classifiers.get().size());
+  EXPECT_SOME_EQ(ip, classifiers.get().front().destinationIP);
+}
+
+
+TEST_F(RoutingVethTest, ROOT_ICMPFilterCreateDuplicated)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  set<string> links;
+  links.insert(TEST_PEER_LINK);
+
+  EXPECT_SOME_TRUE(icmp::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None()),
+      None(),
+      action::Mirror(links)));
+
+  EXPECT_SOME_TRUE(icmp::exists(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None())));
+
+  EXPECT_SOME_FALSE(icmp::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None()),
+      None(),
+      action::Mirror(links)));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_ICMPFilterCreateMultiple)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  net::IP ip1 = net::IP(0x01020304); // 1.2.3.4
+  net::IP ip2 = net::IP(0x05060708); // 5.6.7.8
+
+  EXPECT_SOME_TRUE(icmp::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(ip1),
+      Priority(1, 1),
+      action::Redirect(TEST_PEER_LINK)));
+
+  EXPECT_SOME_TRUE(icmp::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(ip2),
+      Priority(1, 2),
+      action::Redirect(TEST_PEER_LINK)));
+
+  Result<vector<icmp::Classifier> > classifiers =
+    icmp::classifiers(TEST_VETH_LINK, ingress::HANDLE);
+
+  ASSERT_SOME(classifiers);
+  ASSERT_EQ(2u, classifiers.get().size());
+  EXPECT_SOME_EQ(ip1, classifiers.get().front().destinationIP);
+  EXPECT_SOME_EQ(ip2, classifiers.get().back().destinationIP);
+}
+
+
+TEST_F(RoutingVethTest, ROOT_ICMPFilterRemove)
+{
+  ASSERT_SOME(link::create(
+      TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  EXPECT_SOME_TRUE(icmp::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None()),
+      None(),
+      action::Redirect(TEST_PEER_LINK)));
+
+  EXPECT_SOME_TRUE(icmp::exists(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None())));
+
+  EXPECT_SOME_TRUE(icmp::remove(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None())));
+
+  EXPECT_SOME_FALSE(icmp::exists(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None())));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_ICMPFilterUpdate)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  net::IP ip = net::IP(0x01020304); // 1.2.3.4
+
+  set<string> links;
+  links.insert(TEST_PEER_LINK);
+
+  EXPECT_SOME_FALSE(icmp::update(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None()),
+      action::Mirror(links)));
+
+  EXPECT_SOME_TRUE(icmp::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None()),
+      None(),
+      action::Redirect(TEST_PEER_LINK)));
+
+  EXPECT_SOME_TRUE(icmp::exists(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None())));
+
+  EXPECT_SOME_FALSE(icmp::update(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(ip),
+      action::Mirror(links)));
+
+  EXPECT_SOME_TRUE(icmp::update(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None()),
+      action::Mirror(links)));
+
+  EXPECT_SOME_TRUE(icmp::exists(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(None())));
+
+  EXPECT_SOME_FALSE(icmp::exists(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      icmp::Classifier(ip)));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_IPFilterCreate)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  Result<net::MAC> mac = net::mac(TEST_VETH_LINK);
+  ASSERT_SOME(mac);
+
+  net::IP ip = net::IP(0x01020304); // 1.2.3.4
+
+  Try<ip::PortRange> sourcePorts =
+    ip::PortRange::fromBeginEnd(1024, 1027);
+
+  ASSERT_SOME(sourcePorts);
+
+  Try<ip::PortRange> destinationPorts =
+    ip::PortRange::fromBeginEnd(2000, 2000);
+
+  ASSERT_SOME(destinationPorts);
+
+  ip::Classifier classifier =
+    ip::Classifier(
+        mac.get(),
+        ip,
+        sourcePorts.get(),
+        destinationPorts.get());
+
+  EXPECT_SOME_TRUE(ip::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      classifier,
+      None(),
+      action::Redirect(TEST_PEER_LINK)));
+
+  EXPECT_SOME_TRUE(ip::exists(TEST_VETH_LINK, ingress::HANDLE, classifier));
+
+  Result<vector<ip::Classifier> > classifiers =
+    ip::classifiers(TEST_VETH_LINK, ingress::HANDLE);
+
+  ASSERT_SOME(classifiers);
+  ASSERT_EQ(1u, classifiers.get().size());
+  EXPECT_SOME_EQ(mac.get(), classifiers.get().front().destinationMAC);
+  EXPECT_SOME_EQ(ip, classifiers.get().front().destinationIP);
+
+  EXPECT_SOME_EQ(
+      sourcePorts.get(),
+      classifiers.get().front().sourcePorts);
+
+  EXPECT_SOME_EQ(
+      destinationPorts.get(),
+      classifiers.get().front().destinationPorts);
+}
+
+
+TEST_F(RoutingVethTest, ROOT_IPFilterCreate2)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  net::IP ip(0x12345678);
+
+  EXPECT_SOME_TRUE(ip::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      ip::Classifier(None(), ip, None(), None()),
+      None(),
+      action::Redirect(TEST_PEER_LINK)));
+
+  EXPECT_SOME_TRUE(ip::exists(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      ip::Classifier(None(), ip, None(), None())));
+
+  Result<vector<ip::Classifier> > classifiers =
+    ip::classifiers(TEST_VETH_LINK, ingress::HANDLE);
+
+  ASSERT_SOME(classifiers);
+  ASSERT_EQ(1u, classifiers.get().size());
+  EXPECT_NONE(classifiers.get().front().destinationMAC);
+  EXPECT_SOME_EQ(ip, classifiers.get().front().destinationIP);
+  EXPECT_NONE(classifiers.get().front().sourcePorts);
+  EXPECT_NONE(classifiers.get().front().destinationPorts);
+}
+
+
+TEST_F(RoutingVethTest, ROOT_IPFilterCreateDuplicated)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  Result<net::MAC> mac = net::mac(TEST_VETH_LINK);
+  ASSERT_SOME(mac);
+
+  net::IP ip = net::IP(0x01020304); // 1.2.3.4
+
+  Try<ip::PortRange> sourcePorts =
+    ip::PortRange::fromBeginEnd(1024, 1027);
+
+  ASSERT_SOME(sourcePorts);
+
+  Try<ip::PortRange> destinationPorts =
+    ip::PortRange::fromBeginEnd(2000, 2000);
+
+  ASSERT_SOME(destinationPorts);
+
+  ip::Classifier classifier =
+    ip::Classifier(
+        mac.get(),
+        ip,
+        sourcePorts.get(),
+        destinationPorts.get());
+
+  EXPECT_SOME_TRUE(ip::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      classifier,
+      None(),
+      action::Redirect(TEST_PEER_LINK)));
+
+  EXPECT_SOME_TRUE(ip::exists(TEST_VETH_LINK, ingress::HANDLE, classifier));
+
+  EXPECT_SOME_FALSE(ip::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      classifier,
+      None(),
+      action::Redirect(TEST_PEER_LINK)));
+}
+
+
+TEST_F(RoutingVethTest, ROOT_IPFilterCreateMultiple)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  Result<net::MAC> mac = net::mac(TEST_VETH_LINK);
+  ASSERT_SOME(mac);
+
+  net::IP ip = net::IP(0x01020304); // 1.2.3.4
+
+  Try<ip::PortRange> sourcePorts1 =
+    ip::PortRange::fromBeginEnd(1024, 1027);
+
+  ASSERT_SOME(sourcePorts1);
+
+  Try<ip::PortRange> destinationPorts1 =
+    ip::PortRange::fromBeginEnd(2000, 2000);
+
+  ASSERT_SOME(destinationPorts1);
+
+  Try<ip::PortRange> sourcePorts2 =
+    ip::PortRange::fromBeginEnd(3024, 3025);
+
+  ASSERT_SOME(sourcePorts2);
+
+  Try<ip::PortRange> destinationPorts2 =
+    ip::PortRange::fromBeginEnd(4000, 4003);
+
+  ASSERT_SOME(destinationPorts2);
+
+  ip::Classifier classifier1 =
+    ip::Classifier(
+        mac.get(),
+        ip,
+        sourcePorts1.get(),
+        destinationPorts1.get());
+
+  ip::Classifier classifier2 =
+    ip::Classifier(
+        mac.get(),
+        ip,
+        sourcePorts2.get(),
+        destinationPorts2.get());
+
+  EXPECT_SOME_TRUE(ip::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      classifier1,
+      Priority(2, 1),
+      action::Redirect(TEST_PEER_LINK)));
+
+  EXPECT_SOME_TRUE(ip::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      classifier2,
+      Priority(2, 2),
+      action::Redirect(TEST_PEER_LINK)));
+
+  Result<vector<ip::Classifier> > classifiers =
+    ip::classifiers(TEST_VETH_LINK, ingress::HANDLE);
+
+  ASSERT_SOME(classifiers);
+  ASSERT_EQ(2u, classifiers.get().size());
+
+  EXPECT_SOME_EQ(mac.get(), classifiers.get().front().destinationMAC);
+  EXPECT_SOME_EQ(ip, classifiers.get().front().destinationIP);
+
+  EXPECT_SOME_EQ(
+      sourcePorts1.get(),
+      classifiers.get().front().sourcePorts);
+
+  EXPECT_SOME_EQ(
+      destinationPorts1.get(),
+      classifiers.get().front().destinationPorts);
+
+  EXPECT_SOME_EQ(mac.get(), classifiers.get().back().destinationMAC);
+  EXPECT_SOME_EQ(ip, classifiers.get().back().destinationIP);
+
+  EXPECT_SOME_EQ(
+      sourcePorts2.get(),
+      classifiers.get().back().sourcePorts);
+
+  EXPECT_SOME_EQ(
+      destinationPorts2.get(),
+      classifiers.get().back().destinationPorts);
+}
+
+
+TEST_F(RoutingVethTest, ROOT_IPFilterRemove)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  Result<net::MAC> mac = net::mac(TEST_VETH_LINK);
+  ASSERT_SOME(mac);
+
+  net::IP ip = net::IP(0x01020304); // 1.2.3.4
+
+  Try<ip::PortRange> sourcePorts1 =
+    ip::PortRange::fromBeginEnd(1024, 1027);
+
+  ASSERT_SOME(sourcePorts1);
+
+  Try<ip::PortRange> destinationPorts1 =
+    ip::PortRange::fromBeginEnd(2000, 2000);
+
+  ASSERT_SOME(destinationPorts1);
+
+  Try<ip::PortRange> sourcePorts2 =
+    ip::PortRange::fromBeginEnd(3024, 3025);
+
+  ASSERT_SOME(sourcePorts2);
+
+  Try<ip::PortRange> destinationPorts2 =
+    ip::PortRange::fromBeginEnd(4000, 4003);
+
+  ASSERT_SOME(destinationPorts2);
+
+  ip::Classifier classifier1 =
+    ip::Classifier(
+        mac.get(),
+        ip,
+        sourcePorts1.get(),
+        destinationPorts1.get());
+
+  ip::Classifier classifier2 =
+    ip::Classifier(
+        mac.get(),
+        ip,
+        sourcePorts2.get(),
+        destinationPorts2.get());
+
+  EXPECT_SOME_TRUE(ip::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      classifier1,
+      None(),
+      action::Redirect(TEST_PEER_LINK)));
+
+  EXPECT_SOME_TRUE(ip::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      classifier2,
+      None(),
+      action::Redirect(TEST_PEER_LINK)));
+
+  EXPECT_SOME_TRUE(ip::remove(TEST_VETH_LINK, ingress::HANDLE, classifier1));
+  EXPECT_SOME_FALSE(ip::exists(TEST_VETH_LINK, ingress::HANDLE, classifier1));
+
+  EXPECT_SOME_TRUE(ip::remove(TEST_VETH_LINK, ingress::HANDLE, classifier2));
+  EXPECT_SOME_FALSE(ip::exists(TEST_VETH_LINK, ingress::HANDLE, classifier2));
+
+  Result<vector<ip::Classifier> > classifiers =
+    ip::classifiers(TEST_VETH_LINK, ingress::HANDLE);
+
+  ASSERT_SOME(classifiers);
+  EXPECT_EQ(0u, classifiers.get().size());
+}
+
+
+// Test the workaround introduced for MESOS-1617.
+TEST_F(RoutingVethTest, ROOT_HandleGeneration)
+{
+  ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None()));
+
+  EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK));
+  EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK));
+
+  ASSERT_SOME_TRUE(ingress::create(TEST_VETH_LINK));
+
+  Result<net::MAC> mac = net::mac(TEST_VETH_LINK);
+  ASSERT_SOME(mac);
+
+  net::IP ip = net::IP(0x01020304); // 1.2.3.4
+
+  Try<ip::PortRange> sourcePorts1 =
+    ip::PortRange::fromBeginEnd(1024, 1027);
+
+  ASSERT_SOME(sourcePorts1);
+
+  Try<ip::PortRange> destinationPorts1 =
+    ip::PortRange::fromBeginEnd(2000, 2000);
+
+  ASSERT_SOME(destinationPorts1);
+
+  Try<ip::PortRange> sourcePorts2 =
+    ip::PortRange::fromBeginEnd(3024, 3025);
+
+  ASSERT_SOME(sourcePorts2);
+
+  Try<ip::PortRange> destinationPorts2 =
+    ip::PortRange::fromBeginEnd(4000, 4003);
+
+  ASSERT_SOME(destinationPorts2);
+
+  ip::Classifier classifier1 =
+    ip::Classifier(
+        mac.get(),
+        ip,
+        sourcePorts1.get(),
+        destinationPorts1.get());
+
+  ip::Classifier classifier2 =
+    ip::Classifier(
+        mac.get(),
+        ip,
+        sourcePorts2.get(),
+        destinationPorts2.get());
+
+  // Use handle 800:00:fff for the first filter.
+  EXPECT_SOME_TRUE(ip::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      classifier1,
+      Priority(2, 1),
+      U32Handle(0x800, 0x0, 0xfff),
+      action::Redirect(TEST_PEER_LINK)));
+
+  // With the workaround, this filter should be assigned a handle
+  // different than 800:00:fff.
+  EXPECT_SOME_TRUE(ip::create(
+      TEST_VETH_LINK,
+      ingress::HANDLE,
+      classifier2,
+      Priority(2, 1),
+      action::Redirect(TEST_PEER_LINK)));
+
+  // Try to remove the second filter. If we don't have the workaround,
+  // removing the second filter will return false since the kernel
+  // will find the handle matches the first filter.
+  EXPECT_SOME_TRUE(ip::remove(TEST_VETH_LINK, ingress::HANDLE, classifier2));
+}
+
+} // namespace tests {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer/sched_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/sched_tests.cpp b/src/tests/containerizer/sched_tests.cpp
new file mode 100644
index 0000000..00723d0
--- /dev/null
+++ b/src/tests/containerizer/sched_tests.cpp
@@ -0,0 +1,96 @@
+/**
+ * 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 "linux/sched.hpp"
+
+#include <process/gtest.hpp>
+#include <process/reap.hpp>
+
+#include <gtest/gtest.h>
+
+#include <stout/gtest.hpp>
+
+using sched::Policy;
+
+namespace mesos {
+namespace internal {
+namespace tests {
+
+// TODO(idownes): Test the priority and preemption behavior for
+// running competing SCHED_OTHER and SCHED_IDLE tasks.
+
+TEST(SchedTest, ROOT_PolicySelf)
+{
+  Try<Policy> original = sched::policy::get();
+  ASSERT_SOME(original);
+
+  Policy different = (original.get() == Policy::OTHER ? Policy::IDLE
+                                                      : Policy::OTHER);
+
+  // Change our own scheduling policy.
+  EXPECT_SOME(sched::policy::set(different));
+  EXPECT_SOME_EQ(different, sched::policy::get());
+
+  // Change it back.
+  EXPECT_SOME(sched::policy::set(original.get()));
+  EXPECT_SOME_EQ(original.get(), sched::policy::get());
+}
+
+
+// Change the scheduling policy of a different process (our child).
+TEST(SchedTest, ROOT_PolicyChild)
+{
+  Try<Policy> original = sched::policy::get();
+  ASSERT_SOME(original);
+
+  Policy different = (original.get() == Policy::OTHER ? Policy::IDLE
+                                                      : Policy::OTHER);
+
+  pid_t pid = ::fork();
+  ASSERT_NE(-1, pid);
+
+  if (pid == 0) {
+    // Child.
+    sleep(10);
+
+    ABORT("Child process should not reach here");
+  }
+
+  // Continue in parent.
+  // Check the child has inherited our policy.
+  EXPECT_SOME_EQ(original.get(), sched::policy::get(pid));
+
+  // Check we can change the child's policy.
+  EXPECT_SOME(sched::policy::set(different, pid));
+  EXPECT_SOME_EQ(different, sched::policy::get(pid));
+
+  process::Future<Option<int>> status = process::reap(pid);
+
+  // Kill the child process.
+  ASSERT_NE(-1, ::kill(pid, SIGKILL));
+
+  // Wait for the child process.
+  AWAIT_READY(status);
+  ASSERT_SOME(status.get());
+  EXPECT_TRUE(WIFSIGNALED(status.get().get()));
+  EXPECT_EQ(SIGKILL, WTERMSIG(status.get().get()));
+}
+
+} // namespace tests {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer/setns_test_helper.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/setns_test_helper.cpp b/src/tests/containerizer/setns_test_helper.cpp
new file mode 100644
index 0000000..ec68b08
--- /dev/null
+++ b/src/tests/containerizer/setns_test_helper.cpp
@@ -0,0 +1,63 @@
+/**
+ * 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 <set>
+#include <string>
+
+#include <stout/foreach.hpp>
+#include <stout/subcommand.hpp>
+#include <stout/try.hpp>
+
+#include "linux/ns.hpp"
+
+#include "tests/containerizer/setns_test_helper.hpp"
+
+using std::set;
+using std::string;
+
+const char SetnsTestHelper::NAME[] = "SetnsTestHelper";
+
+int SetnsTestHelper::execute()
+{
+  // Get all the available namespaces.
+  set<string> namespaces = ns::namespaces();
+
+  // Note: /proc has not been remounted so we can look up pid 1's
+  // namespaces, even if we're in a separate pid namespace.
+  foreach (const string& ns, namespaces) {
+    if (ns == "pid") {
+      // ns::setns() does not (currently) support pid namespaces so
+      // this should return an error.
+      Try<Nothing> setns = ns::setns(1, ns);
+      if (!setns.isError()) {
+        return 1;
+      }
+    } else if (ns == "user") {
+      // ns::setns() will also fail with user namespaces, so we skip
+      // for now. See MESOS-3083.
+      continue;
+    } else {
+      Try<Nothing> setns = ns::setns(1, ns);
+      if (!setns.isSome()) {
+        return 1;
+      }
+    }
+  }
+
+  return 0;
+}

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer/setns_test_helper.hpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/setns_test_helper.hpp b/src/tests/containerizer/setns_test_helper.hpp
new file mode 100644
index 0000000..51d6378
--- /dev/null
+++ b/src/tests/containerizer/setns_test_helper.hpp
@@ -0,0 +1,35 @@
+/**
+ * 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 __SETNS_TEST_HELPER_HPP__
+#define __SETNS_TEST_HELPER_HPP__
+
+#include <stout/subcommand.hpp>
+
+class SetnsTestHelper : public Subcommand
+{
+public:
+  static const char NAME[];
+
+  SetnsTestHelper() : Subcommand(NAME) {}
+
+protected:
+  virtual int execute();
+};
+
+#endif // __SETNS_TEST_HELPER_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer/setns_test_helper_main.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/setns_test_helper_main.cpp b/src/tests/containerizer/setns_test_helper_main.cpp
new file mode 100644
index 0000000..c8e270a
--- /dev/null
+++ b/src/tests/containerizer/setns_test_helper_main.cpp
@@ -0,0 +1,30 @@
+/**
+ * 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 <stout/subcommand.hpp>
+
+#include "tests/containerizer/setns_test_helper.hpp"
+
+int main(int argc, char** argv)
+{
+  return Subcommand::dispatch(
+      None(),
+      argc,
+      argv,
+      new SetnsTestHelper());
+}

http://git-wip-us.apache.org/repos/asf/mesos/blob/96351372/src/tests/containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer_tests.cpp b/src/tests/containerizer_tests.cpp
deleted file mode 100644
index 9508613..0000000
--- a/src/tests/containerizer_tests.cpp
+++ /dev/null
@@ -1,731 +0,0 @@
-/**
- * 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 <vector>
-
-#include <gmock/gmock.h>
-
-#include <mesos/mesos.hpp>
-
-#include <mesos/slave/isolator.hpp>
-
-#include <process/future.hpp>
-#include <process/owned.hpp>
-
-#include <stout/strings.hpp>
-
-#include "slave/flags.hpp"
-
-#include "slave/containerizer/fetcher.hpp"
-#include "slave/containerizer/launcher.hpp"
-
-#include "slave/containerizer/mesos/containerizer.hpp"
-
-#include "tests/flags.hpp"
-#include "tests/isolator.hpp"
-#include "tests/launcher.hpp"
-#include "tests/mesos.hpp"
-#include "tests/utils.hpp"
-
-using namespace process;
-
-using mesos::internal::master::Master;
-
-using mesos::internal::slave::Fetcher;
-using mesos::internal::slave::Launcher;
-using mesos::internal::slave::MesosContainerizer;
-using mesos::internal::slave::MesosContainerizerProcess;
-using mesos::internal::slave::PosixLauncher;
-using mesos::internal::slave::Provisioner;
-using mesos::internal::slave::Slave;
-
-using mesos::internal::slave::state::ExecutorState;
-using mesos::internal::slave::state::FrameworkState;
-using mesos::internal::slave::state::RunState;
-using mesos::internal::slave::state::SlaveState;
-
-using mesos::slave::ExecutorLimitation;
-using mesos::slave::ExecutorRunState;
-using mesos::slave::Isolator;
-using mesos::slave::IsolatorProcess;
-
-using std::list;
-using std::map;
-using std::string;
-using std::vector;
-
-using testing::_;
-using testing::DoAll;
-using testing::Invoke;
-using testing::Return;
-
-namespace mesos {
-namespace internal {
-namespace tests {
-
-class MesosContainerizerIsolatorPreparationTest :
-  public TemporaryDirectoryTest
-{
-public:
-  // Construct a MesosContainerizer with TestIsolator(s) which use the provided
-  // 'prepare' command(s).
-  Try<MesosContainerizer*> CreateContainerizer(
-      Fetcher* fetcher,
-      const vector<Option<CommandInfo>>& prepares)
-  {
-    vector<Owned<Isolator>> isolators;
-
-    foreach (const Option<CommandInfo>& prepare, prepares) {
-      Try<Isolator*> isolator = TestIsolatorProcess::create(prepare);
-      if (isolator.isError()) {
-        return Error(isolator.error());
-      }
-
-      isolators.push_back(Owned<Isolator>(isolator.get()));
-    }
-
-    slave::Flags flags;
-    flags.launcher_dir = path::join(tests::flags.build_dir, "src");
-
-    Try<Launcher*> launcher = PosixLauncher::create(flags);
-    if (launcher.isError()) {
-      return Error(launcher.error());
-    }
-
-    return new MesosContainerizer(
-        flags,
-        false,
-        fetcher,
-        Owned<Launcher>(launcher.get()),
-        isolators,
-        hashmap<ContainerInfo::Image::Type, Owned<Provisioner>>());
-  }
-
-  Try<MesosContainerizer*> CreateContainerizer(
-      Fetcher* fetcher,
-      const Option<CommandInfo>& prepare)
-  {
-    vector<Option<CommandInfo>> prepares;
-    prepares.push_back(prepare);
-
-    return CreateContainerizer(fetcher, prepares);
-  }
-};
-
-
-// The isolator has a prepare command that succeeds.
-TEST_F(MesosContainerizerIsolatorPreparationTest, ScriptSucceeds)
-{
-  string directory = os::getcwd(); // We're inside a temporary sandbox.
-  string file = path::join(directory, "child.script.executed");
-
-  Fetcher fetcher;
-
-  Try<MesosContainerizer*> containerizer = CreateContainerizer(
-      &fetcher,
-      CREATE_COMMAND_INFO("touch " + file));
-
-  CHECK_SOME(containerizer);
-
-  ContainerID containerId;
-  containerId.set_value("test_container");
-
-  Future<bool> launch = containerizer.get()->launch(
-      containerId,
-      CREATE_EXECUTOR_INFO("executor", "exit 0"),
-      directory,
-      None(),
-      SlaveID(),
-      PID<Slave>(),
-      false);
-
-  // Wait until the launch completes.
-  AWAIT_READY(launch);
-
-  // Wait for the child (preparation script + executor) to complete.
-  Future<containerizer::Termination> wait =
-    containerizer.get()->wait(containerId);
-
-  AWAIT_READY(wait);
-
-  // Check the child exited correctly.
-  EXPECT_TRUE(wait.get().has_status());
-  EXPECT_EQ(0, wait.get().status());
-
-  // Check the preparation script actually ran.
-  EXPECT_TRUE(os::exists(file));
-
-  // Destroy the container.
-  containerizer.get()->destroy(containerId);
-
-  delete containerizer.get();
-}
-
-
-// The isolator has a prepare command that fails.
-TEST_F(MesosContainerizerIsolatorPreparationTest, ScriptFails)
-{
-  string directory = os::getcwd(); // We're inside a temporary sandbox.
-  string file = path::join(directory, "child.script.executed");
-
-  Fetcher fetcher;
-
-  Try<MesosContainerizer*> containerizer = CreateContainerizer(
-      &fetcher,
-      CREATE_COMMAND_INFO("touch " + file + " && exit 1"));
-
-  CHECK_SOME(containerizer);
-
-  ContainerID containerId;
-  containerId.set_value("test_container");
-
-  Future<bool> launch = containerizer.get()->launch(
-      containerId,
-      CREATE_EXECUTOR_INFO("executor", "exit 0"),
-      directory,
-      None(),
-      SlaveID(),
-      PID<Slave>(),
-      false);
-
-  // Wait until the launch completes.
-  AWAIT_READY(launch);
-
-  // Wait for the child (preparation script + executor) to complete.
-  Future<containerizer::Termination> wait =
-    containerizer.get()->wait(containerId);
-
-  AWAIT_READY(wait);
-
-  // Check the child failed to exit correctly.
-  EXPECT_TRUE(wait.get().has_status());
-  EXPECT_NE(0, wait.get().status());
-
-  // Check the preparation script actually ran.
-  EXPECT_TRUE(os::exists(file));
-
-  // Destroy the container.
-  containerizer.get()->destroy(containerId);
-
-  delete containerizer.get();
-}
-
-
-// There are two isolators, one with a prepare command that succeeds
-// and another that fails. The execution order is not defined but the
-// launch should fail from the failing prepare command.
-TEST_F(MesosContainerizerIsolatorPreparationTest, MultipleScripts)
-{
-  string directory = os::getcwd(); // We're inside a temporary sandbox.
-  string file1 = path::join(directory, "child.script.executed.1");
-  string file2 = path::join(directory, "child.script.executed.2");
-
-  vector<Option<CommandInfo>> prepares;
-
-  // This isolator prepare command one will succeed if called first,
-  // otherwise it won't get run.
-  prepares.push_back(CREATE_COMMAND_INFO("touch " + file1 + " && exit 0"));
-
-  // This will fail, either first or after the successful command.
-  prepares.push_back(CREATE_COMMAND_INFO("touch " + file2 + " && exit 1"));
-
-  Fetcher fetcher;
-
-  Try<MesosContainerizer*> containerizer =
-    CreateContainerizer(&fetcher, prepares);
-
-  CHECK_SOME(containerizer);
-
-  ContainerID containerId;
-  containerId.set_value("test_container");
-
-  Future<bool> launch = containerizer.get()->launch(
-      containerId,
-      CREATE_EXECUTOR_INFO("executor", "exit 0"),
-      directory,
-      None(),
-      SlaveID(),
-      PID<Slave>(),
-      false);
-
-  // Wait until the launch completes.
-  AWAIT_READY(launch);
-
-  // Wait for the child (preparation script(s) + executor) to complete.
-  Future<containerizer::Termination> wait =
-    containerizer.get()->wait(containerId);
-  AWAIT_READY(wait);
-
-  // Check the child failed to exit correctly.
-  EXPECT_TRUE(wait.get().has_status());
-  EXPECT_NE(0, wait.get().status());
-
-  // Check the failing preparation script has actually ran.
-  EXPECT_TRUE(os::exists(file2));
-
-  // Destroy the container.
-  containerizer.get()->destroy(containerId);
-
-  delete containerizer.get();
-}
-
-
-class MesosContainerizerExecuteTest : public TemporaryDirectoryTest {};
-
-
-TEST_F(MesosContainerizerExecuteTest, IoRedirection)
-{
-  string directory = os::getcwd(); // We're inside a temporary sandbox.
-
-  slave::Flags flags;
-  flags.launcher_dir = path::join(tests::flags.build_dir, "src");
-
-  Fetcher fetcher;
-
-  // Use local=false so std{err,out} are redirected to files.
-  Try<MesosContainerizer*> containerizer =
-    MesosContainerizer::create(flags, false, &fetcher);
-
-  ASSERT_SOME(containerizer);
-
-  ContainerID containerId;
-  containerId.set_value("test_container");
-
-  string errMsg = "this is stderr";
-  string outMsg = "this is stdout";
-  string command =
-    "(echo '" + errMsg + "' 1>&2) && echo '" + outMsg + "'";
-
-  Future<bool> launch = containerizer.get()->launch(
-      containerId,
-      CREATE_EXECUTOR_INFO("executor", command),
-      directory,
-      None(),
-      SlaveID(),
-      PID<Slave>(),
-      false);
-
-  // Wait for the launch to complete.
-  AWAIT_READY(launch);
-
-  // Wait on the container.
-  Future<containerizer::Termination> wait =
-    containerizer.get()->wait(containerId);
-
-  AWAIT_READY(wait);
-
-  // Check the executor exited correctly.
-  EXPECT_TRUE(wait.get().has_status());
-  EXPECT_EQ(0, wait.get().status());
-
-  // Check that std{err, out} was redirected.
-  // NOTE: Fetcher uses GLOG, which outputs extra information to
-  // stderr.
-  Try<string> stderr = os::read(path::join(directory, "stderr"));
-  ASSERT_SOME(stderr);
-  EXPECT_TRUE(strings::contains(stderr.get(), errMsg));
-
-  EXPECT_SOME_EQ(outMsg + "\n", os::read(path::join(directory, "stdout")));
-
-  delete containerizer.get();
-}
-
-
-class MesosContainerizerDestroyTest : public MesosTest {};
-
-
-class MockMesosContainerizerProcess : public MesosContainerizerProcess
-{
-public:
-  MockMesosContainerizerProcess(
-      const slave::Flags& flags,
-      bool local,
-      Fetcher* fetcher,
-      const Owned<Launcher>& launcher,
-      const vector<Owned<Isolator>>& isolators,
-      const hashmap<ContainerInfo::Image::Type,
-                    Owned<Provisioner>>& provisioners)
-    : MesosContainerizerProcess(
-          flags,
-          local,
-          fetcher,
-          launcher,
-          isolators,
-          provisioners)
-  {
-    // NOTE: See TestContainerizer::setup for why we use
-    // 'EXPECT_CALL' and 'WillRepeatedly' here instead of
-    // 'ON_CALL' and 'WillByDefault'.
-    EXPECT_CALL(*this, exec(_, _))
-      .WillRepeatedly(Invoke(this, &MockMesosContainerizerProcess::_exec));
-  }
-
-  MOCK_METHOD2(
-      exec,
-      Future<bool>(
-          const ContainerID& containerId,
-          int pipeWrite));
-
-  Future<bool> _exec(
-      const ContainerID& containerId,
-      int pipeWrite)
-  {
-    return MesosContainerizerProcess::exec(
-        containerId,
-        pipeWrite);
-  }
-};
-
-
-class MockIsolator : public mesos::slave::Isolator
-{
-public:
-  MockIsolator()
-  {
-    EXPECT_CALL(*this, watch(_))
-      .WillRepeatedly(Return(watchPromise.future()));
-
-    EXPECT_CALL(*this, isolate(_, _))
-      .WillRepeatedly(Return(Nothing()));
-
-    EXPECT_CALL(*this, cleanup(_))
-      .WillRepeatedly(Return(Nothing()));
-
-    EXPECT_CALL(*this, prepare(_, _, _, _, _))
-      .WillRepeatedly(Invoke(this, &MockIsolator::_prepare));
-  }
-
-  MOCK_METHOD2(
-      recover,
-      Future<Nothing>(
-          const list<ExecutorRunState>&,
-          const hashset<ContainerID>&));
-
-  MOCK_METHOD5(
-      prepare,
-      Future<Option<CommandInfo>>(
-          const ContainerID&,
-          const ExecutorInfo&,
-          const string&,
-          const Option<string>&,
-          const Option<string>&));
-
-  virtual Future<Option<CommandInfo>> _prepare(
-      const ContainerID& containerId,
-      const ExecutorInfo& executorInfo,
-      const string& directory,
-      const Option<string>& rootfs,
-      const Option<string>& user)
-  {
-    return None();
-  }
-
-  MOCK_METHOD2(
-      isolate,
-      Future<Nothing>(const ContainerID&, pid_t));
-
-  MOCK_METHOD1(
-      watch,
-      Future<mesos::slave::ExecutorLimitation>(const ContainerID&));
-
-  MOCK_METHOD2(
-      update,
-      Future<Nothing>(const ContainerID&, const Resources&));
-
-  MOCK_METHOD1(
-      usage,
-      Future<ResourceStatistics>(const ContainerID&));
-
-  MOCK_METHOD1(
-      cleanup,
-      Future<Nothing>(const ContainerID&));
-
-  Promise<mesos::slave::ExecutorLimitation> watchPromise;
-};
-
-
-// Destroying a mesos containerizer while it is fetching should
-// complete without waiting for the fetching to finish.
-TEST_F(MesosContainerizerDestroyTest, DestroyWhileFetching)
-{
-  slave::Flags flags = CreateSlaveFlags();
-
-  Try<Launcher*> launcher = PosixLauncher::create(flags);
-  ASSERT_SOME(launcher);
-
-  Fetcher fetcher;
-
-  MockMesosContainerizerProcess* process = new MockMesosContainerizerProcess(
-      flags,
-      true,
-      &fetcher,
-      Owned<Launcher>(launcher.get()),
-      vector<Owned<Isolator>>(),
-      hashmap<ContainerInfo::Image::Type, Owned<Provisioner>>());
-
-  Future<Nothing> exec;
-  Promise<bool> promise;
-
-  // Letting exec hang to simulate a long fetch.
-  EXPECT_CALL(*process, exec(_, _))
-    .WillOnce(DoAll(FutureSatisfy(&exec),
-                    Return(promise.future())));
-
-  MesosContainerizer containerizer((Owned<MesosContainerizerProcess>(process)));
-
-  ContainerID containerId;
-  containerId.set_value("test_container");
-
-  TaskInfo taskInfo;
-  CommandInfo commandInfo;
-  taskInfo.mutable_command()->MergeFrom(commandInfo);
-
-  containerizer.launch(
-      containerId,
-      taskInfo,
-      CREATE_EXECUTOR_INFO("executor", "exit 0"),
-      os::getcwd(),
-      None(),
-      SlaveID(),
-      PID<Slave>(),
-      false);
-
-  Future<containerizer::Termination> wait = containerizer.wait(containerId);
-
-  AWAIT_READY(exec);
-
-  containerizer.destroy(containerId);
-
-  // The container should still exit even if fetch didn't complete.
-  AWAIT_READY(wait);
-}
-
-
-// Destroying a mesos containerizer while it is preparing should wait
-// until isolators are finished preparing before destroying.
-TEST_F(MesosContainerizerDestroyTest, DestroyWhilePreparing)
-{
-  slave::Flags flags = CreateSlaveFlags();
-
-  Try<Launcher*> launcher = PosixLauncher::create(flags);
-  ASSERT_SOME(launcher);
-
-  MockIsolator* isolator = new MockIsolator();
-
-  Future<Nothing> prepare;
-  Promise<Option<CommandInfo>> promise;
-
-  // Simulate a long prepare from the isolator.
-  EXPECT_CALL(*isolator, prepare(_, _, _, _, _))
-    .WillOnce(DoAll(FutureSatisfy(&prepare),
-                    Return(promise.future())));
-
-  Fetcher fetcher;
-
-  MockMesosContainerizerProcess* process = new MockMesosContainerizerProcess(
-      flags,
-      true,
-      &fetcher,
-      Owned<Launcher>(launcher.get()),
-      {Owned<Isolator>(isolator)},
-      hashmap<ContainerInfo::Image::Type, Owned<Provisioner>>());
-
-  MesosContainerizer containerizer((Owned<MesosContainerizerProcess>(process)));
-
-  ContainerID containerId;
-  containerId.set_value("test_container");
-
-  TaskInfo taskInfo;
-  CommandInfo commandInfo;
-  taskInfo.mutable_command()->MergeFrom(commandInfo);
-
-  containerizer.launch(
-      containerId,
-      taskInfo,
-      CREATE_EXECUTOR_INFO("executor", "exit 0"),
-      os::getcwd(),
-      None(),
-      SlaveID(),
-      PID<Slave>(),
-      false);
-
-  Future<containerizer::Termination> wait = containerizer.wait(containerId);
-
-  AWAIT_READY(prepare);
-
-  containerizer.destroy(containerId);
-
-  // The container should not exit until prepare is complete.
-  ASSERT_TRUE(wait.isPending());
-
-  // Need to help the compiler to disambiguate between overloads.
-  Option<CommandInfo> option = commandInfo;
-  promise.set(option);
-
-  AWAIT_READY(wait);
-
-  containerizer::Termination termination = wait.get();
-
-  EXPECT_EQ(
-      "Container destroyed while preparing isolators",
-      termination.message());
-
-  EXPECT_TRUE(termination.killed());
-  EXPECT_FALSE(termination.has_status());
-}
-
-
-// This action destroys the container using the real launcher and
-// waits until the destroy is complete.
-ACTION_P(InvokeDestroyAndWait, launcher)
-{
-  Future<Nothing> destroy = launcher->real->destroy(arg0);
-  AWAIT_READY(destroy);
-}
-
-
-// This test verifies that when a container destruction fails the
-// 'container_destroy_errors' metric is updated.
-TEST_F(MesosContainerizerDestroyTest, LauncherDestroyFailure)
-{
-  // Create a TestLauncher backed by PosixLauncher.
-  slave::Flags flags = CreateSlaveFlags();
-
-  Try<Launcher*> launcher_ = PosixLauncher::create(flags);
-  ASSERT_SOME(launcher_);
-
-  TestLauncher* launcher = new TestLauncher(Owned<Launcher>(launcher_.get()));
-
-  Fetcher fetcher;
-
-  MesosContainerizerProcess* process = new MesosContainerizerProcess(
-      flags,
-      true,
-      &fetcher,
-      Owned<Launcher>(launcher),
-      vector<Owned<Isolator>>(),
-      hashmap<ContainerInfo::Image::Type, Owned<Provisioner>>());
-
-  MesosContainerizer containerizer((Owned<MesosContainerizerProcess>(process)));
-
-  ContainerID containerId;
-  containerId.set_value("test_container");
-
-  TaskInfo taskInfo;
-  CommandInfo commandInfo;
-  taskInfo.mutable_command()->MergeFrom(commandInfo);
-
-  // Destroy the container using the PosixLauncher but return a failed
-  // future to the containerizer.
-  EXPECT_CALL(*launcher, destroy(_))
-    .WillOnce(DoAll(InvokeDestroyAndWait(launcher),
-                    Return(Failure("Destroy failure"))));
-
-  Future<bool> launch = containerizer.launch(
-      containerId,
-      taskInfo,
-      CREATE_EXECUTOR_INFO("executor", "sleep 1000"),
-      os::getcwd(),
-      None(),
-      SlaveID(),
-      PID<Slave>(),
-      false);
-
-  AWAIT_READY(launch);
-
-  Future<containerizer::Termination> wait = containerizer.wait(containerId);
-
-  containerizer.destroy(containerId);
-
-  // The container destroy should fail.
-  AWAIT_FAILED(wait);
-
-  // We settle the clock here to ensure that the processing of
-  // 'MesosContainerizerProcess::__destroy()' is complete and the
-  // metric is updated.
-  Clock::pause();
-  Clock::settle();
-  Clock::resume();
-
-  // Ensure that the metric is updated.
-  JSON::Object metrics = Metrics();
-  ASSERT_EQ(
-      1u,
-      metrics.values.count("containerizer/mesos/container_destroy_errors"));
-  ASSERT_EQ(
-      1u,
-      metrics.values["containerizer/mesos/container_destroy_errors"]);
-}
-
-
-class MesosContainerizerRecoverTest : public MesosTest {};
-
-
-// This test checks that MesosContainerizer doesn't recover executors
-// that were started by another containerizer (e.g: Docker).
-TEST_F(MesosContainerizerRecoverTest, SkipRecoverNonMesosContainers)
-{
-  slave::Flags flags = CreateSlaveFlags();
-  Fetcher fetcher;
-
-  Try<MesosContainerizer*> containerizer =
-    MesosContainerizer::create(flags, true, &fetcher);
-
-  ASSERT_SOME(containerizer);
-
-  ExecutorID executorId;
-  executorId.set_value(UUID::random().toString());
-
-  ContainerID containerId;
-  containerId.set_value(UUID::random().toString());
-
-  ExecutorInfo executorInfo;
-  executorInfo.mutable_container()->set_type(ContainerInfo::DOCKER);
-
-  ExecutorState executorState;
-  executorState.info = executorInfo;
-  executorState.latest = containerId;
-
-  RunState runState;
-  runState.id = containerId;
-  executorState.runs.put(containerId, runState);
-
-  FrameworkState frameworkState;
-  frameworkState.executors.put(executorId, executorState);
-
-  SlaveState slaveState;
-  FrameworkID frameworkId;
-  frameworkId.set_value(UUID::random().toString());
-  slaveState.frameworks.put(frameworkId, frameworkState);
-
-  Future<Nothing> recover = containerizer.get()->recover(slaveState);
-  AWAIT_READY(recover);
-
-  Future<hashset<ContainerID>> containers = containerizer.get()->containers();
-  AWAIT_READY(containers);
-  EXPECT_EQ(0u, containers.get().size());
-
-  delete containerizer.get();
-}
-
-} // namespace tests {
-} // namespace internal {
-} // namespace mesos {