You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by al...@apache.org on 2017/03/13 18:16:11 UTC

[4/7] mesos git commit: Added `Containerizer::remove`.

Added `Containerizer::remove`.

This new method removes the sandbox and runtime directories of a
terminated nested container.

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


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

Branch: refs/heads/master
Commit: cbc88cfa9a412015d91aeca0894b9e7bb463c400
Parents: 321474a
Author: Gast�n Kleiman <ga...@mesosphere.io>
Authored: Mon Mar 13 18:22:26 2017 +0100
Committer: Alexander Rukletsov <al...@apache.org>
Committed: Mon Mar 13 19:15:40 2017 +0100

----------------------------------------------------------------------
 src/slave/containerizer/containerizer.hpp       |  12 ++
 src/slave/containerizer/mesos/containerizer.cpp |  51 ++++++
 src/slave/containerizer/mesos/containerizer.hpp |   4 +
 .../nested_mesos_containerizer_tests.cpp        | 176 +++++++++++++++++++
 4 files changed, 243 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/cbc88cfa/src/slave/containerizer/containerizer.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/containerizer.hpp b/src/slave/containerizer/containerizer.hpp
index f65a9b9..4c31a1f 100644
--- a/src/slave/containerizer/containerizer.hpp
+++ b/src/slave/containerizer/containerizer.hpp
@@ -151,6 +151,18 @@ public:
   virtual process::Future<bool> destroy(const ContainerID& containerId) = 0;
 
   virtual process::Future<hashset<ContainerID>> containers() = 0;
+
+  // Remove a nested container, including its sandbox and runtime directories.
+  //
+  // NOTE: You can only remove a a nested container that has been fully
+  // destroyed and whose parent has not been destroyed yet. If the parent has
+  // already been destroyed, then the sandbox and runtime directories will be
+  // eventually garbage collected. The caller is responsible for ensuring that
+  // `containerId` belongs to a nested container.
+  virtual process::Future<Nothing> remove(const ContainerID& containerId)
+  {
+    return process::Failure("Unsupported");
+  }
 };
 
 } // namespace slave {

http://git-wip-us.apache.org/repos/asf/mesos/blob/cbc88cfa/src/slave/containerizer/mesos/containerizer.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/containerizer.cpp b/src/slave/containerizer/mesos/containerizer.cpp
index 7676a4d..12cf1d8 100644
--- a/src/slave/containerizer/mesos/containerizer.cpp
+++ b/src/slave/containerizer/mesos/containerizer.cpp
@@ -594,6 +594,14 @@ Future<hashset<ContainerID>> MesosContainerizer::containers()
 }
 
 
+Future<Nothing> MesosContainerizer::remove(const ContainerID& containerId)
+{
+  return dispatch(process.get(),
+                  &MesosContainerizerProcess::remove,
+                  containerId);
+}
+
+
 Future<Nothing> MesosContainerizerProcess::recover(
     const Option<state::SlaveState>& state)
 {
@@ -2376,6 +2384,49 @@ void MesosContainerizerProcess::______destroy(
 }
 
 
+Future<Nothing> MesosContainerizerProcess::remove(
+    const ContainerID& containerId)
+{
+  // TODO(gkleiman): Check that recovery has completed before continuing.
+
+  CHECK(containerId.has_parent());
+
+  if (containers_.contains(containerId)) {
+    return Failure("Nested container has not terminated yet");
+  }
+
+  const ContainerID rootContainerId = protobuf::getRootContainerId(containerId);
+
+  if (!containers_.contains(rootContainerId)) {
+    return Failure("Unknown parent container");
+  }
+
+  const string runtimePath =
+    containerizer::paths::getRuntimePath(flags.runtime_dir, containerId);
+
+  if (os::exists(runtimePath)) {
+    Try<Nothing> rmdir = os::rmdir(runtimePath);
+    if (rmdir.isError()) {
+      return Failure(
+          "Failed to remove the runtime directory: " + rmdir.error());
+    }
+  }
+
+  const string sandboxPath = containerizer::paths::getSandboxPath(
+      containers_[rootContainerId]->directory.get(), containerId);
+
+  if (os::exists(sandboxPath)) {
+    Try<Nothing> rmdir = os::rmdir(sandboxPath);
+    if (rmdir.isError()) {
+      return Failure(
+          "Failed to remove the sandbox directory: " + rmdir.error());
+    }
+  }
+
+  return Nothing();
+}
+
+
 Future<Option<int>> MesosContainerizerProcess::reap(
     const ContainerID& containerId,
     pid_t pid)

http://git-wip-us.apache.org/repos/asf/mesos/blob/cbc88cfa/src/slave/containerizer/mesos/containerizer.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/mesos/containerizer.hpp b/src/slave/containerizer/mesos/containerizer.hpp
index 09f94cc..29a99f3 100644
--- a/src/slave/containerizer/mesos/containerizer.hpp
+++ b/src/slave/containerizer/mesos/containerizer.hpp
@@ -114,6 +114,8 @@ public:
 
   virtual process::Future<hashset<ContainerID>> containers();
 
+  virtual process::Future<Nothing> remove(const ContainerID& containerId);
+
 private:
   explicit MesosContainerizer(
       const process::Owned<MesosContainerizerProcess>& process);
@@ -187,6 +189,8 @@ public:
   virtual process::Future<bool> destroy(
       const ContainerID& containerId);
 
+  virtual process::Future<Nothing> remove(const ContainerID& containerId);
+
   virtual process::Future<hashset<ContainerID>> containers();
 
 private:

http://git-wip-us.apache.org/repos/asf/mesos/blob/cbc88cfa/src/tests/containerizer/nested_mesos_containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/nested_mesos_containerizer_tests.cpp b/src/tests/containerizer/nested_mesos_containerizer_tests.cpp
index ea01fe5..fa56d6e 100644
--- a/src/tests/containerizer/nested_mesos_containerizer_tests.cpp
+++ b/src/tests/containerizer/nested_mesos_containerizer_tests.cpp
@@ -29,6 +29,7 @@
 #include <stout/os.hpp>
 #include <stout/try.hpp>
 
+#include <stout/os/exists.hpp>
 #include <stout/os/kill.hpp>
 
 #include <process/future.hpp>
@@ -49,6 +50,8 @@
 using mesos::internal::slave::Fetcher;
 using mesos::internal::slave::MesosContainerizer;
 
+using mesos::internal::slave::containerizer::paths::getRuntimePath;
+using mesos::internal::slave::containerizer::paths::getSandboxPath;
 using mesos::internal::slave::containerizer::paths::buildPath;
 using mesos::internal::slave::containerizer::paths::JOIN;
 using mesos::internal::slave::containerizer::paths::PREFIX;
@@ -1983,6 +1986,179 @@ TEST_F(NestedMesosContainerizerTest, ROOT_CGROUPS_LaunchNestedThreeLevels)
   EXPECT_WTERMSIG_EQ(SIGKILL, wait.get()->status());
 }
 
+
+TEST_F(NestedMesosContainerizerTest, ROOT_CGROUPS_Remove)
+{
+  slave::Flags flags = CreateSlaveFlags();
+  flags.launcher = "linux";
+  flags.isolation = "cgroups/cpu,filesystem/linux,namespaces/pid";
+
+  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);
+
+  Future<bool> launch = containerizer->launch(
+      containerId,
+      None(),
+      createExecutorInfo("executor", "sleep 1000", "cpus:1"),
+      directory.get(),
+      None(),
+      state.id,
+      map<string, string>(),
+      true); // TODO(benh): Ever want to test not checkpointing?
+
+  AWAIT_ASSERT_TRUE(launch);
+
+  // Now launch nested container.
+  ContainerID nestedContainerId;
+  nestedContainerId.mutable_parent()->CopyFrom(containerId);
+  nestedContainerId.set_value(UUID::random().toString());
+
+  launch = containerizer->launch(
+      nestedContainerId,
+      createCommandInfo("true"),
+      None(),
+      None(),
+      state.id);
+
+  AWAIT_ASSERT_TRUE(launch);
+
+  Future<Option<ContainerTermination>> wait =
+    containerizer->wait(nestedContainerId);
+
+  AWAIT_READY(wait);
+  ASSERT_SOME(wait.get());
+  ASSERT_TRUE(wait.get()->has_status());
+  EXPECT_WEXITSTATUS_EQ(0, wait.get()->status());
+
+  // The runtime and sandbox directories must exist.
+  const string runtimePath =
+    getRuntimePath(flags.runtime_dir, nestedContainerId);
+  ASSERT_TRUE(os::exists(runtimePath));
+
+  const string sandboxPath = getSandboxPath(directory.get(), nestedContainerId);
+  ASSERT_TRUE(os::exists(sandboxPath));
+
+  // Now remove the nested container.
+  Future<Nothing> remove = containerizer->remove(nestedContainerId);
+  AWAIT_READY(remove);
+
+  // We now expect the runtime and sandbox directories NOT to exist.
+  EXPECT_FALSE(os::exists(runtimePath));
+  EXPECT_FALSE(os::exists(sandboxPath));
+
+  // We expect `remove` to be idempotent.
+  remove = containerizer->remove(nestedContainerId);
+  AWAIT_READY(remove);
+
+  // Finally destroy the parent container.
+  containerizer->destroy(containerId);
+
+  wait = containerizer->wait(containerId);
+  AWAIT_READY(wait);
+}
+
+
+TEST_F(NestedMesosContainerizerTest,
+       ROOT_CGROUPS_RemoveAfterParentDestroyed)
+{
+  slave::Flags flags = CreateSlaveFlags();
+  flags.launcher = "linux";
+  flags.isolation = "cgroups/cpu,filesystem/linux,namespaces/pid";
+
+  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);
+
+  Future<bool> launch = containerizer->launch(
+      containerId,
+      None(),
+      createExecutorInfo("executor", "sleep 1000", "cpus:1"),
+      directory.get(),
+      None(),
+      state.id,
+      map<string, string>(),
+      true); // TODO(benh): Ever want to test not checkpointing?
+
+  AWAIT_ASSERT_TRUE(launch);
+
+  // Now launch nested container.
+  ContainerID nestedContainerId;
+  nestedContainerId.mutable_parent()->CopyFrom(containerId);
+  nestedContainerId.set_value(UUID::random().toString());
+
+  launch = containerizer->launch(
+      nestedContainerId,
+      createCommandInfo("true"),
+      None(),
+      None(),
+      state.id);
+
+  AWAIT_ASSERT_TRUE(launch);
+
+  Future<Option<ContainerTermination>> wait =
+    containerizer->wait(nestedContainerId);
+
+  AWAIT_READY(wait);
+  ASSERT_SOME(wait.get());
+  ASSERT_TRUE(wait.get()->has_status());
+  EXPECT_WEXITSTATUS_EQ(0, wait.get()->status());
+
+  // The runtime and sandbox directories of the nested container must exist.
+  const string runtimePath =
+    getRuntimePath(flags.runtime_dir, nestedContainerId);
+  ASSERT_TRUE(os::exists(runtimePath));
+
+  const string sandboxPath = getSandboxPath(directory.get(), nestedContainerId);
+  ASSERT_TRUE(os::exists(sandboxPath));
+
+  // Now destroy the parent container.
+  containerizer->destroy(containerId);
+
+  wait = containerizer->wait(containerId);
+  AWAIT_READY(wait);
+
+  // We expect `remove` to fail.
+  Future<Nothing> remove = containerizer->remove(nestedContainerId);
+  AWAIT_FAILED(remove);
+}
+
 } // namespace tests {
 } // namespace internal {
 } // namespace mesos {