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;