Skip to content

Commit

Permalink
fix(detector-aws): Get ECS Container ID from metadata
Browse files Browse the repository at this point in the history
Which prolbem is this PR solving?

- The AWS ECS Resource detector is munging the container ID when running
  in AWS ECS Fargate

ref: #2032

Short description of the changes

- Get ECS container ID from ECS metadata. This will get the correct
  value whether running in ECS Fargate or ECS on EC2
  • Loading branch information
iancward committed Oct 28, 2024
1 parent 9a20e15 commit 5f48d6a
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,6 @@ import {
// Patch until the OpenTelemetry SDK is updated to ship this attribute
import { SemanticResourceAttributes as AdditionalSemanticResourceAttributes } from './SemanticResourceAttributes';
import * as http from 'http';
import * as util from 'util';
import * as fs from 'fs';
import * as os from 'os';
import { getEnv } from '@opentelemetry/core';

Expand All @@ -65,11 +63,6 @@ interface AwsLogOptions {
* plugins of AWS X-Ray. Returns an empty Resource if detection fails.
*/
export class AwsEcsDetectorSync implements DetectorSync {
static readonly CONTAINER_ID_LENGTH = 64;
static readonly DEFAULT_CGROUP_PATH = '/proc/self/cgroup';

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

detect(): IResource {
const attributes = context.with(suppressTracing(context.active()), () =>
this._getAttributes()
Expand All @@ -88,7 +81,7 @@ export class AwsEcsDetectorSync implements DetectorSync {
let resource = new Resource({
[SEMRESATTRS_CLOUD_PROVIDER]: CLOUDPROVIDERVALUES_AWS,
[SEMRESATTRS_CLOUD_PLATFORM]: CLOUDPLATFORMVALUES_AWS_ECS,
}).merge(await AwsEcsDetectorSync._getContainerIdAndHostnameResource());
}).merge(await AwsEcsDetectorSync._getHostnameResource());

const metadataUrl = getEnv().ECS_CONTAINER_METADATA_URI_V4;
if (metadataUrl) {
Expand All @@ -115,43 +108,16 @@ export class AwsEcsDetectorSync implements DetectorSync {
}
}

/**
* Read container ID from cgroup file
* In ECS, even if we fail to find target file
* or target file does not contain container ID
* we do not throw an error but throw warning message
* and then return null string
*/
private static async _getContainerIdAndHostnameResource(): Promise<Resource> {
const hostName = os.hostname();

let containerId = '';
try {
const rawData = await AwsEcsDetectorSync.readFileAsync(
AwsEcsDetectorSync.DEFAULT_CGROUP_PATH,
'utf8'
);
const splitData = rawData.trim().split('\n');
for (const str of splitData) {
if (str.length > AwsEcsDetectorSync.CONTAINER_ID_LENGTH) {
containerId = str.substring(
str.length - AwsEcsDetectorSync.CONTAINER_ID_LENGTH
);
break;
}
}
} catch (e) {
diag.debug('AwsEcsDetector failed to read container ID', e);
}
private static async _getHostnameResource(): Promise<Resource> {
const hostName = os.hostname();

if (hostName || containerId) {
return new Resource({
[SEMRESATTRS_CONTAINER_NAME]: hostName || '',
[SEMRESATTRS_CONTAINER_ID]: containerId || '',
});
}
if (hostName) {
return new Resource({
[SEMRESATTRS_CONTAINER_NAME]: hostName || ''
});
}

return Resource.empty();
return Resource.empty();

Check warning on line 120 in detectors/node/opentelemetry-resource-detector-aws/src/detectors/AwsEcsDetectorSync.ts

View check run for this annotation

Codecov / codecov/patch

detectors/node/opentelemetry-resource-detector-aws/src/detectors/AwsEcsDetectorSync.ts#L120

Added line #L120 was not covered by tests
}

private static async _getMetadataV4Resource(
Expand All @@ -174,10 +140,12 @@ export class AwsEcsDetectorSync implements DetectorSync {
: `${baseArn}:cluster/${cluster}`;

const containerArn: string = containerMetadata['ContainerARN'];
const containerId: string = containerMetadata['DockerId'];

Check warning on line 143 in detectors/node/opentelemetry-resource-detector-aws/src/detectors/AwsEcsDetectorSync.ts

View check run for this annotation

Codecov / codecov/patch

detectors/node/opentelemetry-resource-detector-aws/src/detectors/AwsEcsDetectorSync.ts#L143

Added line #L143 was not covered by tests

// https://github.com/open-telemetry/semantic-conventions/blob/main/semantic_conventions/resource/cloud_provider/aws/ecs.yaml
const attributes: ResourceAttributes = {
[SEMRESATTRS_AWS_ECS_CONTAINER_ARN]: containerArn,
[SEMRESATTRS_CONTAINER_ID]: containerId,
[SEMRESATTRS_AWS_ECS_CLUSTER_ARN]: clusterArn,
[SEMRESATTRS_AWS_ECS_LAUNCHTYPE]: launchType?.toLowerCase(),
[SEMRESATTRS_AWS_ECS_TASK_ARN]: taskArn,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
} from '@opentelemetry/contrib-test-utils';
import { Resource } from '@opentelemetry/resources';
import {
SEMRESATTRS_CONTAINER_ID,
SEMRESATTRS_CLOUD_PLATFORM,
SEMRESATTRS_AWS_ECS_CONTAINER_ARN,
SEMRESATTRS_AWS_ECS_CLUSTER_ARN,
Expand All @@ -51,6 +52,7 @@ interface EcsResourceAttributes {
readonly zone?: string;
readonly clusterArn?: string;
readonly containerArn?: string;
readonly containerId?: string;
readonly launchType?: 'ec2' | 'fargate';
readonly taskArn?: string;
readonly taskFamily?: string;
Expand Down Expand Up @@ -80,6 +82,11 @@ const assertEcsResource = (
resource.attributes[SEMRESATTRS_AWS_ECS_CONTAINER_ARN],
validations.containerArn
);
if (validations.containerId)
assert.strictEqual(
resource.attributes[SEMRESATTRS_CONTAINER_ID],
validations.containerId
);
assert.strictEqual(
resource.attributes[AdditionalSemanticResourceAttributes.CLOUD_RESOURCE_ID],
validations.containerArn
Expand Down Expand Up @@ -343,6 +350,7 @@ describe('AwsEcsResourceDetector', () => {
clusterArn: 'arn:aws:ecs:us-west-2:111122223333:cluster/default',
containerArn:
'arn:aws:ecs:us-west-2:111122223333:container/0206b271-b33f-47ab-86c6-a0ba208a70a9',
containerId: 'ea32192c8553fbff06c9340478a2ff089b2bb5646fb718b4ee206641c9086d66',
launchType: 'ec2',
taskArn:
'arn:aws:ecs:us-west-2:111122223333:task/default/158d1c8083dd49d6b527399fd6414f5c',
Expand All @@ -369,6 +377,7 @@ describe('AwsEcsResourceDetector', () => {
clusterArn: 'arn:aws:ecs:us-west-2:111122223333:cluster/default',
containerArn:
'arn:aws:ecs:us-west-2:111122223333:container/05966557-f16c-49cb-9352-24b3a0dcd0e1',
containerId: 'cd189a933e5849daa93386466019ab50-2495160603',
launchType: 'fargate',
taskArn:
'arn:aws:ecs:us-west-2:111122223333:task/default/e9028f8d5d8e4f258373e7b93ce9a3c3',
Expand All @@ -391,6 +400,7 @@ describe('AwsEcsResourceDetector', () => {
clusterArn: 'arn:aws:ecs:us-west-2:111122223333:cluster/default',
containerArn:
'arn:aws:ecs:us-west-2:111122223333:container/05966557-f16c-49cb-9352-24b3a0dcd0e1',
containerId: 'cd189a933e5849daa93386466019ab50-2495160603',
launchType: 'fargate',
taskArn:
'arn:aws:ecs:us-west-2:111122223333:task/default/e9028f8d5d8e4f258373e7b93ce9a3c3',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
} from '@opentelemetry/contrib-test-utils';
import { Resource } from '@opentelemetry/resources';
import {
SEMRESATTRS_CONTAINER_ID,
SEMRESATTRS_CLOUD_PLATFORM,
SEMRESATTRS_AWS_ECS_CONTAINER_ARN,
SEMRESATTRS_AWS_ECS_CLUSTER_ARN,
Expand All @@ -51,6 +52,7 @@ interface EcsResourceAttributes {
readonly zone?: string;
readonly clusterArn?: string;
readonly containerArn?: string;
readonly containerId?: string;
readonly launchType?: 'ec2' | 'fargate';
readonly taskArn?: string;
readonly taskFamily?: string;
Expand Down Expand Up @@ -80,6 +82,11 @@ const assertEcsResource = (
resource.attributes[SEMRESATTRS_AWS_ECS_CONTAINER_ARN],
validations.containerArn
);
if (validations.containerId)
assert.strictEqual(
resource.attributes[SEMRESATTRS_CONTAINER_ID],
validations.containerId
);
assert.strictEqual(
resource.attributes[AdditionalSemanticResourceAttributes.CLOUD_RESOURCE_ID],
validations.containerArn
Expand Down Expand Up @@ -343,6 +350,7 @@ describe('AwsEcsResourceDetectorSync', () => {
clusterArn: 'arn:aws:ecs:us-west-2:111122223333:cluster/default',
containerArn:
'arn:aws:ecs:us-west-2:111122223333:container/0206b271-b33f-47ab-86c6-a0ba208a70a9',
containerId: 'ea32192c8553fbff06c9340478a2ff089b2bb5646fb718b4ee206641c9086d66',
launchType: 'ec2',
taskArn:
'arn:aws:ecs:us-west-2:111122223333:task/default/158d1c8083dd49d6b527399fd6414f5c',
Expand All @@ -369,6 +377,7 @@ describe('AwsEcsResourceDetectorSync', () => {
clusterArn: 'arn:aws:ecs:us-west-2:111122223333:cluster/default',
containerArn:
'arn:aws:ecs:us-west-2:111122223333:container/05966557-f16c-49cb-9352-24b3a0dcd0e1',
containerId: 'cd189a933e5849daa93386466019ab50-2495160603',
launchType: 'fargate',
taskArn:
'arn:aws:ecs:us-west-2:111122223333:task/default/e9028f8d5d8e4f258373e7b93ce9a3c3',
Expand All @@ -391,6 +400,7 @@ describe('AwsEcsResourceDetectorSync', () => {
clusterArn: 'arn:aws:ecs:us-west-2:111122223333:cluster/default',
containerArn:
'arn:aws:ecs:us-west-2:111122223333:container/05966557-f16c-49cb-9352-24b3a0dcd0e1',
containerId: 'cd189a933e5849daa93386466019ab50-2495160603',
launchType: 'fargate',
taskArn:
'arn:aws:ecs:us-west-2:111122223333:task/default/e9028f8d5d8e4f258373e7b93ce9a3c3',
Expand Down

0 comments on commit 5f48d6a

Please sign in to comment.