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")