You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by an...@apache.org on 2017/01/20 00:52:48 UTC

[6/6] mesos git commit: Made the Agent API able to handle containers nested at arbitrary levels.

Made the Agent API able to handle containers nested at arbitrary levels.

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


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

Branch: refs/heads/master
Commit: dd922d51530931fe513df80b7adfd3dd0d23ae27
Parents: 60c59ad
Author: Gast�n Kleiman <ga...@mesosphere.com>
Authored: Thu Jan 19 16:52:01 2017 -0800
Committer: Anand Mazumdar <an...@apache.org>
Committed: Thu Jan 19 16:52:01 2017 -0800

----------------------------------------------------------------------
 src/slave/http.cpp      |   8 --
 src/slave/slave.cpp     |   7 +-
 src/tests/api_tests.cpp | 201 +++++++++++++++++++++++++++++++++++++------
 3 files changed, 178 insertions(+), 38 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/dd922d51/src/slave/http.cpp
----------------------------------------------------------------------
diff --git a/src/slave/http.cpp b/src/slave/http.cpp
index 0cf07e1..4c64fe5 100644
--- a/src/slave/http.cpp
+++ b/src/slave/http.cpp
@@ -2180,14 +2180,6 @@ Future<Response> Slave::Http::_launchNestedContainer(
     ContentType acceptType,
     const Owned<ObjectApprover>& approver) const
 {
-  // We do not yet support launching containers that are nested
-  // two levels beneath the executor's container.
-  if (containerId.parent().has_parent()) {
-    return NotImplemented(
-        "Only a single level of container nesting is supported currently,"
-        " but 'launch_nested_container.container_id.parent.parent' is set");
-  }
-
   Executor* executor = slave->getExecutor(containerId);
   if (executor == nullptr) {
     return NotFound("Container " + stringify(containerId) + " cannot be found");

http://git-wip-us.apache.org/repos/asf/mesos/blob/dd922d51/src/slave/slave.cpp
----------------------------------------------------------------------
diff --git a/src/slave/slave.cpp b/src/slave/slave.cpp
index 2bb5b4e..0548b04 100644
--- a/src/slave/slave.cpp
+++ b/src/slave/slave.cpp
@@ -6478,19 +6478,20 @@ Executor* Framework::getExecutor(const TaskID& taskId)
 
 Executor* Slave::getExecutor(const ContainerID& containerId) const
 {
+  const ContainerID rootContainerId = protobuf::getRootContainerId(containerId);
+
   // Locate the executor (for now we just loop since we don't
   // index based on container id and this likely won't have a
   // significant performance impact due to the low number of
   // executors per-agent).
-  // TODO(adam-mesos): Support more levels of nesting.
   foreachvalue (Framework* framework, frameworks) {
     foreachvalue (Executor* executor, framework->executors) {
-      if (executor->containerId == containerId ||
-          executor->containerId == containerId.parent()) {
+      if (rootContainerId == executor->containerId) {
         return executor;
       }
     }
   }
+
   return nullptr;
 }
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/dd922d51/src/tests/api_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/api_tests.cpp b/src/tests/api_tests.cpp
index 47fa3bf..8c0ed23 100644
--- a/src/tests/api_tests.cpp
+++ b/src/tests/api_tests.cpp
@@ -3609,29 +3609,61 @@ TEST_P_TEMP_DISABLED_ON_WINDOWS(AgentAPITest, NestedContainerLaunchFalse)
 }
 
 
-TEST_P(AgentAPITest, NestedContainerLaunchGrandchildNotSupported)
+TEST_P_TEMP_DISABLED_ON_WINDOWS(AgentAPITest, NestedContainerLaunch)
 {
   ContentType contentType = GetParam();
 
   Clock::pause();
 
-  Future<Nothing> __recover = FUTURE_DISPATCH(_, &Slave::__recover);
+  Try<Owned<cluster::Master>> master = StartMaster();
+  ASSERT_SOME(master);
 
-  StandaloneMasterDetector detector;
-  Try<Owned<cluster::Slave>> slave = StartSlave(&detector);
+  Owned<MasterDetector> detector = master.get()->createDetector();
+
+  MockExecutor exec(DEFAULT_EXECUTOR_ID);
+  TestContainerizer containerizer(&exec);
+
+  slave::Flags agentFlags = CreateSlaveFlags();
+  Try<Owned<cluster::Slave>> slave =
+    StartSlave(detector.get(), &containerizer, agentFlags);
 
   ASSERT_SOME(slave);
 
-  // Wait for the agent to finish recovery.
-  AWAIT_READY(__recover);
-  Clock::settle();
+  MockScheduler sched;
+  MesosSchedulerDriver driver(
+      &sched, DEFAULT_FRAMEWORK_INFO, master.get()->pid, DEFAULT_CREDENTIAL);
 
-  // Try to launch a grandchild container.
+  EXPECT_CALL(sched, registered(&driver, _, _))
+    .Times(1);
+
+  Future<vector<Offer>> offers;
+  EXPECT_CALL(sched, resourceOffers(&driver, _))
+    .WillOnce(LaunchTasks(DEFAULT_EXECUTOR_INFO, 1, 0.1, 32, "*"))
+    .WillRepeatedly(Return()); // Ignore subsequent offers.
+
+  Future<Nothing> executorRegistered;
+  EXPECT_CALL(exec, registered(_, _, _, _))
+    .WillOnce(FutureSatisfy(&executorRegistered));
+
+  EXPECT_CALL(exec, launchTask(_, _))
+    .Times(1);
+
+  driver.start();
+
+  // Trigger authentication and registration for the agent.
+  Clock::advance(agentFlags.authentication_backoff_factor);
+  Clock::advance(agentFlags.registration_backoff_factor);
+
+  AWAIT_READY(executorRegistered);
+
+  Future<hashset<ContainerID>> containerIds = containerizer.containers();
+  AWAIT_READY(containerIds);
+  ASSERT_EQ(1u, containerIds->size());
+
+  // Launch a nested container and wait for it to finish.
   v1::ContainerID containerId;
   containerId.set_value(UUID::random().toString());
-  containerId.mutable_parent()->set_value(UUID::random().toString());
-  containerId.mutable_parent()->mutable_parent()
-    ->set_value(UUID::random().toString());
+  containerId.mutable_parent()->set_value(containerIds->begin()->value());
 
   {
     v1::agent::Call call;
@@ -3647,12 +3679,58 @@ TEST_P(AgentAPITest, NestedContainerLaunchGrandchildNotSupported)
       serialize(contentType, call),
       stringify(contentType));
 
-    AWAIT_EXPECT_RESPONSE_STATUS_EQ(http::NotImplemented().status, response);
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(http::OK().status, response);
+  }
+
+  Future<v1::agent::Response> wait;
+
+  {
+    v1::agent::Call call;
+    call.set_type(v1::agent::Call::WAIT_NESTED_CONTAINER);
+
+    call.mutable_wait_nested_container()->mutable_container_id()
+      ->CopyFrom(containerId);
+
+    wait = post(slave.get()->pid, call, contentType);
+
+    Clock::settle();
+
+    EXPECT_TRUE(wait.isPending());
+  }
+
+  // Now kill the nested container.
+  {
+    v1::agent::Call call;
+    call.set_type(v1::agent::Call::KILL_NESTED_CONTAINER);
+
+    call.mutable_kill_nested_container()->mutable_container_id()
+      ->CopyFrom(containerId);
+
+    Future<http::Response> response = http::post(
+      slave.get()->pid,
+      "api/v1",
+      createBasicAuthHeaders(DEFAULT_CREDENTIAL),
+      serialize(contentType, call),
+      stringify(contentType));
+
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(http::OK().status, response);
   }
+
+  AWAIT_READY(wait);
+  ASSERT_EQ(v1::agent::Response::WAIT_NESTED_CONTAINER, wait->type());
+
+  // The test containerizer sets exit status to 0 when destroyed.
+  EXPECT_EQ(0, wait->wait_nested_container().exit_status());
+
+  EXPECT_CALL(exec, shutdown(_))
+    .Times(AtMost(1));
+
+  driver.stop();
+  driver.join();
 }
 
 
-TEST_P_TEMP_DISABLED_ON_WINDOWS(AgentAPITest, NestedContainerLaunch)
+TEST_P_TEMP_DISABLED_ON_WINDOWS(AgentAPITest, TwoLevelNestedContainerLaunch)
 {
   ContentType contentType = GetParam();
 
@@ -3701,17 +3779,19 @@ TEST_P_TEMP_DISABLED_ON_WINDOWS(AgentAPITest, NestedContainerLaunch)
   AWAIT_READY(containerIds);
   ASSERT_EQ(1u, containerIds->size());
 
-  // Launch a nested container and wait for it to finish.
-  v1::ContainerID containerId;
-  containerId.set_value(UUID::random().toString());
-  containerId.mutable_parent()->set_value(containerIds->begin()->value());
+  // Launch a two level nested parent/child container and then wait for them to
+  // finish.
+  v1::ContainerID parentContainerId;
+  parentContainerId.set_value(UUID::random().toString());
+  parentContainerId.mutable_parent()->set_value(containerIds->begin()->value());
 
+  // Launch the parent container.
   {
     v1::agent::Call call;
     call.set_type(v1::agent::Call::LAUNCH_NESTED_CONTAINER);
 
     call.mutable_launch_nested_container()->mutable_container_id()
-      ->CopyFrom(containerId);
+      ->CopyFrom(parentContainerId);
 
     Future<http::Response> response = http::post(
       slave.get()->pid,
@@ -3723,29 +3803,69 @@ TEST_P_TEMP_DISABLED_ON_WINDOWS(AgentAPITest, NestedContainerLaunch)
     AWAIT_EXPECT_RESPONSE_STATUS_EQ(http::OK().status, response);
   }
 
-  Future<v1::agent::Response> wait;
+  // Launch the child container.
+  v1::ContainerID childContainerId;
+  childContainerId.set_value(UUID::random().toString());
+  childContainerId.mutable_parent()->CopyFrom(parentContainerId);
+
+  {
+    v1::agent::Call call;
+    call.set_type(v1::agent::Call::LAUNCH_NESTED_CONTAINER);
+
+    call.mutable_launch_nested_container()->mutable_container_id()
+      ->CopyFrom(childContainerId);
+
+    Future<http::Response> response = http::post(
+      slave.get()->pid,
+      "api/v1",
+      createBasicAuthHeaders(DEFAULT_CREDENTIAL),
+      serialize(contentType, call),
+      stringify(contentType));
+
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(http::OK().status, response);
+  }
+
+  // Wait for the parent container.
+  Future<v1::agent::Response> waitParent;
 
   {
     v1::agent::Call call;
     call.set_type(v1::agent::Call::WAIT_NESTED_CONTAINER);
 
     call.mutable_wait_nested_container()->mutable_container_id()
-      ->CopyFrom(containerId);
+      ->CopyFrom(parentContainerId);
 
-    wait = post(slave.get()->pid, call, contentType);
+    waitParent = post(slave.get()->pid, call, contentType);
 
     Clock::settle();
 
-    EXPECT_TRUE(wait.isPending());
+    EXPECT_TRUE(waitParent.isPending());
   }
 
-  // Now kill the nested container.
+  // Wait for the child container.
+  Future<v1::agent::Response> waitChild;
+
+  {
+    v1::agent::Call call;
+    call.set_type(v1::agent::Call::WAIT_NESTED_CONTAINER);
+
+    call.mutable_wait_nested_container()->mutable_container_id()
+      ->CopyFrom(childContainerId);
+
+    waitChild = post(slave.get()->pid, call, contentType);
+
+    Clock::settle();
+
+    EXPECT_TRUE(waitChild.isPending());
+  }
+
+  // Kill the child container.
   {
     v1::agent::Call call;
     call.set_type(v1::agent::Call::KILL_NESTED_CONTAINER);
 
     call.mutable_kill_nested_container()->mutable_container_id()
-      ->CopyFrom(containerId);
+      ->CopyFrom(childContainerId);
 
     Future<http::Response> response = http::post(
       slave.get()->pid,
@@ -3757,11 +3877,38 @@ TEST_P_TEMP_DISABLED_ON_WINDOWS(AgentAPITest, NestedContainerLaunch)
     AWAIT_EXPECT_RESPONSE_STATUS_EQ(http::OK().status, response);
   }
 
-  AWAIT_READY(wait);
-  ASSERT_EQ(v1::agent::Response::WAIT_NESTED_CONTAINER, wait->type());
+  AWAIT_READY(waitChild);
+  ASSERT_EQ(v1::agent::Response::WAIT_NESTED_CONTAINER, waitChild->type());
 
   // The test containerizer sets exit status to 0 when destroyed.
-  EXPECT_EQ(0, wait->wait_nested_container().exit_status());
+  EXPECT_EQ(0, waitChild->wait_nested_container().exit_status());
+
+  // The parent container should still be running.
+  EXPECT_TRUE(waitParent.isPending());
+
+  // Kill the parent container.
+  {
+    v1::agent::Call call;
+    call.set_type(v1::agent::Call::KILL_NESTED_CONTAINER);
+
+    call.mutable_kill_nested_container()->mutable_container_id()
+      ->CopyFrom(parentContainerId);
+
+    Future<http::Response> response = http::post(
+      slave.get()->pid,
+      "api/v1",
+      createBasicAuthHeaders(DEFAULT_CREDENTIAL),
+      serialize(contentType, call),
+      stringify(contentType));
+
+    AWAIT_EXPECT_RESPONSE_STATUS_EQ(http::OK().status, response);
+  }
+
+  AWAIT_READY(waitParent);
+  ASSERT_EQ(v1::agent::Response::WAIT_NESTED_CONTAINER, waitParent->type());
+
+  // The test containerizer sets exit status to 0 when destroyed.
+  EXPECT_EQ(0, waitParent->wait_nested_container().exit_status());
 
   EXPECT_CALL(exec, shutdown(_))
     .Times(AtMost(1));