You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by ji...@apache.org on 2016/12/07 00:07:46 UTC

[4/6] mesos git commit: Added implementation of `watch()` to the 'IOSwitchboard' isolator.

Added implementation of `watch()` to the 'IOSwitchboard' isolator.

We use watch() to trigger the destruction of a container if the
io switchboard server process ever exits unexpectedly.

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


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

Branch: refs/heads/master
Commit: 052abf154510a9047019197e37857e8e26daab72
Parents: 4069e1c
Author: Kevin Klues <kl...@gmail.com>
Authored: Tue Dec 6 15:21:50 2016 -0800
Committer: Jie Yu <yu...@gmail.com>
Committed: Tue Dec 6 15:21:50 2016 -0800

----------------------------------------------------------------------
 include/mesos/mesos.proto                       |  1 +
 include/mesos/v1/mesos.proto                    |  1 +
 .../containerizer/mesos/io/switchboard.cpp      | 54 +++++++++++++++
 .../containerizer/mesos/io/switchboard.hpp      |  4 ++
 .../containerizer/io_switchboard_tests.cpp      | 72 ++++++++++++++++++++
 5 files changed, 132 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/052abf15/include/mesos/mesos.proto
----------------------------------------------------------------------
diff --git a/include/mesos/mesos.proto b/include/mesos/mesos.proto
index 8279225..8b311fb 100644
--- a/include/mesos/mesos.proto
+++ b/include/mesos/mesos.proto
@@ -1649,6 +1649,7 @@ message TaskStatus {
     REASON_GC_ERROR = 4;
     REASON_INVALID_FRAMEWORKID = 5;
     REASON_INVALID_OFFERS = 6;
+    REASON_IO_SWITCHBOARD_EXITED = 27;
     REASON_MASTER_DISCONNECTED = 7;
     REASON_RECONCILIATION = 9;
     REASON_RESOURCES_UNKNOWN = 18;

http://git-wip-us.apache.org/repos/asf/mesos/blob/052abf15/include/mesos/v1/mesos.proto
----------------------------------------------------------------------
diff --git a/include/mesos/v1/mesos.proto b/include/mesos/v1/mesos.proto
index 4628cc6..70fab66 100644
--- a/include/mesos/v1/mesos.proto
+++ b/include/mesos/v1/mesos.proto
@@ -1648,6 +1648,7 @@ message TaskStatus {
     REASON_GC_ERROR = 4;
     REASON_INVALID_FRAMEWORKID = 5;
     REASON_INVALID_OFFERS = 6;
+    REASON_IO_SWITCHBOARD_EXITED = 27;
     REASON_MASTER_DISCONNECTED = 7;
     REASON_RECONCILIATION = 9;
     REASON_RESOURCES_UNKNOWN = 18;

http://git-wip-us.apache.org/repos/asf/mesos/blob/052abf15/src/slave/containerizer/mesos/io/switchboard.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/io/switchboard.cpp b/src/slave/containerizer/mesos/io/switchboard.cpp
index 8d4f1e9..e1ca695 100644
--- a/src/slave/containerizer/mesos/io/switchboard.cpp
+++ b/src/slave/containerizer/mesos/io/switchboard.cpp
@@ -89,6 +89,7 @@ using mesos::slave::ContainerConfig;
 using mesos::slave::ContainerClass;
 using mesos::slave::ContainerIO;
 using mesos::slave::ContainerLaunchInfo;
+using mesos::slave::ContainerLimitation;
 using mesos::slave::ContainerLogger;
 using mesos::slave::ContainerState;
 using mesos::slave::Isolator;
@@ -645,6 +646,59 @@ Future<http::Connection> IOSwitchboard::connect(
 }
 
 
+Future<ContainerLimitation> IOSwitchboard::watch(
+    const ContainerID& containerId)
+{
+#ifdef __WINDOWS__
+  return Future<ContainerLimitation>();
+#else
+  if (local) {
+    return Future<ContainerLimitation>();
+  }
+
+  // We ignore unknown containers here because legacy containers
+  // without an io switchboard directory will not have an info struct
+  // created for during recovery. Likewise, containers launched
+  // by a previous agent with io switchboard server mode disabled will
+  // not have info structs created for them either. In both cases
+  // there is nothing to watch, so we return an unsatisfiable future.
+  if (!infos.contains(containerId)) {
+    return Future<ContainerLimitation>();
+  }
+
+  Future<Option<int>> status = infos[containerId]->status;
+
+  return status
+    .then([](const Option<int>& status) {
+      if (status.isNone()) {
+        return Future<ContainerLimitation>();
+      }
+
+      if (status.isSome() &&
+          WIFEXITED(status.get()) &&
+          WEXITSTATUS(status.get()) == 0) {
+        return Future<ContainerLimitation>();
+      }
+
+      ContainerLimitation limitation;
+      limitation.set_reason(TaskStatus::REASON_IO_SWITCHBOARD_EXITED);
+
+      if (WIFEXITED(status.get())) {
+        limitation.set_message(
+            "'IOSwitchboard' exited with status:"
+            " " + stringify(WEXITSTATUS(status.get())));
+      } else if (WIFSIGNALED(status.get())) {
+        limitation.set_message(
+            "'IOSwitchboard' exited with signal:"
+            " " + stringify(strsignal(WTERMSIG(status.get()))));
+      }
+
+      return Future<ContainerLimitation>(limitation);
+    });
+#endif // __WINDOWS__
+}
+
+
 Future<Nothing> IOSwitchboard::cleanup(
     const ContainerID& containerId)
 {

http://git-wip-us.apache.org/repos/asf/mesos/blob/052abf15/src/slave/containerizer/mesos/io/switchboard.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/io/switchboard.hpp b/src/slave/containerizer/mesos/io/switchboard.hpp
index ca0eee6..a3f19fc 100644
--- a/src/slave/containerizer/mesos/io/switchboard.hpp
+++ b/src/slave/containerizer/mesos/io/switchboard.hpp
@@ -26,6 +26,7 @@
 
 #include <stout/try.hpp>
 
+#include <mesos/slave/containerizer.hpp>
 #include <mesos/slave/container_logger.hpp>
 
 #include "slave/flags.hpp"
@@ -68,6 +69,9 @@ public:
       const ContainerID& containerId,
       const mesos::slave::ContainerConfig& containerConfig);
 
+  virtual process::Future<mesos::slave::ContainerLimitation> watch(
+    const ContainerID& containerId);
+
   virtual process::Future<Nothing> cleanup(
       const ContainerID& containerId);
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/052abf15/src/tests/containerizer/io_switchboard_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/io_switchboard_tests.cpp b/src/tests/containerizer/io_switchboard_tests.cpp
index c8fe876..d98e4f4 100644
--- a/src/tests/containerizer/io_switchboard_tests.cpp
+++ b/src/tests/containerizer/io_switchboard_tests.cpp
@@ -35,6 +35,8 @@
 #include "common/http.hpp"
 #include "common/recordio.hpp"
 
+#include "slave/containerizer/mesos/paths.hpp"
+
 #include "slave/containerizer/mesos/io/switchboard.hpp"
 
 #include "tests/environment.hpp"
@@ -46,6 +48,8 @@ namespace http = process::http;
 namespace unix = process::network::unix;
 #endif // __WINDOWS__
 
+namespace paths = mesos::internal::slave::containerizer::paths;
+
 using mesos::agent::Call;
 using mesos::agent::ProcessIO;
 
@@ -508,6 +512,74 @@ TEST_F(IOSwitchboardTest, OutputRedirectionWithTTY)
   EXPECT_SOME_EQ("HelloWorld", os::read(path::join(directory.get(), "stdout")));
 }
 
+
+// This test verifies that a container will be
+// destroyed if its io switchboard exits unexpectedly.
+TEST_F(IOSwitchboardTest, KillSwitchboardContainerDestroyed)
+{
+  slave::Flags flags = CreateSlaveFlags();
+  flags.launcher = "posix";
+  flags.isolation = "posix/cpu";
+  flags.io_switchboard_enable_server = true;
+
+  Fetcher fetcher;
+
+  Try<MesosContainerizer*> create = MesosContainerizer::create(
+      flags,
+      false,
+      &fetcher);
+
+  ASSERT_SOME(create);
+
+  Owned<MesosContainerizer> containerizer(create.get());
+
+  SlaveState state;
+  state.id = SlaveID();
+
+  AWAIT_READY(containerizer->recover(state));
+
+  ContainerID containerId;
+  containerId.set_value(UUID::random().toString());
+
+  Try<string> directory = environment->mkdtemp();
+  ASSERT_SOME(directory);
+
+  ExecutorInfo executorInfo = createExecutorInfo(
+      "executor",
+      "sleep 1000",
+      "cpus:1");
+
+  Future<bool> launch = containerizer->launch(
+      containerId,
+      None(),
+      executorInfo,
+      directory.get(),
+      None(),
+      SlaveID(),
+      map<string, string>(),
+      true); // TODO(benh): Ever want to test not checkpointing?
+
+  AWAIT_ASSERT_TRUE(launch);
+
+  Result<pid_t> pid = paths::getContainerIOSwitchboardPid(
+        flags.runtime_dir, containerId);
+
+  ASSERT_SOME(pid);
+
+  ASSERT_EQ(0, os::kill(pid.get(), SIGKILL));
+
+  Future<Option<ContainerTermination>> wait = containerizer->wait(containerId);
+
+  AWAIT_READY(wait);
+  ASSERT_SOME(wait.get());
+
+  ASSERT_TRUE(wait.get()->has_status());
+  EXPECT_WTERMSIG_EQ(SIGKILL, wait.get()->status());
+
+  ASSERT_TRUE(wait.get()->reasons().size() == 1);
+  ASSERT_EQ(TaskStatus::REASON_IO_SWITCHBOARD_EXITED,
+            wait.get()->reasons().Get(0));
+}
 #endif // __WINDOWS__
 
 } // namespace tests {