Skip to content

Commit

Permalink
feat: added prefix to look for containerid (#2341)
Browse files Browse the repository at this point in the history
* feature: added prefix to look for containerid

* fix:added seperate utils file and new test file and more tests

* fix:test refactor

---------

Co-authored-by: Amir Blum <amirgiraffe@gmail.com>
  • Loading branch information
abhee11 and blumamir authored Aug 7, 2024
1 parent efe3c92 commit 1991aed
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,19 @@ import { SEMRESATTRS_CONTAINER_ID } from '@opentelemetry/semantic-conventions';
import * as fs from 'fs';
import * as util from 'util';
import { diag } from '@opentelemetry/api';
import { extractContainerIdFromLine } from './utils';

export class ContainerDetector implements Detector {
readonly CONTAINER_ID_LENGTH = 64;
readonly DEFAULT_CGROUP_V1_PATH = '/proc/self/cgroup';
readonly DEFAULT_CGROUP_V2_PATH = '/proc/self/mountinfo';
readonly UTF8_UNICODE = 'utf8';
readonly HOSTNAME = 'hostname';
readonly MARKING_PREFIX = 'containers';
readonly CRIO = 'crio-';
readonly CRI_CONTAINERD = 'cri-containerd-';
readonly DOCKER = 'docker-';
readonly HEX_STRING_REGEX: RegExp = /^[a-f0-9]+$/i;

private static readFileAsync = util.promisify(fs.readFile);

Expand All @@ -51,35 +57,17 @@ export class ContainerDetector implements Detector {
}
}

private async _getContainerIdV1() {
private async _getContainerIdV1(): Promise<string | undefined> {
const rawData = await ContainerDetector.readFileAsync(
this.DEFAULT_CGROUP_V1_PATH,
this.UTF8_UNICODE
);
const splitData = rawData.trim().split('\n');
for (const line of splitData) {
const lastSlashIdx = line.lastIndexOf('/');
if (lastSlashIdx === -1) {
continue;
}
const lastSection = line.substring(lastSlashIdx + 1);
const 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
return lastSection.substring(colonIdx + 1);
} else {
let startIdx = lastSection.lastIndexOf('-');
let endIdx = lastSection.lastIndexOf('.');

startIdx = startIdx === -1 ? 0 : startIdx + 1;
if (endIdx === -1) {
endIdx = lastSection.length;
}
if (startIdx > endIdx) {
continue;
}
return lastSection.substring(startIdx, endIdx);
for (const line of splitData) {
const containerID = extractContainerIdFromLine(line);
if (containerID) {
return containerID;
}
}
return undefined;
Expand All @@ -94,10 +82,19 @@ export class ContainerDetector implements Detector {
.trim()
.split('\n')
.find(s => s.includes(this.HOSTNAME));
const containerIdStr = str
?.split('/')
.find(s => s.length === this.CONTAINER_ID_LENGTH);
return containerIdStr || '';

if (!str) return '';

const strArray = str?.split('/') ?? [];
for (let i = 0; i < strArray.length - 1; i++) {
if (
strArray[i] === this.MARKING_PREFIX &&
strArray[i + 1]?.length === this.CONTAINER_ID_LENGTH
) {
return strArray[i + 1];
}
}
return '';
}

/*
Expand All @@ -107,9 +104,14 @@ export class ContainerDetector implements Detector {
*/
private async _getContainerId(): Promise<string | undefined> {
try {
return (
(await this._getContainerIdV1()) || (await this._getContainerIdV2())
);
const containerIdV1 = await this._getContainerIdV1();
if (containerIdV1) {
return containerIdV1; // If containerIdV1 is a non-empty string, return it.
}
const containerIdV2 = await this._getContainerIdV2();
if (containerIdV2) {
return containerIdV2; // If containerIdV2 is a non-empty string, return it.
}
} catch (e) {
if (e instanceof Error) {
const errorMessage = e.message;
Expand All @@ -119,7 +121,7 @@ export class ContainerDetector implements Detector {
);
}
}
return undefined;
return undefined; // Explicitly return undefined if neither ID is found.
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export const CONTAINER_ID_LENGTH = 64;
export const DEFAULT_CGROUP_V1_PATH = '/proc/self/cgroup';
export const DEFAULT_CGROUP_V2_PATH = '/proc/self/mountinfo';
export const UTF8_UNICODE = 'utf8';
export const HOSTNAME = 'hostname';
export const MARKING_PREFIX = 'containers';
export const CRIO = 'crio-';
export const CRI_CONTAINERD = 'cri-containerd-';
export const DOCKER = 'docker-';
export const HEX_STRING_REGEX = /^[a-f0-9]+$/i;

export function truncatePrefix(lastSection: string, prefix: string): string {
return lastSection.substring(prefix.length);
}

export function extractContainerIdFromLine(line: string): string | undefined {
if (!line) {
return undefined;
}
const sections = line.split('/');
if (sections.length <= 1) {
return undefined;
}
let lastSection = sections[sections.length - 1];

// Handle containerd v1.5.0+ format with systemd cgroup driver
const colonIndex = lastSection.lastIndexOf(':');
if (colonIndex !== -1) {
lastSection = lastSection.substring(colonIndex + 1);
}

// Truncate known prefixes from the last section
if (lastSection.startsWith(CRIO)) {
lastSection = truncatePrefix(lastSection, CRIO);
} else if (lastSection.startsWith(DOCKER)) {
lastSection = truncatePrefix(lastSection, DOCKER);
} else if (lastSection.startsWith(CRI_CONTAINERD)) {
lastSection = truncatePrefix(lastSection, CRI_CONTAINERD);
}
// Remove anything after the first period
if (lastSection.includes('.')) {
lastSection = lastSection.split('.')[0];
}
// Check if the remaining string is a valid hex string
if (HEX_STRING_REGEX.test(lastSection)) {
return lastSection;
}
return undefined;
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ import {
import { ContainerDetector } from '../src';

describe('ContainerDetector', () => {
let readStub;
let readStub: sinon.SinonStub;
const correctCgroupV1Data =
'12:pids:/kubepods.slice/bcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm';
const correctCgroupV2Data = `tmhdefghijklmnopqrstuvwxyzafgrefghiugkmnopqrstuvwxyzabcdefghijkl/hostname
'12:pids:/kubepods.slice/4e6f77206973207468652074696d6520666f7220616c6c20676f6f64206d656e20746f20636f6d6520746f2074686520616964';
const correctCgroupV2Data = `containers/tmhdefghijklmnopqrstuvwxyzafgrefghiugkmnopqrstuvwxyzabcdefghijkl/hostname
fhkjdshgfhsdfjhdsfkjhfkdshkjhfd/host
sahfhfjkhjhfhjdhfjkdhfkjdhfjkhhdsjfhdfhjdhfkj/somethingelse`;

Expand Down Expand Up @@ -63,7 +63,7 @@ describe('ContainerDetector', () => {

assert.ok(resource);
assertContainerResource(resource, {
id: 'bcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm',
id: '4e6f77206973207468652074696d6520666f7220616c6c20676f6f64206d656e20746f20636f6d6520746f2074686520616964',
});
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import * as assert from 'assert';
import { extractContainerIdFromLine } from '../src/detectors/utils';

describe(' extractContainerId from line tests', () => {
it('should extract container ID from crio-prefixed line', () => {
const line =
'11:devices:/kubepods.slice/kubepods-besteffort.slice/kubepods-besteffort-pod5c5979ec_6b2b_11e9_a923_42010a800002.slice/crio-1234567890abcdef.scope';
const expected = '1234567890abcdef';
assert.strictEqual(extractContainerIdFromLine(line), expected);
});

it('should extract container ID from docker-prefixed line', () => {
const line =
'11:devices:/docker/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef';
const expected =
'1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef';
assert.strictEqual(extractContainerIdFromLine(line), expected);
});

it('should extract container ID from cri-containerd-prefixed line', () => {
const line =
'11:devices:/kubepods/burstable/pod2c4b2241-5c01-11e9-8e4e-42010a800002/cri-containerd-1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef';
const expected =
'1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef';
assert.strictEqual(extractContainerIdFromLine(line), expected);
});

it('should handle containerd v1.5.0+ format with systemd cgroup driver', () => {
const line =
'0::/system.slice/containerd.service/kubepods-burstable-pod2c4b2241-5c01-11e9-8e4e-42010a800002.slice:cri-containerd:1234567890abcdef';
const expected = '1234567890abcdef';
assert.strictEqual(extractContainerIdFromLine(line), expected);
});

it('should return undefined for invalid container ID', () => {
const line =
'11:devices:/kubepods.slice/kubepods-besteffort.slice/kubepods-besteffort-pod5c5979ec_6b2b_11e9_a923_42010a800002.slice/invalid-id.scope';
assert.strictEqual(extractContainerIdFromLine(line), undefined);
});

it('should return undefined for empty line', () => {
const line = '';
assert.strictEqual(extractContainerIdFromLine(line), undefined);
});

it('should return undefined for line without container ID', () => {
const line = '11:devices:/';
assert.strictEqual(extractContainerIdFromLine(line), undefined);
});

// Additional test cases
it('should handle line with multiple colons', () => {
const line =
'0::/system.slice/containerd.service/kubepods-burstable-pod2c4b2241-5c01-11e9-8e4e-42010a800002.slice:cri-containerd-1234567890abcdef.extra';
const expected = '1234567890abcdef';
assert.strictEqual(extractContainerIdFromLine(line), expected);
});

it('should return containerid for valid hex string with any length', () => {
const line =
'11:devices:/docker/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcde';
assert.strictEqual(
extractContainerIdFromLine(line),
'1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcde'
);
});

it('should extract container ID with additional suffix', () => {
const line =
'11:devices:/docker/1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef.suffix';
const expected =
'1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef';
assert.strictEqual(extractContainerIdFromLine(line), expected);
});
});

0 comments on commit 1991aed

Please sign in to comment.