Skip to content

Commit

Permalink
Support cgroup v2 (#7167)
Browse files Browse the repository at this point in the history
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.
breedx-splk authored Nov 15, 2022
1 parent 33b0b58 commit b09fb67
Showing 6 changed files with 395 additions and 148 deletions.
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();
}
}
}
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();
}
}
Original file line number Diff line number Diff line change
@@ -5,106 +5,75 @@

package io.opentelemetry.instrumentation.resources;

import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.CONTAINER_ID;

import com.google.errorprone.annotations.MustBeClosed;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.internal.OtelEncodingUtils;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
import java.io.IOException;
import java.nio.file.Files;
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;

/** Factory for {@link Resource} retrieving Container ID information. */
/**
* Factory for {@link Resource} retrieving Container ID information. It supports both cgroup v1 and
* v2 runtimes.
*/
public final class ContainerResource {

private static final Logger logger = Logger.getLogger(ContainerResource.class.getName());
private static final String UNIQUE_HOST_NAME_FILE_NAME = "/proc/self/cgroup";
private static final Resource INSTANCE = buildSingleton(UNIQUE_HOST_NAME_FILE_NAME);
static final Filesystem FILESYSTEM_INSTANCE = new Filesystem();
private static final Resource INSTANCE = buildSingleton();

private static Resource buildSingleton(String uniqueHostNameFileName) {
private static Resource buildSingleton() {
// can't initialize this statically without running afoul of animalSniffer on paths
return buildResource(Paths.get(uniqueHostNameFileName));
return new ContainerResource().buildResource();
}

// package private for testing
static Resource buildResource(Path path) {
return extractContainerId(path)
.map(
containerId ->
Resource.create(Attributes.of(ResourceAttributes.CONTAINER_ID, containerId)))
.orElseGet(Resource::empty);
private final CgroupV1ContainerIdExtractor v1Extractor;
private final CgroupV2ContainerIdExtractor v2Extractor;

private ContainerResource() {
this(new CgroupV1ContainerIdExtractor(), new CgroupV2ContainerIdExtractor());
}

/** Returns resource with container information. */
public static Resource get() {
return INSTANCE;
// Visible for testing
ContainerResource(
CgroupV1ContainerIdExtractor v1Extractor, CgroupV2ContainerIdExtractor v2Extractor) {
this.v1Extractor = v1Extractor;
this.v2Extractor = v2Extractor;
}

/**
* 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
*/
private static Optional<String> extractContainerId(Path cgroupFilePath) {
if (!Files.exists(cgroupFilePath) || !Files.isReadable(cgroupFilePath)) {
return Optional.empty();
}
try (Stream<String> lines = Files.lines(cgroupFilePath)) {
return lines
.filter(line -> !line.isEmpty())
.map(ContainerResource::getIdFromLine)
.filter(Optional::isPresent)
.findFirst()
.orElse(Optional.empty());
} catch (Exception e) {
logger.log(Level.WARNING, "Unable to read file", e);
}
return Optional.empty();
// Visible for testing
Resource buildResource() {
return getContainerId()
.map(id -> Resource.create(Attributes.of(CONTAINER_ID, id)))
.orElseGet(Resource::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();
private Optional<String> getContainerId() {
Optional<String> v1Result = v1Extractor.extractContainerId();
if (v1Result.isPresent()) {
return v1Result;
}
return v2Extractor.extractContainerId();
}

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('.');
/** Returns resource with container information. */
public static Resource get() {
return INSTANCE;
}

startIdx = startIdx == -1 ? 0 : startIdx + 1;
if (endIdx == -1) {
endIdx = lastSection.length();
}
if (startIdx > endIdx) {
return Optional.empty();
}
// Exists for testing
static class Filesystem {

containerId = lastSection.substring(startIdx, endIdx);
boolean isReadable(Path path) {
return Files.isReadable(path);
}

if (OtelEncodingUtils.isValidBase16String(containerId) && !containerId.isEmpty()) {
return Optional.of(containerId);
} else {
return Optional.empty();
@MustBeClosed
Stream<String> lines(Path path) throws IOException {
return Files.lines(path);
}
}

private ContainerResource() {}
}
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"));
}
}
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 ");
}
}
Original file line number Diff line number Diff line change
@@ -5,91 +5,49 @@

package io.opentelemetry.instrumentation.resources;

import static io.opentelemetry.instrumentation.resources.ContainerResource.buildResource;
import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.CONTAINER_ID;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.params.provider.Arguments.arguments;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;

import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;
import java.util.Optional;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
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.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
class ContainerResourceTest {

@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 buildResource_returnsEmptyResource_whenContainerIdIsInvalid(
String line, @TempDir Path tempFolder) throws IOException {
Path cgroup = createCgroup(tempFolder.resolve("cgroup"), line);
assertThat(buildResource(cgroup)).isEqualTo(Resource.empty());
}
public static final String TEST_CONTAINER_ID = "abcdef123123deadbeef";
@Mock CgroupV1ContainerIdExtractor v1;
@Mock CgroupV2ContainerIdExtractor v2;

@Test
void buildResource_returnsEmptyResource_whenFileDoesNotExist(@TempDir Path tempFolder) {
Path cgroup = tempFolder.resolve("DoesNotExist");
assertThat(buildResource(cgroup)).isEqualTo(Resource.empty());
}

@ParameterizedTest
@MethodSource("validLines")
void buildResource_extractsContainerIdFromValidLines(
String line, String expectedContainerId, @TempDir Path tempFolder) throws IOException {
Path cgroup = createCgroup(tempFolder.resolve("cgroup"), line);
assertThat(getContainerId(buildResource(cgroup))).isEqualTo(expectedContainerId);
void v1Success() {
when(v1.extractContainerId()).thenReturn(Optional.of(TEST_CONTAINER_ID));
ContainerResource containerResource = new ContainerResource(v1, v2);
Resource resource = containerResource.buildResource();
assertThat(resource.getAttribute(CONTAINER_ID)).isEqualTo(TEST_CONTAINER_ID);
verifyNoInteractions(v2);
}

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"));
}

private static String getContainerId(Resource resource) {
return resource.getAttribute(ResourceAttributes.CONTAINER_ID);
@Test
void v2Success() {
when(v1.extractContainerId()).thenReturn(Optional.empty());
when(v2.extractContainerId()).thenReturn(Optional.of(TEST_CONTAINER_ID));
ContainerResource containerResource = new ContainerResource(v1, v2);
Resource resource = containerResource.buildResource();
assertThat(resource.getAttribute(CONTAINER_ID)).isEqualTo(TEST_CONTAINER_ID);
}

private static Path createCgroup(Path path, String line) throws IOException {
return Files.write(path, line.getBytes(StandardCharsets.UTF_8));
@Test
void bothVersionsFail() {
when(v1.extractContainerId()).thenReturn(Optional.empty());
when(v2.extractContainerId()).thenReturn(Optional.empty());
ContainerResource containerResource = new ContainerResource(v1, v2);
Resource resource = containerResource.buildResource();
assertThat(resource).isSameAs(Resource.empty());
}
}

0 comments on commit b09fb67

Please sign in to comment.