-
Notifications
You must be signed in to change notification settings - Fork 911
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This resolves #6694. We've been tracking the update to cgroup version support and want to get ahead of the widespread usage. The surface of the existing `ContainerResource` has not changed, but its internals have been factored out to two "extractor" utilities -- one that understands cgroup v1 and another for v2. v1 is attempted and, if successful, the result is used. If v1 fails, then the `ContainerResource` will fall back to v2. As mentioned in #6694, the approach taken in this PR is borrowed from [this SO post](https://stackoverflow.com/questions/68816329/how-to-get-docker-container-id-from-within-the-container-with-cgroup-v2) combined with local experimentation on docker desktop on a Mac, which already uses cgroup2 v2.
- v2.13.3
- v2.13.2
- v2.13.1
- v2.13.0
- v2.12.0
- v2.11.0
- v2.10.0
- v2.9.0
- v2.8.0
- v2.7.0
- v2.6.0
- v2.5.0
- v2.4.0
- v2.3.0
- v2.2.0
- v2.1.0
- v2.0.0
- v1.33.6
- v1.33.5
- v1.33.4
- v1.33.3
- v1.33.2
- v1.33.1
- v1.33.0
- v1.32.1
- v1.32.0
- v1.31.0
- v1.30.0
- v1.29.0
- v1.28.0
- v1.27.0
- v1.26.0
- v1.25.1
- v1.25.0
- v1.24.0
- v1.23.0
- v1.22.1
- v1.22.0
- v1.21.0
- v1.20.2
- v1.20.1
- v1.20.0
1 parent
33b0b58
commit b09fb67
Showing
6 changed files
with
395 additions
and
148 deletions.
There are no files selected for viewing
95 changes: 95 additions & 0 deletions
95
...rc/main/java/io/opentelemetry/instrumentation/resources/CgroupV1ContainerIdExtractor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.instrumentation.resources; | ||
|
||
import io.opentelemetry.api.internal.OtelEncodingUtils; | ||
import java.nio.file.Path; | ||
import java.nio.file.Paths; | ||
import java.util.Optional; | ||
import java.util.logging.Level; | ||
import java.util.logging.Logger; | ||
import java.util.stream.Stream; | ||
|
||
/** Utility for extracting the container ID from runtimes inside cgroup v1 containers. */ | ||
final class CgroupV1ContainerIdExtractor { | ||
|
||
private static final Logger logger = | ||
Logger.getLogger(CgroupV1ContainerIdExtractor.class.getName()); | ||
static final Path V1_CGROUP_PATH = Paths.get("/proc/self/cgroup"); | ||
private final ContainerResource.Filesystem filesystem; | ||
|
||
CgroupV1ContainerIdExtractor() { | ||
this(ContainerResource.FILESYSTEM_INSTANCE); | ||
} | ||
|
||
// Exists for testing | ||
CgroupV1ContainerIdExtractor(ContainerResource.Filesystem filesystem) { | ||
this.filesystem = filesystem; | ||
} | ||
|
||
/** | ||
* Each line of cgroup file looks like "14:name=systemd:/docker/.../... A hex string is expected | ||
* inside the last section separated by '/' Each segment of the '/' can contain metadata separated | ||
* by either '.' (at beginning) or '-' (at end) | ||
* | ||
* @return containerId | ||
*/ | ||
Optional<String> extractContainerId() { | ||
if (!filesystem.isReadable(V1_CGROUP_PATH)) { | ||
return Optional.empty(); | ||
} | ||
try (Stream<String> lines = filesystem.lines(V1_CGROUP_PATH)) { | ||
return lines | ||
.filter(line -> !line.isEmpty()) | ||
.map(CgroupV1ContainerIdExtractor::getIdFromLine) | ||
.filter(Optional::isPresent) | ||
.findFirst() | ||
.orElse(Optional.empty()); | ||
} catch (Exception e) { | ||
logger.log(Level.WARNING, "Unable to read file", e); | ||
} | ||
return Optional.empty(); | ||
} | ||
|
||
private static Optional<String> getIdFromLine(String line) { | ||
// This cgroup output line should have the container id in it | ||
int lastSlashIdx = line.lastIndexOf('/'); | ||
if (lastSlashIdx < 0) { | ||
return Optional.empty(); | ||
} | ||
|
||
String containerId; | ||
|
||
String lastSection = line.substring(lastSlashIdx + 1); | ||
int colonIdx = lastSection.lastIndexOf(':'); | ||
|
||
if (colonIdx != -1) { | ||
// since containerd v1.5.0+, containerId is divided by the last colon when the cgroupDriver is | ||
// systemd: | ||
// https://github.com/containerd/containerd/blob/release/1.5/pkg/cri/server/helpers_linux.go#L64 | ||
containerId = lastSection.substring(colonIdx + 1); | ||
} else { | ||
int startIdx = lastSection.lastIndexOf('-'); | ||
int endIdx = lastSection.lastIndexOf('.'); | ||
|
||
startIdx = startIdx == -1 ? 0 : startIdx + 1; | ||
if (endIdx == -1) { | ||
endIdx = lastSection.length(); | ||
} | ||
if (startIdx > endIdx) { | ||
return Optional.empty(); | ||
} | ||
|
||
containerId = lastSection.substring(startIdx, endIdx); | ||
} | ||
|
||
if (OtelEncodingUtils.isValidBase16String(containerId) && !containerId.isEmpty()) { | ||
return Optional.of(containerId); | ||
} else { | ||
return Optional.empty(); | ||
} | ||
} | ||
} |
56 changes: 56 additions & 0 deletions
56
...rc/main/java/io/opentelemetry/instrumentation/resources/CgroupV2ContainerIdExtractor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.instrumentation.resources; | ||
|
||
import static java.util.Optional.empty; | ||
|
||
import java.io.IOException; | ||
import java.nio.file.Path; | ||
import java.nio.file.Paths; | ||
import java.util.Optional; | ||
import java.util.logging.Level; | ||
import java.util.logging.Logger; | ||
import java.util.regex.Matcher; | ||
import java.util.regex.Pattern; | ||
|
||
/** Utility for extracting the container ID from runtimes inside cgroup v2 containers. */ | ||
class CgroupV2ContainerIdExtractor { | ||
|
||
private static final Logger logger = | ||
Logger.getLogger(CgroupV2ContainerIdExtractor.class.getName()); | ||
|
||
static final Path V2_CGROUP_PATH = Paths.get("/proc/self/mountinfo"); | ||
private static final Pattern CONTAINER_RE = | ||
Pattern.compile(".*/docker/containers/([0-9a-f]{64})/.*"); | ||
|
||
private final ContainerResource.Filesystem filesystem; | ||
|
||
CgroupV2ContainerIdExtractor() { | ||
this(ContainerResource.FILESYSTEM_INSTANCE); | ||
} | ||
|
||
// Exists for testing | ||
CgroupV2ContainerIdExtractor(ContainerResource.Filesystem filesystem) { | ||
this.filesystem = filesystem; | ||
} | ||
|
||
Optional<String> extractContainerId() { | ||
if (!filesystem.isReadable(V2_CGROUP_PATH)) { | ||
return empty(); | ||
} | ||
try { | ||
return filesystem | ||
.lines(V2_CGROUP_PATH) | ||
.map(CONTAINER_RE::matcher) | ||
.filter(Matcher::matches) | ||
.findFirst() | ||
.map(matcher -> matcher.group(1)); | ||
} catch (IOException e) { | ||
logger.log(Level.WARNING, "Unable to read v2 cgroup path", e); | ||
} | ||
return empty(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
101 changes: 101 additions & 0 deletions
101
...est/java/io/opentelemetry/instrumentation/resources/CgroupV1ContainerIdExtractorTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.instrumentation.resources; | ||
|
||
import static io.opentelemetry.instrumentation.resources.CgroupV1ContainerIdExtractor.V1_CGROUP_PATH; | ||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.junit.jupiter.params.provider.Arguments.arguments; | ||
import static org.mockito.ArgumentMatchers.any; | ||
import static org.mockito.Mockito.never; | ||
import static org.mockito.Mockito.verify; | ||
import static org.mockito.Mockito.when; | ||
|
||
import java.io.IOException; | ||
import java.util.Optional; | ||
import java.util.stream.Stream; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.ExtendWith; | ||
import org.junit.jupiter.params.ParameterizedTest; | ||
import org.junit.jupiter.params.provider.Arguments; | ||
import org.junit.jupiter.params.provider.MethodSource; | ||
import org.junit.jupiter.params.provider.ValueSource; | ||
import org.mockito.Mock; | ||
import org.mockito.junit.jupiter.MockitoExtension; | ||
|
||
@ExtendWith(MockitoExtension.class) | ||
class CgroupV1ContainerIdExtractorTest { | ||
|
||
@Mock ContainerResource.Filesystem filesystem; | ||
|
||
@Test | ||
void fileNotReadable() throws IOException { | ||
when(filesystem.isReadable(V1_CGROUP_PATH)).thenReturn(false); | ||
CgroupV1ContainerIdExtractor extractor = new CgroupV1ContainerIdExtractor(filesystem); | ||
Optional<String> result = extractor.extractContainerId(); | ||
assertThat(result).isEmpty(); | ||
verify(filesystem, never()).lines(any()); | ||
} | ||
|
||
@ParameterizedTest | ||
@ValueSource( | ||
strings = { | ||
// invalid containerId (non-hex) | ||
"13:name=systemd:/podruntime/docker/kubepods/ac679f8a8319c8cf7d38e1adf263bc08d23zzzz", | ||
// unrecognized format (last "-" is after last ".") | ||
"13:name=systemd:/podruntime/docker/kubepods/ac679f8.a8319c8cf7d38e1adf263bc08-d23zzzz" | ||
}) | ||
void invalidContainerIds(String line) throws IOException { | ||
when(filesystem.isReadable(V1_CGROUP_PATH)).thenReturn(true); | ||
when(filesystem.lines(V1_CGROUP_PATH)).thenReturn(Stream.of(line)); | ||
|
||
CgroupV1ContainerIdExtractor extractor = new CgroupV1ContainerIdExtractor(filesystem); | ||
Optional<String> result = extractor.extractContainerId(); | ||
assertThat(result).isEmpty(); | ||
} | ||
|
||
@ParameterizedTest | ||
@MethodSource("validLines") | ||
void validCgroupLines(String line, String expectedContainerId) throws IOException { | ||
when(filesystem.isReadable(V1_CGROUP_PATH)).thenReturn(true); | ||
when(filesystem.lines(V1_CGROUP_PATH)).thenReturn(Stream.of(line)); | ||
|
||
CgroupV1ContainerIdExtractor extractor = new CgroupV1ContainerIdExtractor(filesystem); | ||
Optional<String> result = extractor.extractContainerId(); | ||
assertThat(result.orElse("fail")).isEqualTo(expectedContainerId); | ||
} | ||
|
||
static Stream<Arguments> validLines() { | ||
return Stream.of( | ||
// with suffix | ||
arguments( | ||
"13:name=systemd:/podruntime/docker/kubepods/ac679f8a8319c8cf7d38e1adf263bc08d23.aaaa", | ||
"ac679f8a8319c8cf7d38e1adf263bc08d23"), | ||
// with prefix and suffix | ||
arguments( | ||
"13:name=systemd:/podruntime/docker/kubepods/crio-dc679f8a8319c8cf7d38e1adf263bc08d23.stuff", | ||
"dc679f8a8319c8cf7d38e1adf263bc08d23"), | ||
// just container id | ||
arguments( | ||
"13:name=systemd:/pod/d86d75589bf6cc254f3e2cc29debdf85dde404998aa128997a819ff991827356", | ||
"d86d75589bf6cc254f3e2cc29debdf85dde404998aa128997a819ff991827356"), | ||
// with prefix | ||
arguments( | ||
"//\n" | ||
+ "1:name=systemd:/podruntime/docker/kubepods/docker-dc579f8a8319c8cf7d38e1adf263bc08d23" | ||
+ "2:name=systemd:/podruntime/docker/kubepods/docker-dc579f8a8319c8cf7d38e1adf263bc08d23" | ||
+ "3:name=systemd:/podruntime/docker/kubepods/docker-dc579f8a8319c8cf7d38e1adf263bc08d23", | ||
"dc579f8a8319c8cf7d38e1adf263bc08d23"), | ||
// with two dashes in prefix | ||
arguments( | ||
"11:perf_event:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod4415fd05_2c0f_4533_909b_f2180dca8d7c.slice/cri-containerd-713a77a26fe2a38ebebd5709604a048c3d380db1eb16aa43aca0b2499e54733c.scope", | ||
"713a77a26fe2a38ebebd5709604a048c3d380db1eb16aa43aca0b2499e54733c"), | ||
// with colon, env: k8s v1.24.0, the cgroupDriver by systemd(default), and container is | ||
// cri-containerd v1.6.8 | ||
arguments( | ||
"11:devices:/system.slice/containerd.service/kubepods-pod87a18a64_b74a_454a_b10b_a4a36059d0a3.slice:cri-containerd:05c48c82caff3be3d7f1e896981dd410e81487538936914f32b624d168de9db0", | ||
"05c48c82caff3be3d7f1e896981dd410e81487538936914f32b624d168de9db0")); | ||
} | ||
} |
68 changes: 68 additions & 0 deletions
68
...est/java/io/opentelemetry/instrumentation/resources/CgroupV2ContainerIdExtractorTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.instrumentation.resources; | ||
|
||
import static io.opentelemetry.instrumentation.resources.CgroupV2ContainerIdExtractor.V2_CGROUP_PATH; | ||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.mockito.Mockito.when; | ||
|
||
import java.util.Optional; | ||
import java.util.stream.Stream; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.ExtendWith; | ||
import org.mockito.Mock; | ||
import org.mockito.junit.jupiter.MockitoExtension; | ||
|
||
@ExtendWith(MockitoExtension.class) | ||
class CgroupV2ContainerIdExtractorTest { | ||
|
||
@Mock ContainerResource.Filesystem filesystem; | ||
|
||
@Test | ||
void fileNotReadable() { | ||
when(filesystem.isReadable(V2_CGROUP_PATH)).thenReturn(false); | ||
CgroupV2ContainerIdExtractor extractor = new CgroupV2ContainerIdExtractor(filesystem); | ||
Optional<String> result = extractor.extractContainerId(); | ||
assertThat(result).isSameAs(Optional.empty()); | ||
} | ||
|
||
@Test | ||
void extractSuccess() throws Exception { | ||
when(filesystem.isReadable(V2_CGROUP_PATH)).thenReturn(true); | ||
Stream<String> fileContent = getTestFileContent(); | ||
when(filesystem.lines(V2_CGROUP_PATH)).thenReturn(fileContent); | ||
CgroupV2ContainerIdExtractor extractor = new CgroupV2ContainerIdExtractor(filesystem); | ||
Optional<String> result = extractor.extractContainerId(); | ||
assertThat(result.orElse("fail")) | ||
.isEqualTo("dc64b5743252dbaef6e30521c34d6bbd1620c8ce65bdb7bf9e7143b61bb5b183"); | ||
} | ||
|
||
private static Stream<String> getTestFileContent() { | ||
return Stream.of( | ||
"456 375 0:143 / / rw,relatime master:175 - overlay overlay rw,lowerdir=/var/lib/docker/overlay2/l/37L57D2IM7MEWLVE2Q2ECNDT67:/var/lib/docker/overlay2/l/46FCA2JFPCSNFGAR5TSYLLNHLK,upperdir=/var/lib/docker/overlay2/4e82c300793d703c19bdf887bfdad8b0354edda884ea27a8a2df89ab292719a4/diff,workdir=/var/lib/docker/overlay2/4e82c300793d703c19bdf887bfdad8b0354edda884ea27a8a2df89ab292719a4/work", | ||
"457 456 0:146 / /proc rw,nosuid,nodev,noexec,relatime - proc proc rw", | ||
"466 456 0:147 / /dev rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755", | ||
"467 466 0:148 / /dev/pts rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=666", | ||
"468 456 0:149 / /sys ro,nosuid,nodev,noexec,relatime - sysfs sysfs ro", | ||
"469 468 0:30 / /sys/fs/cgroup ro,nosuid,nodev,noexec,relatime - cgroup2 cgroup rw", | ||
"470 466 0:145 / /dev/mqueue rw,nosuid,nodev,noexec,relatime - mqueue mqueue rw", | ||
"471 466 0:150 / /dev/shm rw,nosuid,nodev,noexec,relatime - tmpfs shm rw,size=65536k", | ||
"472 456 254:1 /docker/containers/dc64b5743252dbaef6e30521c34d6bbd1620c8ce65bdb7bf9e7143b61bb5b183/resolv.conf /etc/resolv.conf rw,relatime - ext4 /dev/vda1 rw", | ||
"473 456 254:1 /docker/containers/dc64b5743252dbaef6e30521c34d6bbd1620c8ce65bdb7bf9e7143b61bb5b183/hostname /etc/hostname rw,relatime - ext4 /dev/vda1 rw", | ||
"474 456 254:1 /docker/containers/dc64b5743252dbaef6e30521c34d6bbd1620c8ce65bdb7bf9e7143b61bb5b183/hosts /etc/hosts rw,relatime - ext4 /dev/vda1 rw", | ||
"376 466 0:148 /0 /dev/console rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=666", | ||
"377 457 0:146 /bus /proc/bus ro,nosuid,nodev,noexec,relatime - proc proc rw", | ||
"378 457 0:146 /fs /proc/fs ro,nosuid,nodev,noexec,relatime - proc proc rw", | ||
"379 457 0:146 /irq /proc/irq ro,nosuid,nodev,noexec,relatime - proc proc rw", | ||
"380 457 0:146 /sys /proc/sys ro,nosuid,nodev,noexec,relatime - proc proc rw", | ||
"381 457 0:146 /sysrq-trigger /proc/sysrq-trigger ro,nosuid,nodev,noexec,relatime - proc proc rw", | ||
"382 457 0:151 / /proc/acpi ro,relatime - tmpfs tmpfs ro", | ||
"383 457 0:147 /null /proc/kcore rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755", | ||
"384 457 0:147 /null /proc/keys rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755", | ||
"385 457 0:147 /null /proc/timer_list rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755", | ||
"386 468 0:152 / /sys/firmware ro,relatime - tmpfs tmpfs "); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters