adding container detector (#933)

detect container id from cgroup or mountinfo
This commit is contained in:
Brett McBride 2023-03-03 09:18:42 +11:00 committed by GitHub
parent 746d25a7d1
commit 88ea7c3024
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 163 additions and 0 deletions

View File

@ -165,6 +165,7 @@ interface KnownValues
public const VALUE_DETECTORS_PROCESS_RUNTIME = 'process_runtime';
public const VALUE_DETECTORS_SDK = 'sdk';
public const VALUE_DETECTORS_SDK_PROVIDED = 'sdk_provided';
public const VALUE_DETECTORS_CONTAINER = 'container';
public const OTEL_PHP_DETECTORS = [
self::VALUE_ALL,
self::VALUE_DETECTORS_ENVIRONMENT,
@ -174,6 +175,7 @@ interface KnownValues
self::VALUE_DETECTORS_PROCESS_RUNTIME,
self::VALUE_DETECTORS_SDK,
self::VALUE_DETECTORS_SDK_PROVIDED,
self::VALUE_DETECTORS_CONTAINER,
self::VALUE_NONE,
];
}

View File

@ -0,0 +1,80 @@
<?php
declare(strict_types=1);
namespace OpenTelemetry\SDK\Resource\Detectors;
use OpenTelemetry\SDK\Common\Attribute\Attributes;
use OpenTelemetry\SDK\Resource\ResourceDetectorInterface;
use OpenTelemetry\SDK\Resource\ResourceInfo;
use OpenTelemetry\SemConv\ResourceAttributes;
/**
* @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.18.0/specification/resource/semantic_conventions/container.md
*/
final class Container implements ResourceDetectorInterface
{
private string $dir;
private const CONTAINER_ID_LENGTH = 64;
private const CGROUP_V1 = 'cgroup';
private const CGROUP_V2 = 'mountinfo';
private const HOSTNAME = 'hostname';
public function __construct(string $dir = '/proc/self')
{
$this->dir = $dir;
}
public function getResource(): ResourceInfo
{
$attributes = [];
$id = $this->getContainerId();
if ($id) {
$attributes[ResourceAttributes::CONTAINER_ID] = $id;
}
return ResourceInfo::create(Attributes::create($attributes), ResourceAttributes::SCHEMA_URL);
}
private function getContainerId(): ?string
{
return $this->getContainerIdV2() ?? $this->getContainerIdV1();
}
private function getContainerIdV1(): ?string
{
$data = file_get_contents(sprintf('%s/%s', $this->dir, self::CGROUP_V1));
if (!$data) {
return null;
}
$lines = explode('\n', $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);
}
}
return null;
}
private function getContainerIdV2(): ?string
{
$data = file_get_contents(sprintf('%s/%s', $this->dir, self::CGROUP_V2));
if (!$data) {
return null;
}
$lines = explode(PHP_EOL, $data);
foreach ($lines as $line) {
if (strpos($line, self::HOSTNAME) !== false) {
$parts = explode('/', $line);
foreach ($parts as $part) {
if (strlen($part) === self::CONTAINER_ID_LENGTH) {
return $part;
}
}
}
}
return null;
}
}

View File

@ -44,6 +44,7 @@ class ResourceInfoFactory
new Detectors\ProcessRuntime(),
new Detectors\Sdk(),
new Detectors\SdkProvided(),
new Detectors\Container(),
]))->getResource();
}
@ -78,6 +79,10 @@ class ResourceInfoFactory
case Values::VALUE_DETECTORS_SDK_PROVIDED:
$resourceDetectors[] = new Detectors\SdkProvided();
break;
case Values::VALUE_DETECTORS_CONTAINER:
$resourceDetectors[] = new Detectors\Container();
break;
default:
}

View File

@ -0,0 +1,76 @@
<?php
declare(strict_types=1);
namespace OpenTelemetry\Example\Unit\SDK\Resource\Detectors;
use OpenTelemetry\SDK\Resource\Detectors\Container;
use OpenTelemetry\SemConv\ResourceAttributes;
use org\bovigo\vfs\vfsStream;
use org\bovigo\vfs\vfsStreamFile;
use PHPUnit\Framework\TestCase;
/**
* @covers OpenTelemetry\SDK\Resource\Detectors\Container
*/
class ContainerTest extends TestCase
{
private vfsStreamFile $cgroup;
private vfsStreamFile $mountinfo;
private Container $detector;
public function setUp(): void
{
$root = vfsStream::setup();
$this->cgroup = vfsStream::newFile('cgroup')->at($root);
$this->mountinfo = vfsStream::newFile('mountinfo')->at($root);
$this->detector = new Container($root->url());
}
public function test_valid_v1(): void
{
$valid = 'a8493b8a4f6f23b65c5db50be86619ca4da078da040aa3d5ccff26fe50de205d';
$this->cgroup->setContent($valid);
$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));
}
public function test_invalid_v1(): void
{
$this->cgroup->setContent('0::/');
$resource = $this->detector->getResource();
$this->assertEmpty($resource->getAttributes());
}
public function test_valid_v2(): 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 function test_invalid_v2(): void
{
$data = <<< EOS
1581 1359 259:2 /var/lib/docker/containers/a8493b8a4f6f23b65c5db50be86619ca4da078da040aa3d5ccff26fe50de205d/wrongkeyword
EOS;
$this->mountinfo->withContent($data);
$resource = $this->detector->getResource();
$this->assertEmpty($resource->getAttributes());
}
}