diff --git a/detectors/node/opentelemetry-resource-detector-aws/src/detectors/AwsEcsDetector.ts b/detectors/node/opentelemetry-resource-detector-aws/src/detectors/AwsEcsDetector.ts index cdea591fde..dc295f532a 100644 --- a/detectors/node/opentelemetry-resource-detector-aws/src/detectors/AwsEcsDetector.ts +++ b/detectors/node/opentelemetry-resource-detector-aws/src/detectors/AwsEcsDetector.ts @@ -15,50 +15,68 @@ */ import { diag } from '@opentelemetry/api'; -import { - Detector, - Resource, - ResourceDetectionConfig, -} from '@opentelemetry/resources'; +import { Detector, Resource } from '@opentelemetry/resources'; import { CloudProviderValues, CloudPlatformValues, SemanticResourceAttributes, } from '@opentelemetry/semantic-conventions'; +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'; +const HTTP_TIMEOUT_IN_MS = 1000; + +interface AwsLogOptions { + readonly 'awslogs-region'?: string; + readonly 'awslogs-group'?: string; + readonly 'awslogs-stream'?: string; +} + /** * The AwsEcsDetector can be used to detect if a process is running in AWS * ECS and return a {@link Resource} populated with data about the ECS * plugins of AWS X-Ray. Returns an empty Resource if detection fails. */ export class AwsEcsDetector implements Detector { - readonly CONTAINER_ID_LENGTH = 64; - readonly DEFAULT_CGROUP_PATH = '/proc/self/cgroup'; + static readonly CONTAINER_ID_LENGTH = 64; + static readonly DEFAULT_CGROUP_PATH = '/proc/self/cgroup'; + private static readFileAsync = util.promisify(fs.readFile); - async detect(_config?: ResourceDetectionConfig): Promise { + async detect(): Promise { const env = getEnv(); if (!env.ECS_CONTAINER_METADATA_URI_V4 && !env.ECS_CONTAINER_METADATA_URI) { diag.debug('AwsEcsDetector failed: Process is not on ECS'); return Resource.empty(); } - const hostName = os.hostname(); - const containerId = await this._getContainerId(); - - return !hostName && !containerId - ? Resource.empty() - : new Resource({ - [SemanticResourceAttributes.CLOUD_PROVIDER]: CloudProviderValues.AWS, - [SemanticResourceAttributes.CLOUD_PLATFORM]: - CloudPlatformValues.AWS_ECS, - [SemanticResourceAttributes.CONTAINER_NAME]: hostName || '', - [SemanticResourceAttributes.CONTAINER_ID]: containerId || '', - }); + let resource = new Resource({ + [SemanticResourceAttributes.CLOUD_PROVIDER]: CloudProviderValues.AWS, + [SemanticResourceAttributes.CLOUD_PLATFORM]: CloudPlatformValues.AWS_ECS, + }).merge(await AwsEcsDetector._getContainerIdAndHostnameResource()); + + const metadataUrl = getEnv().ECS_CONTAINER_METADATA_URI_V4; + if (metadataUrl) { + const [containerMetadata, taskMetadata] = await Promise.all([ + AwsEcsDetector._getUrlAsJson(metadataUrl), + AwsEcsDetector._getUrlAsJson(`${metadataUrl}/task`), + ]); + + const metadatav4Resource = await AwsEcsDetector._getMetadataV4Resource( + containerMetadata, + taskMetadata + ); + const logsResource = await AwsEcsDetector._getLogResource( + containerMetadata + ); + + resource = resource.merge(metadatav4Resource).merge(logsResource); + } + + return resource; } /** @@ -68,22 +86,146 @@ export class AwsEcsDetector implements Detector { * we do not throw an error but throw warning message * and then return null string */ - private async _getContainerId(): Promise { + private static async _getContainerIdAndHostnameResource(): Promise { + const hostName = os.hostname(); + + let containerId = ''; try { const rawData = await AwsEcsDetector.readFileAsync( - this.DEFAULT_CGROUP_PATH, + AwsEcsDetector.DEFAULT_CGROUP_PATH, 'utf8' ); const splitData = rawData.trim().split('\n'); for (const str of splitData) { - if (str.length > this.CONTAINER_ID_LENGTH) { - return str.substring(str.length - this.CONTAINER_ID_LENGTH); + if (str.length > AwsEcsDetector.CONTAINER_ID_LENGTH) { + containerId = str.substring( + str.length - AwsEcsDetector.CONTAINER_ID_LENGTH + ); + break; } } } catch (e) { - diag.warn(`AwsEcsDetector failed to read container ID: ${e.message}`); + diag.warn('AwsEcsDetector failed to read container ID', e); + } + + if (hostName || containerId) { + return new Resource({ + [SemanticResourceAttributes.CONTAINER_NAME]: hostName || '', + [SemanticResourceAttributes.CONTAINER_ID]: containerId || '', + }); + } + + return Resource.empty(); + } + + private static async _getMetadataV4Resource( + containerMetadata: any, + taskMetadata: any + ): Promise { + const launchType: string = taskMetadata['LaunchType']; + const taskArn: string = taskMetadata['TaskARN']; + + const baseArn: string = taskArn.substring(0, taskArn.lastIndexOf(':')); + const cluster: string = taskMetadata['Cluster']; + + const clusterArn = cluster.startsWith('arn:') + ? cluster + : `${baseArn}:cluster/${cluster}`; + + const containerArn: string = containerMetadata['ContainerARN']; + + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/semantic_conventions/resource/cloud_provider/aws/ecs.yaml + return new Resource({ + [SemanticResourceAttributes.AWS_ECS_CONTAINER_ARN]: containerArn, + [SemanticResourceAttributes.AWS_ECS_CLUSTER_ARN]: clusterArn, + [SemanticResourceAttributes.AWS_ECS_LAUNCHTYPE]: + launchType?.toLowerCase(), + [SemanticResourceAttributes.AWS_ECS_TASK_ARN]: taskArn, + [SemanticResourceAttributes.AWS_ECS_TASK_FAMILY]: taskMetadata['Family'], + [SemanticResourceAttributes.AWS_ECS_TASK_REVISION]: + taskMetadata['Revision'], + }); + } + + private static async _getLogResource( + containerMetadata: any + ): Promise { + if ( + containerMetadata['LogDriver'] !== 'awslogs' || + !containerMetadata['LogOptions'] + ) { + return Resource.EMPTY; } - return undefined; + + const containerArn = containerMetadata['ContainerARN']!; + const logOptions = containerMetadata['LogOptions'] as AwsLogOptions; + + const logsRegion = + logOptions['awslogs-region'] || + AwsEcsDetector._getRegionFromArn(containerArn); + + const awsAccount = AwsEcsDetector._getAccountFromArn(containerArn); + + const logsGroupName = logOptions['awslogs-group']!; + const logsGroupArn = `arn:aws:logs:${logsRegion}:${awsAccount}:log-group:${logsGroupName}`; + const logsStreamName = logOptions['awslogs-stream']!; + const logsStreamArn = `arn:aws:logs:${logsRegion}:${awsAccount}:log-group:${logsGroupName}:log-stream:${logsStreamName}`; + + return new Resource({ + [SemanticResourceAttributes.AWS_LOG_GROUP_NAMES]: [logsGroupName], + [SemanticResourceAttributes.AWS_LOG_GROUP_ARNS]: [logsGroupArn], + [SemanticResourceAttributes.AWS_LOG_STREAM_NAMES]: [logsStreamName], + [SemanticResourceAttributes.AWS_LOG_STREAM_ARNS]: [logsStreamArn], + }); + } + + private static _getAccountFromArn(containerArn: string): string { + const match = /arn:aws:ecs:[^:]+:([^:]+):.*/.exec(containerArn); + return match![1]; + } + + private static _getRegionFromArn(containerArn: string): string { + const match = /arn:aws:ecs:([^:]+):.*/.exec(containerArn); + return match![1]; + } + + private static _getUrlAsJson(url: string): Promise { + return new Promise((resolve, reject) => { + const request = http.get(url, (response: http.IncomingMessage) => { + if (response.statusCode && response.statusCode >= 400) { + reject( + new Error( + `Request to '${url}' failed with status ${response.statusCode}` + ) + ); + } + /* + * Concatenate the response out of chunks: + * https://nodejs.org/api/stream.html#stream_event_data + */ + let responseBody = ''; + response.on( + 'data', + (chunk: Buffer) => (responseBody += chunk.toString()) + ); + // All the data has been read, resolve the Promise + response.on('end', () => resolve(responseBody)); + /* + * https://nodejs.org/api/http.html#httprequesturl-options-callback, see the + * 'In the case of a premature connection close after the response is received' + * case + */ + request.on('error', reject); + }); + + // Set an aggressive timeout to prevent lock-ups + request.setTimeout(HTTP_TIMEOUT_IN_MS, () => { + request.destroy(); + }); + // Connection error, disconnection, etc. + request.on('error', reject); + request.end(); + }).then(responseBodyRaw => JSON.parse(responseBodyRaw)); } } diff --git a/detectors/node/opentelemetry-resource-detector-aws/test/detectors/AwsEcsDetector.test.ts b/detectors/node/opentelemetry-resource-detector-aws/test/detectors/AwsEcsDetector.test.ts index 04a3c24b12..b1f7b58a18 100644 --- a/detectors/node/opentelemetry-resource-detector-aws/test/detectors/AwsEcsDetector.test.ts +++ b/detectors/node/opentelemetry-resource-detector-aws/test/detectors/AwsEcsDetector.test.ts @@ -15,6 +15,7 @@ */ import * as assert from 'assert'; +import * as nock from 'nock'; import * as sinon from 'sinon'; import { awsEcsDetector, @@ -22,11 +23,96 @@ import { } from '../../src/detectors/AwsEcsDetector'; import { assertEmptyResource, + assertCloudResource, assertContainerResource, } from '@opentelemetry/contrib-test-utils'; +import { Resource } from '@opentelemetry/resources'; +import { + CloudProviderValues, + CloudPlatformValues, + SemanticResourceAttributes, +} from '@opentelemetry/semantic-conventions'; +import { readFileSync } from 'fs'; import * as os from 'os'; +import { join } from 'path'; + +interface EcsResourceAttributes { + readonly clusterArn?: string; + readonly containerArn?: string; + readonly launchType?: 'ec2' | 'fargate'; + readonly taskArn?: string; + readonly taskFamily?: string; + readonly taskRevision?: string; + readonly logGroupNames?: Array; + readonly logGroupArns?: Array; + readonly logStreamNames?: Array; + readonly logStreamArns?: Array; +} -describe('BeanstalkResourceDetector', () => { +const assertEcsResource = ( + resource: Resource, + validations: EcsResourceAttributes +) => { + assertCloudResource(resource, { + provider: CloudProviderValues.AWS, + }); + assert.strictEqual( + resource.attributes[SemanticResourceAttributes.CLOUD_PLATFORM], + CloudPlatformValues.AWS_ECS + ); + if (validations.containerArn) + assert.strictEqual( + resource.attributes[SemanticResourceAttributes.AWS_ECS_CONTAINER_ARN], + validations.containerArn + ); + if (validations.clusterArn) + assert.strictEqual( + resource.attributes[SemanticResourceAttributes.AWS_ECS_CLUSTER_ARN], + validations.clusterArn + ); + if (validations.launchType) + assert.strictEqual( + resource.attributes[SemanticResourceAttributes.AWS_ECS_LAUNCHTYPE], + validations.launchType + ); + if (validations.taskArn) + assert.strictEqual( + resource.attributes[SemanticResourceAttributes.AWS_ECS_TASK_ARN], + validations.taskArn + ); + if (validations.taskFamily) + assert.strictEqual( + resource.attributes[SemanticResourceAttributes.AWS_ECS_TASK_FAMILY], + validations.taskFamily + ); + if (validations.taskRevision) + assert.strictEqual( + resource.attributes[SemanticResourceAttributes.AWS_ECS_TASK_REVISION], + validations.taskRevision + ); + if (validations.logGroupNames) + assert.deepEqual( + resource.attributes[SemanticResourceAttributes.AWS_LOG_GROUP_NAMES], + validations.logGroupNames + ); + if (validations.logGroupArns) + assert.deepEqual( + resource.attributes[SemanticResourceAttributes.AWS_LOG_GROUP_ARNS], + validations.logGroupArns + ); + if (validations.logStreamNames) + assert.deepEqual( + resource.attributes[SemanticResourceAttributes.AWS_LOG_STREAM_NAMES], + validations.logStreamNames + ); + if (validations.logStreamArns) + assert.deepEqual( + resource.attributes[SemanticResourceAttributes.AWS_LOG_STREAM_ARNS], + validations.logStreamArns + ); +}; + +describe('AwsEcsResourceDetector', () => { const errorMsg = { fileNotFoundError: new Error('cannot find cgroup file'), }; @@ -50,23 +136,6 @@ describe('BeanstalkResourceDetector', () => { sinon.restore(); }); - it('should successfully return resource data', async () => { - process.env.ECS_CONTAINER_METADATA_URI_V4 = 'ecs_metadata_v4_uri'; - sinon.stub(os, 'hostname').returns(hostNameData); - readStub = sinon - .stub(AwsEcsDetector, 'readFileAsync' as any) - .resolves(correctCgroupData); - - const resource = await awsEcsDetector.detect(); - - sinon.assert.calledOnce(readStub); - assert.ok(resource); - assertContainerResource(resource, { - name: 'abcd.test.testing.com', - id: 'bcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm', - }); - }); - it('should successfully return resource data with noisy cgroup file', async () => { process.env.ECS_CONTAINER_METADATA_URI = 'ecs_metadata_v3_uri'; sinon.stub(os, 'hostname').returns(hostNameData); @@ -78,6 +147,7 @@ describe('BeanstalkResourceDetector', () => { sinon.assert.calledOnce(readStub); assert.ok(resource); + assertEcsResource(resource, {}); assertContainerResource(resource, { name: 'abcd.test.testing.com', id: 'bcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm', @@ -95,6 +165,7 @@ describe('BeanstalkResourceDetector', () => { sinon.assert.calledOnce(readStub); assert.ok(resource); + assertEcsResource(resource, {}); assertContainerResource(resource, { name: 'abcd.test.testing.com', id: 'bcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm', @@ -114,63 +185,213 @@ describe('BeanstalkResourceDetector', () => { assertEmptyResource(resource); }); - it('should return resource only with hostname attribute without cgroup file', async () => { - process.env.ECS_CONTAINER_METADATA_URI_V4 = 'ecs_metadata_v4_uri'; - sinon.stub(os, 'hostname').returns(hostNameData); - readStub = sinon - .stub(AwsEcsDetector, 'readFileAsync' as any) - .rejects(errorMsg.fileNotFoundError); + describe('with Metadata URI v4 available', () => { + const ECS_CONTAINER_METADATA_URI_V4 = + 'http://169.254.170.2/v4/96d36db6cf2942269b2c2c0c9540c444-4190541037'; - const resource = await awsEcsDetector.detect(); - - sinon.assert.calledOnce(readStub); - assert.ok(resource); - assertContainerResource(resource, { - name: 'abcd.test.testing.com', + beforeEach(() => { + process.env.ECS_CONTAINER_METADATA_URI_V4 = ECS_CONTAINER_METADATA_URI_V4; }); - }); - it('should return resource only with hostname attribute when cgroup file does not contain valid container ID', async () => { - process.env.ECS_CONTAINER_METADATA_URI_V4 = 'ecs_metadata_v4_uri'; - sinon.stub(os, 'hostname').returns(hostNameData); - readStub = sinon.stub(AwsEcsDetector, 'readFileAsync' as any).resolves(''); + describe('when succesfully retrieving the data', () => { + function generateLaunchTypeTests( + resourceAttributes: EcsResourceAttributes, + suffix: String = '' + ) { + let nockScope: nock.Scope; - const resource = await awsEcsDetector.detect(); + beforeEach(() => { + function readTestFileName(testFileName: string) { + const testResource = join( + __dirname, + `test-resources/${testFileName}` + ); - sinon.assert.calledOnce(readStub); - assert.ok(resource); - assertContainerResource(resource, { - name: 'abcd.test.testing.com', - }); - }); + return readFileSync(testResource, 'utf-8'); + } - it('should return resource only with container ID attribute without hostname', async () => { - process.env.ECS_CONTAINER_METADATA_URI_V4 = 'ecs_metadata_v4_uri'; - sinon.stub(os, 'hostname').returns(''); - readStub = sinon - .stub(AwsEcsDetector, 'readFileAsync' as any) - .resolves(correctCgroupData); + const containerResponseBody = readTestFileName( + `metadatav4-response-container-${resourceAttributes.launchType!}${suffix}.json` + ); + const taskResponseBody = readTestFileName( + `metadatav4-response-task-${resourceAttributes.launchType!}${suffix}.json` + ); - const resource = await awsEcsDetector.detect(); + nockScope = nock('http://169.254.170.2:80') + .persist(false) + .get('/v4/96d36db6cf2942269b2c2c0c9540c444-4190541037') + .reply(200, () => containerResponseBody) + .get('/v4/96d36db6cf2942269b2c2c0c9540c444-4190541037/task') + .reply(200, () => taskResponseBody); + }); - sinon.assert.calledOnce(readStub); - assert.ok(resource); - assertContainerResource(resource, { - id: 'bcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm', + afterEach(() => { + if (nockScope) { + nockScope.done(); + } + }); + + it('should successfully return resource data', async () => { + sinon.stub(os, 'hostname').returns(hostNameData); + readStub = sinon + .stub(AwsEcsDetector, 'readFileAsync' as any) + .resolves(correctCgroupData); + + const resource = await awsEcsDetector.detect(); + + sinon.assert.calledOnce(readStub); + assert.ok(resource); + assertEcsResource(resource, resourceAttributes); + assertContainerResource(resource, { + name: 'abcd.test.testing.com', + id: 'bcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm', + }); + }); + + it('should return resource only with hostname attribute without cgroup file', async () => { + sinon.stub(os, 'hostname').returns(hostNameData); + readStub = sinon + .stub(AwsEcsDetector, 'readFileAsync' as any) + .rejects(errorMsg.fileNotFoundError); + + const resource = await awsEcsDetector.detect(); + + sinon.assert.calledOnce(readStub); + assert.ok(resource); + assertEcsResource(resource, resourceAttributes); + assertContainerResource(resource, { + name: 'abcd.test.testing.com', + }); + }); + + it('should return resource only with hostname attribute when cgroup file does not contain valid container ID', async () => { + sinon.stub(os, 'hostname').returns(hostNameData); + readStub = sinon + .stub(AwsEcsDetector, 'readFileAsync' as any) + .resolves(''); + + const resource = await awsEcsDetector.detect(); + + sinon.assert.calledOnce(readStub); + assert.ok(resource); + assertEcsResource(resource, resourceAttributes); + assertContainerResource(resource, { + name: 'abcd.test.testing.com', + }); + }); + + it('should return resource only with container ID attribute without hostname', async () => { + sinon.stub(os, 'hostname').returns(''); + readStub = sinon + .stub(AwsEcsDetector, 'readFileAsync' as any) + .resolves(correctCgroupData); + + const resource = await awsEcsDetector.detect(); + + sinon.assert.calledOnce(readStub); + assert.ok(resource); + assertEcsResource(resource, resourceAttributes); + assertContainerResource(resource, { + id: 'bcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm', + }); + }); + + it('should return metadata v4 resource attributes when both hostname and container ID are invalid', async () => { + sinon.stub(os, 'hostname').returns(''); + readStub = sinon + .stub(AwsEcsDetector, 'readFileAsync' as any) + .rejects(errorMsg.fileNotFoundError); + + const resource = await awsEcsDetector.detect(); + + sinon.assert.calledOnce(readStub); + assert.ok(resource); + assertEcsResource(resource, resourceAttributes); + }); + } + + describe('on Ec2', () => { + describe('with AWS CloudWatch as log driver', () => { + generateLaunchTypeTests({ + clusterArn: 'arn:aws:ecs:us-west-2:111122223333:cluster/default', + containerArn: + 'arn:aws:ecs:us-west-2:111122223333:container/0206b271-b33f-47ab-86c6-a0ba208a70a9', + launchType: 'ec2', + taskArn: + 'arn:aws:ecs:us-west-2:111122223333:task/default/158d1c8083dd49d6b527399fd6414f5c', + taskFamily: 'curltest', + taskRevision: '26', + logGroupNames: ['/ecs/metadata'], + logGroupArns: [ + 'arn:aws:logs:us-west-2:111122223333:log-group:/ecs/metadata', + ], + logStreamNames: ['ecs/curl/8f03e41243824aea923aca126495f665'], + logStreamArns: [ + 'arn:aws:logs:us-west-2:111122223333:log-group:/ecs/metadata:log-stream:ecs/curl/8f03e41243824aea923aca126495f665', + ], + }); + }); + }); + + describe('on Fargate', () => { + describe('with AWS CloudWatch as log driver', () => { + generateLaunchTypeTests({ + clusterArn: 'arn:aws:ecs:us-west-2:111122223333:cluster/default', + containerArn: + 'arn:aws:ecs:us-west-2:111122223333:container/05966557-f16c-49cb-9352-24b3a0dcd0e1', + launchType: 'fargate', + taskArn: + 'arn:aws:ecs:us-west-2:111122223333:task/default/e9028f8d5d8e4f258373e7b93ce9a3c3', + taskFamily: 'curltest', + taskRevision: '3', + logGroupNames: ['/ecs/containerlogs'], + logGroupArns: [ + 'arn:aws:logs:us-west-2:111122223333:log-group:/ecs/containerlogs', + ], + logStreamNames: ['ecs/curl/cd189a933e5849daa93386466019ab50'], + logStreamArns: [ + 'arn:aws:logs:us-west-2:111122223333:log-group:/ecs/containerlogs:log-stream:ecs/curl/cd189a933e5849daa93386466019ab50', + ], + }); + }); + + describe('with AWS Firelens as log driver', () => { + generateLaunchTypeTests( + { + clusterArn: 'arn:aws:ecs:us-west-2:111122223333:cluster/default', + containerArn: + 'arn:aws:ecs:us-west-2:111122223333:container/05966557-f16c-49cb-9352-24b3a0dcd0e1', + launchType: 'fargate', + taskArn: + 'arn:aws:ecs:us-west-2:111122223333:task/default/e9028f8d5d8e4f258373e7b93ce9a3c3', + taskFamily: 'curltest', + taskRevision: '3', + logGroupNames: undefined, + logGroupArns: undefined, + logStreamNames: undefined, + logStreamArns: undefined, + }, + '-logsfirelens' + ); + }); + }); }); - }); - it('should return empty resource when both hostname and container ID are invalid', async () => { - process.env.ECS_CONTAINER_METADATA_URI_V4 = 'ecs_metadata_v4_uri'; - sinon.stub(os, 'hostname').returns(''); - readStub = sinon - .stub(AwsEcsDetector, 'readFileAsync' as any) - .rejects(errorMsg.fileNotFoundError); + describe('when failing to fetch metadata', async () => { + const error = new Error('ERROR'); - const resource = await awsEcsDetector.detect(); + beforeEach(() => { + sinon.stub(AwsEcsDetector, '_getUrlAsJson' as any).rejects(error); + }); - sinon.assert.calledOnce(readStub); - assert.ok(resource); - assertEmptyResource(resource); + it('should reject with an error', async () => { + try { + await awsEcsDetector.detect(); + throw new Error('Should not have reached here'); + } catch (err) { + assert.strictEqual(err, error); + } + }); + }); }); }); diff --git a/detectors/node/opentelemetry-resource-detector-aws/test/detectors/test-resources/metadatav4-response-container-ec2.json b/detectors/node/opentelemetry-resource-detector-aws/test/detectors/test-resources/metadatav4-response-container-ec2.json new file mode 100644 index 0000000000..9354e74f39 --- /dev/null +++ b/detectors/node/opentelemetry-resource-detector-aws/test/detectors/test-resources/metadatav4-response-container-ec2.json @@ -0,0 +1,44 @@ +{ + "DockerId": "ea32192c8553fbff06c9340478a2ff089b2bb5646fb718b4ee206641c9086d66", + "Name": "curl", + "DockerName": "ecs-curltest-24-curl-cca48e8dcadd97805600", + "Image": "111122223333.dkr.ecr.us-west-2.amazonaws.com/curltest:latest", + "ImageID": "sha256:d691691e9652791a60114e67b365688d20d19940dde7c4736ea30e660d8d3553", + "Labels": { + "com.amazonaws.ecs.cluster": "default", + "com.amazonaws.ecs.container-name": "curl", + "com.amazonaws.ecs.task-arn": "arn:aws:ecs:us-west-2:111122223333:task/default/8f03e41243824aea923aca126495f665", + "com.amazonaws.ecs.task-definition-family": "curltest", + "com.amazonaws.ecs.task-definition-version": "24" + }, + "DesiredStatus": "RUNNING", + "KnownStatus": "RUNNING", + "Limits": { + "CPU": 10, + "Memory": 128 + }, + "CreatedAt": "2020-10-02T00:15:07.620912337Z", + "StartedAt": "2020-10-02T00:15:08.062559351Z", + "Type": "NORMAL", + "LogDriver": "awslogs", + "LogOptions": { + "awslogs-create-group": "true", + "awslogs-group": "/ecs/metadata", + "awslogs-region": "us-west-2", + "awslogs-stream": "ecs/curl/8f03e41243824aea923aca126495f665" + }, + "ContainerARN": "arn:aws:ecs:us-west-2:111122223333:container/0206b271-b33f-47ab-86c6-a0ba208a70a9", + "Networks": [ + { + "NetworkMode": "awsvpc", + "IPv4Addresses": [ + "10.0.2.100" + ], + "AttachmentIndex": 0, + "MACAddress": "0e:9e:32:c7:48:85", + "IPv4SubnetCIDRBlock": "10.0.2.0/24", + "PrivateDNSName": "ip-10-0-2-100.us-west-2.compute.internal", + "SubnetGatewayIpv4Address": "10.0.2.1/24" + } + ] +} \ No newline at end of file diff --git a/detectors/node/opentelemetry-resource-detector-aws/test/detectors/test-resources/metadatav4-response-container-fargate-logsfirelens.json b/detectors/node/opentelemetry-resource-detector-aws/test/detectors/test-resources/metadatav4-response-container-fargate-logsfirelens.json new file mode 100644 index 0000000000..b87f1d7203 --- /dev/null +++ b/detectors/node/opentelemetry-resource-detector-aws/test/detectors/test-resources/metadatav4-response-container-fargate-logsfirelens.json @@ -0,0 +1,44 @@ +{ + "DockerId": "cd189a933e5849daa93386466019ab50-2495160603", + "Name": "curl", + "DockerName": "curl", + "Image": "111122223333.dkr.ecr.us-west-2.amazonaws.com/curltest:latest", + "ImageID": "sha256:25f3695bedfb454a50f12d127839a68ad3caf91e451c1da073db34c542c4d2cb", + "Labels": { + "com.amazonaws.ecs.cluster": "arn:aws:ecs:us-west-2:111122223333:cluster/default", + "com.amazonaws.ecs.container-name": "curl", + "com.amazonaws.ecs.task-arn": "arn:aws:ecs:us-west-2:111122223333:task/default/cd189a933e5849daa93386466019ab50", + "com.amazonaws.ecs.task-definition-family": "curltest", + "com.amazonaws.ecs.task-definition-version": "2" + }, + "DesiredStatus": "RUNNING", + "KnownStatus": "RUNNING", + "Limits": { + "CPU": 10, + "Memory": 128 + }, + "CreatedAt": "2020-10-08T20:09:11.44527186Z", + "StartedAt": "2020-10-08T20:09:11.44527186Z", + "Type": "NORMAL", + "Networks": [ + { + "NetworkMode": "awsvpc", + "IPv4Addresses": [ + "192.0.2.3" + ], + "AttachmentIndex": 0, + "MACAddress": "0a:de:f6:10:51:e5", + "IPv4SubnetCIDRBlock": "192.0.2.0/24", + "DomainNameServers": [ + "192.0.2.2" + ], + "DomainNameSearchList": [ + "us-west-2.compute.internal" + ], + "PrivateDNSName": "ip-10-0-0-222.us-west-2.compute.internal", + "SubnetGatewayIpv4Address": "192.0.2.0/24" + } + ], + "ContainerARN": "arn:aws:ecs:us-west-2:111122223333:container/05966557-f16c-49cb-9352-24b3a0dcd0e1", + "LogDriver": "awsfirelens" +} \ No newline at end of file diff --git a/detectors/node/opentelemetry-resource-detector-aws/test/detectors/test-resources/metadatav4-response-container-fargate.json b/detectors/node/opentelemetry-resource-detector-aws/test/detectors/test-resources/metadatav4-response-container-fargate.json new file mode 100644 index 0000000000..ccbe70bc44 --- /dev/null +++ b/detectors/node/opentelemetry-resource-detector-aws/test/detectors/test-resources/metadatav4-response-container-fargate.json @@ -0,0 +1,50 @@ +{ + "DockerId": "cd189a933e5849daa93386466019ab50-2495160603", + "Name": "curl", + "DockerName": "curl", + "Image": "111122223333.dkr.ecr.us-west-2.amazonaws.com/curltest:latest", + "ImageID": "sha256:25f3695bedfb454a50f12d127839a68ad3caf91e451c1da073db34c542c4d2cb", + "Labels": { + "com.amazonaws.ecs.cluster": "arn:aws:ecs:us-west-2:111122223333:cluster/default", + "com.amazonaws.ecs.container-name": "curl", + "com.amazonaws.ecs.task-arn": "arn:aws:ecs:us-west-2:111122223333:task/default/cd189a933e5849daa93386466019ab50", + "com.amazonaws.ecs.task-definition-family": "curltest", + "com.amazonaws.ecs.task-definition-version": "2" + }, + "DesiredStatus": "RUNNING", + "KnownStatus": "RUNNING", + "Limits": { + "CPU": 10, + "Memory": 128 + }, + "CreatedAt": "2020-10-08T20:09:11.44527186Z", + "StartedAt": "2020-10-08T20:09:11.44527186Z", + "Type": "NORMAL", + "Networks": [ + { + "NetworkMode": "awsvpc", + "IPv4Addresses": [ + "192.0.2.3" + ], + "AttachmentIndex": 0, + "MACAddress": "0a:de:f6:10:51:e5", + "IPv4SubnetCIDRBlock": "192.0.2.0/24", + "DomainNameServers": [ + "192.0.2.2" + ], + "DomainNameSearchList": [ + "us-west-2.compute.internal" + ], + "PrivateDNSName": "ip-10-0-0-222.us-west-2.compute.internal", + "SubnetGatewayIpv4Address": "192.0.2.0/24" + } + ], + "ContainerARN": "arn:aws:ecs:us-west-2:111122223333:container/05966557-f16c-49cb-9352-24b3a0dcd0e1", + "LogOptions": { + "awslogs-create-group": "true", + "awslogs-group": "/ecs/containerlogs", + "awslogs-region": "us-west-2", + "awslogs-stream": "ecs/curl/cd189a933e5849daa93386466019ab50" + }, + "LogDriver": "awslogs" +} \ No newline at end of file diff --git a/detectors/node/opentelemetry-resource-detector-aws/test/detectors/test-resources/metadatav4-response-task-ec2.json b/detectors/node/opentelemetry-resource-detector-aws/test/detectors/test-resources/metadatav4-response-task-ec2.json new file mode 100644 index 0000000000..bb9bd6f050 --- /dev/null +++ b/detectors/node/opentelemetry-resource-detector-aws/test/detectors/test-resources/metadatav4-response-task-ec2.json @@ -0,0 +1,94 @@ +{ + "Cluster": "default", + "TaskARN": "arn:aws:ecs:us-west-2:111122223333:task/default/158d1c8083dd49d6b527399fd6414f5c", + "Family": "curltest", + "Revision": "26", + "DesiredStatus": "RUNNING", + "KnownStatus": "RUNNING", + "PullStartedAt": "2020-10-02T00:43:06.202617438Z", + "PullStoppedAt": "2020-10-02T00:43:06.31288465Z", + "AvailabilityZone": "us-west-2d", + "LaunchType": "EC2", + "Containers": [ + { + "DockerId": "598cba581fe3f939459eaba1e071d5c93bb2c49b7d1ba7db6bb19deeb70d8e38", + "Name": "~internal~ecs~pause", + "DockerName": "ecs-curltest-26-internalecspause-e292d586b6f9dade4a00", + "Image": "amazon/amazon-ecs-pause:0.1.0", + "ImageID": "", + "Labels": { + "com.amazonaws.ecs.cluster": "default", + "com.amazonaws.ecs.container-name": "~internal~ecs~pause", + "com.amazonaws.ecs.task-arn": "arn:aws:ecs:us-west-2:111122223333:task/default/158d1c8083dd49d6b527399fd6414f5c", + "com.amazonaws.ecs.task-definition-family": "curltest", + "com.amazonaws.ecs.task-definition-version": "26" + }, + "DesiredStatus": "RESOURCES_PROVISIONED", + "KnownStatus": "RESOURCES_PROVISIONED", + "Limits": { + "CPU": 0, + "Memory": 0 + }, + "CreatedAt": "2020-10-02T00:43:05.602352471Z", + "StartedAt": "2020-10-02T00:43:06.076707576Z", + "Type": "CNI_PAUSE", + "Networks": [ + { + "NetworkMode": "awsvpc", + "IPv4Addresses": [ + "10.0.2.61" + ], + "AttachmentIndex": 0, + "MACAddress": "0e:10:e2:01:bd:91", + "IPv4SubnetCIDRBlock": "10.0.2.0/24", + "PrivateDNSName": "ip-10-0-2-61.us-west-2.compute.internal", + "SubnetGatewayIpv4Address": "10.0.2.1/24" + } + ] + }, + { + "DockerId": "ee08638adaaf009d78c248913f629e38299471d45fe7dc944d1039077e3424ca", + "Name": "curl", + "DockerName": "ecs-curltest-26-curl-a0e7dba5aca6d8cb2e00", + "Image": "111122223333.dkr.ecr.us-west-2.amazonaws.com/curltest:latest", + "ImageID": "sha256:d691691e9652791a60114e67b365688d20d19940dde7c4736ea30e660d8d3553", + "Labels": { + "com.amazonaws.ecs.cluster": "default", + "com.amazonaws.ecs.container-name": "curl", + "com.amazonaws.ecs.task-arn": "arn:aws:ecs:us-west-2:111122223333:task/default/158d1c8083dd49d6b527399fd6414f5c", + "com.amazonaws.ecs.task-definition-family": "curltest", + "com.amazonaws.ecs.task-definition-version": "26" + }, + "DesiredStatus": "RUNNING", + "KnownStatus": "RUNNING", + "Limits": { + "CPU": 10, + "Memory": 128 + }, + "CreatedAt": "2020-10-02T00:43:06.326590752Z", + "StartedAt": "2020-10-02T00:43:06.767535449Z", + "Type": "NORMAL", + "LogDriver": "awslogs", + "LogOptions": { + "awslogs-create-group": "true", + "awslogs-group": "/ecs/metadata", + "awslogs-region": "us-west-2", + "awslogs-stream": "ecs/curl/158d1c8083dd49d6b527399fd6414f5c" + }, + "ContainerARN": "arn:aws:ecs:us-west-2:111122223333:container/abb51bdd-11b4-467f-8f6c-adcfe1fe059d", + "Networks": [ + { + "NetworkMode": "awsvpc", + "IPv4Addresses": [ + "10.0.2.61" + ], + "AttachmentIndex": 0, + "MACAddress": "0e:10:e2:01:bd:91", + "IPv4SubnetCIDRBlock": "10.0.2.0/24", + "PrivateDNSName": "ip-10-0-2-61.us-west-2.compute.internal", + "SubnetGatewayIpv4Address": "10.0.2.1/24" + } + ] + } + ] +} \ No newline at end of file diff --git a/detectors/node/opentelemetry-resource-detector-aws/test/detectors/test-resources/metadatav4-response-task-fargate-logsfirelens.json b/detectors/node/opentelemetry-resource-detector-aws/test/detectors/test-resources/metadatav4-response-task-fargate-logsfirelens.json new file mode 100644 index 0000000000..2f6c3a7714 --- /dev/null +++ b/detectors/node/opentelemetry-resource-detector-aws/test/detectors/test-resources/metadatav4-response-task-fargate-logsfirelens.json @@ -0,0 +1,71 @@ +{ + "Cluster": "arn:aws:ecs:us-west-2:111122223333:cluster/default", + "TaskARN": "arn:aws:ecs:us-west-2:111122223333:task/default/e9028f8d5d8e4f258373e7b93ce9a3c3", + "Family": "curltest", + "Revision": "3", + "DesiredStatus": "RUNNING", + "KnownStatus": "RUNNING", + "Limits": { + "CPU": 0.25, + "Memory": 512 + }, + "PullStartedAt": "2020-10-08T20:47:16.053330955Z", + "PullStoppedAt": "2020-10-08T20:47:19.592684631Z", + "AvailabilityZone": "us-west-2a", + "Containers": [ + { + "DockerId": "e9028f8d5d8e4f258373e7b93ce9a3c3-2495160603", + "Name": "curl", + "DockerName": "curl", + "Image": "111122223333.dkr.ecr.us-west-2.amazonaws.com/curltest:latest", + "ImageID": "sha256:25f3695bedfb454a50f12d127839a68ad3caf91e451c1da073db34c542c4d2cb", + "Labels": { + "com.amazonaws.ecs.cluster": "arn:aws:ecs:us-west-2:111122223333:cluster/default", + "com.amazonaws.ecs.container-name": "curl", + "com.amazonaws.ecs.task-arn": "arn:aws:ecs:us-west-2:111122223333:task/default/e9028f8d5d8e4f258373e7b93ce9a3c3", + "com.amazonaws.ecs.task-definition-family": "curltest", + "com.amazonaws.ecs.task-definition-version": "3" + }, + "DesiredStatus": "RUNNING", + "KnownStatus": "RUNNING", + "Limits": { + "CPU": 10, + "Memory": 128 + }, + "CreatedAt": "2020-10-08T20:47:20.567813946Z", + "StartedAt": "2020-10-08T20:47:20.567813946Z", + "Type": "NORMAL", + "Networks": [ + { + "NetworkMode": "awsvpc", + "IPv4Addresses": [ + "192.0.2.3" + ], + "IPv6Addresses": [ + "2001:dB8:10b:1a00:32bf:a372:d80f:e958" + ], + "AttachmentIndex": 0, + "MACAddress": "02:b7:20:19:72:39", + "IPv4SubnetCIDRBlock": "192.0.2.0/24", + "IPv6SubnetCIDRBlock": "2600:1f13:10b:1a00::/64", + "DomainNameServers": [ + "192.0.2.2" + ], + "DomainNameSearchList": [ + "us-west-2.compute.internal" + ], + "PrivateDNSName": "ip-172-31-30-173.us-west-2.compute.internal", + "SubnetGatewayIpv4Address": "192.0.2.0/24" + } + ], + "ClockDrift": { + "ClockErrorBound": 0.5458234999999999, + "ReferenceTimestamp": "2021-09-07T16:57:44Z", + "ClockSynchronizationStatus": "SYNCHRONIZED" + }, + "ContainerARN": "arn:aws:ecs:us-west-2:111122223333:container/1bdcca8b-f905-4ee6-885c-4064cb70f6e6", + "LogDriver": "awsfirelens" + } + ], + "LaunchType": "FARGATE" +} \ No newline at end of file diff --git a/detectors/node/opentelemetry-resource-detector-aws/test/detectors/test-resources/metadatav4-response-task-fargate.json b/detectors/node/opentelemetry-resource-detector-aws/test/detectors/test-resources/metadatav4-response-task-fargate.json new file mode 100644 index 0000000000..7979db708d --- /dev/null +++ b/detectors/node/opentelemetry-resource-detector-aws/test/detectors/test-resources/metadatav4-response-task-fargate.json @@ -0,0 +1,77 @@ +{ + "Cluster": "arn:aws:ecs:us-west-2:111122223333:cluster/default", + "TaskARN": "arn:aws:ecs:us-west-2:111122223333:task/default/e9028f8d5d8e4f258373e7b93ce9a3c3", + "Family": "curltest", + "Revision": "3", + "DesiredStatus": "RUNNING", + "KnownStatus": "RUNNING", + "Limits": { + "CPU": 0.25, + "Memory": 512 + }, + "PullStartedAt": "2020-10-08T20:47:16.053330955Z", + "PullStoppedAt": "2020-10-08T20:47:19.592684631Z", + "AvailabilityZone": "us-west-2a", + "Containers": [ + { + "DockerId": "e9028f8d5d8e4f258373e7b93ce9a3c3-2495160603", + "Name": "curl", + "DockerName": "curl", + "Image": "111122223333.dkr.ecr.us-west-2.amazonaws.com/curltest:latest", + "ImageID": "sha256:25f3695bedfb454a50f12d127839a68ad3caf91e451c1da073db34c542c4d2cb", + "Labels": { + "com.amazonaws.ecs.cluster": "arn:aws:ecs:us-west-2:111122223333:cluster/default", + "com.amazonaws.ecs.container-name": "curl", + "com.amazonaws.ecs.task-arn": "arn:aws:ecs:us-west-2:111122223333:task/default/e9028f8d5d8e4f258373e7b93ce9a3c3", + "com.amazonaws.ecs.task-definition-family": "curltest", + "com.amazonaws.ecs.task-definition-version": "3" + }, + "DesiredStatus": "RUNNING", + "KnownStatus": "RUNNING", + "Limits": { + "CPU": 10, + "Memory": 128 + }, + "CreatedAt": "2020-10-08T20:47:20.567813946Z", + "StartedAt": "2020-10-08T20:47:20.567813946Z", + "Type": "NORMAL", + "Networks": [ + { + "NetworkMode": "awsvpc", + "IPv4Addresses": [ + "192.0.2.3" + ], + "IPv6Addresses": [ + "2001:dB8:10b:1a00:32bf:a372:d80f:e958" + ], + "AttachmentIndex": 0, + "MACAddress": "02:b7:20:19:72:39", + "IPv4SubnetCIDRBlock": "192.0.2.0/24", + "IPv6SubnetCIDRBlock": "2600:1f13:10b:1a00::/64", + "DomainNameServers": [ + "192.0.2.2" + ], + "DomainNameSearchList": [ + "us-west-2.compute.internal" + ], + "PrivateDNSName": "ip-172-31-30-173.us-west-2.compute.internal", + "SubnetGatewayIpv4Address": "192.0.2.0/24" + } + ], + "ClockDrift": { + "ClockErrorBound": 0.5458234999999999, + "ReferenceTimestamp": "2021-09-07T16:57:44Z", + "ClockSynchronizationStatus": "SYNCHRONIZED" + }, + "ContainerARN": "arn:aws:ecs:us-west-2:111122223333:container/1bdcca8b-f905-4ee6-885c-4064cb70f6e6", + "LogOptions": { + "awslogs-create-group": "true", + "awslogs-group": "/ecs/containerlogs", + "awslogs-region": "us-west-2", + "awslogs-stream": "ecs/curl/e9028f8d5d8e4f258373e7b93ce9a3c3" + }, + "LogDriver": "awslogs" + } + ], + "LaunchType": "FARGATE" +} \ No newline at end of file