From 54c226988d44f762caf734582e090a0570778b85 Mon Sep 17 00:00:00 2001 From: AliDatadog Date: Tue, 19 Dec 2023 18:22:09 +0100 Subject: [PATCH 1/3] Add build flags and stub + make sure the inode is reported only if we're not in the host cgroup namespace --- internal/{container.go => container_linux.go} | 44 ++++++++++++++----- ...tainer_test.go => container_linux_test.go} | 2 + internal/container_stub.go | 19 ++++++++ 3 files changed, 54 insertions(+), 11 deletions(-) rename internal/{container.go => container_linux.go} (76%) rename internal/{container_test.go => container_linux_test.go} (99%) create mode 100644 internal/container_stub.go diff --git a/internal/container.go b/internal/container_linux.go similarity index 76% rename from internal/container.go rename to internal/container_linux.go index d8ad8492ea..8b8d0566a7 100644 --- a/internal/container.go +++ b/internal/container_linux.go @@ -3,6 +3,8 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016 Datadog, Inc. +//go:build linux + package internal import ( @@ -20,20 +22,20 @@ const ( // cgroupPath is the path to the cgroup file where we can find the container id if one exists. cgroupPath = "/proc/self/cgroup" - // cgroupV2Key is the key used to store the cgroup v2 identify the cgroupv2 mount point in the cgroupMounts map. - cgroupV2Key = "cgroupv2" - // cgroupV1BaseController is the base controller used to identify the cgroup v1 mount point in the cgroupMounts map. cgroupV1BaseController = "memory" // defaultCgroupMountPath is the path to the cgroup mount point. defaultCgroupMountPath = "/sys/fs/cgroup" -) -const ( uuidSource = "[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}$" containerSource = "[0-9a-f]{64}" taskSource = "[0-9a-f]{32}-\\d+" + + // From https://github.com/torvalds/linux/blob/5859a2b1991101d6b978f3feb5325dad39421f29/include/linux/proc_ns.h#L41-L49 + // Currently, host namespace inode number are hardcoded, which can be used to detect + // if we're running in host namespace or not (does not work when running in DinD) + hostCgroupNamespaceInode = 0xEFFFFFFB ) var ( @@ -47,9 +49,8 @@ var ( containerID string // entityID is the entityID to use for the container. It is the `cid-` if the container id available, - // otherwise the cgroup v2 node inode prefixed with `in-` or an empty string on cgroup v1 or incompatible OS. - // It is retrieved by finding cgroup2 mounts in /proc/mounts, finding the cgroup v2 node path in /proc/self/cgroup and - // calling stat on mountPath+nodePath to get the inode. + // otherwise the cgroup node controller's inode prefixed with `in-` or an empty string on incompatible OS. + // We use the memory controller on cgroupv1 and the root cgroup on cgroupv2. entityID string ) @@ -145,16 +146,37 @@ func inodeForPath(path string) string { return fmt.Sprintf("in-%d", stats.Ino) } -// readEntityID attempts to return the cgroup v2 node inode or empty on failure. +// readEntityID attempts to return the cgroup node inode or empty on failure. func readEntityID(mountPath, cgroupPath string) string { + // First try to emit the containerID if available. It will be retrieved if the container is + // running in the host cgroup namespace, independently of the cgroup version. if containerID != "" { return "cid-" + containerID } + // Rely on the inode if we're not running in the host cgroup namespace. + if isHostCgroupNamespace() { + return "" + } return getCgroupInode(mountPath, cgroupPath) } -// EntityID attempts to return the container ID or the cgroup v2 node inode if the container ID is not available. -// The cid is prefixed with `cid-` and the inode with `in-`. +// EntityID attempts to return the container ID or the cgroup node controller's inode if the container ID +// is not available. The cid is prefixed with `cid-` and the inode with `in-`. func EntityID() string { return entityID } + +// isHostCgroupNamespace checks if the agent is running in the host cgroup namespace. +func isHostCgroupNamespace() bool { + fi, err := os.Stat("/proc/self/ns/cgroup") + if err != nil { + return false + } + + stat, ok := fi.Sys().(*syscall.Stat_t) + if ok { + return stat.Ino == hostCgroupNamespaceInode + } + + return false +} diff --git a/internal/container_test.go b/internal/container_linux_test.go similarity index 99% rename from internal/container_test.go rename to internal/container_linux_test.go index 5f831d16bb..8b61c9066b 100644 --- a/internal/container_test.go +++ b/internal/container_linux_test.go @@ -3,6 +3,8 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016 Datadog, Inc. +//go:build test && linux + package internal import ( diff --git a/internal/container_stub.go b/internal/container_stub.go new file mode 100644 index 0000000000..c6c2487406 --- /dev/null +++ b/internal/container_stub.go @@ -0,0 +1,19 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016 Datadog, Inc. + +//go:build !linux + +package internal + +// ContainerID attempts to return the container ID from /proc/self/cgroup or empty on failure. +func ContainerID() string { + return "" +} + +// EntityID attempts to return the container ID or the cgroup v2 node inode if the container ID is not available. +// The cid is prefixed with `cid-` and the inode with `in-`. +func EntityID() string { + return "" +} From 901f30d1c4055bb2ec6ee7fcf972e173924bfd2b Mon Sep 17 00:00:00 2001 From: AliDatadog Date: Tue, 19 Dec 2023 19:11:51 +0100 Subject: [PATCH 2/3] remove test build flag --- internal/container_linux_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/container_linux_test.go b/internal/container_linux_test.go index 8b61c9066b..511620845e 100644 --- a/internal/container_linux_test.go +++ b/internal/container_linux_test.go @@ -3,7 +3,7 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016 Datadog, Inc. -//go:build test && linux +//go:build linux package internal From d27c2f97d0557525783501f99a153e5518a248ee Mon Sep 17 00:00:00 2001 From: AliDatadog Date: Tue, 19 Dec 2023 19:24:35 +0100 Subject: [PATCH 3/3] make tests non environment specific --- internal/container_linux.go | 6 +++--- internal/container_linux_test.go | 7 +++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/internal/container_linux.go b/internal/container_linux.go index 8b8d0566a7..237c293e21 100644 --- a/internal/container_linux.go +++ b/internal/container_linux.go @@ -56,7 +56,7 @@ var ( func init() { containerID = readContainerID(cgroupPath) - entityID = readEntityID(defaultCgroupMountPath, cgroupPath) + entityID = readEntityID(defaultCgroupMountPath, cgroupPath, isHostCgroupNamespace()) } // parseContainerID finds the first container ID reading from r and returns it. @@ -147,14 +147,14 @@ func inodeForPath(path string) string { } // readEntityID attempts to return the cgroup node inode or empty on failure. -func readEntityID(mountPath, cgroupPath string) string { +func readEntityID(mountPath, cgroupPath string, isHostCgroupNamespace bool) string { // First try to emit the containerID if available. It will be retrieved if the container is // running in the host cgroup namespace, independently of the cgroup version. if containerID != "" { return "cid-" + containerID } // Rely on the inode if we're not running in the host cgroup namespace. - if isHostCgroupNamespace() { + if isHostCgroupNamespace { return "" } return getCgroupInode(mountPath, cgroupPath) diff --git a/internal/container_linux_test.go b/internal/container_linux_test.go index 511620845e..029ff647c4 100644 --- a/internal/container_linux_test.go +++ b/internal/container_linux_test.go @@ -92,7 +92,7 @@ func TestReadEntityIDPrioritizeCID(t *testing.T) { defer func(cid string) { containerID = cid }(containerID) containerID = "fakeContainerID" - eid := readEntityID("", "") + eid := readEntityID("", "", true) assert.Equal(t, "cid-fakeContainerID", eid) } @@ -119,8 +119,11 @@ func TestReadEntityIDFallbackOnInode(t *testing.T) { err = procSelfCgroup.Close() require.NoError(t, err) - eid := readEntityID(sysFsCgroupPath, procSelfCgroup.Name()) + eid := readEntityID(sysFsCgroupPath, procSelfCgroup.Name(), false) assert.Equal(t, expectedInode, eid) + + emptyEid := readEntityID(sysFsCgroupPath, procSelfCgroup.Name(), true) + assert.Equal(t, "", emptyEid) } func TestParsegroupControllerPath(t *testing.T) {