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
commit 2241e64caf7ecd735d9a8aaabf02c3f5f3a35ab8 Author: Jason Zhou <[email protected]> AuthorDate: Fri Jul 26 16:53:34 2024 -0400 [cgroups2] Add helper to normalize allow/deny list. This patch adds a public helper function to abstract away the logic used to make a list comply with the 'normalized' requirements. As a reminder, an allow or deny list is 'normalized' iff everything below are true: 1. No Entry has empty accesses specified. 2. No two entries on the same list can have the same selector (type, major & minor numbers). 3. No two entries on the same list can be encompassed by the other entry (see Entry::encompasses). Review: https://reviews.apache.org/r/75104/ --- src/linux/cgroups2.cpp | 59 +++++++++++++++++++++++++++ src/linux/cgroups2.hpp | 5 +++ src/tests/containerizer/cgroups2_tests.cpp | 65 ++++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+) diff --git a/src/linux/cgroups2.cpp b/src/linux/cgroups2.cpp index bd6018050..5b027c5fb 100644 --- a/src/linux/cgroups2.cpp +++ b/src/linux/cgroups2.cpp @@ -30,6 +30,7 @@ #include <process/pid.hpp> #include <stout/adaptor.hpp> +#include <stout/linkedhashmap.hpp> #include <stout/none.hpp> #include <stout/numify.hpp> #include <stout/os.hpp> @@ -1530,6 +1531,64 @@ bool normalized(const vector<Entry>& query) return true; } + +vector<Entry> normalize(const vector<Entry>& to_normalize) +{ + auto strip_empties = [](const vector<Entry>& entries) { + vector<Entry> stripped = {}; + foreach (const Entry& entry, entries) { + if (!entry.access.none()) { + stripped.push_back(entry); + } + } + return stripped; + }; + + auto deduplicate = [](const vector<Entry>& entries) { + LinkedHashMap<string, Entry> deduplicated; + foreach (const Entry& entry, entries) { + if (!deduplicated.contains(stringify(entry.selector))) { + deduplicated[stringify(entry.selector)] = entry; + } + + Entry& e = deduplicated.at(stringify(entry.selector)); + e.access.write |= entry.access.write; + e.access.read |= entry.access.read; + e.access.mknod |= entry.access.mknod; + } + + return deduplicated.values(); + }; + + auto strip_encompassed = [](const vector<Entry>& entries) { + vector<Entry> result = {}; + foreach (const Entry& entry, entries) { + bool is_encompassed = [&]() { + foreach (const Entry& other, entries) { + if (!cgroups::devices::operator==(entry.selector, other.selector) + && other.encompasses(entry)) { + return true; + } + } + return false; + }(); + + // Skip entries that are encompassed by other entries. + if (!is_encompassed) { + result.push_back(entry); + } + } + return result; + }; + + vector<Entry> result = to_normalize; + result = strip_empties(result); + result = deduplicate(result); + result = strip_encompassed(result); + CHECK(normalized(result)); + return result; +} + } // namespace devices { } // namespace cgroups2 { diff --git a/src/linux/cgroups2.hpp b/src/linux/cgroups2.hpp index ef4e67932..e13e57076 100644 --- a/src/linux/cgroups2.hpp +++ b/src/linux/cgroups2.hpp @@ -451,6 +451,11 @@ Try<Nothing> configure( // entry (see Entry::encompasses). bool normalized(const std::vector<Entry>& entries); + +// Modifies the given entries such that the three normalization requirements +// are fulfilled. +std::vector<Entry> normalize(const std::vector<Entry>& entries); + } // namespace devices { } // namespace cgroups2 { diff --git a/src/tests/containerizer/cgroups2_tests.cpp b/src/tests/containerizer/cgroups2_tests.cpp index 388639bed..de31a330a 100644 --- a/src/tests/containerizer/cgroups2_tests.cpp +++ b/src/tests/containerizer/cgroups2_tests.cpp @@ -907,6 +907,71 @@ TEST(Cgroups2DevicesTest, NormalizedTest) })); } + +TEST(Cgroups2DevicesTest, NormalizeTest) +{ + // Empty entries are eliminated. + devices::Entry empty_entry; + empty_entry.selector.type = devices::Entry::Selector::Type::CHARACTER; + empty_entry.selector.major = 1; + empty_entry.selector.minor = 3; + empty_entry.access.read = false; + empty_entry.access.write = false; + empty_entry.access.mknod = false; + EXPECT_EQ( + vector<devices::Entry>{}, + cgroups2::devices::normalize({empty_entry})); + + // Entries with matching type, major & minor numbers are combined into one + EXPECT_EQ( + vector<devices::Entry>{ + CHECK_NOTERROR(devices::Entry::parse("b 3:1 rwm")) + }, + cgroups2::devices::normalize({ + CHECK_NOTERROR(devices::Entry::parse("b 3:1 rw")), + CHECK_NOTERROR(devices::Entry::parse("b 3:1 m")) + })); + + // Entries that are encompassed by another are eliminated + EXPECT_EQ( + vector<devices::Entry>{CHECK_NOTERROR(devices::Entry::parse("c *:* rw"))}, + cgroups2::devices::normalize({ + CHECK_NOTERROR(devices::Entry::parse("c *:* rw")), + CHECK_NOTERROR(devices::Entry::parse("c 3:1 w")) + })); + + // Test all three scenarios + EXPECT_EQ( + vector<devices::Entry>{CHECK_NOTERROR(devices::Entry::parse("c *:* rw"))}, + cgroups2::devices::normalize({ + empty_entry, + CHECK_NOTERROR(devices::Entry::parse("c *:* r")), + CHECK_NOTERROR(devices::Entry::parse("c *:* w")), + CHECK_NOTERROR(devices::Entry::parse("c 3:1 w")) + })); + + // Normalized lists are unaffected + vector<devices::Entry> already_normalized = { + CHECK_NOTERROR(devices::Entry::parse("c *:* m")), + CHECK_NOTERROR(devices::Entry::parse("b *:* m")), + CHECK_NOTERROR(devices::Entry::parse("c 5:1 rwm")), + CHECK_NOTERROR(devices::Entry::parse("c 4:0 rwm")), + CHECK_NOTERROR(devices::Entry::parse("c 4:1 rwm")), + CHECK_NOTERROR(devices::Entry::parse("c 136:* rwm")), + CHECK_NOTERROR(devices::Entry::parse("c 5:2 rwm")), + CHECK_NOTERROR(devices::Entry::parse("c 10:200 rwm")), + CHECK_NOTERROR(devices::Entry::parse("c 1:3 rwm")), + CHECK_NOTERROR(devices::Entry::parse("c 1:5 rwm")), + CHECK_NOTERROR(devices::Entry::parse("c 1:7 rwm")), + CHECK_NOTERROR(devices::Entry::parse("c 5:0 rwm")), + CHECK_NOTERROR(devices::Entry::parse("c 1:9 rwm")), + CHECK_NOTERROR(devices::Entry::parse("c 1:8 rwm")) + }; + EXPECT_TRUE(cgroups2::devices::normalized(already_normalized)); + EXPECT_EQ(already_normalized, + cgroups2::devices::normalize(already_normalized)); +} + } // namespace tests { } // namespace internal {
