Skip to content
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

k8s container id detection #191

Merged
merged 1 commit into from
Sep 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/ResourceDetectors/Container/.php-cs-fixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
'blank_line_before_statement' => true,
'cast_spaces' => true,
'declare_strict_types' => true,
'function_typehint_space' => true,
'type_declaration_spaces' => true,
'include' => true,
'lowercase_cast' => true,
'new_with_braces' => true,
Expand All @@ -34,7 +34,7 @@
'phpdoc_scalar' => true,
'phpdoc_types' => true,
'short_scalar_cast' => true,
'single_blank_line_before_namespace' => true,
'blank_lines_before_namespace' => true,
'single_quote' => true,
'trailing_comma_in_multiline' => true,
])
Expand Down
1 change: 1 addition & 0 deletions src/ResourceDetectors/Container/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# OpenTelemetry Container Detector

This package provides an OpenTelemetry `ResourceDetector` which will detect docker container id at runtime, using either V1 (cgroup) or V2 (mountinfo).
It should work with docker, kubernetes, and podman containers.

## Requirements

Expand Down
2 changes: 1 addition & 1 deletion src/ResourceDetectors/Container/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"prefer-stable": true,
"require": {
"php": "^7.0|^8.0",
"open-telemetry/sdk": ">= 1.0.0beta10"
"open-telemetry/sdk": ">= 1.0.0RC1 <= 2"
},
"autoload": {
"psr-4": {
Expand Down
30 changes: 23 additions & 7 deletions src/ResourceDetectors/Container/src/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@

/**
* @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.18.0/specification/resource/semantic_conventions/container.md
* @see https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/v1.29.0/instrumentation/resources/library/src/main/java/io/opentelemetry/instrumentation/resources/ContainerResource.java
*/
final class Container implements ResourceDetectorInterface
{
private string $dir;
private const CONTAINER_ID_LENGTH = 64;
private const CONTAINER_ID_REGEX = '/^[0-9a-f]{64}$/';
private const V1_CONTAINER_ID_REGEX = '/\.?[0-9a-f]{64}\-?/';
private const CGROUP_V1 = 'cgroup';
private const CGROUP_V2 = 'mountinfo';
private const HOSTNAME = 'hostname';
Expand All @@ -38,9 +40,14 @@ public function getResource(): ResourceInfo

private function getContainerId(): ?string
{
return $this->getContainerIdV2() ?? $this->getContainerIdV1();
return $this->getContainerIdV1() ?? $this->getContainerIdV2();
}

/**
* 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)
*/
private function getContainerIdV1(): ?string
{
if (!file_exists(sprintf('%s/%s', $this->dir, self::CGROUP_V1))) {
Expand All @@ -50,11 +57,20 @@ private function getContainerIdV1(): ?string
if (!$data) {
return null;
}
$lines = explode('\n', $data);
$lines = explode(PHP_EOL, $data);
foreach ($lines as $line) {
if (strlen($line) >= self::CONTAINER_ID_LENGTH) {
//if string is longer than CONTAINER_ID_LENGTH, return the last CONTAINER_ID_LENGTH chars
return substr($line, strlen($line) - self::CONTAINER_ID_LENGTH);
if (strpos($line, '/') === false) {
continue;
}
$parts = explode('/', $line);
$section = end($parts);
$colon = strrpos($section, ':');
if ($colon !== false) {
return substr($section, $colon);
}
$matches = [];
if (preg_match(self::V1_CONTAINER_ID_REGEX, $section, $matches) === 1) {
return $matches[0];
}
}

Expand All @@ -75,7 +91,7 @@ private function getContainerIdV2(): ?string
if (strpos($line, self::HOSTNAME) !== false) {
$parts = explode('/', $line);
foreach ($parts as $part) {
if (strlen($part) === self::CONTAINER_ID_LENGTH) {
if (preg_match(self::CONTAINER_ID_REGEX, $part) === 1) {
return $part;
}
}
Expand Down
85 changes: 72 additions & 13 deletions src/ResourceDetectors/Container/tests/Unit/ContainerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,65 @@ public function setUp(): void
$this->detector = new Container($root->url());
}

public function test_valid_v1(): void
/**
* cgroup (v1) should take precedence over mountinfo (v2)
* @dataProvider cgroupMountinfoProvider
*/
public function test_with_cgroup_and_mountinfo(string $cgroup, string $mountinfo, string $expected): void
{
$valid = 'a8493b8a4f6f23b65c5db50be86619ca4da078da040aa3d5ccff26fe50de205d';
$this->cgroup->setContent($valid);
$cgroup && $this->cgroup->setContent($cgroup);
$mountinfo && $this->mountinfo->setContent($mountinfo);
$resource = $this->detector->getResource();

$this->assertSame($expected, $resource->getAttributes()->get(ResourceAttributes::CONTAINER_ID));
}

public static function cgroupMountinfoProvider(): array
{
return [
'k8s' => [
file_get_contents(__DIR__ . '/fixtures/v1.cgroup.k8s.txt'),
file_get_contents(__DIR__ . '/fixtures/v2.mountinfo.k8s.txt'),
'78ea929aa43e7b71f7c36583d82038d92a76800bf5da9b8850e8bd7b514bc075',
],
'docker with invalid cgroup' => [
'no-container-ids-here',
file_get_contents(__DIR__ . '/fixtures/v2.mountinfo.docker.txt'),
'a8493b8a4f6f23b65c5db50be86619ca4da078da040aa3d5ccff26fe50de205d',
],
'podman' => [
'no-container-ids-here',
file_get_contents(__DIR__ . '/fixtures/v2.mountinfo.podman.txt'),
'2a33efc76e519c137fe6093179653788bed6162d4a15e5131c8e835c968afbe6',
],
];
}

/**
* @dataProvider cgroupProvider
*/
public function test_valid_v1(string $data, string $expected): void
{
$this->cgroup->setContent($data);
$resource = $this->detector->getResource();

$this->assertSame(ResourceAttributes::SCHEMA_URL, $resource->getSchemaUrl());
$this->assertIsString($resource->getAttributes()->get(ResourceAttributes::CONTAINER_ID));
$this->assertSame($valid, $resource->getAttributes()->get(ResourceAttributes::CONTAINER_ID));
$this->assertSame($expected, $resource->getAttributes()->get(ResourceAttributes::CONTAINER_ID));
}

public static function cgroupProvider(): array
{
return [
'docker' => [
file_get_contents(__DIR__ . '/fixtures/v1.cgroup.docker.txt'),
'7be92808767a667f35c8505cbf40d14e931ef6db5b0210329cf193b15ba9d605',
],
'k8s' => [
file_get_contents(__DIR__ . '/fixtures/v1.cgroup.k8s.txt'),
'78ea929aa43e7b71f7c36583d82038d92a76800bf5da9b8850e8bd7b514bc075',
],
];
}

public function test_invalid_v1(): void
Expand All @@ -44,23 +94,32 @@ public function test_invalid_v1(): void
$this->assertEmpty($resource->getAttributes());
}

public function test_valid_v2(): void
/**
* @dataProvider mountinfoProvider
*/
public function test_valid_v2(string $data, string $expected): void
{
$expected = 'a8493b8a4f6f23b65c5db50be86619ca4da078da040aa3d5ccff26fe50de205d';
$data = <<< EOS
1366 1365 0:30 / /sys/fs/cgroup ro,nosuid,nodev,noexec,relatime - cgroup2 cgroup rw
1408 1362 0:107 / /dev/mqueue rw,nosuid,nodev,noexec,relatime - mqueue mqueue rw
1579 1362 0:112 / /dev/shm rw,nosuid,nodev,noexec,relatime - tmpfs shm rw,size=65536k,inode64
1581 1359 259:2 /var/lib/docker/containers/a8493b8a4f6f23b65c5db50be86619ca4da078da040aa3d5ccff26fe50de205d/hostname /etc/hostname rw,relatime - ext4 /dev/nvme0n1p2 rw,errors=remount-ro
1583 1359 259:3 /brett/docker/otel/opentelemetry-php /usr/src/myapp rw,relatime - ext4 /dev/nvme0n1p3 rw
EOS;
$this->mountinfo->withContent($data);
$resource = $this->detector->getResource();

$this->assertCount(1, $resource->getAttributes());
$this->assertSame($expected, $resource->getAttributes()->get(ResourceAttributes::CONTAINER_ID));
}

public static function mountinfoProvider(): array
{
return [
'docker' => [
file_get_contents(__DIR__ . '/fixtures/v2.mountinfo.docker.txt'),
'a8493b8a4f6f23b65c5db50be86619ca4da078da040aa3d5ccff26fe50de205d',
],
'podman' => [
file_get_contents(__DIR__ . '/fixtures/v2.mountinfo.podman.txt'),
'2a33efc76e519c137fe6093179653788bed6162d4a15e5131c8e835c968afbe6',
],
];
}

public function test_invalid_v2(): void
{
$data = <<< EOS
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
14:name=systemd:/docker/7be92808767a667f35c8505cbf40d14e931ef6db5b0210329cf193b15ba9d605
13:pids:/docker/7be92808767a667f35c8505cbf40d14e931ef6db5b0210329cf193b15ba9d605
12:hugetlb:/docker/7be92808767a667f35c8505cbf40d14e931ef6db5b0210329cf193b15ba9d605
11:net_prio:/docker/7be92808767a667f35c8505cbf40d14e931ef6db5b0210329cf193b15ba9d605
10:perf_event:/docker/7be92808767a667f35c8505cbf40d14e931ef6db5b0210329cf193b15ba9d605
9:net_cls:/docker/7be92808767a667f35c8505cbf40d14e931ef6db5b0210329cf193b15ba9d605
8:freezer:/docker/7be92808767a667f35c8505cbf40d14e931ef6db5b0210329cf193b15ba9d605
7:devices:/docker/7be92808767a667f35c8505cbf40d14e931ef6db5b0210329cf193b15ba9d605
6:memory:/docker/7be92808767a667f35c8505cbf40d14e931ef6db5b0210329cf193b15ba9d605
5:blkio:/docker/7be92808767a667f35c8505cbf40d14e931ef6db5b0210329cf193b15ba9d605
4:cpuacct:/docker/7be92808767a667f35c8505cbf40d14e931ef6db5b0210329cf193b15ba9d605
3:cpu:/docker/7be92808767a667f35c8505cbf40d14e931ef6db5b0210329cf193b15ba9d605
2:cpuset:/docker/7be92808767a667f35c8505cbf40d14e931ef6db5b0210329cf193b15ba9d605
1:name=openrc:/docker
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
13:devices:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod542985b6_0726_4d82_bf85_36e327b167b3.slice/crio-78ea929aa43e7b71f7c36583d82038d92a76800bf5da9b8850e8bd7b514bc075.scope
12:pids:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod542985b6_0726_4d82_bf85_36e327b167b3.slice/crio-78ea929aa43e7b71f7c36583d82038d92a76800bf5da9b8850e8bd7b514bc075.scope
11:perf_event:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod542985b6_0726_4d82_bf85_36e327b167b3.slice/crio-78ea929aa43e7b71f7c36583d82038d92a76800bf5da9b8850e8bd7b514bc075.scope
10:freezer:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod542985b6_0726_4d82_bf85_36e327b167b3.slice/crio-78ea929aa43e7b71f7c36583d82038d92a76800bf5da9b8850e8bd7b514bc075.scope
9:cpuset:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod542985b6_0726_4d82_bf85_36e327b167b3.slice/crio-78ea929aa43e7b71f7c36583d82038d92a76800bf5da9b8850e8bd7b514bc075.scope
8:hugetlb:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod542985b6_0726_4d82_bf85_36e327b167b3.slice/crio-78ea929aa43e7b71f7c36583d82038d92a76800bf5da9b8850e8bd7b514bc075.scope
7:rdma:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod542985b6_0726_4d82_bf85_36e327b167b3.slice/crio-78ea929aa43e7b71f7c36583d82038d92a76800bf5da9b8850e8bd7b514bc075.scope
6:misc:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod542985b6_0726_4d82_bf85_36e327b167b3.slice/crio-78ea929aa43e7b71f7c36583d82038d92a76800bf5da9b8850e8bd7b514bc075.scope
5:memory:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod542985b6_0726_4d82_bf85_36e327b167b3.slice/crio-78ea929aa43e7b71f7c36583d82038d92a76800bf5da9b8850e8bd7b514bc075.scope
4:cpu,cpuacct:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod542985b6_0726_4d82_bf85_36e327b167b3.slice/crio-78ea929aa43e7b71f7c36583d82038d92a76800bf5da9b8850e8bd7b514bc075.scope
3:blkio:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod542985b6_0726_4d82_bf85_36e327b167b3.slice/crio-78ea929aa43e7b71f7c36583d82038d92a76800bf5da9b8850e8bd7b514bc075.scope
2:net_cls,net_prio:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod542985b6_0726_4d82_bf85_36e327b167b3.slice/crio-78ea929aa43e7b71f7c36583d82038d92a76800bf5da9b8850e8bd7b514bc075.scope
1:name=systemd:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod542985b6_0726_4d82_bf85_36e327b167b3.slice/crio-78ea929aa43e7b71f7c36583d82038d92a76800bf5da9b8850e8bd7b514bc075.scope
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
1366 1365 0:30 / /sys/fs/cgroup ro,nosuid,nodev,noexec,relatime - cgroup2 cgroup rw
1408 1362 0:107 / /dev/mqueue rw,nosuid,nodev,noexec,relatime - mqueue mqueue rw
1579 1362 0:112 / /dev/shm rw,nosuid,nodev,noexec,relatime - tmpfs shm rw,size=65536k,inode64
1581 1359 259:2 /var/lib/docker/containers/a8493b8a4f6f23b65c5db50be86619ca4da078da040aa3d5ccff26fe50de205d/hostname /etc/hostname rw,relatime - ext4 /dev/nvme0n1p2 rw,errors=remount-ro
1583 1359 259:3 /brett/docker/otel/opentelemetry-php /usr/src/myapp rw,relatime - ext4 /dev/nvme0n1p3 rw
Loading
Loading