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

feat: added prefix to look for containerid #2341

Merged
merged 8 commits into from
Aug 7, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,19 @@
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 @@
}
}

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('-');
blumamir marked this conversation as resolved.
Show resolved Hide resolved
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 @@
.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
blumamir marked this conversation as resolved.
Show resolved Hide resolved
) {
return strArray[i + 1];
}
}
return '';

Check warning on line 97 in detectors/node/opentelemetry-resource-detector-container/src/detectors/ContainerDetector.ts

View check run for this annotation

Codecov / codecov/patch

detectors/node/opentelemetry-resource-detector-container/src/detectors/ContainerDetector.ts#L97

Added line #L97 was not covered by tests
}

/*
Expand All @@ -107,9 +104,14 @@
*/
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 @@
);
}
}
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;

Check warning on line 37 in detectors/node/opentelemetry-resource-detector-container/src/detectors/utils.ts

View check run for this annotation

Codecov / codecov/patch

detectors/node/opentelemetry-resource-detector-container/src/detectors/utils.ts#L37

Added line #L37 was not covered by tests
}
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);

Check warning on line 51 in detectors/node/opentelemetry-resource-detector-container/src/detectors/utils.ts

View check run for this annotation

Codecov / codecov/patch

detectors/node/opentelemetry-resource-detector-container/src/detectors/utils.ts#L51

Added line #L51 was not covered by tests
} 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);
});
});