Added a test `ROOT_NoTransitionFromKillingToFinished`.

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


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

Branch: refs/heads/master
Commit: 815263ead42f64fd0ff320614cfada30b2e8f899
Parents: f569aee
Author: Qian Zhang <zhq527...@gmail.com>
Authored: Wed Oct 4 23:38:11 2017 +0800
Committer: Qian Zhang <zhq527...@gmail.com>
Committed: Wed Nov 1 17:21:45 2017 +0800

----------------------------------------------------------------------
 src/tests/default_executor_tests.cpp  | 181 +++++++++++++++++++++++++++++
 src/tests/kill_policy_test_helper.cpp |   9 ++
 2 files changed, 190 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/815263ea/src/tests/default_executor_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/default_executor_tests.cpp 
b/src/tests/default_executor_tests.cpp
index f485b61..21950f5 100644
--- a/src/tests/default_executor_tests.cpp
+++ b/src/tests/default_executor_tests.cpp
@@ -48,6 +48,7 @@
 
 #include "tests/cluster.hpp"
 #include "tests/containerizer.hpp"
+#include "tests/kill_policy_test_helper.hpp"
 #include "tests/mesos.hpp"
 
 #include "slave/containerizer/mesos/containerizer.hpp"
@@ -1409,6 +1410,186 @@ TEST_P(DefaultExecutorTest, SigkillExecutor)
 }
 
 
+// This test verifies that a task will transition from `TASK_KILLING`
+// to `TASK_KILLED` rather than `TASK_FINISHED` when it is killed,
+// even if it returns an "EXIT_STATUS" of 0 on receiving a SIGTERM.
+TEST_P(DefaultExecutorTest, ROOT_NoTransitionFromKillingToFinished)
+{
+  Try<Owned<cluster::Master>> master = StartMaster();
+  ASSERT_SOME(master);
+
+  slave::Flags flags = CreateSlaveFlags();
+  flags.containerizers = GetParam();
+
+  Fetcher fetcher(flags);
+
+  Try<MesosContainerizer*> create =
+    MesosContainerizer::create(flags, true, &fetcher);
+
+  ASSERT_SOME(create);
+
+  Owned<Containerizer> containerizer(create.get());
+
+  Owned<MasterDetector> detector = master.get()->createDetector();
+
+  Try<Owned<cluster::Slave>> slave = StartSlave(
+        detector.get(),
+        containerizer.get(),
+        flags);
+
+  ASSERT_SOME(slave);
+
+  auto scheduler = std::make_shared<v1::MockHTTPScheduler>();
+
+  // Start the framework with the task killing capability.
+  v1::FrameworkInfo::Capability capability;
+  capability.set_type(v1::FrameworkInfo::Capability::TASK_KILLING_STATE);
+
+  v1::FrameworkInfo frameworkInfo = v1::DEFAULT_FRAMEWORK_INFO;
+  frameworkInfo.add_capabilities()->CopyFrom(capability);
+
+  EXPECT_CALL(*scheduler, connected(_))
+    .WillOnce(v1::scheduler::SendSubscribe(frameworkInfo));
+
+  Future<v1::scheduler::Event::Subscribed> subscribed;
+  EXPECT_CALL(*scheduler, subscribed(_, _))
+    .WillOnce(FutureArg<1>(&subscribed));
+
+  Future<v1::scheduler::Event::Offers> offers;
+  EXPECT_CALL(*scheduler, offers(_, _))
+    .WillOnce(FutureArg<1>(&offers))
+    .WillRepeatedly(Return()); // Ignore subsequent offers.
+
+  EXPECT_CALL(*scheduler, heartbeat(_))
+    .WillRepeatedly(Return()); // Ignore heartbeats.
+
+  v1::scheduler::TestMesos mesos(
+      master.get()->pid,
+      ContentType::PROTOBUF,
+      scheduler);
+
+  AWAIT_READY(subscribed);
+  v1::FrameworkID frameworkId(subscribed->framework_id());
+
+  v1::ExecutorInfo executorInfo = v1::createExecutorInfo(
+      v1::DEFAULT_EXECUTOR_ID,
+      None(),
+      "cpus:0.1;mem:32;disk:32",
+      v1::ExecutorInfo::DEFAULT,
+      frameworkId);
+
+  AWAIT_READY(offers);
+  ASSERT_FALSE(offers->offers().empty());
+
+  const v1::Offer& offer = offers->offers(0);
+  const v1::AgentID& agentId = offer.agent_id();
+
+  v1::CommandInfo commandInfo;
+  commandInfo.set_shell(false);
+  commandInfo.set_value(getTestHelperPath("test-helper"));
+  commandInfo.add_arguments("test-helper");
+  commandInfo.add_arguments(KillPolicyTestHelper::NAME);
+  commandInfo.add_arguments("--sleep_duration=0");
+
+  v1::TaskInfo taskInfo = v1::createTask(
+      agentId,
+      v1::Resources::parse("cpus:0.1;mem:32;disk:32").get(),
+      commandInfo);
+
+  Future<v1::scheduler::Event::Update> startingUpdate;
+  Future<v1::scheduler::Event::Update> runningUpdate;
+  EXPECT_CALL(*scheduler, update(_, _))
+    .WillOnce(
+        DoAll(
+            FutureArg<1>(&startingUpdate),
+            v1::scheduler::SendAcknowledge(frameworkId, agentId)))
+    .WillOnce(
+        DoAll(
+            FutureArg<1>(&runningUpdate),
+            v1::scheduler::SendAcknowledge(frameworkId, agentId)));
+
+  mesos.send(
+      v1::createCallAccept(
+          frameworkId,
+          offer,
+          {v1::LAUNCH_GROUP(
+              executorInfo, v1::createTaskGroupInfo({taskInfo}))}));
+
+  AWAIT_READY(startingUpdate);
+  ASSERT_EQ(TASK_STARTING, startingUpdate->status().state());
+
+  AWAIT_READY_FOR(runningUpdate, Seconds(60));
+  ASSERT_EQ(TASK_RUNNING, runningUpdate->status().state());
+  ASSERT_EQ(taskInfo.task_id(), runningUpdate->status().task_id());
+
+  v1::ContainerStatus status = runningUpdate->status().container_status();
+
+  ASSERT_TRUE(status.has_container_id());
+  EXPECT_TRUE(status.container_id().has_parent());
+
+  v1::ContainerID executorContainerId = status.container_id().parent();
+
+  Future<Option<ContainerTermination>> wait =
+    containerizer->wait(devolve(executorContainerId));
+
+  string executorSandbox = slave::paths::getExecutorLatestRunPath(
+      flags.work_dir,
+      devolve(agentId),
+      devolve(frameworkId),
+      devolve(executorInfo.executor_id()));
+
+  string filePath = path::join(
+      executorSandbox,
+      "tasks",
+      taskInfo.task_id().value(),
+      KillPolicyTestHelper::NAME);
+
+  // Wait up to 5 seconds for the `test-helper` program to create a file into
+  // its sandbox which is a signal that the task has been fully started.
+  Duration waited = Duration::zero();
+  do {
+    if (os::exists(filePath)) {
+      break;
+    }
+
+    os::sleep(Seconds(1));
+    waited += Seconds(1);
+  } while (waited < Seconds(5));
+
+  EXPECT_TRUE(os::exists(filePath));
+
+  Future<v1::scheduler::Event::Update> killingUpdate;
+  Future<v1::scheduler::Event::Update> killedUpdate;
+  EXPECT_CALL(*scheduler, update(_, _))
+    .WillOnce(
+        DoAll(
+            FutureArg<1>(&killingUpdate),
+            v1::scheduler::SendAcknowledge(frameworkId, agentId)))
+    .WillOnce(
+        DoAll(
+            FutureArg<1>(&killedUpdate),
+              v1::scheduler::SendAcknowledge(frameworkId, agentId)));
+
+  // Now kill the task in the task group, the default executor will
+  // call the agent to send SIGTERM to the container. The `test-helper`
+  // program will return an "EXIT_STATUS" of 0 on receiving a SIGTERM.
+  mesos.send(v1::createCallKill(frameworkId, taskInfo.task_id()));
+
+  AWAIT_READY(killingUpdate);
+  ASSERT_EQ(TASK_KILLING, killingUpdate->status().state());
+  ASSERT_EQ(taskInfo.task_id(), killingUpdate->status().task_id());
+
+  AWAIT_READY(killedUpdate);
+  ASSERT_EQ(TASK_KILLED, killedUpdate->status().state());
+  ASSERT_EQ(taskInfo.task_id(), killedUpdate->status().task_id());
+
+  AWAIT_READY(wait);
+  ASSERT_SOME(wait.get());
+  ASSERT_TRUE(wait.get()->has_status());
+  ASSERT_EQ(0, wait.get()->status());
+}
+
+
 #ifdef __linux__
 // This test verifies that tasks from two different
 // task groups can share the same pid namespace.

http://git-wip-us.apache.org/repos/asf/mesos/blob/815263ea/src/tests/kill_policy_test_helper.cpp
----------------------------------------------------------------------
diff --git a/src/tests/kill_policy_test_helper.cpp 
b/src/tests/kill_policy_test_helper.cpp
index a188059..28768a3 100644
--- a/src/tests/kill_policy_test_helper.cpp
+++ b/src/tests/kill_policy_test_helper.cpp
@@ -58,6 +58,15 @@ int KillPolicyTestHelper::execute()
   action.sa_handler = sigtermHandler;
   sigaction(SIGTERM, &action, nullptr);
 
+  // Create a file in the sandbox which can be considered as
+  // a signal that this test helper has been fully started.
+  Try<Nothing> touch = os::touch(KillPolicyTestHelper::NAME);
+  if (touch.isError()) {
+    std::cerr << "Failed to create file '" << KillPolicyTestHelper::NAME
+              << "': " << touch.error() << std::endl;
+    return EXIT_FAILURE;
+  }
+
   // Block the process until we get a signal.
   pause();
 

Reply via email to