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 0416c958b [cgroups2] Introduce interface to get cgroups nested inside 
of a cgroup.
0416c958b is described below

commit 0416c958b085ea4a6f9930857004dc6b25c47fd1
Author: Devin Leamy <[email protected]>
AuthorDate: Thu Apr 4 22:53:04 2024 +0000

    [cgroups2] Introduce interface to get cgroups nested inside of a cgroup.
    
    Introduces
    ```
            cgroups2::get(cgroup)
    ```
    which returns the cgroups inside of the given cgroup.
---
 src/linux/cgroups2.cpp                     | 41 +++++++++++++++++++++
 src/linux/cgroups2.hpp                     |  4 +++
 src/tests/containerizer/cgroups2_tests.cpp | 58 ++++++++++++++++++++++++++++++
 3 files changed, 103 insertions(+)

diff --git a/src/linux/cgroups2.cpp b/src/linux/cgroups2.cpp
index fa74e88a0..8b401a178 100644
--- a/src/linux/cgroups2.cpp
+++ b/src/linux/cgroups2.cpp
@@ -14,6 +14,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <fts.h>
+
 #include "linux/cgroups2.hpp"
 
 #include <iterator>
@@ -306,6 +308,45 @@ bool exists(const string& cgroup)
 }
 
 
+Try<set<string>> get(const string& cgroup)
+{
+  const string& path = cgroups2::path(cgroup);
+  char* paths[] = {const_cast<char*>(path.c_str()), nullptr};
+
+  FTS* tree = fts_open(paths, FTS_NOCHDIR, nullptr);
+  if (tree == nullptr) {
+    return ErrnoError("Failed to start traversing filesystem");
+  }
+
+  FTSENT* node;
+  set<string> cgroups;
+  while ((node = fts_read(tree)) != nullptr) {
+    // Use post-order walk here. fts_level is the depth of the traversal,
+    // numbered from -1 to N, where the file/dir was found. The traversal root
+    // itself is numbered 0. fts_info includes flags for the current node.
+    // FTS_DP indicates a directory being visited in postorder.
+    if (node->fts_level > 0 && node->fts_info & FTS_DP) {
+      string _cgroup = strings::trim(
+          node->fts_path + MOUNT_POINT.length(), "/");
+      cgroups.insert(_cgroup);
+    }
+  }
+
+  if (errno != 0) {
+    Error error =
+      ErrnoError("Failed to read a node while traversing the filesystem");
+    fts_close(tree);
+    return error;
+  }
+
+  if (fts_close(tree) != 0) {
+    return ErrnoError("Failed to stop traversing file system");
+  }
+
+  return cgroups;
+}
+
+
 Try<Nothing> create(const string& cgroup, bool recursive)
 {
   const string path = cgroups2::path(cgroup);
diff --git a/src/linux/cgroups2.hpp b/src/linux/cgroups2.hpp
index cab679428..3a31ca0da 100644
--- a/src/linux/cgroups2.hpp
+++ b/src/linux/cgroups2.hpp
@@ -58,6 +58,10 @@ Try<Nothing> unmount();
 bool exists(const std::string& cgroup);
 
 
+// Get all of the cgroups under a given cgroup.
+Try<std::set<std::string>> get(const std::string& cgroup = ROOT_CGROUP);
+
+
 // Creates a cgroup off of the base hierarchy, i.e. /sys/fs/cgroup/<cgroup>.
 // cgroup can be a nested cgroup (e.g. foo/bar/baz). If cgroup is a nested
 // cgroup and the parent cgroups do not exist, an error will be returned unless
diff --git a/src/tests/containerizer/cgroups2_tests.cpp 
b/src/tests/containerizer/cgroups2_tests.cpp
index b05b656cd..cddf2e428 100644
--- a/src/tests/containerizer/cgroups2_tests.cpp
+++ b/src/tests/containerizer/cgroups2_tests.cpp
@@ -311,6 +311,64 @@ TEST_F(Cgroups2Test, ROOT_CGROUPS2_CpuBandwidthLimit)
 }
 
 
+TEST_F(Cgroups2Test, ROOT_CGROUPS2_GetCgroups)
+{
+  vector<string> cgroups = {
+    path::join(TEST_CGROUP, "test1"),
+    path::join(TEST_CGROUP, "test1/a"),
+    path::join(TEST_CGROUP, "test1/b"),
+    path::join(TEST_CGROUP, "test1/b/c"),
+    path::join(TEST_CGROUP, "test2"),
+    path::join(TEST_CGROUP, "test2/a"),
+    path::join(TEST_CGROUP, "test2/a/b"),
+    path::join(TEST_CGROUP, "test3"),
+  };
+
+  foreach (const string& cgroup, cgroups) {
+    ASSERT_SOME(cgroups2::create(cgroup, true));
+  }
+
+  EXPECT_SOME_EQ(
+      set<string>({
+        path::join(TEST_CGROUP, "test1"),
+        path::join(TEST_CGROUP, "test1/a"),
+        path::join(TEST_CGROUP, "test1/b"),
+        path::join(TEST_CGROUP, "test1/b/c"),
+        path::join(TEST_CGROUP, "test2"),
+        path::join(TEST_CGROUP, "test2/a"),
+        path::join(TEST_CGROUP, "test2/a/b"),
+        path::join(TEST_CGROUP, "test3"),
+      }),
+      cgroups2::get(TEST_CGROUP));
+
+  EXPECT_SOME_EQ(
+      set<string>({
+        path::join(TEST_CGROUP, "test1/a"),
+        path::join(TEST_CGROUP, "test1/b"),
+        path::join(TEST_CGROUP, "test1/b/c"),
+      }),
+      cgroups2::get(path::join(TEST_CGROUP, "test1")));
+
+  EXPECT_SOME_EQ(set<string>({
+        path::join(TEST_CGROUP, "test2/a"),
+        path::join(TEST_CGROUP, "test2/a/b"),
+      }),
+      cgroups2::get(path::join(TEST_CGROUP, "test2")));
+
+  EXPECT_SOME_EQ(
+      set<string>({
+        path::join(TEST_CGROUP, "test1/b/c")
+      }),
+      cgroups2::get(path::join(TEST_CGROUP, "test1/b")));
+
+  // Destroy the cgroups in reverse order so the most deeply-nested cgroups
+  // are removed first.
+  for (int i = cgroups.size() - 1; i >= 0; --i) {
+    ASSERT_SOME(cgroups2::destroy(cgroups[i]));
+  }
+}
+
+
 // Arguments for os::open(). Combination of a path and an access type.
 typedef pair<string, int> OpenArgs;
 

Reply via email to