-
Notifications
You must be signed in to change notification settings - Fork 19
feat: support additional cgroup formats for container-id parsing #222
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 10 commits
edb4f89
bb3d458
2174c80
136d4cc
43f214a
d6e9297
6251ac4
7fb67c9
70d6db4
f0abaad
565c4e0
587405f
08909b1
376793e
039883a
d8576b9
9d1946b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,10 @@ | ||
| #include "platform_util.h" | ||
|
|
||
| #include <cassert> | ||
| #include <cstdint> | ||
| #include <fstream> | ||
| #include <iostream> | ||
| #include <regex> | ||
|
|
||
| // clang-format off | ||
| #if defined(__x86_64__) || defined(_M_X64) | ||
|
|
@@ -289,18 +292,10 @@ Expected<InMemoryFile> InMemoryFile::make(StringView) { | |
| namespace container { | ||
| namespace { | ||
| #if defined(__linux__) || defined(__unix__) | ||
| /// Magic numbers from linux/magic.h: | ||
| /// <https://github.com/torvalds/linux/blob/ca91b9500108d4cf083a635c2e11c884d5dd20ea/include/uapi/linux/magic.h#L71> | ||
| constexpr uint64_t CGROUP_SUPER_MAGIC = 0x27e0eb; | ||
| constexpr uint64_t CGROUP2_SUPER_MAGIC = 0x63677270; | ||
|
|
||
| /// Magic number from linux/proc_ns.h: | ||
| /// <https://github.com/torvalds/linux/blob/5859a2b1991101d6b978f3feb5325dad39421f29/include/linux/proc_ns.h#L41-L49> | ||
| constexpr ino_t HOST_CGROUP_NAMESPACE_INODE = 0xeffffffb; | ||
|
|
||
| /// Represents the cgroup version of the current process. | ||
| enum class Cgroup : char { v1, v2 }; | ||
|
|
||
| Optional<ino_t> get_inode(std::string_view path) { | ||
| struct stat buf; | ||
| if (stat(path.data(), &buf) != 0) { | ||
|
|
@@ -323,46 +318,37 @@ bool is_running_in_host_namespace() { | |
| return false; | ||
| } | ||
|
|
||
| Optional<Cgroup> get_cgroup_version() { | ||
| struct statfs buf; | ||
|
|
||
| if (statfs("/sys/fs/cgroup", &buf) != 0) { | ||
| Optional<std::string> find_container_id_from_cgroup() { | ||
| auto cgroup_fd = std::ifstream("/proc/self/cgroup", std::ios::in); | ||
| if (!cgroup_fd.is_open()) { | ||
| return nullopt; | ||
| } | ||
|
|
||
| if (buf.f_type == CGROUP_SUPER_MAGIC) | ||
| return Cgroup::v1; | ||
| else if (buf.f_type == CGROUP2_SUPER_MAGIC) | ||
| return Cgroup::v2; | ||
|
|
||
| return nullopt; | ||
| } | ||
|
|
||
| Optional<std::string> find_docker_container_id_from_cgroup() { | ||
| auto cgroup_fd = std::ifstream("/proc/self/cgroup", std::ios::in); | ||
| if (!cgroup_fd.is_open()) return nullopt; | ||
|
|
||
| return find_docker_container_id(cgroup_fd); | ||
| return find_container_id(cgroup_fd); | ||
| } | ||
| #endif | ||
| } // namespace | ||
|
|
||
| Optional<std::string> find_docker_container_id(std::istream& source) { | ||
| constexpr std::string_view docker_str = "docker-"; | ||
| Optional<std::string> find_container_id(std::istream& source) { | ||
| static const std::string uuid_regex_str = | ||
| "[0-9a-f]{8}[-_][0-9a-f]{4}[-_][0-9a-f]{4}[-_][0-9a-f]{4}[-_][0-9a-f]{12}" | ||
| "|(?:[0-9a-f]{8}(?:-[0-9a-f]{4}){4}$)"; | ||
| static const std::string container_regex_str = "[0-9a-f]{64}"; | ||
| static const std::string task_regex_str = "[0-9a-f]{32}-\\d+"; | ||
| static const std::regex path_reg("(?:.+)?(" + uuid_regex_str + "|" + | ||
| container_regex_str + "|" + task_regex_str + | ||
| ")(?:\\.scope)?$"); | ||
|
|
||
| std::string line; | ||
| while (std::getline(source, line)) { | ||
| // Example: | ||
| // `0::/system.slice/docker-abcdef0123456789abcdef0123456789.scope` | ||
| if (auto beg = line.find(docker_str); beg != std::string::npos) { | ||
| beg += docker_str.size(); | ||
| auto end = line.find(".scope", beg); | ||
| if (end == std::string::npos || end - beg <= 0) { | ||
| continue; | ||
| } | ||
| std::smatch match; | ||
| if (std::regex_match(line, match, path_reg)) { | ||
|
zacharycmontoya marked this conversation as resolved.
Outdated
|
||
| assert(match.ready()); | ||
| assert(match.size() == 2); | ||
|
|
||
| auto container_id = line.substr(beg, end - beg); | ||
| return container_id; | ||
| return match.str(1); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -371,32 +357,18 @@ Optional<std::string> find_docker_container_id(std::istream& source) { | |
|
|
||
| Optional<ContainerID> get_id() { | ||
| #if defined(__linux__) || defined(__unix__) | ||
| if (is_running_in_host_namespace()) { | ||
| // Not in a container, no need to continue. | ||
| return nullopt; | ||
| } | ||
|
|
||
| auto maybe_cgroup = get_cgroup_version(); | ||
| if (!maybe_cgroup) return nullopt; | ||
|
|
||
| // Determine the container ID or inode | ||
| ContainerID id; | ||
| switch (*maybe_cgroup) { | ||
| case Cgroup::v1: { | ||
| if (auto maybe_id = find_docker_container_id_from_cgroup()) { | ||
| id.value = *maybe_id; | ||
| id.type = ContainerID::Type::container_id; | ||
| break; | ||
| } | ||
| if (auto maybe_id = find_container_id_from_cgroup()) { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At this moment, this changes the behavior to align more closely with the .NET and Java libraries:
Additional changes I plan to make:
This has been tested with system-tests by running the cpp_nginx library locally against this system-tests PR (system-tests#4925). To get an nginx-datadog build, I had the CI run on the zach.montoya/test-dd-trace-cpp-container-id branch of Datadog/nginx-datadog, with the latest commit here.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the current code, we clearly follow two paths: one for cgroup v1 and one for cgroup v2. Most importantly, since we know for sure we can’t get the container ID for cgroup v2, there’s no need to try in that case. I spent a good amount of time reading the RFC, and I believe this version matches it better. I suggest we keep it this way. EDIT: Given that the container ID is not found, it should report the inode. My understanding is using the inode alone be should sufficient for host-level tag correlation. Have we had a chance to investigate why this approach might not be working as expected?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The issue I had (maybe this is simply a Docker in Docker scenario) is that the original I can take a closer look at
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Just to make sure I'm understanding you correctly on this, is the newly proposed code your preference? Or is your preference the previous code where we clearly separated the logical paths based on cgroup v1 / v2? The "this" and "current" words in your comment were slightly ambiguous to me when reading
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Following up on this, I ran the nginx:1.25.4 container (the one where the cpp_nginx weblog is deployed) and there's no
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So actually, I was able to run this, which I think functions the same as However, the type is tmpfs, which is not I've also confirmed that the
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
My bad. I'd rather keep the previous code.
cgroup v1 controller are usually mounter under
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Understood, will follow up with the requested changes 👍🏼
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| id.value = *maybe_id; | ||
| id.type = ContainerID::Type::container_id; | ||
| } else if (!is_running_in_host_namespace()) { | ||
| // NOTE(@dmehala): failed to find the container ID, try getting the cgroup inode. | ||
| auto maybe_inode = get_inode("/sys/fs/cgroup"); | ||
| if (maybe_inode) { | ||
| id.type = ContainerID::Type::cgroup_inode; | ||
| id.value = std::to_string(*maybe_inode); | ||
| } | ||
| // NOTE(@dmehala): failed to find the container ID, try getting the cgroup | ||
| // inode. | ||
| [[fallthrough]]; | ||
| case Cgroup::v2: { | ||
| if (auto maybe_inode = get_inode("/sys/fs/cgroup")) { | ||
| id.type = ContainerID::Type::cgroup_inode; | ||
| id.value = std::to_string(*maybe_inode); | ||
| } | ||
| }; break; | ||
| } | ||
|
|
||
| return id; | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.