You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by qi...@apache.org on 2017/11/28 12:50:58 UTC

[4/4] mesos git commit: Added 3 tests for TCP/HTTP(S) health check support for Docker container.

Added 3 tests for TCP/HTTP(S) health check support for Docker container.

Removed 3 tests which only run on IPv4 Docker network.
  ROOT_DOCKER_DockerHealthyTaskViaHTTP
  ROOT_DOCKER_DockerHealthyTaskViaHTTPS
  ROOT_DOCKER_DockerHealthyTaskViaTCP

Added 3 tests which run on both IPv4 and IPv6 Docker networks, so they
should cover the above 3 tests.
  ROOT_DOCKER_USERNETWORK_HealthyTaskViaHTTP
  ROOT_DOCKER_USERNETWORK_HealthyTaskViaHTTPS
  ROOT_DOCKER_USERNETWORK_HealthyTaskViaTCP

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


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

Branch: refs/heads/master
Commit: 00dfbc80be40cb0cb45fd9d5f96d3f508c005eca
Parents: 83805df
Author: Qian Zhang <zh...@gmail.com>
Authored: Fri Nov 17 20:41:19 2017 +0800
Committer: Qian Zhang <zh...@gmail.com>
Committed: Tue Nov 28 19:09:54 2017 +0800

----------------------------------------------------------------------
 .../docker_containerizer_tests.cpp              |  57 +-
 src/tests/health_check_tests.cpp                | 705 ++++++++++---------
 src/tests/mesos.hpp                             |  67 ++
 3 files changed, 425 insertions(+), 404 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/00dfbc80/src/tests/containerizer/docker_containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/docker_containerizer_tests.cpp b/src/tests/containerizer/docker_containerizer_tests.cpp
index 67945dd..7a42bb9 100644
--- a/src/tests/containerizer/docker_containerizer_tests.cpp
+++ b/src/tests/containerizer/docker_containerizer_tests.cpp
@@ -78,8 +78,6 @@ using testing::Eq;
 using testing::Invoke;
 using testing::Return;
 
-constexpr char DOCKER_IPv6_NETWORK[] = "mesos-docker-ip6-test";
-
 namespace process {
 
 // We need to reinitialize libprocess in order to test against
@@ -4929,65 +4927,14 @@ class DockerContainerizerIPv6UserNetworkTest : public DockerContainerizerTest
 protected:
   virtual void SetUp()
   {
+    createDockerIPv6UserNetwork();
     DockerContainerizerTest::SetUp();
-
-    Try<string> dockerCommand = strings::format(
-        "docker network create --driver=bridge --ipv6 "
-        "--subnet=fd01::/64 %s",
-        DOCKER_IPv6_NETWORK);
-
-    Try<Subprocess> s = subprocess(
-        dockerCommand.get(),
-        Subprocess::PATH("/dev/null"),
-        Subprocess::PATH("/dev/null"),
-        Subprocess::PIPE());
-
-    ASSERT_SOME(s) << "Unable to create the docker IPv6 network: "
-                   << DOCKER_IPv6_NETWORK;
-
-    Future<string> err = io::read(s->err().get());
-
-    // Wait for the network to be created.
-    AWAIT_READY(s->status());
-    AWAIT_READY(err);
-
-    ASSERT_SOME(s->status().get());
-    ASSERT_EQ(s->status().get().get(), 0)
-      << "Unable to create the docker IPv6 network "
-      << DOCKER_IPv6_NETWORK
-      << " : " << err.get();
   }
 
   virtual void TearDown()
   {
     DockerContainerizerTest::TearDown();
-
-    Try<string> dockerCommand = strings::format(
-        "docker network rm %s",
-        DOCKER_IPv6_NETWORK);
-
-    Try<Subprocess> s = subprocess(
-        dockerCommand.get(),
-        Subprocess::PATH("/dev/null"),
-        Subprocess::PATH("/dev/null"),
-        Subprocess::PIPE());
-
-    // This is best effort cleanup. In case of an error just a log an
-    // error.
-    ASSERT_SOME(s) << "Unable to delete the docker IPv6 network: "
-                   << DOCKER_IPv6_NETWORK;
-
-    Future<string> err = io::read(s->err().get());
-
-    // Wait for the network to be deleted.
-    AWAIT_READY(s->status());
-    AWAIT_READY(err);
-    ASSERT_SOME(s->status().get());
-
-    ASSERT_EQ(s->status().get().get(), 0)
-      << "Unable to delete the docker IPv6 network "
-      << DOCKER_IPv6_NETWORK
-      << " : " << err.get();
+    removeDockerIPv6UserNetwork();
   }
 };
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/00dfbc80/src/tests/health_check_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/health_check_tests.cpp b/src/tests/health_check_tests.cpp
index c0dcba2..56721fa 100644
--- a/src/tests/health_check_tests.cpp
+++ b/src/tests/health_check_tests.cpp
@@ -1942,41 +1942,53 @@ TEST_F(HealthCheckTest, ROOT_INTERNET_CURL_HealthyTaskViaTCPWithContainerImage)
 }
 
 
-// Tests a healthy docker task via HTTP. To emulate a task responsive
-// to HTTP health checks, starts Netcat in the docker "alpine" image.
 TEST_F_TEMP_DISABLED_ON_WINDOWS(
-    HealthCheckTest, ROOT_DOCKER_DockerHealthyTaskViaHTTP)
+    HealthCheckTest, DefaultExecutorCommandHealthCheck)
 {
-  Shared<Docker> docker(new MockDocker(
-      tests::flags.docker, tests::flags.docker_socket));
-
   Try<Owned<cluster::Master>> master = StartMaster();
   ASSERT_SOME(master);
 
-  slave::Flags agentFlags = CreateSlaveFlags();
+  slave::Flags flags = CreateSlaveFlags();
+#ifndef USE_SSL_SOCKET
+  // Disable operator API authentication for the default executor. Executor
+  // authentication currently has SSL as a dependency, so we cannot require
+  // executors to authenticate with the agent operator API if Mesos was not
+  // built with SSL support.
+  flags.authenticate_http_readwrite = false;
 
-  Fetcher fetcher(agentFlags);
+  // Set permissive ACLs in the agent so that the local authorizer will be
+  // loaded and implicit executor authorization will be tested.
+  ACLs acls;
+  acls.set_permissive(true);
 
-  Try<ContainerLogger*> logger =
-    ContainerLogger::create(agentFlags.container_logger);
-  ASSERT_SOME(logger);
+  flags.acls = acls;
+#endif // USE_SSL_SOCKET
 
-  MockDockerContainerizer containerizer(
-      agentFlags,
-      &fetcher,
-      Owned<ContainerLogger>(logger.get()),
-      docker);
+  Fetcher fetcher(flags);
+
+  // We have to explicitly create a `Containerizer` in non-local mode,
+  // because `LaunchNestedContainerSession` (used by command health
+  // checks) tries to start a IO switchboard, which doesn't work in
+  // local mode yet.
+  Try<MesosContainerizer*> _containerizer =
+    MesosContainerizer::create(flags, false, &fetcher);
+
+  ASSERT_SOME(_containerizer);
 
+  Owned<slave::Containerizer> containerizer(_containerizer.get());
   Owned<MasterDetector> detector = master.get()->createDetector();
+
   Try<Owned<cluster::Slave>> agent =
-    StartSlave(detector.get(), &containerizer, agentFlags);
+    StartSlave(detector.get(), containerizer.get(), flags);
   ASSERT_SOME(agent);
 
   MockScheduler sched;
   MesosSchedulerDriver driver(
-      &sched, DEFAULT_FRAMEWORK_INFO, master.get()->pid, DEFAULT_CREDENTIAL);
+    &sched, DEFAULT_FRAMEWORK_INFO, master.get()->pid, DEFAULT_CREDENTIAL);
 
-  EXPECT_CALL(sched, registered(&driver, _, _));
+  Future<FrameworkID> frameworkId;
+  EXPECT_CALL(sched, registered(&driver, _, _))
+    .WillOnce(FutureArg<1>(&frameworkId));
 
   Future<vector<Offer>> offers;
   EXPECT_CALL(sched, resourceOffers(&driver, _))
@@ -1985,134 +1997,132 @@ TEST_F_TEMP_DISABLED_ON_WINDOWS(
 
   driver.start();
 
+  AWAIT_READY(frameworkId);
+
   AWAIT_READY(offers);
   ASSERT_FALSE(offers->empty());
 
-  const uint16_t testPort = getFreePort().get();
-
-  // Use Netcat to launch a HTTP server.
-  const string command = strings::format(
-      "nc -lk -p %u -e echo -e \"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\"",
-      testPort).get();
-
-  TaskInfo task = createTask(offers.get()[0], command);
+  Future<TaskStatus> statusStarting;
+  Future<TaskStatus> statusRunning;
+  Future<TaskStatus> statusHealthy;
 
-  // The docker container runs in bridge network mode.
-  //
-  // TODO(tnachen): Use local image to test if possible.
-  ContainerInfo containerInfo;
-  containerInfo.set_type(ContainerInfo::DOCKER);
-  containerInfo.mutable_docker()->set_image("alpine");
-  containerInfo.mutable_docker()->set_network(
-      ContainerInfo::DockerInfo::BRIDGE);
+  EXPECT_CALL(sched, statusUpdate(&driver, _))
+    .WillOnce(FutureArg<1>(&statusStarting))
+    .WillOnce(FutureArg<1>(&statusRunning))
+    .WillOnce(FutureArg<1>(&statusHealthy));
 
-  task.mutable_container()->CopyFrom(containerInfo);
+  TaskInfo task = createTask(offers->front(), "sleep 120");
 
-  // Set `grace_period_seconds` here because it takes some time to
-  // launch Netcat to serve requests.
   HealthCheck healthCheck;
-  healthCheck.set_type(HealthCheck::HTTP);
-  healthCheck.mutable_http()->set_port(testPort);
+
+  healthCheck.set_type(HealthCheck::COMMAND);
+  healthCheck.mutable_command()->set_value("exit $STATUS");
   healthCheck.set_delay_seconds(0);
   healthCheck.set_interval_seconds(0);
-  healthCheck.set_grace_period_seconds(15);
+  healthCheck.set_grace_period_seconds(0);
+
+  Environment::Variable* variable = healthCheck.mutable_command()->
+    mutable_environment()->mutable_variables()->Add();
+  variable->set_name("STATUS");
+  variable->set_value("0");
 
   task.mutable_health_check()->CopyFrom(healthCheck);
 
-  Future<ContainerID> containerId;
-  EXPECT_CALL(containerizer, launch(_, _, _, _))
-    .WillOnce(DoAll(FutureArg<0>(&containerId),
-                    Invoke(&containerizer,
-                           &MockDockerContainerizer::_launch)));
+  Resources executorResources =
+    allocatedResources(Resources::parse("cpus:0.1;mem:32;disk:32").get(), "*");
 
-  Future<TaskStatus> statusStarting;
-  Future<TaskStatus> statusRunning;
-  Future<TaskStatus> statusHealthy;
+  task.mutable_resources()->CopyFrom(task.resources() - executorResources);
 
-  EXPECT_CALL(sched, statusUpdate(&driver, _))
-    .WillOnce(FutureArg<1>(&statusStarting))
-    .WillOnce(FutureArg<1>(&statusRunning))
-    .WillOnce(FutureArg<1>(&statusHealthy))
-    .WillRepeatedly(Return()); // Ignore subsequent updates.
+  TaskGroupInfo taskGroup;
+  taskGroup.add_tasks()->CopyFrom(task);
 
-  driver.launchTasks(offers.get()[0].id(), {task});
+  ExecutorInfo executor;
+  executor.mutable_executor_id()->set_value("default");
+  executor.set_type(ExecutorInfo::DEFAULT);
+  executor.mutable_framework_id()->CopyFrom(frameworkId.get());
+  executor.mutable_resources()->CopyFrom(executorResources);
+  executor.mutable_shutdown_grace_period()->set_nanoseconds(Seconds(10).ns());
 
-  AWAIT_READY(containerId);
+  driver.acceptOffers(
+      {offers->front().id()}, {LAUNCH_GROUP(executor, taskGroup)});
 
-  AWAIT_READY(statusStarting);
-  EXPECT_EQ(TASK_STARTING, statusStarting->state());
+  AWAIT_READY(statusRunning);
+  EXPECT_EQ(TASK_STARTING, statusStarting.get().state());
 
   AWAIT_READY(statusRunning);
-  EXPECT_EQ(TASK_RUNNING, statusRunning->state());
+  EXPECT_EQ(TASK_RUNNING, statusRunning.get().state());
 
   AWAIT_READY(statusHealthy);
-  EXPECT_EQ(TASK_RUNNING, statusHealthy->state());
+  EXPECT_EQ(TASK_RUNNING, statusHealthy.get().state());
   EXPECT_EQ(
       TaskStatus::REASON_TASK_HEALTH_CHECK_STATUS_UPDATED,
       statusHealthy->reason());
-  EXPECT_TRUE(statusHealthy->has_healthy());
-  EXPECT_TRUE(statusHealthy->healthy());
+  EXPECT_TRUE(statusHealthy.get().has_healthy());
+  EXPECT_TRUE(statusHealthy.get().healthy());
 
-  Future<Option<ContainerTermination>> termination =
-    containerizer.wait(containerId.get());
+  Future<hashset<ContainerID>> containerIds = containerizer->containers();
+
+  AWAIT_READY(containerIds);
 
   driver.stop();
   driver.join();
 
-  AWAIT_READY(termination);
-  EXPECT_SOME(termination.get());
-
-  agent.get()->terminate();
-  agent->reset();
-
-  Future<std::list<Docker::Container>> containers =
-    docker->ps(true, slave::DOCKER_NAME_PREFIX);
-
-  AWAIT_READY(containers);
-
-  // Clean up all mesos launched docker containers.
-  foreach (const Docker::Container& container, containers.get()) {
-    AWAIT_READY_FOR(docker->rm(container.id, true), Seconds(30));
+  // Cleanup all mesos launched containers.
+  foreach (const ContainerID& containerId, containerIds.get()) {
+    AWAIT_READY(containerizer->wait(containerId));
   }
 }
 
 
-// Tests a healthy docker task via HTTPS. To emulate a task responsive
-// to HTTPS health checks, starts an HTTPS server in the docker
-// "haosdent/https-server" image.
+// Tests a healthy docker task via CMD health checks using the
+// DefaultExecutor.
 TEST_F_TEMP_DISABLED_ON_WINDOWS(
-    HealthCheckTest, ROOT_DOCKER_DockerHealthyTaskViaHTTPS)
+    HealthCheckTest, DefaultExecutorWithDockerImageCommandHealthCheck)
 {
-  Shared<Docker> docker(new MockDocker(
-      tests::flags.docker, tests::flags.docker_socket));
-
   Try<Owned<cluster::Master>> master = StartMaster();
   ASSERT_SOME(master);
 
-  slave::Flags agentFlags = CreateSlaveFlags();
+  slave::Flags flags = CreateSlaveFlags();
+#ifndef USE_SSL_SOCKET
+  // Disable operator API authentication for the default executor. Executor
+  // authentication currently has SSL as a dependency, so we cannot require
+  // executors to authenticate with the agent operator API if Mesos was not
+  // built with SSL support.
+  flags.authenticate_http_readwrite = false;
 
-  Fetcher fetcher(agentFlags);
+  // Set permissive ACLs in the agent so that the local authorizer will be
+  // loaded and implicit executor authorization will be tested.
+  ACLs acls;
+  acls.set_permissive(true);
 
-  Try<ContainerLogger*> logger =
-    ContainerLogger::create(agentFlags.container_logger);
-  ASSERT_SOME(logger);
+  flags.acls = acls;
+#endif // USE_SSL_SOCKET
 
-  MockDockerContainerizer containerizer(
-      agentFlags,
-      &fetcher,
-      Owned<ContainerLogger>(logger.get()),
-      docker);
+  Fetcher fetcher(flags);
 
+  // We have to explicitly create a `Containerizer` in non-local mode,
+  // because `LaunchNestedContainerSession` (used by command health
+  // checks) tries to start a IO switchboard, which doesn't work in
+  // local mode yet.
+  Try<MesosContainerizer*> _containerizer =
+    MesosContainerizer::create(flags, false, &fetcher);
+
+  ASSERT_SOME(_containerizer);
+
+  Owned<slave::Containerizer> containerizer(_containerizer.get());
   Owned<MasterDetector> detector = master.get()->createDetector();
+
   Try<Owned<cluster::Slave>> agent =
-    StartSlave(detector.get(), &containerizer, agentFlags);
+    StartSlave(detector.get(), containerizer.get(), flags);
   ASSERT_SOME(agent);
 
   MockScheduler sched;
   MesosSchedulerDriver driver(
-      &sched, DEFAULT_FRAMEWORK_INFO, master.get()->pid, DEFAULT_CREDENTIAL);
+    &sched, DEFAULT_FRAMEWORK_INFO, master.get()->pid, DEFAULT_CREDENTIAL);
 
-  EXPECT_CALL(sched, registered(&driver, _, _));
+  Future<FrameworkID> frameworkId;
+  EXPECT_CALL(sched, registered(&driver, _, _))
+    .WillOnce(FutureArg<1>(&frameworkId));
 
   Future<vector<Offer>> offers;
   EXPECT_CALL(sched, resourceOffers(&driver, _))
@@ -2121,108 +2131,138 @@ TEST_F_TEMP_DISABLED_ON_WINDOWS(
 
   driver.start();
 
+  AWAIT_READY(frameworkId);
+
   AWAIT_READY(offers);
   ASSERT_FALSE(offers->empty());
 
-  const uint16_t testPort = getFreePort().get();
+  Future<TaskStatus> statusStarting;
+  Future<TaskStatus> statusRunning;
+  Future<TaskStatus> statusHealthy;
 
-  const string command = strings::format(
-      "python https_server.py %u",
-      testPort).get();
+  EXPECT_CALL(sched, statusUpdate(&driver, _))
+    .WillOnce(FutureArg<1>(&statusStarting))
+    .WillOnce(FutureArg<1>(&statusRunning))
+    .WillOnce(FutureArg<1>(&statusHealthy));
 
-  TaskInfo task = createTask(offers.get()[0], command);
+  TaskInfo task = createTask(offers->front(), "sleep 120");
 
-  // The docker container runs in bridge network mode.
-  // Refer to https://github.com/haosdent/https-server/ for how the
-  // docker image `haosdent/https-server` works.
-  //
-  // TODO(haosdent): Use local image to test if possible.
+  // TODO(tnachen): Use local image to test if possible.
   ContainerInfo containerInfo;
-  containerInfo.set_type(ContainerInfo::DOCKER);
-  containerInfo.mutable_docker()->set_image("haosdent/https-server");
-  containerInfo.mutable_docker()->set_network(
-      ContainerInfo::DockerInfo::BRIDGE);
+  containerInfo.set_type(ContainerInfo::MESOS);
+  containerInfo.mutable_docker()->set_image("alpine");
 
   task.mutable_container()->CopyFrom(containerInfo);
 
-  // Set `grace_period_seconds` here because it takes some time to
-  // launch the HTTPS server to serve requests.
   HealthCheck healthCheck;
-  healthCheck.set_type(HealthCheck::HTTP);
-  healthCheck.mutable_http()->set_port(testPort);
-  healthCheck.mutable_http()->set_scheme("https");
+
+  healthCheck.set_type(HealthCheck::COMMAND);
+  healthCheck.mutable_command()->set_value("exit $STATUS");
   healthCheck.set_delay_seconds(0);
   healthCheck.set_interval_seconds(0);
-  healthCheck.set_grace_period_seconds(15);
+  healthCheck.set_grace_period_seconds(0);
+
+  Environment::Variable* variable = healthCheck.mutable_command()->
+    mutable_environment()->mutable_variables()->Add();
+  variable->set_name("STATUS");
+  variable->set_value("0");
 
   task.mutable_health_check()->CopyFrom(healthCheck);
 
-  Future<ContainerID> containerId;
-  EXPECT_CALL(containerizer, launch(_, _, _, _))
-    .WillOnce(DoAll(FutureArg<0>(&containerId),
-                    Invoke(&containerizer,
-                           &MockDockerContainerizer::_launch)));
+  Resources executorResources =
+    allocatedResources(Resources::parse("cpus:0.1;mem:32;disk:32").get(), "*");
 
-  Future<TaskStatus> statusStarting;
-  Future<TaskStatus> statusRunning;
-  Future<TaskStatus> statusHealthy;
+  task.mutable_resources()->CopyFrom(task.resources() - executorResources);
 
-  EXPECT_CALL(sched, statusUpdate(&driver, _))
-    .WillOnce(FutureArg<1>(&statusStarting))
-    .WillOnce(FutureArg<1>(&statusRunning))
-    .WillOnce(FutureArg<1>(&statusHealthy))
-    .WillRepeatedly(Return()); // Ignore subsequent updates.
+  TaskGroupInfo taskGroup;
+  taskGroup.add_tasks()->CopyFrom(task);
 
-  driver.launchTasks(offers.get()[0].id(), {task});
+  ExecutorInfo executor;
+  executor.mutable_executor_id()->set_value("default");
+  executor.set_type(ExecutorInfo::DEFAULT);
+  executor.mutable_framework_id()->CopyFrom(frameworkId.get());
+  executor.mutable_resources()->CopyFrom(executorResources);
+  executor.mutable_shutdown_grace_period()->set_nanoseconds(Seconds(10).ns());
 
-  AWAIT_READY(containerId);
+  driver.acceptOffers(
+      {offers->front().id()}, {LAUNCH_GROUP(executor, taskGroup)});
 
   AWAIT_READY(statusStarting);
-  EXPECT_EQ(TASK_STARTING, statusStarting->state());
+  EXPECT_EQ(TASK_STARTING, statusStarting.get().state());
 
-  // Increase time here to wait for pulling image finish.
-  AWAIT_READY_FOR(statusRunning, Seconds(60));
-  EXPECT_EQ(TASK_RUNNING, statusRunning->state());
+  AWAIT_READY(statusRunning);
+  EXPECT_EQ(TASK_RUNNING, statusRunning.get().state());
 
   AWAIT_READY(statusHealthy);
-  EXPECT_EQ(TASK_RUNNING, statusHealthy->state());
+  EXPECT_EQ(TASK_RUNNING, statusHealthy.get().state());
   EXPECT_EQ(
       TaskStatus::REASON_TASK_HEALTH_CHECK_STATUS_UPDATED,
       statusHealthy->reason());
-  EXPECT_TRUE(statusHealthy->has_healthy());
-  EXPECT_TRUE(statusHealthy->healthy());
+  EXPECT_TRUE(statusHealthy.get().has_healthy());
+  EXPECT_TRUE(statusHealthy.get().healthy());
 
-  Future<Option<ContainerTermination>> termination =
-    containerizer.wait(containerId.get());
+  Future<hashset<ContainerID>> containerIds = containerizer->containers();
+
+  AWAIT_READY(containerIds);
 
   driver.stop();
   driver.join();
 
-  AWAIT_READY(termination);
-  EXPECT_SOME(termination.get());
+  // Cleanup all mesos launched containers.
+  foreach (const ContainerID& containerId, containerIds.get()) {
+    AWAIT_READY(containerizer->wait(containerId));
+  }
+}
 
-  agent.get()->terminate();
-  agent->reset();
 
-  Future<std::list<Docker::Container>> containers =
-    docker->ps(true, slave::DOCKER_NAME_PREFIX);
+// Fixture for testing TCP/HTTP(S) health check support
+// for Docker containers on Docker user network.
+class DockerContainerizerHealthCheckTest
+  : public MesosTest,
+    public ::testing::WithParamInterface<NetworkInfo::Protocol>
+{
+protected:
+  virtual void SetUp()
+  {
+    createDockerIPv6UserNetwork();
+  }
 
-  AWAIT_READY(containers);
+  virtual void TearDown()
+  {
+    Try<Owned<Docker>> docker = Docker::create(
+        tests::flags.docker,
+        tests::flags.docker_socket,
+        false);
 
-  // Clean up all mesos launched docker containers.
-  foreach (const Docker::Container& container, containers.get()) {
-    AWAIT_READY_FOR(docker->rm(container.id, true), Seconds(30));
+    ASSERT_SOME(docker);
+
+    Future<std::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));
+    }
+
+    removeDockerIPv6UserNetwork();
   }
-}
+};
 
 
-// Tests a healthy docker task via TCP. To emulate a task responsive
-// to TCP health checks, starts Netcat in the docker "alpine" image.
-//
-// NOTE: This test is almost identical to ROOT_DOCKER_DockerHealthyTaskViaHTTP
-// with the difference being TCP health check.
-TEST_F_TEMP_DISABLED_ON_WINDOWS(
-    HealthCheckTest, ROOT_DOCKER_DockerHealthyTaskViaTCP)
+// The tests are parameterized by the network protocol.
+INSTANTIATE_TEST_CASE_P(
+    NetworkProtocol,
+    DockerContainerizerHealthCheckTest,
+    ::testing::Values(NetworkInfo::IPv4, NetworkInfo::IPv6));
+
+
+// Tests a healthy Docker task via HTTP. To emulate a task responsive
+// to HTTP health checks, starts Netcat in the Docker "alpine" image.
+TEST_P_TEMP_DISABLED_ON_WINDOWS(
+    DockerContainerizerHealthCheckTest,
+    ROOT_DOCKER_USERNETWORK_HealthyTaskViaHTTP)
 {
   Shared<Docker> docker(new MockDocker(
       tests::flags.docker, tests::flags.docker_socket));
@@ -2265,31 +2305,30 @@ TEST_F_TEMP_DISABLED_ON_WINDOWS(
   AWAIT_READY(offers);
   ASSERT_FALSE(offers->empty());
 
-  const uint16_t testPort = getFreePort().get();
-
   // Use Netcat to launch a HTTP server.
-  const string command = strings::format(
-      "nc -lk -p %u -e echo -e \"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\"",
-      testPort).get();
-
-  TaskInfo task = createTask(offers.get()[0], command);
+  TaskInfo task = createTask(
+      offers.get()[0],
+      "nc -lk -p 80 -e echo -e \"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\"");
 
-  // The docker container runs in bridge network mode.
-  //
   // TODO(tnachen): Use local image to test if possible.
   ContainerInfo containerInfo;
   containerInfo.set_type(ContainerInfo::DOCKER);
   containerInfo.mutable_docker()->set_image("alpine");
-  containerInfo.mutable_docker()->set_network(
-      ContainerInfo::DockerInfo::BRIDGE);
+  containerInfo.mutable_docker()->set_network(ContainerInfo::DockerInfo::USER);
+
+  // Setup the Docker IPv6 network.
+  NetworkInfo networkInfo;
+  networkInfo.set_name(DOCKER_IPv6_NETWORK);
+  containerInfo.add_network_infos()->CopyFrom(networkInfo);
 
   task.mutable_container()->CopyFrom(containerInfo);
 
   // Set `grace_period_seconds` here because it takes some time to
   // launch Netcat to serve requests.
   HealthCheck healthCheck;
-  healthCheck.set_type(HealthCheck::TCP);
-  healthCheck.mutable_tcp()->set_port(testPort);
+  healthCheck.set_type(HealthCheck::HTTP);
+  healthCheck.mutable_http()->set_port(80);
+  healthCheck.mutable_http()->set_protocol(GetParam());
   healthCheck.set_delay_seconds(0);
   healthCheck.set_interval_seconds(0);
   healthCheck.set_grace_period_seconds(15);
@@ -2327,6 +2366,7 @@ TEST_F_TEMP_DISABLED_ON_WINDOWS(
   EXPECT_EQ(
       TaskStatus::REASON_TASK_HEALTH_CHECK_STATUS_UPDATED,
       statusHealthy->reason());
+
   EXPECT_TRUE(statusHealthy->has_healthy());
   EXPECT_TRUE(statusHealthy->healthy());
 
@@ -2338,69 +2378,46 @@ TEST_F_TEMP_DISABLED_ON_WINDOWS(
 
   AWAIT_READY(termination);
   EXPECT_SOME(termination.get());
-
-  agent.get()->terminate();
-  agent->reset();
-
-  Future<std::list<Docker::Container>> containers =
-    docker->ps(true, slave::DOCKER_NAME_PREFIX);
-
-  AWAIT_READY(containers);
-
-  // Clean up all mesos launched docker containers.
-  foreach (const Docker::Container& container, containers.get()) {
-    AWAIT_READY_FOR(docker->rm(container.id, true), Seconds(30));
-  }
 }
 
 
-TEST_F_TEMP_DISABLED_ON_WINDOWS(
-    HealthCheckTest, DefaultExecutorCommandHealthCheck)
-{
-  Try<Owned<cluster::Master>> master = StartMaster();
-  ASSERT_SOME(master);
-
-  slave::Flags flags = CreateSlaveFlags();
-#ifndef USE_SSL_SOCKET
-  // Disable operator API authentication for the default executor. Executor
-  // authentication currently has SSL as a dependency, so we cannot require
-  // executors to authenticate with the agent operator API if Mesos was not
-  // built with SSL support.
-  flags.authenticate_http_readwrite = false;
-
-  // Set permissive ACLs in the agent so that the local authorizer will be
-  // loaded and implicit executor authorization will be tested.
-  ACLs acls;
-  acls.set_permissive(true);
-
-  flags.acls = acls;
-#endif // USE_SSL_SOCKET
+// Tests a healthy Docker task via HTTPS. To emulate a task
+// responsive to HTTPS health checks, starts an HTTPS server
+// in the Docker "zhq527725/https-server" image.
+TEST_P_TEMP_DISABLED_ON_WINDOWS(
+    DockerContainerizerHealthCheckTest,
+    ROOT_DOCKER_USERNETWORK_HealthyTaskViaHTTPS)
+{
+  Shared<Docker> docker(new MockDocker(
+      tests::flags.docker, tests::flags.docker_socket));
 
-  Fetcher fetcher(flags);
+  Try<Owned<cluster::Master>> master = StartMaster();
+  ASSERT_SOME(master);
 
-  // We have to explicitly create a `Containerizer` in non-local mode,
-  // because `LaunchNestedContainerSession` (used by command health
-  // checks) tries to start a IO switchboard, which doesn't work in
-  // local mode yet.
-  Try<MesosContainerizer*> _containerizer =
-    MesosContainerizer::create(flags, false, &fetcher);
+  slave::Flags agentFlags = CreateSlaveFlags();
 
-  ASSERT_SOME(_containerizer);
+  Fetcher fetcher(agentFlags);
 
-  Owned<slave::Containerizer> containerizer(_containerizer.get());
-  Owned<MasterDetector> detector = master.get()->createDetector();
+  Try<ContainerLogger*> logger =
+    ContainerLogger::create(agentFlags.container_logger);
+  ASSERT_SOME(logger);
+
+  MockDockerContainerizer containerizer(
+      agentFlags,
+      &fetcher,
+      Owned<ContainerLogger>(logger.get()),
+      docker);
 
+  Owned<MasterDetector> detector = master.get()->createDetector();
   Try<Owned<cluster::Slave>> agent =
-    StartSlave(detector.get(), containerizer.get(), flags);
+    StartSlave(detector.get(), &containerizer, agentFlags);
   ASSERT_SOME(agent);
 
   MockScheduler sched;
   MesosSchedulerDriver driver(
-    &sched, DEFAULT_FRAMEWORK_INFO, master.get()->pid, DEFAULT_CREDENTIAL);
+      &sched, DEFAULT_FRAMEWORK_INFO, master.get()->pid, DEFAULT_CREDENTIAL);
 
-  Future<FrameworkID> frameworkId;
-  EXPECT_CALL(sched, registered(&driver, _, _))
-    .WillOnce(FutureArg<1>(&frameworkId));
+  EXPECT_CALL(sched, registered(&driver, _, _));
 
   Future<vector<Offer>> offers;
   EXPECT_CALL(sched, resourceOffers(&driver, _))
@@ -2409,132 +2426,128 @@ TEST_F_TEMP_DISABLED_ON_WINDOWS(
 
   driver.start();
 
-  AWAIT_READY(frameworkId);
-
   AWAIT_READY(offers);
   ASSERT_FALSE(offers->empty());
 
-  Future<TaskStatus> statusStarting;
-  Future<TaskStatus> statusRunning;
-  Future<TaskStatus> statusHealthy;
+  TaskInfo task = createTask(
+      offers.get()[0],
+      "python https_server.py 443");
 
-  EXPECT_CALL(sched, statusUpdate(&driver, _))
-    .WillOnce(FutureArg<1>(&statusStarting))
-    .WillOnce(FutureArg<1>(&statusRunning))
-    .WillOnce(FutureArg<1>(&statusHealthy));
+  // Refer to https://github.com/qianzhangxa/https-server for
+  // how the Docker image `zhq527725/https-server` works.
+  //
+  // TODO(qianzhang): Use local image to test if possible.
+  ContainerInfo containerInfo;
+  containerInfo.set_type(ContainerInfo::DOCKER);
+  containerInfo.mutable_docker()->set_image("zhq527725/https-server");
+  containerInfo.mutable_docker()->set_network(ContainerInfo::DockerInfo::USER);
 
-  TaskInfo task = createTask(offers->front(), "sleep 120");
+  // Setup the Docker IPv6 network.
+  NetworkInfo networkInfo;
+  networkInfo.set_name(DOCKER_IPv6_NETWORK);
+  containerInfo.add_network_infos()->CopyFrom(networkInfo);
 
-  HealthCheck healthCheck;
+  task.mutable_container()->CopyFrom(containerInfo);
 
-  healthCheck.set_type(HealthCheck::COMMAND);
-  healthCheck.mutable_command()->set_value("exit $STATUS");
+  // Set `grace_period_seconds` here because it takes some time to
+  // launch the HTTPS server to serve requests.
+  HealthCheck healthCheck;
+  healthCheck.set_type(HealthCheck::HTTP);
+  healthCheck.mutable_http()->set_port(443);
+  healthCheck.mutable_http()->set_scheme("https");
+  healthCheck.mutable_http()->set_protocol(GetParam());
   healthCheck.set_delay_seconds(0);
   healthCheck.set_interval_seconds(0);
-  healthCheck.set_grace_period_seconds(0);
-
-  Environment::Variable* variable = healthCheck.mutable_command()->
-    mutable_environment()->mutable_variables()->Add();
-  variable->set_name("STATUS");
-  variable->set_value("0");
+  healthCheck.set_grace_period_seconds(15);
 
   task.mutable_health_check()->CopyFrom(healthCheck);
 
-  Resources executorResources =
-    allocatedResources(Resources::parse("cpus:0.1;mem:32;disk:32").get(), "*");
+  Future<ContainerID> containerId;
+  EXPECT_CALL(containerizer, launch(_, _, _, _))
+    .WillOnce(DoAll(FutureArg<0>(&containerId),
+                    Invoke(&containerizer,
+                           &MockDockerContainerizer::_launch)));
 
-  task.mutable_resources()->CopyFrom(task.resources() - executorResources);
+  Future<TaskStatus> statusStarting;
+  Future<TaskStatus> statusRunning;
+  Future<TaskStatus> statusHealthy;
 
-  TaskGroupInfo taskGroup;
-  taskGroup.add_tasks()->CopyFrom(task);
+  EXPECT_CALL(sched, statusUpdate(&driver, _))
+    .WillOnce(FutureArg<1>(&statusStarting))
+    .WillOnce(FutureArg<1>(&statusRunning))
+    .WillOnce(FutureArg<1>(&statusHealthy))
+    .WillRepeatedly(Return()); // Ignore subsequent updates.
 
-  ExecutorInfo executor;
-  executor.mutable_executor_id()->set_value("default");
-  executor.set_type(ExecutorInfo::DEFAULT);
-  executor.mutable_framework_id()->CopyFrom(frameworkId.get());
-  executor.mutable_resources()->CopyFrom(executorResources);
-  executor.mutable_shutdown_grace_period()->set_nanoseconds(Seconds(10).ns());
+  driver.launchTasks(offers.get()[0].id(), {task});
 
-  driver.acceptOffers(
-      {offers->front().id()}, {LAUNCH_GROUP(executor, taskGroup)});
+  AWAIT_READY(containerId);
 
-  AWAIT_READY(statusRunning);
-  EXPECT_EQ(TASK_STARTING, statusStarting.get().state());
+  AWAIT_READY(statusStarting);
+  EXPECT_EQ(TASK_STARTING, statusStarting->state());
 
-  AWAIT_READY(statusRunning);
-  EXPECT_EQ(TASK_RUNNING, statusRunning.get().state());
+  // Increase time here to wait for pulling image finish.
+  AWAIT_READY_FOR(statusRunning, Seconds(60));
+  EXPECT_EQ(TASK_RUNNING, statusRunning->state());
 
   AWAIT_READY(statusHealthy);
-  EXPECT_EQ(TASK_RUNNING, statusHealthy.get().state());
+  EXPECT_EQ(TASK_RUNNING, statusHealthy->state());
   EXPECT_EQ(
       TaskStatus::REASON_TASK_HEALTH_CHECK_STATUS_UPDATED,
       statusHealthy->reason());
-  EXPECT_TRUE(statusHealthy.get().has_healthy());
-  EXPECT_TRUE(statusHealthy.get().healthy());
-
-  Future<hashset<ContainerID>> containerIds = containerizer->containers();
+  EXPECT_TRUE(statusHealthy->has_healthy());
+  EXPECT_TRUE(statusHealthy->healthy());
 
-  AWAIT_READY(containerIds);
+  Future<Option<ContainerTermination>> termination =
+    containerizer.wait(containerId.get());
 
   driver.stop();
   driver.join();
 
-  // Cleanup all mesos launched containers.
-  foreach (const ContainerID& containerId, containerIds.get()) {
-    AWAIT_READY(containerizer->wait(containerId));
-  }
+  AWAIT_READY(termination);
+  EXPECT_SOME(termination.get());
 }
 
 
-// Tests a healthy docker task via CMD health checks using the
-// DefaultExecutor.
-TEST_F_TEMP_DISABLED_ON_WINDOWS(
-    HealthCheckTest, DefaultExecutorWithDockerImageCommandHealthCheck)
+// Tests a healthy Docker task via TCP. To emulate a task responsive
+// to TCP health checks, starts Netcat in the Docker "alpine" image.
+//
+// NOTE:
+// This test is almost identical to ROOT_DOCKER_USERNETWORK_HealthyTaskViaHTTP
+// with the difference being TCP health check.
+TEST_P_TEMP_DISABLED_ON_WINDOWS(
+    DockerContainerizerHealthCheckTest,
+    ROOT_DOCKER_USERNETWORK_HealthyTaskViaTCP)
 {
+  Shared<Docker> docker(new MockDocker(
+      tests::flags.docker, tests::flags.docker_socket));
+
   Try<Owned<cluster::Master>> master = StartMaster();
   ASSERT_SOME(master);
 
-  slave::Flags flags = CreateSlaveFlags();
-#ifndef USE_SSL_SOCKET
-  // Disable operator API authentication for the default executor. Executor
-  // authentication currently has SSL as a dependency, so we cannot require
-  // executors to authenticate with the agent operator API if Mesos was not
-  // built with SSL support.
-  flags.authenticate_http_readwrite = false;
-
-  // Set permissive ACLs in the agent so that the local authorizer will be
-  // loaded and implicit executor authorization will be tested.
-  ACLs acls;
-  acls.set_permissive(true);
-
-  flags.acls = acls;
-#endif // USE_SSL_SOCKET
+  slave::Flags agentFlags = CreateSlaveFlags();
 
-  Fetcher fetcher(flags);
+  Fetcher fetcher(agentFlags);
 
-  // We have to explicitly create a `Containerizer` in non-local mode,
-  // because `LaunchNestedContainerSession` (used by command health
-  // checks) tries to start a IO switchboard, which doesn't work in
-  // local mode yet.
-  Try<MesosContainerizer*> _containerizer =
-    MesosContainerizer::create(flags, false, &fetcher);
+  Try<ContainerLogger*> logger =
+    ContainerLogger::create(agentFlags.container_logger);
+  ASSERT_SOME(logger);
 
-  ASSERT_SOME(_containerizer);
+  MockDockerContainerizer containerizer(
+      agentFlags,
+      &fetcher,
+      Owned<ContainerLogger>(logger.get()),
+      docker);
 
-  Owned<slave::Containerizer> containerizer(_containerizer.get());
   Owned<MasterDetector> detector = master.get()->createDetector();
-
   Try<Owned<cluster::Slave>> agent =
-    StartSlave(detector.get(), containerizer.get(), flags);
+    StartSlave(detector.get(), &containerizer, agentFlags);
   ASSERT_SOME(agent);
 
   MockScheduler sched;
   MesosSchedulerDriver driver(
-    &sched, DEFAULT_FRAMEWORK_INFO, master.get()->pid, DEFAULT_CREDENTIAL);
+      &sched, DEFAULT_FRAMEWORK_INFO, master.get()->pid, DEFAULT_CREDENTIAL);
 
-  Future<FrameworkID> frameworkId;
-  EXPECT_CALL(sched, registered(&driver, _, _))
-    .WillOnce(FutureArg<1>(&frameworkId));
+  EXPECT_CALL(sched, registered(&driver, _, _));
 
   Future<vector<Offer>> offers;
   EXPECT_CALL(sched, resourceOffers(&driver, _))
@@ -2543,87 +2556,81 @@ TEST_F_TEMP_DISABLED_ON_WINDOWS(
 
   driver.start();
 
-  AWAIT_READY(frameworkId);
-
   AWAIT_READY(offers);
   ASSERT_FALSE(offers->empty());
 
-  Future<TaskStatus> statusStarting;
-  Future<TaskStatus> statusRunning;
-  Future<TaskStatus> statusHealthy;
-
-  EXPECT_CALL(sched, statusUpdate(&driver, _))
-    .WillOnce(FutureArg<1>(&statusStarting))
-    .WillOnce(FutureArg<1>(&statusRunning))
-    .WillOnce(FutureArg<1>(&statusHealthy));
-
-  TaskInfo task = createTask(offers->front(), "sleep 120");
+  // Use Netcat to launch a HTTP server.
+  TaskInfo task = createTask(
+      offers.get()[0],
+      "nc -lk -p 80 -e echo -e \"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\"");
 
   // TODO(tnachen): Use local image to test if possible.
   ContainerInfo containerInfo;
-  containerInfo.set_type(ContainerInfo::MESOS);
+  containerInfo.set_type(ContainerInfo::DOCKER);
   containerInfo.mutable_docker()->set_image("alpine");
+  containerInfo.mutable_docker()->set_network(ContainerInfo::DockerInfo::USER);
+
+  // Setup the Docker IPv6 network.
+  NetworkInfo networkInfo;
+  networkInfo.set_name(DOCKER_IPv6_NETWORK);
+  containerInfo.add_network_infos()->CopyFrom(networkInfo);
 
   task.mutable_container()->CopyFrom(containerInfo);
 
+  // Set `grace_period_seconds` here because it takes some time to
+  // launch Netcat to serve requests.
   HealthCheck healthCheck;
-
-  healthCheck.set_type(HealthCheck::COMMAND);
-  healthCheck.mutable_command()->set_value("exit $STATUS");
+  healthCheck.set_type(HealthCheck::TCP);
+  healthCheck.mutable_tcp()->set_port(80);
+  healthCheck.mutable_tcp()->set_protocol(GetParam());
   healthCheck.set_delay_seconds(0);
   healthCheck.set_interval_seconds(0);
-  healthCheck.set_grace_period_seconds(0);
-
-  Environment::Variable* variable = healthCheck.mutable_command()->
-    mutable_environment()->mutable_variables()->Add();
-  variable->set_name("STATUS");
-  variable->set_value("0");
+  healthCheck.set_grace_period_seconds(15);
 
   task.mutable_health_check()->CopyFrom(healthCheck);
 
-  Resources executorResources =
-    allocatedResources(Resources::parse("cpus:0.1;mem:32;disk:32").get(), "*");
+  Future<ContainerID> containerId;
+  EXPECT_CALL(containerizer, launch(_, _, _, _))
+    .WillOnce(DoAll(FutureArg<0>(&containerId),
+                    Invoke(&containerizer,
+                           &MockDockerContainerizer::_launch)));
 
-  task.mutable_resources()->CopyFrom(task.resources() - executorResources);
+  Future<TaskStatus> statusStarting;
+  Future<TaskStatus> statusRunning;
+  Future<TaskStatus> statusHealthy;
 
-  TaskGroupInfo taskGroup;
-  taskGroup.add_tasks()->CopyFrom(task);
+  EXPECT_CALL(sched, statusUpdate(&driver, _))
+    .WillOnce(FutureArg<1>(&statusStarting))
+    .WillOnce(FutureArg<1>(&statusRunning))
+    .WillOnce(FutureArg<1>(&statusHealthy))
+    .WillRepeatedly(Return()); // Ignore subsequent updates.
 
-  ExecutorInfo executor;
-  executor.mutable_executor_id()->set_value("default");
-  executor.set_type(ExecutorInfo::DEFAULT);
-  executor.mutable_framework_id()->CopyFrom(frameworkId.get());
-  executor.mutable_resources()->CopyFrom(executorResources);
-  executor.mutable_shutdown_grace_period()->set_nanoseconds(Seconds(10).ns());
+  driver.launchTasks(offers.get()[0].id(), {task});
 
-  driver.acceptOffers(
-      {offers->front().id()}, {LAUNCH_GROUP(executor, taskGroup)});
+  AWAIT_READY(containerId);
 
   AWAIT_READY(statusStarting);
-  EXPECT_EQ(TASK_STARTING, statusStarting.get().state());
+  EXPECT_EQ(TASK_STARTING, statusStarting->state());
 
   AWAIT_READY(statusRunning);
-  EXPECT_EQ(TASK_RUNNING, statusRunning.get().state());
+  EXPECT_EQ(TASK_RUNNING, statusRunning->state());
 
   AWAIT_READY(statusHealthy);
-  EXPECT_EQ(TASK_RUNNING, statusHealthy.get().state());
+  EXPECT_EQ(TASK_RUNNING, statusHealthy->state());
   EXPECT_EQ(
       TaskStatus::REASON_TASK_HEALTH_CHECK_STATUS_UPDATED,
       statusHealthy->reason());
-  EXPECT_TRUE(statusHealthy.get().has_healthy());
-  EXPECT_TRUE(statusHealthy.get().healthy());
-
-  Future<hashset<ContainerID>> containerIds = containerizer->containers();
+  EXPECT_TRUE(statusHealthy->has_healthy());
+  EXPECT_TRUE(statusHealthy->healthy());
 
-  AWAIT_READY(containerIds);
+  Future<Option<ContainerTermination>> termination =
+    containerizer.wait(containerId.get());
 
   driver.stop();
   driver.join();
 
-  // Cleanup all mesos launched containers.
-  foreach (const ContainerID& containerId, containerIds.get()) {
-    AWAIT_READY(containerizer->wait(containerId));
-  }
+  AWAIT_READY(termination);
+  EXPECT_SOME(termination.get());
 }
 
 } // namespace tests {

http://git-wip-us.apache.org/repos/asf/mesos/blob/00dfbc80/src/tests/mesos.hpp
----------------------------------------------------------------------
diff --git a/src/tests/mesos.hpp b/src/tests/mesos.hpp
index e5c2b69..f02c7c6 100644
--- a/src/tests/mesos.hpp
+++ b/src/tests/mesos.hpp
@@ -46,10 +46,12 @@
 #include <process/future.hpp>
 #include <process/gmock.hpp>
 #include <process/gtest.hpp>
+#include <process/io.hpp>
 #include <process/owned.hpp>
 #include <process/pid.hpp>
 #include <process/process.hpp>
 #include <process/queue.hpp>
+#include <process/subprocess.hpp>
 
 #include <process/ssl/gtest.hpp>
 
@@ -107,6 +109,8 @@ constexpr char DEFAULT_EXECUTOR_SECRET_KEY[] =
   "PcWRQlrJAt871oWgSH+n52vMZ3aVI+AFMzXSo8+sUfMk83IGp0WJefhzeQsjDlGH"
   "GYQgCAuGim0BE2X5U+lEue8s697uQpAO8L/FFRuDH2s";
 
+constexpr char DOCKER_IPv6_NETWORK[] = "mesos-docker-ip6-test";
+
 
 // Forward declarations.
 class MockExecutor;
@@ -2088,6 +2092,69 @@ inline mesos::Environment createEnvironment(
 }
 
 
+inline void createDockerIPv6UserNetwork()
+{
+  // Create a Docker user network with IPv6 enabled.
+  Try<std::string> dockerCommand = strings::format(
+      "docker network create --driver=bridge --ipv6 "
+      "--subnet=fd01::/64 %s",
+      DOCKER_IPv6_NETWORK);
+
+  Try<process::Subprocess> s = subprocess(
+      dockerCommand.get(),
+      process::Subprocess::PATH("/dev/null"),
+      process::Subprocess::PATH("/dev/null"),
+      process::Subprocess::PIPE());
+
+  ASSERT_SOME(s) << "Unable to create the Docker IPv6 network: "
+                 << DOCKER_IPv6_NETWORK;
+
+  process::Future<std::string> err = process::io::read(s->err().get());
+
+  // Wait for the network to be created.
+  AWAIT_READY(s->status());
+  AWAIT_READY(err);
+
+  ASSERT_SOME(s->status().get());
+  ASSERT_EQ(s->status().get().get(), 0)
+    << "Unable to create the Docker IPv6 network "
+    << DOCKER_IPv6_NETWORK
+    << " : " << err.get();
+}
+
+
+inline void removeDockerIPv6UserNetwork()
+{
+  // Delete the Docker user network.
+  Try<std::string> dockerCommand = strings::format(
+      "docker network rm %s",
+      DOCKER_IPv6_NETWORK);
+
+  Try<process::Subprocess> s = subprocess(
+      dockerCommand.get(),
+      process::Subprocess::PATH("/dev/null"),
+      process::Subprocess::PATH("/dev/null"),
+      process::Subprocess::PIPE());
+
+  // This is best effort cleanup. In case of an error just a log an
+  // error.
+  ASSERT_SOME(s) << "Unable to delete the Docker IPv6 network: "
+                 << DOCKER_IPv6_NETWORK;
+
+  process::Future<std::string> err = process::io::read(s->err().get());
+
+  // Wait for the network to be deleted.
+  AWAIT_READY(s->status());
+  AWAIT_READY(err);
+
+  ASSERT_SOME(s->status().get());
+  ASSERT_EQ(s->status().get().get(), 0)
+    << "Unable to delete the Docker IPv6 network "
+    << DOCKER_IPv6_NETWORK
+    << " : " << err.get();
+}
+
+
 // Macros to get/create (default) ExecutorInfos and FrameworkInfos.
 #define DEFAULT_EXECUTOR_INFO createExecutorInfo("default", "exit 1")