This is an automated email from the ASF dual-hosted git repository.

bmahler pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mesos.git


The following commit(s) were added to refs/heads/master by this push:
     new d63cf20e3 [cgroups2] Introduce utility to parse a container id from a 
cgroup path.
d63cf20e3 is described below

commit d63cf20e3112b18d26c74fd8ea7f7ba2903521df
Author: Devin Leamy <[email protected]>
AuthorDate: Fri Apr 5 17:34:31 2024 -0400

    [cgroups2] Introduce utility to parse a container id from a cgroup path.
    
    During agent recovery, we parse the directories in the cgroup hierarchy
    to determine what containers were previously running in the agent.
    
    Here we implement the cgroup directory parsing for cgroups v2's updated
    cgroup directory structure.
    
    This closes #551
---
 src/linux/cgroups2.cpp                             |  8 ++-
 src/slave/containerizer/mesos/paths.cpp            | 41 ++++++++++++++
 src/slave/containerizer/mesos/paths.hpp            | 10 ++++
 .../mesos_containerizer_paths_tests.cpp            | 66 ++++++++++++++++++++++
 4 files changed, 122 insertions(+), 3 deletions(-)

diff --git a/src/linux/cgroups2.cpp b/src/linux/cgroups2.cpp
index b5e4babe9..00534d755 100644
--- a/src/linux/cgroups2.cpp
+++ b/src/linux/cgroups2.cpp
@@ -381,7 +381,7 @@ Try<Nothing> destroy(const string& cgroup)
   // the most deeply nested directories first.
   Try<Nothing> kill = cgroups2::kill(cgroup);
   if (kill.isError()) {
-    return Error("Failed to kill processes in cgroup");
+    return Error("Failed to kill processes in cgroup: " + kill.error());
   }
 
   Try<set<string>> cgroups = cgroups2::get(cgroup);
@@ -389,12 +389,14 @@ Try<Nothing> destroy(const string& cgroup)
     return Error("Failed to get nested cgroups: " + cgroups.error());
   }
 
-  vector<string> sorted(cgroups->begin(), cgroups->end());
+  vector<string> sorted(
+      std::make_move_iterator(cgroups->begin()),
+      std::make_move_iterator(cgroups->end()));
   sorted.push_back(cgroup);
   std::sort(sorted.rbegin(), sorted.rend());
 
   foreach (const string& cgroup, sorted) {
-    const string& path = cgroups2::path(cgroup);
+    const string path = cgroups2::path(cgroup);
     Try<Nothing> rmdir = os::rmdir(path, false);
     if (rmdir.isError()) {
       return Error(
diff --git a/src/slave/containerizer/mesos/paths.cpp 
b/src/slave/containerizer/mesos/paths.cpp
index c379dc1d8..126950279 100644
--- a/src/slave/containerizer/mesos/paths.cpp
+++ b/src/slave/containerizer/mesos/paths.cpp
@@ -677,6 +677,47 @@ string container(
   else      { return path::join(root, container);}
 }
 
+
+Option<ContainerID> containerId(
+    const string& root,
+    const string& cgroup)
+{
+  // Remove the trailing `/leaf`, if present.
+  string path = strings::remove(cgroup, "/leaf", strings::SUFFIX);
+  // Remove leading or trailing slashes.
+  path = strings::trim(path, "/");
+
+  vector<string> tokens =
+    strings::tokenize(path, stringify(os::PATH_SEPARATOR));
+
+  if (tokens.size() == 0) {
+    // Root.
+    return None();
+  }
+  if (tokens.back() == "agent") {
+    // Mesos Agent.
+    return None();
+  }
+
+  Option<ContainerID> current;
+  foreach (const string& token, tokens) {
+    if (token == CGROUP_SEPARATOR) {
+      continue;
+    }
+
+    ContainerID id;
+    id.set_value(token);
+
+    if (current.isSome()) {
+      id.mutable_parent()->CopyFrom(*current);
+    }
+
+    current = id;
+  }
+
+  return current;
+}
+
 } // namespace cgroups2 {
 } // namespace paths {
 } // namespace containerizer {
diff --git a/src/slave/containerizer/mesos/paths.hpp 
b/src/slave/containerizer/mesos/paths.hpp
index a5edb98d4..6acd72363 100644
--- a/src/slave/containerizer/mesos/paths.hpp
+++ b/src/slave/containerizer/mesos/paths.hpp
@@ -320,6 +320,16 @@ std::string container(
     const ContainerID& containerId,
     bool leaf = false);
 
+
+// Get a containerId from a container's cgroup.
+// Inverse of `cgroups2::container(root, id)`.
+//
+// Leaf paths (which end in `/leaf`) and non-leaf paths will resolve to the
+// same container id.
+Option<ContainerID> containerId(
+    const std::string& root,
+    const std::string& cgroup);
+
 } // namespace cgroups2 {
 } // namespace paths {
 } // namespace containerizer {
diff --git a/src/tests/containerizer/mesos_containerizer_paths_tests.cpp 
b/src/tests/containerizer/mesos_containerizer_paths_tests.cpp
index e194b3b92..45afc8465 100644
--- a/src/tests/containerizer/mesos_containerizer_paths_tests.cpp
+++ b/src/tests/containerizer/mesos_containerizer_paths_tests.cpp
@@ -18,8 +18,11 @@
 
 #include <gtest/gtest.h>
 
+#include <stout/gtest.hpp>
 #include <stout/path.hpp>
 
+#include "linux/cgroups2.hpp"
+
 #include "slave/containerizer/mesos/paths.hpp"
 
 using std::string;
@@ -132,6 +135,69 @@ TEST(MesosContainerizerPathsTest, CGROUPS2_Cgroups2Paths)
             cgroups2::container("mesos", child2, true));
 }
 
+
+TEST(MesosContainerizerPathsTest, CGROUPS2_Cgroups2ParsePaths)
+{
+  namespace cgroups2 = mesos::internal::slave::containerizer::paths::cgroups2;
+
+  EXPECT_NONE(cgroups2::containerId("mesos", ""));
+  EXPECT_NONE(cgroups2::containerId("mesos", "/"));
+  EXPECT_NONE(cgroups2::containerId("mesos", cgroups2::agent("mesos")));
+  EXPECT_NONE(cgroups2::containerId("mesos", cgroups2::agent("mesos", true)));
+
+  // Setup the container chain: id1 -> id2 -> id3
+  ContainerID id1;
+  id1.set_value("id1");
+
+  ContainerID id2;
+  id2.set_value("id2");
+  id2.mutable_parent()->CopyFrom(id1);
+
+  ContainerID id3;
+  id3.set_value("id3");
+  id3.mutable_parent()->CopyFrom(id2);
+
+  auto EXPECT_SOME_ID_EQ =
+    [](ContainerID expected, Option<ContainerID> _actual) {
+    EXPECT_SOME(_actual);
+
+    ContainerID actual = *_actual;
+    while (expected.has_parent() && actual.has_parent()) {
+      EXPECT_EQ(expected.value(), actual.value());
+      expected = expected.parent();
+      actual = actual.parent();
+    }
+
+    EXPECT_EQ(expected.has_parent(), actual.has_parent());
+    EXPECT_EQ(expected.value(), actual.value());
+  };
+
+  EXPECT_SOME_ID_EQ(
+      id3,
+      cgroups2::containerId("mesos", cgroups2::container("mesos", id3)));
+
+  EXPECT_SOME_ID_EQ(
+      id2,
+      cgroups2::containerId("mesos", cgroups2::container("mesos", id2)));
+
+  EXPECT_SOME_ID_EQ(
+      id1,
+      cgroups2::containerId("mesos", cgroups2::container("mesos", id1)));
+
+  // Test leaf cgroups.
+  EXPECT_SOME_ID_EQ(
+      id3,
+      cgroups2::containerId("mesos", cgroups2::container("mesos", id3, true)));
+
+  EXPECT_SOME_ID_EQ(
+      id2,
+      cgroups2::containerId("mesos", cgroups2::container("mesos", id2, true)));
+
+  EXPECT_SOME_ID_EQ(
+      id1,
+      cgroups2::containerId("mesos", cgroups2::container("mesos", id1, true)));
+}
+
 } // namespace tests {
 } // namespace internal {
 } // namespace mesos {

Reply via email to