From 7fa1cf3f30cbc3e69634db68b928971c75cbfed6 Mon Sep 17 00:00:00 2001 From: Lo Date: Thu, 29 Oct 2020 00:23:09 -0700 Subject: [PATCH 01/26] feat: add EKS Resource detector --- .../src/detectors/AwsEksDetector.ts | 193 ++++++++++++++++++ .../test/detectors/AwsEksDetector.test.ts | 0 2 files changed, 193 insertions(+) create mode 100644 packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts create mode 100644 packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts diff --git a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts new file mode 100644 index 00000000000..a6fc9a7a29c --- /dev/null +++ b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts @@ -0,0 +1,193 @@ +/* + * 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 { + Detector, + Resource, + CONTAINER_RESOURCE, + K8S_RESOURCE, + ResourceDetectionConfigWithLogger, + } from '@opentelemetry/resources'; + import * as http from 'http'; + import * as fs from 'fs'; + import * as util from 'util'; + import * as tls from 'tls'; + + /** + * The AwsEksDetector can be used to detect if a process is running in AWS Elastic + * Kubernetes and return a {@link Resource} populated with data about the Kubernetes + * plugins of AWS X-Ray. Returns an empty Resource if detection fails. + * + * See https://docs.amazonaws.cn/en_us/xray/latest/devguide/xray-guide.pdf + * for more details about detecting information for Elastic Kubernetes plugins + */ + + class AwsEksDetector implements Detector { + readonly K8S_SVC_URL = "https://kubernetes.default.svc"; + readonly K8S_TOKEN_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/token"; + readonly K8S_CERT_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"; + readonly AUTH_CONFIGMAP_PATH ="/api/v1/namespaces/kube-system/configmaps/aws-auth"; + readonly CW_CONFIGMAP_PATH = "/api/v1/namespaces/amazon-cloudwatch/configmaps/cluster-info"; + readonly CONTAINER_ID_LENGTH = 64; + readonly DEFAULT_CGROUP_PATH = "/proc/self/cgroup"; + readonly MILLISECOND_TIME_OUT = 2000; + + private static readFileAsync = util.promisify(fs.readFile); + + async detect(config: ResourceDetectionConfigWithLogger): Promise { + if (!this._isEks(config)) { + config.logger.debug('AwsEcsDetector failed: Process is not running on Eks'); + return Resource.empty(); + } + + const containerId = await this._getContainerId(config); + const clusterName = await this._getClusterName(config); + + return !containerId && !clusterName + ? Resource.empty() + : new Resource({ + [K8S_RESOURCE.CLUSTER_NAME]: clusterName || '', + [CONTAINER_RESOURCE.ID]: containerId || '', + }); + } + + private async _isEks(config: ResourceDetectionConfigWithLogger): Promise { + fs.access(this.K8S_TOKEN_PATH, (err) => { + config.logger.debug('Not running on K8S'); + return false; + }); + fs.access(this.K8S_CERT_PATH, (err) => { + config.logger.debug('Not running on K8S'); + return false; + }); + const secureContext = tls.createSecureContext({ + ca: fs.readFileSync(this.K8S_CERT_PATH), + }); + const options = { + host: this.K8S_SVC_URL, + path: this.AUTH_CONFIGMAP_PATH, + method: 'GET', + timeout: this.MILLISECOND_TIME_OUT, + HEADERS: { + "Authorization" : this._getK8sCredHeader(config), + }, + ca: secureContext, + } + const awsAuth = this._fetchString(options); + return !!awsAuth; + } + + private async _getClusterName(config: ResourceDetectionConfigWithLogger): Promise { + const secureContext = tls.createSecureContext({ + ca: fs.readFileSync(this.K8S_CERT_PATH), + }); + const options = { + host: this.K8S_SVC_URL, + path: this.AUTH_CONFIGMAP_PATH, + method: 'GET', + timeout: this.MILLISECOND_TIME_OUT, + HEADERS: { + "Authorization" : this._getK8sCredHeader(config), + }, + ca: secureContext, + } + const clusterName = this._fetchString(options); + return clusterName; + } + + private async _getK8sCredHeader(config: ResourceDetectionConfigWithLogger): Promise { + try { + const content = await AwsEksDetector.readFileAsync( + this.K8S_TOKEN_PATH, + 'utf8' + ); + return "Bearer " + content; + } catch (e) { + config.logger.warn(`AwsEksDetector failed to read container ID: ${e.message}`); + } + return ""; + } + + /** + * Read container ID from cgroup file + * In EKS, 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 async _getContainerId(config: ResourceDetectionConfigWithLogger): Promise { + try { + const content = await AwsEksDetector.readFileAsync( + this.DEFAULT_CGROUP_PATH, + 'utf8' + ); + const splitData = content.trim().split('\n'); + for (const str of splitData) { + if (str.length > this.CONTAINER_ID_LENGTH) { + return str.substring(str.length - this.CONTAINER_ID_LENGTH); + } + } + } + catch (e) { + config.logger.warn(`AwsEksDetector failed to read container ID: ${e.message}`); + } + return undefined; + } + + /** + * Establishes an HTTP connection to AWS instance document url. + * If the application is running on an Eks instance, we should be able + * to get back a valid JSON document. Parses that document and stores + * the identity properties in a local map. + */ + private async _fetchString(options: http.RequestOptions): Promise { + return new Promise((resolve, reject) => { + const timeoutId = setTimeout(() => { + req.abort(); + reject(new Error('Eks metadata api request timed out.')); + }, 2000); + + const req = http.request(options, res => { + clearTimeout(timeoutId); + const { statusCode } = res; + res.setEncoding('utf8'); + let rawData = ''; + res.on('data', chunk => (rawData += chunk)); + res.on('end', () => { + if (statusCode && statusCode >= 200 && statusCode < 300) { + try { + resolve(rawData); + } catch (e) { + reject(e); + } + } else { + reject( + new Error('Failed to load page, status code: ' + statusCode) + ); + } + }); + }); + req.on('error', err => { + clearTimeout(timeoutId); + reject(err); + }); + req.end(); + }); + } +} + + export const awsEksDetector = new AwsEksDetector(); \ No newline at end of file diff --git a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts new file mode 100644 index 00000000000..e69de29bb2d From d3ad41cfa777fcda437be70f9c979caed535d68b Mon Sep 17 00:00:00 2001 From: Lo Date: Thu, 29 Oct 2020 12:38:59 -0700 Subject: [PATCH 02/26] test: add mock tests --- .../src/detectors/AwsEksDetector.ts | 12 +- .../src/detectors/index.ts | 1 + .../test/detectors/AwsEksDetector.test.ts | 108 ++++++++++++++++++ 3 files changed, 117 insertions(+), 4 deletions(-) diff --git a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts index a6fc9a7a29c..e92acb7dba5 100644 --- a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts +++ b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts @@ -36,7 +36,7 @@ import { * for more details about detecting information for Elastic Kubernetes plugins */ - class AwsEksDetector implements Detector { +export class AwsEksDetector implements Detector { readonly K8S_SVC_URL = "https://kubernetes.default.svc"; readonly K8S_TOKEN_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/token"; readonly K8S_CERT_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"; @@ -85,7 +85,9 @@ import { HEADERS: { "Authorization" : this._getK8sCredHeader(config), }, - ca: secureContext, + agentOptions: { + ca: secureContext, + } } const awsAuth = this._fetchString(options); return !!awsAuth; @@ -97,13 +99,15 @@ import { }); const options = { host: this.K8S_SVC_URL, - path: this.AUTH_CONFIGMAP_PATH, + path: this.CW_CONFIGMAP_PATH, method: 'GET', timeout: this.MILLISECOND_TIME_OUT, HEADERS: { "Authorization" : this._getK8sCredHeader(config), }, - ca: secureContext, + agentOptions: { + ca: secureContext, + } } const clusterName = this._fetchString(options); return clusterName; diff --git a/packages/opentelemetry-resource-detector-aws/src/detectors/index.ts b/packages/opentelemetry-resource-detector-aws/src/detectors/index.ts index 01986175c3e..e873f14e89e 100644 --- a/packages/opentelemetry-resource-detector-aws/src/detectors/index.ts +++ b/packages/opentelemetry-resource-detector-aws/src/detectors/index.ts @@ -17,3 +17,4 @@ export * from './AwsEc2Detector'; export * from './AwsBeanstalkDetector'; export * from './AwsEcsDetector'; +export * from './AwsEksDetector'; diff --git a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts index e69de29bb2d..8b7d4b99b57 100644 --- a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts +++ b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts @@ -0,0 +1,108 @@ +/* + * 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 nock from 'nock'; +import * as assert from 'assert'; +import { Resource } from '@opentelemetry/resources'; +import { awsEksDetector } from '../../src'; +import { + assertK8sResource, +} from '@opentelemetry/resources/test/util/resource-assertions'; +import { NoopLogger } from '@opentelemetry/core'; + +const K8S_SVC_URL = awsEksDetector.K8S_SVC_URL; +const K8S_TOKEN_PATH = awsEksDetector.K8S_TOKEN_PATH; +const K8S_CERT_PATH = awsEksDetector.K8S_CERT_PATH; +const AUTH_CONFIGMAP_PATH = awsEksDetector.AUTH_CONFIGMAP_PATH; +const CW_CONFIGMAP_PATH = awsEksDetector.CW_CONFIGMAP_PATH; + +const mockedClusterResponse = "my-cluster"; +const correctCgroupData = + 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm'; +const unexpectedCgroupdata = + 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'; +const mockedK8sCredentials = "Bearer 31ada4fd-adec-460c-809a-9e56ceb75269"; + +describe('awsEksDetector', () => { + beforeEach(() => { + nock.disableNetConnect(); + nock.cleanAll(); + }); + + afterEach(() => { + nock.enableNetConnect(); + }); +}); + +describe('on succesful request', () => { + it ('should return an aws_eks_instance_resource', async () => { + const scope = nock(K8S_SVC_URL) + .get(CW_CONFIGMAP_PATH) + .matchHeader("Authorizations", mockedK8sCredentials) + .reply(200, mockedClusterResponse) + const resource: Resource = await awsEksDetector.detect({ + logger: new NoopLogger(), + }); + + scope.done(); + + assert.ok(resource); + assertK8sResource(resource, { + clusterName: 'my-cluster' + }) +}); +}); + +describe('on unsuccessful request', () => { + it ('should throw when receiving error response code', async () => { + const expectedError = new Error('Failed to load page, status code: 404'); + const scope = nock(K8S_SVC_URL) + .get(CW_CONFIGMAP_PATH) + .matchHeader("Authorizations", mockedK8sCredentials) + .reply(404, () => new Error()); + + try { + await awsEksDetector.detect({ + logger: new NoopLogger(), + }); + assert.ok(false, 'Expected to throw'); + } catch (err) { + assert.deepStrictEqual(err, expectedError); + } + + scope.done(); + + it ('should throw when timed out', async () => { + const expectedError = new Error('Failed to load page, status code: 404'); + const scope = nock(K8S_SVC_URL) + .get(CW_CONFIGMAP_PATH) + .matchHeader("Authorizations", mockedK8sCredentials) + .delayConnection(2500) + .reply(200, () => mockedClusterResponse); + + try { + await awsEksDetector.detect({ + logger: new NoopLogger(), + }); + assert.ok(false, 'Expected to throw'); + } catch (err) { + assert.deepStrictEqual(err, expectedError); + } + + scope.done(); + }); +}); +}); \ No newline at end of file From 47ed09c49ea2981eca99ce39b4c5e61fb60ecc00 Mon Sep 17 00:00:00 2001 From: Lo Date: Thu, 29 Oct 2020 21:27:20 -0700 Subject: [PATCH 03/26] fix: use file read async instead of sync --- .../src/detectors/AwsEksDetector.ts | 55 +++++----- .../test/detectors/AwsEksDetector.test.ts | 102 +++++++----------- 2 files changed, 68 insertions(+), 89 deletions(-) diff --git a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts index e92acb7dba5..ed037dc8cab 100644 --- a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts +++ b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts @@ -47,35 +47,36 @@ export class AwsEksDetector implements Detector { readonly MILLISECOND_TIME_OUT = 2000; private static readFileAsync = util.promisify(fs.readFile); + private static fileAccessAsync = util.promisify(fs.access); async detect(config: ResourceDetectionConfigWithLogger): Promise { - if (!this._isEks(config)) { - config.logger.debug('AwsEcsDetector failed: Process is not running on Eks'); - return Resource.empty(); - } + try { + AwsEksDetector.fileAccessAsync(this.K8S_TOKEN_PATH); + AwsEksDetector.fileAccessAsync(this.K8S_CERT_PATH); + + if (!this._isEks(config)) { + config.logger.debug('AwsEcsDetector failed: Process is not running on Eks'); + return Resource.empty(); + } - const containerId = await this._getContainerId(config); - const clusterName = await this._getClusterName(config); + const containerId = await this._getContainerId(config); + const clusterName = await this._getClusterName(config); - return !containerId && !clusterName - ? Resource.empty() - : new Resource({ - [K8S_RESOURCE.CLUSTER_NAME]: clusterName || '', - [CONTAINER_RESOURCE.ID]: containerId || '', - }); + return !containerId + ? Resource.empty() + : new Resource({ + [K8S_RESOURCE.CLUSTER_NAME]: clusterName || '', + [CONTAINER_RESOURCE.ID]: containerId || '', + }); + } catch (e) { + config.logger.debug('Not running on K8S'); + return Resource.empty(); + } } private async _isEks(config: ResourceDetectionConfigWithLogger): Promise { - fs.access(this.K8S_TOKEN_PATH, (err) => { - config.logger.debug('Not running on K8S'); - return false; - }); - fs.access(this.K8S_CERT_PATH, (err) => { - config.logger.debug('Not running on K8S'); - return false; - }); const secureContext = tls.createSecureContext({ - ca: fs.readFileSync(this.K8S_CERT_PATH), + ca: JSON.stringify(AwsEksDetector.readFileAsync(this.K8S_CERT_PATH)), }); const options = { host: this.K8S_SVC_URL, @@ -95,7 +96,7 @@ export class AwsEksDetector implements Detector { private async _getClusterName(config: ResourceDetectionConfigWithLogger): Promise { const secureContext = tls.createSecureContext({ - ca: fs.readFileSync(this.K8S_CERT_PATH), + ca: JSON.stringify(AwsEksDetector.readFileAsync(this.K8S_CERT_PATH)), }); const options = { host: this.K8S_SVC_URL, @@ -109,8 +110,7 @@ export class AwsEksDetector implements Detector { ca: secureContext, } } - const clusterName = this._fetchString(options); - return clusterName; + return this._fetchString(options); } private async _getK8sCredHeader(config: ResourceDetectionConfigWithLogger): Promise { @@ -135,18 +135,17 @@ export class AwsEksDetector implements Detector { */ private async _getContainerId(config: ResourceDetectionConfigWithLogger): Promise { try { - const content = await AwsEksDetector.readFileAsync( + const rawData = await AwsEksDetector.readFileAsync( this.DEFAULT_CGROUP_PATH, 'utf8' ); - const splitData = content.trim().split('\n'); + 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); } } - } - catch (e) { + } catch (e) { config.logger.warn(`AwsEksDetector failed to read container ID: ${e.message}`); } return undefined; diff --git a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts index 8b7d4b99b57..3a26474ca74 100644 --- a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts +++ b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts @@ -15,11 +15,12 @@ */ import * as nock from 'nock'; +import * as sinon from 'sinon'; import * as assert from 'assert'; import { Resource } from '@opentelemetry/resources'; -import { awsEksDetector } from '../../src'; +import { awsEksDetector, AwsEksDetector } from '../../src'; import { - assertK8sResource, + assertK8sResource, assertContainerResource, } from '@opentelemetry/resources/test/util/resource-assertions'; import { NoopLogger } from '@opentelemetry/core'; @@ -29,80 +30,59 @@ const K8S_CERT_PATH = awsEksDetector.K8S_CERT_PATH; const AUTH_CONFIGMAP_PATH = awsEksDetector.AUTH_CONFIGMAP_PATH; const CW_CONFIGMAP_PATH = awsEksDetector.CW_CONFIGMAP_PATH; -const mockedClusterResponse = "my-cluster"; -const correctCgroupData = +describe('awsEksDetector', () => { + let sandbox: sinon.SinonSandbox; + let readStub, fileStub; + const correctCgroupData = 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm'; -const unexpectedCgroupdata = - 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'; -const mockedK8sCredentials = "Bearer 31ada4fd-adec-460c-809a-9e56ceb75269"; + const mockedClusterResponse = "my-cluster"; + const mockedK8sCredentials = "Bearer 31ada4fd-adec-460c-809a-9e56ceb75269"; -describe('awsEksDetector', () => { beforeEach(() => { nock.disableNetConnect(); nock.cleanAll(); + sandbox = sinon.createSandbox(); }); afterEach(() => { nock.enableNetConnect(); + sandbox.restore(); }); -}); - -describe('on succesful request', () => { - it ('should return an aws_eks_instance_resource', async () => { - const scope = nock(K8S_SVC_URL) - .get(CW_CONFIGMAP_PATH) - .matchHeader("Authorizations", mockedK8sCredentials) - .reply(200, mockedClusterResponse) - const resource: Resource = await awsEksDetector.detect({ - logger: new NoopLogger(), - }); - - scope.done(); - - assert.ok(resource); - assertK8sResource(resource, { - clusterName: 'my-cluster' - }) -}); -}); - -describe('on unsuccessful request', () => { - it ('should throw when receiving error response code', async () => { - const expectedError = new Error('Failed to load page, status code: 404'); - const scope = nock(K8S_SVC_URL) - .get(CW_CONFIGMAP_PATH) - .matchHeader("Authorizations", mockedK8sCredentials) - .reply(404, () => new Error()); - - try { - await awsEksDetector.detect({ - logger: new NoopLogger(), - }); - assert.ok(false, 'Expected to throw'); - } catch (err) { - assert.deepStrictEqual(err, expectedError); - } - scope.done(); + describe('on succesful request', () => { + it ('should return an aws_eks_instance_resource', async () => { + fileStub = sandbox + .stub(AwsEksDetector, 'fileAccessAsync' as any) + .resolves(); + readStub = sinon.stub(AwsEksDetector, 'readFileAsync' as any); + readStub.onCall(1).resolves(correctCgroupData); + readStub.onCall(2).returns(mockedK8sCredentials); + readStub.onCall(3).returns(mockedK8sCredentials); - it ('should throw when timed out', async () => { - const expectedError = new Error('Failed to load page, status code: 404'); - const scope = nock(K8S_SVC_URL) + const scope = nock(K8S_SVC_URL) + .get(AUTH_CONFIGMAP_PATH) + .matchHeader('Authorizations', mockedK8sCredentials) + .reply(200, () => true) .get(CW_CONFIGMAP_PATH) - .matchHeader("Authorizations", mockedK8sCredentials) - .delayConnection(2500) + .matchHeader('Authorizations', mockedK8sCredentials) .reply(200, () => mockedClusterResponse); - - try { - await awsEksDetector.detect({ - logger: new NoopLogger(), - }); - assert.ok(false, 'Expected to throw'); - } catch (err) { - assert.deepStrictEqual(err, expectedError); - } + + const resource: Resource = await awsEksDetector.detect({ + logger: new NoopLogger(), + }); scope.done(); + + sandbox.assert.calledTwice(fileStub); + sandbox.assert.calledThrice(readStub); + + assert.ok(resource); + assertK8sResource(resource, { + clusterName: 'my-cluster', + }) + assertContainerResource(resource, { + id: 'bcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm', + }) }); -}); + }); }); \ No newline at end of file From 2f4aeb27a57925e9e04f83899ccf9aaa3ded5f92 Mon Sep 17 00:00:00 2001 From: Lo Date: Fri, 30 Oct 2020 04:54:06 -0700 Subject: [PATCH 04/26] feat: update implementation of https requests --- .../src/detectors/AwsEksDetector.ts | 31 +-- .../test/detectors/AwsEksDetector.test.ts | 184 +++++++++++++++--- 2 files changed, 166 insertions(+), 49 deletions(-) diff --git a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts index ed037dc8cab..f85bdb9e0b8 100644 --- a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts +++ b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts @@ -22,10 +22,9 @@ import { K8S_RESOURCE, ResourceDetectionConfigWithLogger, } from '@opentelemetry/resources'; - import * as http from 'http'; + import * as https from 'https'; import * as fs from 'fs'; import * as util from 'util'; - import * as tls from 'tls'; /** * The AwsEksDetector can be used to detect if a process is running in AWS Elastic @@ -51,8 +50,8 @@ export class AwsEksDetector implements Detector { async detect(config: ResourceDetectionConfigWithLogger): Promise { try { - AwsEksDetector.fileAccessAsync(this.K8S_TOKEN_PATH); - AwsEksDetector.fileAccessAsync(this.K8S_CERT_PATH); + await AwsEksDetector.fileAccessAsync(this.K8S_TOKEN_PATH); + await AwsEksDetector.fileAccessAsync(this.K8S_CERT_PATH); if (!this._isEks(config)) { config.logger.debug('AwsEcsDetector failed: Process is not running on Eks'); @@ -62,42 +61,34 @@ export class AwsEksDetector implements Detector { const containerId = await this._getContainerId(config); const clusterName = await this._getClusterName(config); - return !containerId + return !containerId && !clusterName ? Resource.empty() : new Resource({ [K8S_RESOURCE.CLUSTER_NAME]: clusterName || '', [CONTAINER_RESOURCE.ID]: containerId || '', }); } catch (e) { - config.logger.debug('Not running on K8S'); + config.logger.warn('Not running on K8S'); return Resource.empty(); } } private async _isEks(config: ResourceDetectionConfigWithLogger): Promise { - const secureContext = tls.createSecureContext({ - ca: JSON.stringify(AwsEksDetector.readFileAsync(this.K8S_CERT_PATH)), - }); const options = { - host: this.K8S_SVC_URL, + hostname: this.K8S_SVC_URL, path: this.AUTH_CONFIGMAP_PATH, method: 'GET', timeout: this.MILLISECOND_TIME_OUT, HEADERS: { "Authorization" : this._getK8sCredHeader(config), }, - agentOptions: { - ca: secureContext, - } + ca: JSON.stringify(AwsEksDetector.readFileAsync(this.K8S_CERT_PATH)), } const awsAuth = this._fetchString(options); return !!awsAuth; } private async _getClusterName(config: ResourceDetectionConfigWithLogger): Promise { - const secureContext = tls.createSecureContext({ - ca: JSON.stringify(AwsEksDetector.readFileAsync(this.K8S_CERT_PATH)), - }); const options = { host: this.K8S_SVC_URL, path: this.CW_CONFIGMAP_PATH, @@ -106,9 +97,7 @@ export class AwsEksDetector implements Detector { HEADERS: { "Authorization" : this._getK8sCredHeader(config), }, - agentOptions: { - ca: secureContext, - } + ca: JSON.stringify(AwsEksDetector.readFileAsync(this.K8S_CERT_PATH)), } return this._fetchString(options); } @@ -157,14 +146,14 @@ export class AwsEksDetector implements Detector { * to get back a valid JSON document. Parses that document and stores * the identity properties in a local map. */ - private async _fetchString(options: http.RequestOptions): Promise { + private async _fetchString(options: https.RequestOptions): Promise { return new Promise((resolve, reject) => { const timeoutId = setTimeout(() => { req.abort(); reject(new Error('Eks metadata api request timed out.')); }, 2000); - const req = http.request(options, res => { + const req = https.request(options, res => { clearTimeout(timeoutId); const { statusCode } = res; res.setEncoding('utf8'); diff --git a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts index 3a26474ca74..de930f04091 100644 --- a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts +++ b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts @@ -20,27 +20,21 @@ import * as assert from 'assert'; import { Resource } from '@opentelemetry/resources'; import { awsEksDetector, AwsEksDetector } from '../../src'; import { - assertK8sResource, assertContainerResource, + assertK8sResource, assertContainerResource, assertEmptyResource, } from '@opentelemetry/resources/test/util/resource-assertions'; import { NoopLogger } from '@opentelemetry/core'; -const K8S_SVC_URL = awsEksDetector.K8S_SVC_URL; -const K8S_TOKEN_PATH = awsEksDetector.K8S_TOKEN_PATH; -const K8S_CERT_PATH = awsEksDetector.K8S_CERT_PATH; -const AUTH_CONFIGMAP_PATH = awsEksDetector.AUTH_CONFIGMAP_PATH; -const CW_CONFIGMAP_PATH = awsEksDetector.CW_CONFIGMAP_PATH; - describe('awsEksDetector', () => { + const errorMsg = { + fileNotFoundError: new Error('cannot find cgroup file'), + }; let sandbox: sinon.SinonSandbox; - let readStub, fileStub; + let readStub, fileStub, isEksStub, getClusterStub, getContainerStub, fetchStub; const correctCgroupData = 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm'; const mockedClusterResponse = "my-cluster"; - const mockedK8sCredentials = "Bearer 31ada4fd-adec-460c-809a-9e56ceb75269"; beforeEach(() => { - nock.disableNetConnect(); - nock.cleanAll(); sandbox = sinon.createSandbox(); }); @@ -49,40 +43,174 @@ describe('awsEksDetector', () => { sandbox.restore(); }); - describe('on succesful request', () => { + describe('on successful request', () => { it ('should return an aws_eks_instance_resource', async () => { fileStub = sandbox .stub(AwsEksDetector, 'fileAccessAsync' as any) .resolves(); - readStub = sinon.stub(AwsEksDetector, 'readFileAsync' as any); - readStub.onCall(1).resolves(correctCgroupData); - readStub.onCall(2).returns(mockedK8sCredentials); - readStub.onCall(3).returns(mockedK8sCredentials); - - const scope = nock(K8S_SVC_URL) - .get(AUTH_CONFIGMAP_PATH) - .matchHeader('Authorizations', mockedK8sCredentials) - .reply(200, () => true) - .get(CW_CONFIGMAP_PATH) - .matchHeader('Authorizations', mockedK8sCredentials) - .reply(200, () => mockedClusterResponse); + readStub = sandbox.stub(AwsEksDetector, 'readFileAsync' as any); + readStub.resolves(correctCgroupData); + + fetchStub = sandbox.stub(awsEksDetector, '_fetchString' as any) + .onCall(0).resolves('aws-auth'); + getClusterStub = sandbox.stub(awsEksDetector, '_getClusterName' as any).resolves(mockedClusterResponse); const resource: Resource = await awsEksDetector.detect({ logger: new NoopLogger(), }); - scope.done(); + assert.ok(resource); + assertK8sResource(resource, { + clusterName: 'my-cluster', + }) + assertContainerResource(resource, { + id: 'bcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm', + }) + }); + + it ('should return a resource with cluster Name attribute without a container Id', async () => { + fileStub = sandbox + .stub(AwsEksDetector, 'fileAccessAsync' as any) + .resolves(''); + readStub = sandbox.stub(AwsEksDetector, 'readFileAsync' as any) + .resolves(correctCgroupData); + + isEksStub = sandbox.stub(awsEksDetector, '_isEks' as any).resolves(true); + getContainerStub = sandbox.stub(awsEksDetector, '_getContainerId' as any).resolves(''); + getClusterStub = sandbox.stub(awsEksDetector, '_getClusterName' as any).resolves(mockedClusterResponse); - sandbox.assert.calledTwice(fileStub); - sandbox.assert.calledThrice(readStub); + const resource: Resource = await awsEksDetector.detect({ + logger: new NoopLogger(), + }); assert.ok(resource); + assertContainerResource(resource, { + id: '', + }); assertK8sResource(resource, { clusterName: 'my-cluster', }) + }); + + it ('should return a resource with container ID attribute without a clusterName', async () => { + fileStub = sandbox + .stub(AwsEksDetector, 'fileAccessAsync' as any) + .resolves(''); + readStub = sandbox.stub(AwsEksDetector, 'readFileAsync' as any) + .resolves(correctCgroupData); + + isEksStub = sandbox.stub(awsEksDetector, '_isEks' as any).resolves(true); + getClusterStub = sandbox.stub(awsEksDetector, '_getClusterName' as any).resolves(''); + + const resource: Resource = await awsEksDetector.detect({ + logger: new NoopLogger(), + }); + + assert.ok(resource); + assertK8sResource(resource, { + clusterName: '', + }) assertContainerResource(resource, { - id: 'bcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm', + id: 'bcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm' + }); + }); + + it ('should return a resource with clusterName attribute when cgroup file does not contain valid Container ID', async () => { + fileStub = sandbox + .stub(AwsEksDetector, 'fileAccessAsync' as any) + .resolves(''); + readStub = sandbox.stub(AwsEksDetector, 'readFileAsync' as any) + .resolves(''); + + isEksStub = sandbox.stub(awsEksDetector, '_isEks' as any).resolves(true); + getClusterStub = sandbox.stub(awsEksDetector, '_getClusterName' as any).resolves(mockedClusterResponse); + + const resource: Resource = await awsEksDetector.detect({ + logger: new NoopLogger(), + }); + + assert.ok(resource); + assert.ok(resource); + assertK8sResource(resource, { + clusterName: 'my-cluster' }) + assertContainerResource(resource, { + id: '' + }); + }); + + it ('should return a resource with clusterName attribute when cgroup file does not exist', async () => { + fileStub = sandbox + .stub(AwsEksDetector, 'fileAccessAsync' as any) + .resolves(''); + readStub = sandbox.stub(AwsEksDetector, 'readFileAsync' as any) + .rejects(errorMsg.fileNotFoundError); + + isEksStub = sandbox.stub(awsEksDetector, '_isEks' as any).resolves(true); + getClusterStub = sandbox.stub(awsEksDetector, '_getClusterName' as any).resolves(mockedClusterResponse); + + const resource: Resource = await awsEksDetector.detect({ + logger: new NoopLogger(), + }); + + assert.ok(resource); + assert.ok(resource); + assertK8sResource(resource, { + clusterName: 'my-cluster' + }) + assertContainerResource(resource, { + id: '' + }); + }); + + it ('should return an empty resource when not running on Eks', async () => { + fileStub = sandbox + .stub(AwsEksDetector, 'fileAccessAsync' as any) + .resolves(''); + + isEksStub = sandbox.stub(awsEksDetector, '_isEks' as any).resolves(false); + + const resource: Resource = await awsEksDetector.detect({ + logger: new NoopLogger(), + }); + + assert.ok(resource); + assertEmptyResource(resource); + }); + + it ('should return an empty resource when k8s file does not exist', async () => { + const errorMsg = { + fileNotFoundError: new Error('cannot file k8s token file'), + }; + fileStub = sandbox + .stub(AwsEksDetector, 'fileAccessAsync' as any) + .rejects(errorMsg.fileNotFoundError); + + const resource: Resource = await awsEksDetector.detect({ + logger: new NoopLogger(), + }); + + assert.ok(resource); + assertEmptyResource(resource); + }); + + it ('should return an empty resource when containerId and clusterName are invalid', async () => { + fileStub = sandbox + .stub(AwsEksDetector, 'fileAccessAsync' as any) + .resolves(''); + readStub = sandbox.stub(AwsEksDetector, 'readFileAsync' as any) + .resolves(correctCgroupData); + + isEksStub = sandbox.stub(awsEksDetector, '_isEks' as any).resolves(true); + getContainerStub = sandbox.stub(awsEksDetector, '_getContainerId' as any).resolves(''); + getClusterStub = sandbox.stub(awsEksDetector, '_getClusterName' as any).resolves(''); + + const resource: Resource = await awsEksDetector.detect({ + logger: new NoopLogger(), + }); + + assert.ok(resource); + assertEmptyResource(resource); }); }); }); \ No newline at end of file From e19a78164219591b303ef966d673d27571219d19 Mon Sep 17 00:00:00 2001 From: Lo Date: Fri, 6 Nov 2020 02:12:02 -0800 Subject: [PATCH 05/26] test: added more concrete unit tests and comments --- .../src/detectors/AwsEksDetector.ts | 256 ++++++++++------- .../test/detectors/AwsEksDetector.test.ts | 269 ++++++++++++------ 2 files changed, 333 insertions(+), 192 deletions(-) diff --git a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts index f85bdb9e0b8..b379253f145 100644 --- a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts +++ b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts @@ -14,19 +14,18 @@ * limitations under the License. */ - import { - Detector, - Resource, - CONTAINER_RESOURCE, - K8S_RESOURCE, - ResourceDetectionConfigWithLogger, - } from '@opentelemetry/resources'; - import * as https from 'https'; - import * as fs from 'fs'; - import * as util from 'util'; + Detector, + Resource, + CONTAINER_RESOURCE, + K8S_RESOURCE, + ResourceDetectionConfigWithLogger, +} from '@opentelemetry/resources'; +import * as https from 'https'; +import * as fs from 'fs'; +import * as util from 'util'; - /** +/** * The AwsEksDetector can be used to detect if a process is running in AWS Elastic * Kubernetes and return a {@link Resource} populated with data about the Kubernetes * plugins of AWS X-Ray. Returns an empty Resource if detection fails. @@ -34,111 +33,148 @@ import { * See https://docs.amazonaws.cn/en_us/xray/latest/devguide/xray-guide.pdf * for more details about detecting information for Elastic Kubernetes plugins */ - + export class AwsEksDetector implements Detector { - readonly K8S_SVC_URL = "https://kubernetes.default.svc"; - readonly K8S_TOKEN_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/token"; - readonly K8S_CERT_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"; - readonly AUTH_CONFIGMAP_PATH ="/api/v1/namespaces/kube-system/configmaps/aws-auth"; - readonly CW_CONFIGMAP_PATH = "/api/v1/namespaces/amazon-cloudwatch/configmaps/cluster-info"; - readonly CONTAINER_ID_LENGTH = 64; - readonly DEFAULT_CGROUP_PATH = "/proc/self/cgroup"; - readonly MILLISECOND_TIME_OUT = 2000; + readonly K8S_SVC_URL = 'kubernetes.default.svc'; + readonly K8S_TOKEN_PATH = + '/var/run/secrets/kubernetes.io/serviceaccount/token'; + readonly K8S_CERT_PATH = + '/var/run/secrets/kubernetes.io/serviceaccount/ca.crt'; + readonly AUTH_CONFIGMAP_PATH = + '/api/v1/namespaces/kube-system/configmaps/aws-auth'; + readonly CW_CONFIGMAP_PATH = + '/api/v1/namespaces/amazon-cloudwatch/configmaps/cluster-info'; + readonly CONTAINER_ID_LENGTH = 64; + readonly DEFAULT_CGROUP_PATH = '/proc/self/cgroup'; + readonly MILLISECOND_TIME_OUT = 2000; - private static readFileAsync = util.promisify(fs.readFile); - private static fileAccessAsync = util.promisify(fs.access); - - async detect(config: ResourceDetectionConfigWithLogger): Promise { - try { - await AwsEksDetector.fileAccessAsync(this.K8S_TOKEN_PATH); - await AwsEksDetector.fileAccessAsync(this.K8S_CERT_PATH); - - if (!this._isEks(config)) { - config.logger.debug('AwsEcsDetector failed: Process is not running on Eks'); - return Resource.empty(); - } - - const containerId = await this._getContainerId(config); - const clusterName = await this._getClusterName(config); + private static readFileAsync = util.promisify(fs.readFile); + private static fileAccessAsync = util.promisify(fs.access); - return !containerId && !clusterName - ? Resource.empty() - : new Resource({ - [K8S_RESOURCE.CLUSTER_NAME]: clusterName || '', - [CONTAINER_RESOURCE.ID]: containerId || '', - }); - } catch (e) { - config.logger.warn('Not running on K8S'); + /** + * The AWSEksDetector can be used to detect if a process is running in AWS + * Eks and returns a promise containing a {@link Resource} + * populated with instance metadata. Returns a promise containing an + * empty {@link Resource} if the connection to kubernetes process + * or aws config maps fails + * @param config The resource detection config with a required logger + */ + async detect(config: ResourceDetectionConfigWithLogger): Promise { + try { + await AwsEksDetector.fileAccessAsync(this.K8S_TOKEN_PATH); + const k8Scert = await AwsEksDetector.readFileAsync(this.K8S_CERT_PATH); + + if (!this._isEks(config, k8Scert)) { return Resource.empty(); } - } - private async _isEks(config: ResourceDetectionConfigWithLogger): Promise { - const options = { - hostname: this.K8S_SVC_URL, - path: this.AUTH_CONFIGMAP_PATH, - method: 'GET', - timeout: this.MILLISECOND_TIME_OUT, - HEADERS: { - "Authorization" : this._getK8sCredHeader(config), - }, - ca: JSON.stringify(AwsEksDetector.readFileAsync(this.K8S_CERT_PATH)), - } - const awsAuth = this._fetchString(options); - return !!awsAuth; + const containerId = await this._getContainerId(config); + const clusterName = await this._getClusterName(config, k8Scert); + + return !containerId && !clusterName + ? Resource.empty() + : new Resource({ + [K8S_RESOURCE.CLUSTER_NAME]: clusterName || '', + [CONTAINER_RESOURCE.ID]: containerId || '', + }); + } catch (e) { + config.logger.warn('Not running on K8S'); + return Resource.empty(); } + } - private async _getClusterName(config: ResourceDetectionConfigWithLogger): Promise { - const options = { - host: this.K8S_SVC_URL, - path: this.CW_CONFIGMAP_PATH, - method: 'GET', - timeout: this.MILLISECOND_TIME_OUT, - HEADERS: { - "Authorization" : this._getK8sCredHeader(config), - }, - ca: JSON.stringify(AwsEksDetector.readFileAsync(this.K8S_CERT_PATH)), - } - return this._fetchString(options); - } + /** + * Attempts to make a connection to AWS Config map which will + * determine whether the process is running on an Eks + * process if the config map is empty or not + * @param config The resource detection config with a required logger + */ + private async _isEks( + config: ResourceDetectionConfigWithLogger, + k8scert: Buffer + ): Promise { + const options = { + hostname: this.K8S_SVC_URL, + path: this.AUTH_CONFIGMAP_PATH, + method: 'GET', + timeout: this.MILLISECOND_TIME_OUT, + headers: { + Authorization: await this._getK8sCredHeader(config), + }, + ca: k8scert, + }; + return !!(await this._fetchString(options)); + } - private async _getK8sCredHeader(config: ResourceDetectionConfigWithLogger): Promise { - try { - const content = await AwsEksDetector.readFileAsync( - this.K8S_TOKEN_PATH, - 'utf8' - ); - return "Bearer " + content; - } catch (e) { - config.logger.warn(`AwsEksDetector failed to read container ID: ${e.message}`); - } - return ""; + /** + * Attempts to make a connection to Amazon Cloudwatch + * Config Maps to grab cluster name + * @param config The resource detection config with a required logger + */ + private async _getClusterName( + config: ResourceDetectionConfigWithLogger, + k8scert: Buffer + ): Promise { + const options = { + host: this.K8S_SVC_URL, + path: this.CW_CONFIGMAP_PATH, + method: 'GET', + timeout: this.MILLISECOND_TIME_OUT, + headers: { + Authorization: await this._getK8sCredHeader(config), + }, + ca: k8scert, + }; + return await this._fetchString(options); + } + /** + * Reads the Kubernetes token path and returns kubernetes + * credential header + * @param config The resource detection config with a required logger + */ + private async _getK8sCredHeader( + config: ResourceDetectionConfigWithLogger + ): Promise { + try { + const content = await AwsEksDetector.readFileAsync( + this.K8S_TOKEN_PATH, + 'utf8' + ); + return 'Bearer ' + content; + } catch (e) { + config.logger.warn('Unable to load K8s client token.', e); } + return ''; + } - /** - * Read container ID from cgroup file - * In EKS, 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 async _getContainerId(config: ResourceDetectionConfigWithLogger): Promise { - try { - const rawData = await AwsEksDetector.readFileAsync( - this.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); - } - } - } catch (e) { - config.logger.warn(`AwsEksDetector failed to read container ID: ${e.message}`); - } - return undefined; + /** + * Read container ID from cgroup file + * In EKS, 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 async _getContainerId( + config: ResourceDetectionConfigWithLogger + ): Promise { + try { + const rawData = await AwsEksDetector.readFileAsync( + this.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); + } } + } catch (e) { + config.logger.warn( + `AwsEksDetector failed to read container ID: ${e.message}` + ); + } + return undefined; + } /** * Establishes an HTTP connection to AWS instance document url. @@ -146,8 +182,10 @@ export class AwsEksDetector implements Detector { * to get back a valid JSON document. Parses that document and stores * the identity properties in a local map. */ - private async _fetchString(options: https.RequestOptions): Promise { - return new Promise((resolve, reject) => { + private async _fetchString( + options: https.RequestOptions + ): Promise { + return await new Promise((resolve, reject) => { const timeoutId = setTimeout(() => { req.abort(); reject(new Error('Eks metadata api request timed out.')); @@ -179,7 +217,7 @@ export class AwsEksDetector implements Detector { }); req.end(); }); - } + } } - - export const awsEksDetector = new AwsEksDetector(); \ No newline at end of file + +export const awsEksDetector = new AwsEksDetector(); diff --git a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts index de930f04091..b35be60f479 100644 --- a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts +++ b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts @@ -20,155 +20,203 @@ import * as assert from 'assert'; import { Resource } from '@opentelemetry/resources'; import { awsEksDetector, AwsEksDetector } from '../../src'; import { - assertK8sResource, assertContainerResource, assertEmptyResource, + assertK8sResource, + assertContainerResource, + assertEmptyResource, } from '@opentelemetry/resources/test/util/resource-assertions'; import { NoopLogger } from '@opentelemetry/core'; +const K8S_SVC_URL = awsEksDetector.K8S_SVC_URL; +const AUTH_CONFIGMAP_PATH = awsEksDetector.AUTH_CONFIGMAP_PATH; +const CW_CONFIGMAP_PATH = awsEksDetector.CW_CONFIGMAP_PATH; + describe('awsEksDetector', () => { const errorMsg = { fileNotFoundError: new Error('cannot find cgroup file'), }; let sandbox: sinon.SinonSandbox; - let readStub, fileStub, isEksStub, getClusterStub, getContainerStub, fetchStub; + let readStub, fileStub, getCredStub; const correctCgroupData = 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm'; - const mockedClusterResponse = "my-cluster"; + const mockedClusterResponse = 'my-cluster'; + const mockedAwsAuth = 'my-auth'; + const k8s_token = 'Bearer 31ada4fd-adec-460c-809a-9e56ceb75269'; beforeEach(() => { sandbox = sinon.createSandbox(); + nock.disableNetConnect(); + nock.cleanAll(); }); afterEach(() => { - nock.enableNetConnect(); sandbox.restore(); + nock.enableNetConnect(); }); describe('on successful request', () => { - it ('should return an aws_eks_instance_resource', async () => { + it('should return an aws_eks_instance_resource', async () => { fileStub = sandbox .stub(AwsEksDetector, 'fileAccessAsync' as any) .resolves(); - readStub = sandbox.stub(AwsEksDetector, 'readFileAsync' as any); - readStub.resolves(correctCgroupData); + readStub = sandbox + .stub(AwsEksDetector, 'readFileAsync' as any) + .resolves(correctCgroupData); + getCredStub = sandbox + .stub(awsEksDetector, '_getK8sCredHeader' as any) + .resolves(k8s_token); + const scope = nock('https://' + K8S_SVC_URL) + .persist() + .get(AUTH_CONFIGMAP_PATH) + .matchHeader('Authorization', k8s_token) + .reply(200, () => mockedAwsAuth) + .get(CW_CONFIGMAP_PATH) + .matchHeader('Authorization', k8s_token) + .reply(200, () => mockedClusterResponse); - fetchStub = sandbox.stub(awsEksDetector, '_fetchString' as any) - .onCall(0).resolves('aws-auth'); - getClusterStub = sandbox.stub(awsEksDetector, '_getClusterName' as any).resolves(mockedClusterResponse); - const resource: Resource = await awsEksDetector.detect({ logger: new NoopLogger(), }); + scope.done(); + assert.ok(resource); assertK8sResource(resource, { clusterName: 'my-cluster', - }) + }); assertContainerResource(resource, { id: 'bcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm', - }) + }); }); - it ('should return a resource with cluster Name attribute without a container Id', async () => { + it('should return a resource with clusterName attribute without cgroup file', async () => { fileStub = sandbox .stub(AwsEksDetector, 'fileAccessAsync' as any) - .resolves(''); - readStub = sandbox.stub(AwsEksDetector, 'readFileAsync' as any) - .resolves(correctCgroupData); - - isEksStub = sandbox.stub(awsEksDetector, '_isEks' as any).resolves(true); - getContainerStub = sandbox.stub(awsEksDetector, '_getContainerId' as any).resolves(''); - getClusterStub = sandbox.stub(awsEksDetector, '_getClusterName' as any).resolves(mockedClusterResponse); - + .resolves(); + readStub = sandbox + .stub(AwsEksDetector, 'readFileAsync' as any) + .onSecondCall() + .rejects(errorMsg.fileNotFoundError); + getCredStub = sandbox + .stub(awsEksDetector, '_getK8sCredHeader' as any) + .resolves(k8s_token); + const scope = nock('https://' + K8S_SVC_URL) + .persist() + .get(AUTH_CONFIGMAP_PATH) + .matchHeader('Authorization', k8s_token) + .reply(200, () => mockedAwsAuth) + .get(CW_CONFIGMAP_PATH) + .matchHeader('Authorization', k8s_token) + .reply(200, () => mockedClusterResponse); + const resource: Resource = await awsEksDetector.detect({ - logger: new NoopLogger(), + logger: new NoopLogger(), }); + scope.done(); + assert.ok(resource); - assertContainerResource(resource, { - id: '', - }); assertK8sResource(resource, { clusterName: 'my-cluster', - }) + }); }); - it ('should return a resource with container ID attribute without a clusterName', async () => { + it('should return a resource with container ID attribute without a clusterName', async () => { fileStub = sandbox .stub(AwsEksDetector, 'fileAccessAsync' as any) - .resolves(''); - readStub = sandbox.stub(AwsEksDetector, 'readFileAsync' as any) + .resolves(); + readStub = sandbox + .stub(AwsEksDetector, 'readFileAsync' as any) .resolves(correctCgroupData); + getCredStub = sandbox + .stub(awsEksDetector, '_getK8sCredHeader' as any) + .resolves(k8s_token); + const scope = nock('https://' + K8S_SVC_URL) + .persist() + .get(AUTH_CONFIGMAP_PATH) + .matchHeader('Authorization', k8s_token) + .reply(200, () => mockedAwsAuth) + .get(CW_CONFIGMAP_PATH) + .matchHeader('Authorization', k8s_token) + .reply(200, () => ''); - isEksStub = sandbox.stub(awsEksDetector, '_isEks' as any).resolves(true); - getClusterStub = sandbox.stub(awsEksDetector, '_getClusterName' as any).resolves(''); - const resource: Resource = await awsEksDetector.detect({ - logger: new NoopLogger(), + logger: new NoopLogger(), }); + scope.done(); + assert.ok(resource); - assertK8sResource(resource, { - clusterName: '', - }) assertContainerResource(resource, { - id: 'bcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm' + id: 'bcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm', }); }); - it ('should return a resource with clusterName attribute when cgroup file does not contain valid Container ID', async () => { + it('should return a resource with clusterName attribute when cgroup file does not contain valid Container ID', async () => { fileStub = sandbox .stub(AwsEksDetector, 'fileAccessAsync' as any) + .resolves(); + readStub = sandbox + .stub(AwsEksDetector, 'readFileAsync' as any) + .onSecondCall() .resolves(''); - readStub = sandbox.stub(AwsEksDetector, 'readFileAsync' as any) - .resolves(''); - - isEksStub = sandbox.stub(awsEksDetector, '_isEks' as any).resolves(true); - getClusterStub = sandbox.stub(awsEksDetector, '_getClusterName' as any).resolves(mockedClusterResponse); - + getCredStub = sandbox + .stub(awsEksDetector, '_getK8sCredHeader' as any) + .resolves(k8s_token); + const scope = nock('https://' + K8S_SVC_URL) + .persist() + .get(AUTH_CONFIGMAP_PATH) + .matchHeader('Authorization', k8s_token) + .reply(200, () => mockedAwsAuth) + .get(CW_CONFIGMAP_PATH) + .matchHeader('Authorization', k8s_token) + .reply(200, () => mockedClusterResponse); + const resource: Resource = await awsEksDetector.detect({ logger: new NoopLogger(), }); + scope.done(); + assert.ok(resource); assert.ok(resource); assertK8sResource(resource, { - clusterName: 'my-cluster' - }) - assertContainerResource(resource, { - id: '' + clusterName: 'my-cluster', }); }); - it ('should return a resource with clusterName attribute when cgroup file does not exist', async () => { + it('should return an empty resource when not running on Eks', async () => { fileStub = sandbox .stub(AwsEksDetector, 'fileAccessAsync' as any) .resolves(''); - readStub = sandbox.stub(AwsEksDetector, 'readFileAsync' as any) - .rejects(errorMsg.fileNotFoundError); - - isEksStub = sandbox.stub(awsEksDetector, '_isEks' as any).resolves(true); - getClusterStub = sandbox.stub(awsEksDetector, '_getClusterName' as any).resolves(mockedClusterResponse); + readStub = sandbox + .stub(AwsEksDetector, 'readFileAsync' as any) + .resolves(correctCgroupData); + getCredStub = sandbox + .stub(awsEksDetector, '_getK8sCredHeader' as any) + .resolves(k8s_token); + const scope = nock('https://' + K8S_SVC_URL) + .persist() + .get(AUTH_CONFIGMAP_PATH) + .matchHeader('Authorization', k8s_token) + .reply(200, () => ''); const resource: Resource = await awsEksDetector.detect({ logger: new NoopLogger(), }); + scope.done(); + assert.ok(resource); - assert.ok(resource); - assertK8sResource(resource, { - clusterName: 'my-cluster' - }) - assertContainerResource(resource, { - id: '' - }); + assertEmptyResource(resource); }); - it ('should return an empty resource when not running on Eks', async () => { + it('should return an empty resource when k8s token file does not exist', async () => { + const errorMsg = { + fileNotFoundError: new Error('cannot file k8s token file'), + }; fileStub = sandbox .stub(AwsEksDetector, 'fileAccessAsync' as any) - .resolves(''); - - isEksStub = sandbox.stub(awsEksDetector, '_isEks' as any).resolves(false); + .rejects(errorMsg.fileNotFoundError); const resource: Resource = await awsEksDetector.detect({ logger: new NoopLogger(), @@ -178,39 +226,94 @@ describe('awsEksDetector', () => { assertEmptyResource(resource); }); - it ('should return an empty resource when k8s file does not exist', async () => { - const errorMsg = { - fileNotFoundError: new Error('cannot file k8s token file'), - }; + it('should return an empty resource when containerId and clusterName are invalid', async () => { fileStub = sandbox .stub(AwsEksDetector, 'fileAccessAsync' as any) + .resolves(''); + readStub = sandbox + .stub(AwsEksDetector, 'readFileAsync' as any) + .onSecondCall() .rejects(errorMsg.fileNotFoundError); - + + getCredStub = sandbox + .stub(awsEksDetector, '_getK8sCredHeader' as any) + .resolves(k8s_token); + const scope = nock('https://' + K8S_SVC_URL) + .persist() + .get(AUTH_CONFIGMAP_PATH) + .matchHeader('Authorization', k8s_token) + .reply(200, () => mockedAwsAuth) + .get(CW_CONFIGMAP_PATH) + .matchHeader('Authorization', k8s_token) + .reply(200, () => ''); + const resource: Resource = await awsEksDetector.detect({ logger: new NoopLogger(), }); + scope.isDone(); + assert.ok(resource); assertEmptyResource(resource); }); + }); - it ('should return an empty resource when containerId and clusterName are invalid', async () => { + describe('on unsuccesful request', () => { + it('should throw when receiving error response code', async () => { + const expectedError = new Error('Eks metadata api request timed out.'); fileStub = sandbox .stub(AwsEksDetector, 'fileAccessAsync' as any) - .resolves(''); - readStub = sandbox.stub(AwsEksDetector, 'readFileAsync' as any) + .resolves(); + readStub = sandbox + .stub(AwsEksDetector, 'readFileAsync' as any) .resolves(correctCgroupData); + getCredStub = sandbox + .stub(awsEksDetector, '_getK8sCredHeader' as any) + .resolves(k8s_token); + const scope = nock('https://' + K8S_SVC_URL) + .persist() + .get(AUTH_CONFIGMAP_PATH) + .matchHeader('Authorization', k8s_token) + .delayConnection(2500) + .reply(200, () => mockedAwsAuth); - isEksStub = sandbox.stub(awsEksDetector, '_isEks' as any).resolves(true); - getContainerStub = sandbox.stub(awsEksDetector, '_getContainerId' as any).resolves(''); - getClusterStub = sandbox.stub(awsEksDetector, '_getClusterName' as any).resolves(''); - - const resource: Resource = await awsEksDetector.detect({ + try { + await awsEksDetector.detect({ logger: new NoopLogger(), - }); + }); + } catch (err) { + assert.deepStrictEqual(err, expectedError); + } - assert.ok(resource); - assertEmptyResource(resource); + scope.done(); + }); + + it('should return an empty resource when timed out', async () => { + const expectedError = new Error('Failed to load page, status code: 404'); + fileStub = sandbox + .stub(AwsEksDetector, 'fileAccessAsync' as any) + .resolves(); + readStub = sandbox + .stub(AwsEksDetector, 'readFileAsync' as any) + .resolves(correctCgroupData); + getCredStub = sandbox + .stub(awsEksDetector, '_getK8sCredHeader' as any) + .resolves(k8s_token); + const scope = nock('https://' + K8S_SVC_URL) + .persist() + .get(AUTH_CONFIGMAP_PATH) + .matchHeader('Authorization', k8s_token) + .reply(404, () => new Error()); + + try { + await awsEksDetector.detect({ + logger: new NoopLogger(), + }); + } catch (err) { + assert.deepStrictEqual(err, expectedError); + } + + scope.done(); }); }); -}); \ No newline at end of file +}); From 9661a938004a6222b029df60916b06aba4ae6cfd Mon Sep 17 00:00:00 2001 From: Lo Date: Tue, 10 Nov 2020 11:14:07 -0800 Subject: [PATCH 06/26] fix: update naming conventions consistentcy --- .../src/detectors/AwsEksDetector.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts index b379253f145..eac0ba033df 100644 --- a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts +++ b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts @@ -62,14 +62,14 @@ export class AwsEksDetector implements Detector { async detect(config: ResourceDetectionConfigWithLogger): Promise { try { await AwsEksDetector.fileAccessAsync(this.K8S_TOKEN_PATH); - const k8Scert = await AwsEksDetector.readFileAsync(this.K8S_CERT_PATH); + const k8scert = await AwsEksDetector.readFileAsync(this.K8S_CERT_PATH); - if (!this._isEks(config, k8Scert)) { + if (!this._isEks(config, k8scert)) { return Resource.empty(); } const containerId = await this._getContainerId(config); - const clusterName = await this._getClusterName(config, k8Scert); + const clusterName = await this._getClusterName(config, k8scert); return !containerId && !clusterName ? Resource.empty() @@ -78,7 +78,7 @@ export class AwsEksDetector implements Detector { [CONTAINER_RESOURCE.ID]: containerId || '', }); } catch (e) { - config.logger.warn('Not running on K8S'); + config.logger.warn('This process is not running on Kubernetes because either the token path or certificate path cannot be accessed ', e); return Resource.empty(); } } @@ -91,7 +91,7 @@ export class AwsEksDetector implements Detector { */ private async _isEks( config: ResourceDetectionConfigWithLogger, - k8scert: Buffer + cert: Buffer ): Promise { const options = { hostname: this.K8S_SVC_URL, @@ -101,7 +101,7 @@ export class AwsEksDetector implements Detector { headers: { Authorization: await this._getK8sCredHeader(config), }, - ca: k8scert, + ca: cert, }; return !!(await this._fetchString(options)); } @@ -113,7 +113,7 @@ export class AwsEksDetector implements Detector { */ private async _getClusterName( config: ResourceDetectionConfigWithLogger, - k8scert: Buffer + cert: Buffer ): Promise { const options = { host: this.K8S_SVC_URL, @@ -123,7 +123,7 @@ export class AwsEksDetector implements Detector { headers: { Authorization: await this._getK8sCredHeader(config), }, - ca: k8scert, + ca: cert, }; return await this._fetchString(options); } @@ -142,7 +142,7 @@ export class AwsEksDetector implements Detector { ); return 'Bearer ' + content; } catch (e) { - config.logger.warn('Unable to load K8s client token.', e); + config.logger.warn('Unable to read Kubernetes client token.', e); } return ''; } From 234872ee4c7c1d25845653a46077c1db3914dfcd Mon Sep 17 00:00:00 2001 From: Lo Date: Tue, 10 Nov 2020 12:43:19 -0800 Subject: [PATCH 07/26] fix: updated naming consistency --- .../src/detectors/AwsEksDetector.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts index eac0ba033df..b64aaea8bc1 100644 --- a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts +++ b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts @@ -52,7 +52,7 @@ export class AwsEksDetector implements Detector { private static fileAccessAsync = util.promisify(fs.access); /** - * The AWSEksDetector can be used to detect if a process is running in AWS + * The AwsEksDetector can be used to detect if a process is running in AWS * Eks and returns a promise containing a {@link Resource} * populated with instance metadata. Returns a promise containing an * empty {@link Resource} if the connection to kubernetes process From f4080234545e671563346727508cabbd376ab38d Mon Sep 17 00:00:00 2001 From: Lo Date: Tue, 10 Nov 2020 18:24:08 -0800 Subject: [PATCH 08/26] fix: updated files to adhere to linter --- .../src/detectors/AwsEksDetector.ts | 2 +- .../test/detectors/AwsEksDetector.test.ts | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts index b64aaea8bc1..e046243e784 100644 --- a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts +++ b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts @@ -78,7 +78,7 @@ export class AwsEksDetector implements Detector { [CONTAINER_RESOURCE.ID]: containerId || '', }); } catch (e) { - config.logger.warn('This process is not running on Kubernetes because either the token path or certificate path cannot be accessed ', e); + config.logger.warn('Process is not running on K8S', e); return Resource.empty(); } } diff --git a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts index b35be60f479..c6a729315e2 100644 --- a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts +++ b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts @@ -34,14 +34,14 @@ describe('awsEksDetector', () => { const errorMsg = { fileNotFoundError: new Error('cannot find cgroup file'), }; - let sandbox: sinon.SinonSandbox; - let readStub, fileStub, getCredStub; const correctCgroupData = 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm'; const mockedClusterResponse = 'my-cluster'; const mockedAwsAuth = 'my-auth'; const k8s_token = 'Bearer 31ada4fd-adec-460c-809a-9e56ceb75269'; + let sandbox: sinon.SinonSandbox; + let readStub, fileStub, getCredStub; beforeEach(() => { sandbox = sinon.createSandbox(); nock.disableNetConnect(); @@ -79,6 +79,9 @@ describe('awsEksDetector', () => { scope.done(); + sandbox.assert.calledOnce(fileStub); + sandbox.assert.calledOnce(readStub); + sandbox.assert.calledOnce(getCredStub); assert.ok(resource); assertK8sResource(resource, { clusterName: 'my-cluster', From 6c6c51eaed2a0501de1142d3d3e7392949746847 Mon Sep 17 00:00:00 2001 From: Lo Date: Tue, 10 Nov 2020 18:41:36 -0800 Subject: [PATCH 09/26] fix: updated files to adhere to linter --- .../test/detectors/AwsEksDetector.test.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts index c6a729315e2..5e431e75cdf 100644 --- a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts +++ b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts @@ -34,14 +34,15 @@ describe('awsEksDetector', () => { const errorMsg = { fileNotFoundError: new Error('cannot find cgroup file'), }; + const correctCgroupData = 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm'; const mockedClusterResponse = 'my-cluster'; const mockedAwsAuth = 'my-auth'; const k8s_token = 'Bearer 31ada4fd-adec-460c-809a-9e56ceb75269'; - let sandbox: sinon.SinonSandbox; let readStub, fileStub, getCredStub; + beforeEach(() => { sandbox = sinon.createSandbox(); nock.disableNetConnect(); @@ -80,8 +81,9 @@ describe('awsEksDetector', () => { scope.done(); sandbox.assert.calledOnce(fileStub); - sandbox.assert.calledOnce(readStub); - sandbox.assert.calledOnce(getCredStub); + sandbox.assert.calledTwice(readStub); + sandbox.assert.calledTwice(getCredStub); + assert.ok(resource); assertK8sResource(resource, { clusterName: 'my-cluster', From 9b5cb670d21c23885ce832e6e4ab0cb401ba6b0b Mon Sep 17 00:00:00 2001 From: Lo Date: Thu, 26 Nov 2020 02:33:19 -0800 Subject: [PATCH 10/26] fix: add parsing to Cluster JSON response --- .../src/detectors/AwsEksDetector.ts | 12 ++++++++---- .../test/detectors/AwsEksDetector.test.ts | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts index e046243e784..9b52ab47471 100644 --- a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts +++ b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts @@ -125,7 +125,13 @@ export class AwsEksDetector implements Detector { }, ca: cert, }; - return await this._fetchString(options); + const response = await this._fetchString(options); + try { + return JSON.parse(response).data['cluster.name']; + } catch (e) { + config.logger.warn('Cannot get cluster name on EKS', e); + } + return ''; } /** * Reads the Kubernetes token path and returns kubernetes @@ -182,9 +188,7 @@ export class AwsEksDetector implements Detector { * to get back a valid JSON document. Parses that document and stores * the identity properties in a local map. */ - private async _fetchString( - options: https.RequestOptions - ): Promise { + private async _fetchString(options: https.RequestOptions): Promise { return await new Promise((resolve, reject) => { const timeoutId = setTimeout(() => { req.abort(); diff --git a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts index 5e431e75cdf..ca0f7ed44bc 100644 --- a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts +++ b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts @@ -37,7 +37,7 @@ describe('awsEksDetector', () => { const correctCgroupData = 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm'; - const mockedClusterResponse = 'my-cluster'; + const mockedClusterResponse = '{"data":{"cluster.name":"my-cluster"}}'; const mockedAwsAuth = 'my-auth'; const k8s_token = 'Bearer 31ada4fd-adec-460c-809a-9e56ceb75269'; let sandbox: sinon.SinonSandbox; From 10b89d0d6fdc7f43d97349aef594e77a07e0744e Mon Sep 17 00:00:00 2001 From: Lo Date: Wed, 2 Dec 2020 16:16:58 -0800 Subject: [PATCH 11/26] chore: update naming consistency --- .../src/detectors/AwsEksDetector.ts | 37 ++++++++++--------- .../test/detectors/AwsEksDetector.test.ts | 2 +- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts index 9b52ab47471..c4cae33d14d 100644 --- a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts +++ b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts @@ -46,14 +46,15 @@ export class AwsEksDetector implements Detector { '/api/v1/namespaces/amazon-cloudwatch/configmaps/cluster-info'; readonly CONTAINER_ID_LENGTH = 64; readonly DEFAULT_CGROUP_PATH = '/proc/self/cgroup'; - readonly MILLISECOND_TIME_OUT = 2000; + readonly TIMEOUT_MS = 2000; + readonly UTF8_UNICODE = 'utf8'; private static readFileAsync = util.promisify(fs.readFile); private static fileAccessAsync = util.promisify(fs.access); /** - * The AwsEksDetector can be used to detect if a process is running in AWS - * Eks and returns a promise containing a {@link Resource} + * The AwsEksDetector can be used to detect if a process is running on Amazon + * Elastic Kubernetes and returns a promise containing a {@link Resource} * populated with instance metadata. Returns a promise containing an * empty {@link Resource} if the connection to kubernetes process * or aws config maps fails @@ -85,7 +86,7 @@ export class AwsEksDetector implements Detector { /** * Attempts to make a connection to AWS Config map which will - * determine whether the process is running on an Eks + * determine whether the process is running on an EKS * process if the config map is empty or not * @param config The resource detection config with a required logger */ @@ -94,14 +95,14 @@ export class AwsEksDetector implements Detector { cert: Buffer ): Promise { const options = { - hostname: this.K8S_SVC_URL, - path: this.AUTH_CONFIGMAP_PATH, - method: 'GET', - timeout: this.MILLISECOND_TIME_OUT, + ca: cert, headers: { Authorization: await this._getK8sCredHeader(config), }, - ca: cert, + hostname: this.K8S_SVC_URL, + method: 'GET', + path: this.AUTH_CONFIGMAP_PATH, + timeout: this.TIMEOUT_MS, }; return !!(await this._fetchString(options)); } @@ -116,14 +117,14 @@ export class AwsEksDetector implements Detector { cert: Buffer ): Promise { const options = { - host: this.K8S_SVC_URL, - path: this.CW_CONFIGMAP_PATH, - method: 'GET', - timeout: this.MILLISECOND_TIME_OUT, + ca: cert, headers: { Authorization: await this._getK8sCredHeader(config), }, - ca: cert, + host: this.K8S_SVC_URL, + method: 'GET', + path: this.CW_CONFIGMAP_PATH, + timeout: this.TIMEOUT_MS, }; const response = await this._fetchString(options); try { @@ -144,7 +145,7 @@ export class AwsEksDetector implements Detector { try { const content = await AwsEksDetector.readFileAsync( this.K8S_TOKEN_PATH, - 'utf8' + this.UTF8_UNICODE ); return 'Bearer ' + content; } catch (e) { @@ -166,7 +167,7 @@ export class AwsEksDetector implements Detector { try { const rawData = await AwsEksDetector.readFileAsync( this.DEFAULT_CGROUP_PATH, - 'utf8' + this.UTF8_UNICODE ); const splitData = rawData.trim().split('\n'); for (const str of splitData) { @@ -192,13 +193,13 @@ export class AwsEksDetector implements Detector { return await new Promise((resolve, reject) => { const timeoutId = setTimeout(() => { req.abort(); - reject(new Error('Eks metadata api request timed out.')); + reject(new Error('EKS metadata api request timed out.')); }, 2000); const req = https.request(options, res => { clearTimeout(timeoutId); const { statusCode } = res; - res.setEncoding('utf8'); + res.setEncoding(this.UTF8_UNICODE); let rawData = ''; res.on('data', chunk => (rawData += chunk)); res.on('end', () => { diff --git a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts index ca0f7ed44bc..d4749897926 100644 --- a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts +++ b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts @@ -189,7 +189,7 @@ describe('awsEksDetector', () => { }); }); - it('should return an empty resource when not running on Eks', async () => { + it('should return an empty resource when not running on EKS', async () => { fileStub = sandbox .stub(AwsEksDetector, 'fileAccessAsync' as any) .resolves(''); From 76a5f84171c1982250531e71bb1c9dc1234b3c95 Mon Sep 17 00:00:00 2001 From: Lo Date: Thu, 29 Oct 2020 12:38:59 -0700 Subject: [PATCH 12/26] test: add mock tests --- .../test/detectors/AwsEksDetector.test.ts | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts index d4749897926..eef03c72aea 100644 --- a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts +++ b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts @@ -15,6 +15,7 @@ */ import * as nock from 'nock'; +<<<<<<< HEAD import * as sinon from 'sinon'; import * as assert from 'assert'; import { Resource } from '@opentelemetry/resources'; @@ -23,10 +24,18 @@ import { assertK8sResource, assertContainerResource, assertEmptyResource, +======= +import * as assert from 'assert'; +import { Resource } from '@opentelemetry/resources'; +import { awsEksDetector } from '../../src'; +import { + assertK8sResource, +>>>>>>> 4c54840bb... test: add mock tests } from '@opentelemetry/resources/test/util/resource-assertions'; import { NoopLogger } from '@opentelemetry/core'; const K8S_SVC_URL = awsEksDetector.K8S_SVC_URL; +<<<<<<< HEAD const AUTH_CONFIGMAP_PATH = awsEksDetector.AUTH_CONFIGMAP_PATH; const CW_CONFIGMAP_PATH = awsEksDetector.CW_CONFIGMAP_PATH; @@ -45,11 +54,28 @@ describe('awsEksDetector', () => { beforeEach(() => { sandbox = sinon.createSandbox(); +======= +const K8S_TOKEN_PATH = awsEksDetector.K8S_TOKEN_PATH; +const K8S_CERT_PATH = awsEksDetector.K8S_CERT_PATH; +const AUTH_CONFIGMAP_PATH = awsEksDetector.AUTH_CONFIGMAP_PATH; +const CW_CONFIGMAP_PATH = awsEksDetector.CW_CONFIGMAP_PATH; + +const mockedClusterResponse = "my-cluster"; +const correctCgroupData = + 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm'; +const unexpectedCgroupdata = + 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'; +const mockedK8sCredentials = "Bearer 31ada4fd-adec-460c-809a-9e56ceb75269"; + +describe('awsEksDetector', () => { + beforeEach(() => { +>>>>>>> 4c54840bb... test: add mock tests nock.disableNetConnect(); nock.cleanAll(); }); afterEach(() => { +<<<<<<< HEAD sandbox.restore(); nock.enableNetConnect(); }); @@ -286,11 +312,50 @@ describe('awsEksDetector', () => { await awsEksDetector.detect({ logger: new NoopLogger(), }); +======= + nock.enableNetConnect(); + }); +}); + +describe('on succesful request', () => { + it ('should return an aws_eks_instance_resource', async () => { + const scope = nock(K8S_SVC_URL) + .get(CW_CONFIGMAP_PATH) + .matchHeader("Authorizations", mockedK8sCredentials) + .reply(200, mockedClusterResponse) + const resource: Resource = await awsEksDetector.detect({ + logger: new NoopLogger(), + }); + + scope.done(); + + assert.ok(resource); + assertK8sResource(resource, { + clusterName: 'my-cluster' + }) +}); +}); + +describe('on unsuccessful request', () => { + it ('should throw when receiving error response code', async () => { + const expectedError = new Error('Failed to load page, status code: 404'); + const scope = nock(K8S_SVC_URL) + .get(CW_CONFIGMAP_PATH) + .matchHeader("Authorizations", mockedK8sCredentials) + .reply(404, () => new Error()); + + try { + await awsEksDetector.detect({ + logger: new NoopLogger(), + }); + assert.ok(false, 'Expected to throw'); +>>>>>>> 4c54840bb... test: add mock tests } catch (err) { assert.deepStrictEqual(err, expectedError); } scope.done(); +<<<<<<< HEAD }); it('should return an empty resource when timed out', async () => { @@ -314,11 +379,32 @@ describe('awsEksDetector', () => { await awsEksDetector.detect({ logger: new NoopLogger(), }); +======= + + it ('should throw when timed out', async () => { + const expectedError = new Error('Failed to load page, status code: 404'); + const scope = nock(K8S_SVC_URL) + .get(CW_CONFIGMAP_PATH) + .matchHeader("Authorizations", mockedK8sCredentials) + .delayConnection(2500) + .reply(200, () => mockedClusterResponse); + + try { + await awsEksDetector.detect({ + logger: new NoopLogger(), + }); + assert.ok(false, 'Expected to throw'); +>>>>>>> 4c54840bb... test: add mock tests } catch (err) { assert.deepStrictEqual(err, expectedError); } scope.done(); }); +<<<<<<< HEAD }); }); +======= +}); +}); +>>>>>>> 4c54840bb... test: add mock tests From 3a8155df705345694f62521f864d4c2c28d293f1 Mon Sep 17 00:00:00 2001 From: Lo Date: Thu, 29 Oct 2020 21:27:20 -0700 Subject: [PATCH 13/26] fix: use file read async instead of sync --- .../src/detectors/AwsEksDetector.ts | 109 ++++++++++++++++++ .../test/detectors/AwsEksDetector.test.ts | 70 +++++++++-- 2 files changed, 167 insertions(+), 12 deletions(-) diff --git a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts index c4cae33d14d..11867cfe696 100644 --- a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts +++ b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts @@ -49,6 +49,7 @@ export class AwsEksDetector implements Detector { readonly TIMEOUT_MS = 2000; readonly UTF8_UNICODE = 'utf8'; +<<<<<<< HEAD private static readFileAsync = util.promisify(fs.readFile); private static fileAccessAsync = util.promisify(fs.access); @@ -81,9 +82,38 @@ export class AwsEksDetector implements Detector { } catch (e) { config.logger.warn('Process is not running on K8S', e); return Resource.empty(); +======= + private static readFileAsync = util.promisify(fs.readFile); + private static fileAccessAsync = util.promisify(fs.access); + + async detect(config: ResourceDetectionConfigWithLogger): Promise { + try { + AwsEksDetector.fileAccessAsync(this.K8S_TOKEN_PATH); + AwsEksDetector.fileAccessAsync(this.K8S_CERT_PATH); + + if (!this._isEks(config)) { + config.logger.debug('AwsEcsDetector failed: Process is not running on Eks'); + return Resource.empty(); + } + + const containerId = await this._getContainerId(config); + const clusterName = await this._getClusterName(config); + + return !containerId + ? Resource.empty() + : new Resource({ + [K8S_RESOURCE.CLUSTER_NAME]: clusterName || '', + [CONTAINER_RESOURCE.ID]: containerId || '', + }); + } catch (e) { + config.logger.debug('Not running on K8S'); + return Resource.empty(); + } +>>>>>>> 7d354c340... fix: use file read async instead of sync } } +<<<<<<< HEAD /** * Attempts to make a connection to AWS Config map which will * determine whether the process is running on an EKS @@ -150,10 +180,31 @@ export class AwsEksDetector implements Detector { return 'Bearer ' + content; } catch (e) { config.logger.warn('Unable to read Kubernetes client token.', e); +======= + private async _isEks(config: ResourceDetectionConfigWithLogger): Promise { + const secureContext = tls.createSecureContext({ + ca: JSON.stringify(AwsEksDetector.readFileAsync(this.K8S_CERT_PATH)), + }); + const options = { + host: this.K8S_SVC_URL, + path: this.AUTH_CONFIGMAP_PATH, + method: 'GET', + timeout: this.MILLISECOND_TIME_OUT, + HEADERS: { + "Authorization" : this._getK8sCredHeader(config), + }, + agentOptions: { + ca: secureContext, + } + } + const awsAuth = this._fetchString(options); + return !!awsAuth; +>>>>>>> 7d354c340... fix: use file read async instead of sync } return ''; } +<<<<<<< HEAD /** * Read container ID from cgroup file * In EKS, even if we fail to find target file @@ -182,6 +233,64 @@ export class AwsEksDetector implements Detector { } return undefined; } +======= + private async _getClusterName(config: ResourceDetectionConfigWithLogger): Promise { + const secureContext = tls.createSecureContext({ + ca: JSON.stringify(AwsEksDetector.readFileAsync(this.K8S_CERT_PATH)), + }); + const options = { + host: this.K8S_SVC_URL, + path: this.CW_CONFIGMAP_PATH, + method: 'GET', + timeout: this.MILLISECOND_TIME_OUT, + HEADERS: { + "Authorization" : this._getK8sCredHeader(config), + }, + agentOptions: { + ca: secureContext, + } + } + return this._fetchString(options); + } + + private async _getK8sCredHeader(config: ResourceDetectionConfigWithLogger): Promise { + try { + const content = await AwsEksDetector.readFileAsync( + this.K8S_TOKEN_PATH, + 'utf8' + ); + return "Bearer " + content; + } catch (e) { + config.logger.warn(`AwsEksDetector failed to read container ID: ${e.message}`); + } + return ""; + } + + /** + * Read container ID from cgroup file + * In EKS, 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 async _getContainerId(config: ResourceDetectionConfigWithLogger): Promise { + try { + const rawData = await AwsEksDetector.readFileAsync( + this.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); + } + } + } catch (e) { + config.logger.warn(`AwsEksDetector failed to read container ID: ${e.message}`); + } + return undefined; + } +>>>>>>> 7d354c340... fix: use file read async instead of sync /** * Establishes an HTTP connection to AWS instance document url. diff --git a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts index eef03c72aea..39182063364 100644 --- a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts +++ b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts @@ -16,6 +16,7 @@ import * as nock from 'nock'; <<<<<<< HEAD +<<<<<<< HEAD import * as sinon from 'sinon'; import * as assert from 'assert'; import { Resource } from '@opentelemetry/resources'; @@ -25,12 +26,19 @@ import { assertContainerResource, assertEmptyResource, ======= +======= +import * as sinon from 'sinon'; +>>>>>>> 7d354c340... fix: use file read async instead of sync import * as assert from 'assert'; import { Resource } from '@opentelemetry/resources'; -import { awsEksDetector } from '../../src'; +import { awsEksDetector, AwsEksDetector } from '../../src'; import { +<<<<<<< HEAD assertK8sResource, >>>>>>> 4c54840bb... test: add mock tests +======= + assertK8sResource, assertContainerResource, +>>>>>>> 7d354c340... fix: use file read async instead of sync } from '@opentelemetry/resources/test/util/resource-assertions'; import { NoopLogger } from '@opentelemetry/core'; @@ -60,18 +68,19 @@ const K8S_CERT_PATH = awsEksDetector.K8S_CERT_PATH; const AUTH_CONFIGMAP_PATH = awsEksDetector.AUTH_CONFIGMAP_PATH; const CW_CONFIGMAP_PATH = awsEksDetector.CW_CONFIGMAP_PATH; -const mockedClusterResponse = "my-cluster"; -const correctCgroupData = +describe('awsEksDetector', () => { + let sandbox: sinon.SinonSandbox; + let readStub, fileStub; + const correctCgroupData = 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm'; -const unexpectedCgroupdata = - 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'; -const mockedK8sCredentials = "Bearer 31ada4fd-adec-460c-809a-9e56ceb75269"; + const mockedClusterResponse = "my-cluster"; + const mockedK8sCredentials = "Bearer 31ada4fd-adec-460c-809a-9e56ceb75269"; -describe('awsEksDetector', () => { beforeEach(() => { >>>>>>> 4c54840bb... test: add mock tests nock.disableNetConnect(); nock.cleanAll(); + sandbox = sinon.createSandbox(); }); afterEach(() => { @@ -314,7 +323,9 @@ describe('awsEksDetector', () => { }); ======= nock.enableNetConnect(); + sandbox.restore(); }); +<<<<<<< HEAD }); describe('on succesful request', () => { @@ -379,15 +390,28 @@ describe('on unsuccessful request', () => { await awsEksDetector.detect({ logger: new NoopLogger(), }); +======= ======= - it ('should throw when timed out', async () => { - const expectedError = new Error('Failed to load page, status code: 404'); - const scope = nock(K8S_SVC_URL) + describe('on succesful request', () => { + it ('should return an aws_eks_instance_resource', async () => { + fileStub = sandbox + .stub(AwsEksDetector, 'fileAccessAsync' as any) + .resolves(); + readStub = sinon.stub(AwsEksDetector, 'readFileAsync' as any); + readStub.onCall(1).resolves(correctCgroupData); + readStub.onCall(2).returns(mockedK8sCredentials); + readStub.onCall(3).returns(mockedK8sCredentials); +>>>>>>> 7d354c340... fix: use file read async instead of sync + + const scope = nock(K8S_SVC_URL) + .get(AUTH_CONFIGMAP_PATH) + .matchHeader('Authorizations', mockedK8sCredentials) + .reply(200, () => true) .get(CW_CONFIGMAP_PATH) - .matchHeader("Authorizations", mockedK8sCredentials) - .delayConnection(2500) + .matchHeader('Authorizations', mockedK8sCredentials) .reply(200, () => mockedClusterResponse); +<<<<<<< HEAD try { await awsEksDetector.detect({ @@ -398,9 +422,27 @@ describe('on unsuccessful request', () => { } catch (err) { assert.deepStrictEqual(err, expectedError); } +======= + + const resource: Resource = await awsEksDetector.detect({ + logger: new NoopLogger(), + }); +>>>>>>> 7d354c340... fix: use file read async instead of sync scope.done(); + + sandbox.assert.calledTwice(fileStub); + sandbox.assert.calledThrice(readStub); + + assert.ok(resource); + assertK8sResource(resource, { + clusterName: 'my-cluster', + }) + assertContainerResource(resource, { + id: 'bcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm', + }) }); +<<<<<<< HEAD <<<<<<< HEAD }); }); @@ -408,3 +450,7 @@ describe('on unsuccessful request', () => { }); }); >>>>>>> 4c54840bb... test: add mock tests +======= + }); +}); +>>>>>>> 7d354c340... fix: use file read async instead of sync From fecc96d76eec53d5f0808e8b412eab8e478905ba Mon Sep 17 00:00:00 2001 From: Lo Date: Fri, 30 Oct 2020 04:54:06 -0700 Subject: [PATCH 14/26] feat: update implementation of https requests --- .../src/detectors/AwsEksDetector.ts | 41 ++-- .../test/detectors/AwsEksDetector.test.ts | 175 +++++++++++++++++- 2 files changed, 192 insertions(+), 24 deletions(-) diff --git a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts index 11867cfe696..d2f64d2c009 100644 --- a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts +++ b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts @@ -15,6 +15,7 @@ */ import { +<<<<<<< HEAD Detector, Resource, CONTAINER_RESOURCE, @@ -24,6 +25,17 @@ import { import * as https from 'https'; import * as fs from 'fs'; import * as util from 'util'; +======= + Detector, + Resource, + CONTAINER_RESOURCE, + K8S_RESOURCE, + ResourceDetectionConfigWithLogger, + } from '@opentelemetry/resources'; + import * as https from 'https'; + import * as fs from 'fs'; + import * as util from 'util'; +>>>>>>> b9e63372f... feat: update implementation of https requests /** * The AwsEksDetector can be used to detect if a process is running in AWS Elastic @@ -88,8 +100,8 @@ export class AwsEksDetector implements Detector { async detect(config: ResourceDetectionConfigWithLogger): Promise { try { - AwsEksDetector.fileAccessAsync(this.K8S_TOKEN_PATH); - AwsEksDetector.fileAccessAsync(this.K8S_CERT_PATH); + await AwsEksDetector.fileAccessAsync(this.K8S_TOKEN_PATH); + await AwsEksDetector.fileAccessAsync(this.K8S_CERT_PATH); if (!this._isEks(config)) { config.logger.debug('AwsEcsDetector failed: Process is not running on Eks'); @@ -99,14 +111,14 @@ export class AwsEksDetector implements Detector { const containerId = await this._getContainerId(config); const clusterName = await this._getClusterName(config); - return !containerId + return !containerId && !clusterName ? Resource.empty() : new Resource({ [K8S_RESOURCE.CLUSTER_NAME]: clusterName || '', [CONTAINER_RESOURCE.ID]: containerId || '', }); } catch (e) { - config.logger.debug('Not running on K8S'); + config.logger.warn('Not running on K8S'); return Resource.empty(); } >>>>>>> 7d354c340... fix: use file read async instead of sync @@ -182,20 +194,15 @@ export class AwsEksDetector implements Detector { config.logger.warn('Unable to read Kubernetes client token.', e); ======= private async _isEks(config: ResourceDetectionConfigWithLogger): Promise { - const secureContext = tls.createSecureContext({ - ca: JSON.stringify(AwsEksDetector.readFileAsync(this.K8S_CERT_PATH)), - }); const options = { - host: this.K8S_SVC_URL, + hostname: this.K8S_SVC_URL, path: this.AUTH_CONFIGMAP_PATH, method: 'GET', timeout: this.MILLISECOND_TIME_OUT, HEADERS: { "Authorization" : this._getK8sCredHeader(config), }, - agentOptions: { - ca: secureContext, - } + ca: JSON.stringify(AwsEksDetector.readFileAsync(this.K8S_CERT_PATH)), } const awsAuth = this._fetchString(options); return !!awsAuth; @@ -235,9 +242,6 @@ export class AwsEksDetector implements Detector { } ======= private async _getClusterName(config: ResourceDetectionConfigWithLogger): Promise { - const secureContext = tls.createSecureContext({ - ca: JSON.stringify(AwsEksDetector.readFileAsync(this.K8S_CERT_PATH)), - }); const options = { host: this.K8S_SVC_URL, path: this.CW_CONFIGMAP_PATH, @@ -246,9 +250,7 @@ export class AwsEksDetector implements Detector { HEADERS: { "Authorization" : this._getK8sCredHeader(config), }, - agentOptions: { - ca: secureContext, - } + ca: JSON.stringify(AwsEksDetector.readFileAsync(this.K8S_CERT_PATH)), } return this._fetchString(options); } @@ -298,8 +300,13 @@ export class AwsEksDetector implements Detector { * to get back a valid JSON document. Parses that document and stores * the identity properties in a local map. */ +<<<<<<< HEAD private async _fetchString(options: https.RequestOptions): Promise { return await new Promise((resolve, reject) => { +======= + private async _fetchString(options: https.RequestOptions): Promise { + return new Promise((resolve, reject) => { +>>>>>>> b9e63372f... feat: update implementation of https requests const timeoutId = setTimeout(() => { req.abort(); reject(new Error('EKS metadata api request timed out.')); diff --git a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts index 39182063364..9116791ce58 100644 --- a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts +++ b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts @@ -22,6 +22,7 @@ import * as assert from 'assert'; import { Resource } from '@opentelemetry/resources'; import { awsEksDetector, AwsEksDetector } from '../../src'; import { +<<<<<<< HEAD assertK8sResource, assertContainerResource, assertEmptyResource, @@ -68,18 +69,29 @@ const K8S_CERT_PATH = awsEksDetector.K8S_CERT_PATH; const AUTH_CONFIGMAP_PATH = awsEksDetector.AUTH_CONFIGMAP_PATH; const CW_CONFIGMAP_PATH = awsEksDetector.CW_CONFIGMAP_PATH; +======= + assertK8sResource, assertContainerResource, assertEmptyResource, +} from '@opentelemetry/resources/test/util/resource-assertions'; +import { NoopLogger } from '@opentelemetry/core'; + +>>>>>>> b9e63372f... feat: update implementation of https requests describe('awsEksDetector', () => { + const errorMsg = { + fileNotFoundError: new Error('cannot find cgroup file'), + }; let sandbox: sinon.SinonSandbox; - let readStub, fileStub; + let readStub, fileStub, isEksStub, getClusterStub, getContainerStub, fetchStub; const correctCgroupData = 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm'; const mockedClusterResponse = "my-cluster"; - const mockedK8sCredentials = "Bearer 31ada4fd-adec-460c-809a-9e56ceb75269"; beforeEach(() => { +<<<<<<< HEAD >>>>>>> 4c54840bb... test: add mock tests nock.disableNetConnect(); nock.cleanAll(); +======= +>>>>>>> b9e63372f... feat: update implementation of https requests sandbox = sinon.createSandbox(); }); @@ -393,11 +405,12 @@ describe('on unsuccessful request', () => { ======= ======= - describe('on succesful request', () => { + describe('on successful request', () => { it ('should return an aws_eks_instance_resource', async () => { fileStub = sandbox .stub(AwsEksDetector, 'fileAccessAsync' as any) .resolves(); +<<<<<<< HEAD readStub = sinon.stub(AwsEksDetector, 'readFileAsync' as any); readStub.onCall(1).resolves(correctCgroupData); readStub.onCall(2).returns(mockedK8sCredentials); @@ -423,24 +436,172 @@ describe('on unsuccessful request', () => { assert.deepStrictEqual(err, expectedError); } ======= +======= + readStub = sandbox.stub(AwsEksDetector, 'readFileAsync' as any); + readStub.resolves(correctCgroupData); + + fetchStub = sandbox.stub(awsEksDetector, '_fetchString' as any) + .onCall(0).resolves('aws-auth'); + getClusterStub = sandbox.stub(awsEksDetector, '_getClusterName' as any).resolves(mockedClusterResponse); +>>>>>>> b9e63372f... feat: update implementation of https requests const resource: Resource = await awsEksDetector.detect({ logger: new NoopLogger(), }); >>>>>>> 7d354c340... fix: use file read async instead of sync - scope.done(); + assert.ok(resource); + assertK8sResource(resource, { + clusterName: 'my-cluster', + }) + assertContainerResource(resource, { + id: 'bcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm', + }) + }); + + it ('should return a resource with cluster Name attribute without a container Id', async () => { + fileStub = sandbox + .stub(AwsEksDetector, 'fileAccessAsync' as any) + .resolves(''); + readStub = sandbox.stub(AwsEksDetector, 'readFileAsync' as any) + .resolves(correctCgroupData); - sandbox.assert.calledTwice(fileStub); - sandbox.assert.calledThrice(readStub); + isEksStub = sandbox.stub(awsEksDetector, '_isEks' as any).resolves(true); + getContainerStub = sandbox.stub(awsEksDetector, '_getContainerId' as any).resolves(''); + getClusterStub = sandbox.stub(awsEksDetector, '_getClusterName' as any).resolves(mockedClusterResponse); + + const resource: Resource = await awsEksDetector.detect({ + logger: new NoopLogger(), + }); assert.ok(resource); + assertContainerResource(resource, { + id: '', + }); assertK8sResource(resource, { clusterName: 'my-cluster', }) + }); + + it ('should return a resource with container ID attribute without a clusterName', async () => { + fileStub = sandbox + .stub(AwsEksDetector, 'fileAccessAsync' as any) + .resolves(''); + readStub = sandbox.stub(AwsEksDetector, 'readFileAsync' as any) + .resolves(correctCgroupData); + + isEksStub = sandbox.stub(awsEksDetector, '_isEks' as any).resolves(true); + getClusterStub = sandbox.stub(awsEksDetector, '_getClusterName' as any).resolves(''); + + const resource: Resource = await awsEksDetector.detect({ + logger: new NoopLogger(), + }); + + assert.ok(resource); + assertK8sResource(resource, { + clusterName: '', + }) assertContainerResource(resource, { - id: 'bcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm', + id: 'bcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm' + }); + }); + + it ('should return a resource with clusterName attribute when cgroup file does not contain valid Container ID', async () => { + fileStub = sandbox + .stub(AwsEksDetector, 'fileAccessAsync' as any) + .resolves(''); + readStub = sandbox.stub(AwsEksDetector, 'readFileAsync' as any) + .resolves(''); + + isEksStub = sandbox.stub(awsEksDetector, '_isEks' as any).resolves(true); + getClusterStub = sandbox.stub(awsEksDetector, '_getClusterName' as any).resolves(mockedClusterResponse); + + const resource: Resource = await awsEksDetector.detect({ + logger: new NoopLogger(), + }); + + assert.ok(resource); + assert.ok(resource); + assertK8sResource(resource, { + clusterName: 'my-cluster' + }) + assertContainerResource(resource, { + id: '' + }); + }); + + it ('should return a resource with clusterName attribute when cgroup file does not exist', async () => { + fileStub = sandbox + .stub(AwsEksDetector, 'fileAccessAsync' as any) + .resolves(''); + readStub = sandbox.stub(AwsEksDetector, 'readFileAsync' as any) + .rejects(errorMsg.fileNotFoundError); + + isEksStub = sandbox.stub(awsEksDetector, '_isEks' as any).resolves(true); + getClusterStub = sandbox.stub(awsEksDetector, '_getClusterName' as any).resolves(mockedClusterResponse); + + const resource: Resource = await awsEksDetector.detect({ + logger: new NoopLogger(), + }); + + assert.ok(resource); + assert.ok(resource); + assertK8sResource(resource, { + clusterName: 'my-cluster' }) + assertContainerResource(resource, { + id: '' + }); + }); + + it ('should return an empty resource when not running on Eks', async () => { + fileStub = sandbox + .stub(AwsEksDetector, 'fileAccessAsync' as any) + .resolves(''); + + isEksStub = sandbox.stub(awsEksDetector, '_isEks' as any).resolves(false); + + const resource: Resource = await awsEksDetector.detect({ + logger: new NoopLogger(), + }); + + assert.ok(resource); + assertEmptyResource(resource); + }); + + it ('should return an empty resource when k8s file does not exist', async () => { + const errorMsg = { + fileNotFoundError: new Error('cannot file k8s token file'), + }; + fileStub = sandbox + .stub(AwsEksDetector, 'fileAccessAsync' as any) + .rejects(errorMsg.fileNotFoundError); + + const resource: Resource = await awsEksDetector.detect({ + logger: new NoopLogger(), + }); + + assert.ok(resource); + assertEmptyResource(resource); + }); + + it ('should return an empty resource when containerId and clusterName are invalid', async () => { + fileStub = sandbox + .stub(AwsEksDetector, 'fileAccessAsync' as any) + .resolves(''); + readStub = sandbox.stub(AwsEksDetector, 'readFileAsync' as any) + .resolves(correctCgroupData); + + isEksStub = sandbox.stub(awsEksDetector, '_isEks' as any).resolves(true); + getContainerStub = sandbox.stub(awsEksDetector, '_getContainerId' as any).resolves(''); + getClusterStub = sandbox.stub(awsEksDetector, '_getClusterName' as any).resolves(''); + + const resource: Resource = await awsEksDetector.detect({ + logger: new NoopLogger(), + }); + + assert.ok(resource); + assertEmptyResource(resource); }); <<<<<<< HEAD <<<<<<< HEAD From 322235afcb8648835c7a6f780aa1cb2e87304b24 Mon Sep 17 00:00:00 2001 From: Lo Date: Tue, 10 Nov 2020 11:14:07 -0800 Subject: [PATCH 15/26] fix: update naming conventions consistentcy --- .../src/detectors/AwsEksDetector.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts index d2f64d2c009..ff5e7c1d900 100644 --- a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts +++ b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts @@ -92,7 +92,11 @@ export class AwsEksDetector implements Detector { [CONTAINER_RESOURCE.ID]: containerId || '', }); } catch (e) { +<<<<<<< HEAD config.logger.warn('Process is not running on K8S', e); +======= + config.logger.warn('This process is not running on Kubernetes because either the token path or certificate path cannot be accessed ', e); +>>>>>>> 21e3884b1... fix: update naming conventions consistentcy return Resource.empty(); ======= private static readFileAsync = util.promisify(fs.readFile); @@ -141,10 +145,14 @@ export class AwsEksDetector implements Detector { headers: { Authorization: await this._getK8sCredHeader(config), }, +<<<<<<< HEAD hostname: this.K8S_SVC_URL, method: 'GET', path: this.AUTH_CONFIGMAP_PATH, timeout: this.TIMEOUT_MS, +======= + ca: cert, +>>>>>>> 21e3884b1... fix: update naming conventions consistentcy }; return !!(await this._fetchString(options)); } @@ -163,10 +171,14 @@ export class AwsEksDetector implements Detector { headers: { Authorization: await this._getK8sCredHeader(config), }, +<<<<<<< HEAD host: this.K8S_SVC_URL, method: 'GET', path: this.CW_CONFIGMAP_PATH, timeout: this.TIMEOUT_MS, +======= + ca: cert, +>>>>>>> 21e3884b1... fix: update naming conventions consistentcy }; const response = await this._fetchString(options); try { @@ -192,6 +204,7 @@ export class AwsEksDetector implements Detector { return 'Bearer ' + content; } catch (e) { config.logger.warn('Unable to read Kubernetes client token.', e); +<<<<<<< HEAD ======= private async _isEks(config: ResourceDetectionConfigWithLogger): Promise { const options = { @@ -207,6 +220,8 @@ export class AwsEksDetector implements Detector { const awsAuth = this._fetchString(options); return !!awsAuth; >>>>>>> 7d354c340... fix: use file read async instead of sync +======= +>>>>>>> 21e3884b1... fix: update naming conventions consistentcy } return ''; } From 9ff4f178783a15663dac3fb5269a0c7e0aedcfdf Mon Sep 17 00:00:00 2001 From: Lo Date: Tue, 10 Nov 2020 12:43:19 -0800 Subject: [PATCH 16/26] fix: updated naming consistency --- .../src/detectors/AwsEksDetector.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts index ff5e7c1d900..268e996fc24 100644 --- a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts +++ b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts @@ -66,8 +66,13 @@ export class AwsEksDetector implements Detector { private static fileAccessAsync = util.promisify(fs.access); /** +<<<<<<< HEAD * The AwsEksDetector can be used to detect if a process is running on Amazon * Elastic Kubernetes and returns a promise containing a {@link Resource} +======= + * The AwsEksDetector can be used to detect if a process is running in AWS + * Eks and returns a promise containing a {@link Resource} +>>>>>>> 37f44d48d... fix: updated naming consistency * populated with instance metadata. Returns a promise containing an * empty {@link Resource} if the connection to kubernetes process * or aws config maps fails From 1eb40662d9ef023f351babe5c79835545ff643f0 Mon Sep 17 00:00:00 2001 From: Lo Date: Tue, 10 Nov 2020 18:24:08 -0800 Subject: [PATCH 17/26] fix: updated files to adhere to linter --- .../src/detectors/AwsEksDetector.ts | 4 ++++ .../test/detectors/AwsEksDetector.test.ts | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts index 268e996fc24..6f2f923e8f2 100644 --- a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts +++ b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts @@ -97,11 +97,15 @@ export class AwsEksDetector implements Detector { [CONTAINER_RESOURCE.ID]: containerId || '', }); } catch (e) { +<<<<<<< HEAD <<<<<<< HEAD config.logger.warn('Process is not running on K8S', e); ======= config.logger.warn('This process is not running on Kubernetes because either the token path or certificate path cannot be accessed ', e); >>>>>>> 21e3884b1... fix: update naming conventions consistentcy +======= + config.logger.warn('Process is not running on K8S', e); +>>>>>>> 7359cb1e4... fix: updated files to adhere to linter return Resource.empty(); ======= private static readFileAsync = util.promisify(fs.readFile); diff --git a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts index 9116791ce58..5c6cb9271df 100644 --- a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts +++ b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts @@ -52,7 +52,10 @@ describe('awsEksDetector', () => { const errorMsg = { fileNotFoundError: new Error('cannot find cgroup file'), }; +<<<<<<< HEAD +======= +>>>>>>> 7359cb1e4... fix: updated files to adhere to linter const correctCgroupData = 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm'; const mockedClusterResponse = '{"data":{"cluster.name":"my-cluster"}}'; @@ -61,6 +64,8 @@ describe('awsEksDetector', () => { let sandbox: sinon.SinonSandbox; let readStub, fileStub, getCredStub; + let sandbox: sinon.SinonSandbox; + let readStub, fileStub, getCredStub; beforeEach(() => { sandbox = sinon.createSandbox(); ======= @@ -128,9 +133,14 @@ describe('awsEksDetector', () => { scope.done(); sandbox.assert.calledOnce(fileStub); +<<<<<<< HEAD sandbox.assert.calledTwice(readStub); sandbox.assert.calledTwice(getCredStub); +======= + sandbox.assert.calledOnce(readStub); + sandbox.assert.calledOnce(getCredStub); +>>>>>>> 7359cb1e4... fix: updated files to adhere to linter assert.ok(resource); assertK8sResource(resource, { clusterName: 'my-cluster', From 43a88206e4d020693a232251536331953f7d0173 Mon Sep 17 00:00:00 2001 From: Lo Date: Tue, 10 Nov 2020 18:41:36 -0800 Subject: [PATCH 18/26] fix: updated files to adhere to linter --- .../test/detectors/AwsEksDetector.test.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts index 5c6cb9271df..9a9d9523d39 100644 --- a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts +++ b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts @@ -53,19 +53,27 @@ describe('awsEksDetector', () => { fileNotFoundError: new Error('cannot find cgroup file'), }; <<<<<<< HEAD +<<<<<<< HEAD ======= >>>>>>> 7359cb1e4... fix: updated files to adhere to linter +======= + +>>>>>>> 7d6a1c05d... fix: updated files to adhere to linter const correctCgroupData = 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm'; const mockedClusterResponse = '{"data":{"cluster.name":"my-cluster"}}'; const mockedAwsAuth = 'my-auth'; const k8s_token = 'Bearer 31ada4fd-adec-460c-809a-9e56ceb75269'; +<<<<<<< HEAD let sandbox: sinon.SinonSandbox; let readStub, fileStub, getCredStub; +======= +>>>>>>> 7d6a1c05d... fix: updated files to adhere to linter let sandbox: sinon.SinonSandbox; let readStub, fileStub, getCredStub; + beforeEach(() => { sandbox = sinon.createSandbox(); ======= @@ -133,6 +141,7 @@ describe('awsEksDetector', () => { scope.done(); sandbox.assert.calledOnce(fileStub); +<<<<<<< HEAD <<<<<<< HEAD sandbox.assert.calledTwice(readStub); sandbox.assert.calledTwice(getCredStub); @@ -141,6 +150,11 @@ describe('awsEksDetector', () => { sandbox.assert.calledOnce(readStub); sandbox.assert.calledOnce(getCredStub); >>>>>>> 7359cb1e4... fix: updated files to adhere to linter +======= + sandbox.assert.calledTwice(readStub); + sandbox.assert.calledTwice(getCredStub); + +>>>>>>> 7d6a1c05d... fix: updated files to adhere to linter assert.ok(resource); assertK8sResource(resource, { clusterName: 'my-cluster', From 20d7ea4b6eaa6aeca4e15c980033fa356ae5ceea Mon Sep 17 00:00:00 2001 From: Lo Date: Thu, 26 Nov 2020 02:33:19 -0800 Subject: [PATCH 19/26] fix: add parsing to Cluster JSON response --- .../src/detectors/AwsEksDetector.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts index 6f2f923e8f2..febcd8b5259 100644 --- a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts +++ b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts @@ -325,6 +325,9 @@ export class AwsEksDetector implements Detector { * the identity properties in a local map. */ <<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> eda056317... fix: add parsing to Cluster JSON response private async _fetchString(options: https.RequestOptions): Promise { return await new Promise((resolve, reject) => { ======= From c3dd72b10708136ef5bf0423fdc5e7496bb1176d Mon Sep 17 00:00:00 2001 From: Lo Date: Wed, 2 Dec 2020 16:41:09 -0800 Subject: [PATCH 20/26] chore: rebase master --- .../src/detectors/AwsEksDetector.ts | 143 -------- .../test/detectors/AwsEksDetector.test.ts | 321 +----------------- 2 files changed, 2 insertions(+), 462 deletions(-) diff --git a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts index febcd8b5259..c4cae33d14d 100644 --- a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts +++ b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts @@ -15,7 +15,6 @@ */ import { -<<<<<<< HEAD Detector, Resource, CONTAINER_RESOURCE, @@ -25,17 +24,6 @@ import { import * as https from 'https'; import * as fs from 'fs'; import * as util from 'util'; -======= - Detector, - Resource, - CONTAINER_RESOURCE, - K8S_RESOURCE, - ResourceDetectionConfigWithLogger, - } from '@opentelemetry/resources'; - import * as https from 'https'; - import * as fs from 'fs'; - import * as util from 'util'; ->>>>>>> b9e63372f... feat: update implementation of https requests /** * The AwsEksDetector can be used to detect if a process is running in AWS Elastic @@ -61,18 +49,12 @@ export class AwsEksDetector implements Detector { readonly TIMEOUT_MS = 2000; readonly UTF8_UNICODE = 'utf8'; -<<<<<<< HEAD private static readFileAsync = util.promisify(fs.readFile); private static fileAccessAsync = util.promisify(fs.access); /** -<<<<<<< HEAD * The AwsEksDetector can be used to detect if a process is running on Amazon * Elastic Kubernetes and returns a promise containing a {@link Resource} -======= - * The AwsEksDetector can be used to detect if a process is running in AWS - * Eks and returns a promise containing a {@link Resource} ->>>>>>> 37f44d48d... fix: updated naming consistency * populated with instance metadata. Returns a promise containing an * empty {@link Resource} if the connection to kubernetes process * or aws config maps fails @@ -97,48 +79,11 @@ export class AwsEksDetector implements Detector { [CONTAINER_RESOURCE.ID]: containerId || '', }); } catch (e) { -<<<<<<< HEAD -<<<<<<< HEAD config.logger.warn('Process is not running on K8S', e); -======= - config.logger.warn('This process is not running on Kubernetes because either the token path or certificate path cannot be accessed ', e); ->>>>>>> 21e3884b1... fix: update naming conventions consistentcy -======= - config.logger.warn('Process is not running on K8S', e); ->>>>>>> 7359cb1e4... fix: updated files to adhere to linter return Resource.empty(); -======= - private static readFileAsync = util.promisify(fs.readFile); - private static fileAccessAsync = util.promisify(fs.access); - - async detect(config: ResourceDetectionConfigWithLogger): Promise { - try { - await AwsEksDetector.fileAccessAsync(this.K8S_TOKEN_PATH); - await AwsEksDetector.fileAccessAsync(this.K8S_CERT_PATH); - - if (!this._isEks(config)) { - config.logger.debug('AwsEcsDetector failed: Process is not running on Eks'); - return Resource.empty(); - } - - const containerId = await this._getContainerId(config); - const clusterName = await this._getClusterName(config); - - return !containerId && !clusterName - ? Resource.empty() - : new Resource({ - [K8S_RESOURCE.CLUSTER_NAME]: clusterName || '', - [CONTAINER_RESOURCE.ID]: containerId || '', - }); - } catch (e) { - config.logger.warn('Not running on K8S'); - return Resource.empty(); - } ->>>>>>> 7d354c340... fix: use file read async instead of sync } } -<<<<<<< HEAD /** * Attempts to make a connection to AWS Config map which will * determine whether the process is running on an EKS @@ -154,14 +99,10 @@ export class AwsEksDetector implements Detector { headers: { Authorization: await this._getK8sCredHeader(config), }, -<<<<<<< HEAD hostname: this.K8S_SVC_URL, method: 'GET', path: this.AUTH_CONFIGMAP_PATH, timeout: this.TIMEOUT_MS, -======= - ca: cert, ->>>>>>> 21e3884b1... fix: update naming conventions consistentcy }; return !!(await this._fetchString(options)); } @@ -180,14 +121,10 @@ export class AwsEksDetector implements Detector { headers: { Authorization: await this._getK8sCredHeader(config), }, -<<<<<<< HEAD host: this.K8S_SVC_URL, method: 'GET', path: this.CW_CONFIGMAP_PATH, timeout: this.TIMEOUT_MS, -======= - ca: cert, ->>>>>>> 21e3884b1... fix: update naming conventions consistentcy }; const response = await this._fetchString(options); try { @@ -213,29 +150,10 @@ export class AwsEksDetector implements Detector { return 'Bearer ' + content; } catch (e) { config.logger.warn('Unable to read Kubernetes client token.', e); -<<<<<<< HEAD -======= - private async _isEks(config: ResourceDetectionConfigWithLogger): Promise { - const options = { - hostname: this.K8S_SVC_URL, - path: this.AUTH_CONFIGMAP_PATH, - method: 'GET', - timeout: this.MILLISECOND_TIME_OUT, - HEADERS: { - "Authorization" : this._getK8sCredHeader(config), - }, - ca: JSON.stringify(AwsEksDetector.readFileAsync(this.K8S_CERT_PATH)), - } - const awsAuth = this._fetchString(options); - return !!awsAuth; ->>>>>>> 7d354c340... fix: use file read async instead of sync -======= ->>>>>>> 21e3884b1... fix: update naming conventions consistentcy } return ''; } -<<<<<<< HEAD /** * Read container ID from cgroup file * In EKS, even if we fail to find target file @@ -264,59 +182,6 @@ export class AwsEksDetector implements Detector { } return undefined; } -======= - private async _getClusterName(config: ResourceDetectionConfigWithLogger): Promise { - const options = { - host: this.K8S_SVC_URL, - path: this.CW_CONFIGMAP_PATH, - method: 'GET', - timeout: this.MILLISECOND_TIME_OUT, - HEADERS: { - "Authorization" : this._getK8sCredHeader(config), - }, - ca: JSON.stringify(AwsEksDetector.readFileAsync(this.K8S_CERT_PATH)), - } - return this._fetchString(options); - } - - private async _getK8sCredHeader(config: ResourceDetectionConfigWithLogger): Promise { - try { - const content = await AwsEksDetector.readFileAsync( - this.K8S_TOKEN_PATH, - 'utf8' - ); - return "Bearer " + content; - } catch (e) { - config.logger.warn(`AwsEksDetector failed to read container ID: ${e.message}`); - } - return ""; - } - - /** - * Read container ID from cgroup file - * In EKS, 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 async _getContainerId(config: ResourceDetectionConfigWithLogger): Promise { - try { - const rawData = await AwsEksDetector.readFileAsync( - this.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); - } - } - } catch (e) { - config.logger.warn(`AwsEksDetector failed to read container ID: ${e.message}`); - } - return undefined; - } ->>>>>>> 7d354c340... fix: use file read async instead of sync /** * Establishes an HTTP connection to AWS instance document url. @@ -324,16 +189,8 @@ export class AwsEksDetector implements Detector { * to get back a valid JSON document. Parses that document and stores * the identity properties in a local map. */ -<<<<<<< HEAD -<<<<<<< HEAD -======= ->>>>>>> eda056317... fix: add parsing to Cluster JSON response private async _fetchString(options: https.RequestOptions): Promise { return await new Promise((resolve, reject) => { -======= - private async _fetchString(options: https.RequestOptions): Promise { - return new Promise((resolve, reject) => { ->>>>>>> b9e63372f... feat: update implementation of https requests const timeoutId = setTimeout(() => { req.abort(); reject(new Error('EKS metadata api request timed out.')); diff --git a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts index 9a9d9523d39..4bf872268c7 100644 --- a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts +++ b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts @@ -15,36 +15,18 @@ */ import * as nock from 'nock'; -<<<<<<< HEAD -<<<<<<< HEAD import * as sinon from 'sinon'; import * as assert from 'assert'; import { Resource } from '@opentelemetry/resources'; import { awsEksDetector, AwsEksDetector } from '../../src'; import { -<<<<<<< HEAD assertK8sResource, assertContainerResource, assertEmptyResource, -======= -======= -import * as sinon from 'sinon'; ->>>>>>> 7d354c340... fix: use file read async instead of sync -import * as assert from 'assert'; -import { Resource } from '@opentelemetry/resources'; -import { awsEksDetector, AwsEksDetector } from '../../src'; -import { -<<<<<<< HEAD - assertK8sResource, ->>>>>>> 4c54840bb... test: add mock tests -======= - assertK8sResource, assertContainerResource, ->>>>>>> 7d354c340... fix: use file read async instead of sync } from '@opentelemetry/resources/test/util/resource-assertions'; import { NoopLogger } from '@opentelemetry/core'; const K8S_SVC_URL = awsEksDetector.K8S_SVC_URL; -<<<<<<< HEAD const AUTH_CONFIGMAP_PATH = awsEksDetector.AUTH_CONFIGMAP_PATH; const CW_CONFIGMAP_PATH = awsEksDetector.CW_CONFIGMAP_PATH; @@ -52,64 +34,22 @@ describe('awsEksDetector', () => { const errorMsg = { fileNotFoundError: new Error('cannot find cgroup file'), }; -<<<<<<< HEAD -<<<<<<< HEAD -======= ->>>>>>> 7359cb1e4... fix: updated files to adhere to linter -======= - ->>>>>>> 7d6a1c05d... fix: updated files to adhere to linter const correctCgroupData = 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm'; const mockedClusterResponse = '{"data":{"cluster.name":"my-cluster"}}'; const mockedAwsAuth = 'my-auth'; const k8s_token = 'Bearer 31ada4fd-adec-460c-809a-9e56ceb75269'; -<<<<<<< HEAD - let sandbox: sinon.SinonSandbox; - let readStub, fileStub, getCredStub; - -======= ->>>>>>> 7d6a1c05d... fix: updated files to adhere to linter let sandbox: sinon.SinonSandbox; let readStub, fileStub, getCredStub; beforeEach(() => { sandbox = sinon.createSandbox(); -======= -const K8S_TOKEN_PATH = awsEksDetector.K8S_TOKEN_PATH; -const K8S_CERT_PATH = awsEksDetector.K8S_CERT_PATH; -const AUTH_CONFIGMAP_PATH = awsEksDetector.AUTH_CONFIGMAP_PATH; -const CW_CONFIGMAP_PATH = awsEksDetector.CW_CONFIGMAP_PATH; - -======= - assertK8sResource, assertContainerResource, assertEmptyResource, -} from '@opentelemetry/resources/test/util/resource-assertions'; -import { NoopLogger } from '@opentelemetry/core'; - ->>>>>>> b9e63372f... feat: update implementation of https requests -describe('awsEksDetector', () => { - const errorMsg = { - fileNotFoundError: new Error('cannot find cgroup file'), - }; - let sandbox: sinon.SinonSandbox; - let readStub, fileStub, isEksStub, getClusterStub, getContainerStub, fetchStub; - const correctCgroupData = - 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm'; - const mockedClusterResponse = "my-cluster"; - - beforeEach(() => { -<<<<<<< HEAD ->>>>>>> 4c54840bb... test: add mock tests nock.disableNetConnect(); nock.cleanAll(); -======= ->>>>>>> b9e63372f... feat: update implementation of https requests - sandbox = sinon.createSandbox(); }); afterEach(() => { -<<<<<<< HEAD sandbox.restore(); nock.enableNetConnect(); }); @@ -141,20 +81,9 @@ describe('awsEksDetector', () => { scope.done(); sandbox.assert.calledOnce(fileStub); -<<<<<<< HEAD -<<<<<<< HEAD sandbox.assert.calledTwice(readStub); sandbox.assert.calledTwice(getCredStub); -======= - sandbox.assert.calledOnce(readStub); - sandbox.assert.calledOnce(getCredStub); ->>>>>>> 7359cb1e4... fix: updated files to adhere to linter -======= - sandbox.assert.calledTwice(readStub); - sandbox.assert.calledTwice(getCredStub); - ->>>>>>> 7d6a1c05d... fix: updated files to adhere to linter assert.ok(resource); assertK8sResource(resource, { clusterName: 'my-cluster', @@ -357,52 +286,11 @@ describe('awsEksDetector', () => { await awsEksDetector.detect({ logger: new NoopLogger(), }); -======= - nock.enableNetConnect(); - sandbox.restore(); - }); -<<<<<<< HEAD -}); - -describe('on succesful request', () => { - it ('should return an aws_eks_instance_resource', async () => { - const scope = nock(K8S_SVC_URL) - .get(CW_CONFIGMAP_PATH) - .matchHeader("Authorizations", mockedK8sCredentials) - .reply(200, mockedClusterResponse) - const resource: Resource = await awsEksDetector.detect({ - logger: new NoopLogger(), - }); - - scope.done(); - - assert.ok(resource); - assertK8sResource(resource, { - clusterName: 'my-cluster' - }) -}); -}); - -describe('on unsuccessful request', () => { - it ('should throw when receiving error response code', async () => { - const expectedError = new Error('Failed to load page, status code: 404'); - const scope = nock(K8S_SVC_URL) - .get(CW_CONFIGMAP_PATH) - .matchHeader("Authorizations", mockedK8sCredentials) - .reply(404, () => new Error()); - - try { - await awsEksDetector.detect({ - logger: new NoopLogger(), - }); - assert.ok(false, 'Expected to throw'); ->>>>>>> 4c54840bb... test: add mock tests } catch (err) { assert.deepStrictEqual(err, expectedError); } scope.done(); -<<<<<<< HEAD }); it('should return an empty resource when timed out', async () => { @@ -426,216 +314,11 @@ describe('on unsuccessful request', () => { await awsEksDetector.detect({ logger: new NoopLogger(), }); -======= -======= - - describe('on successful request', () => { - it ('should return an aws_eks_instance_resource', async () => { - fileStub = sandbox - .stub(AwsEksDetector, 'fileAccessAsync' as any) - .resolves(); -<<<<<<< HEAD - readStub = sinon.stub(AwsEksDetector, 'readFileAsync' as any); - readStub.onCall(1).resolves(correctCgroupData); - readStub.onCall(2).returns(mockedK8sCredentials); - readStub.onCall(3).returns(mockedK8sCredentials); ->>>>>>> 7d354c340... fix: use file read async instead of sync - - const scope = nock(K8S_SVC_URL) - .get(AUTH_CONFIGMAP_PATH) - .matchHeader('Authorizations', mockedK8sCredentials) - .reply(200, () => true) - .get(CW_CONFIGMAP_PATH) - .matchHeader('Authorizations', mockedK8sCredentials) - .reply(200, () => mockedClusterResponse); -<<<<<<< HEAD - - try { - await awsEksDetector.detect({ - logger: new NoopLogger(), - }); - assert.ok(false, 'Expected to throw'); ->>>>>>> 4c54840bb... test: add mock tests } catch (err) { assert.deepStrictEqual(err, expectedError); } -======= -======= - readStub = sandbox.stub(AwsEksDetector, 'readFileAsync' as any); - readStub.resolves(correctCgroupData); - - fetchStub = sandbox.stub(awsEksDetector, '_fetchString' as any) - .onCall(0).resolves('aws-auth'); - getClusterStub = sandbox.stub(awsEksDetector, '_getClusterName' as any).resolves(mockedClusterResponse); ->>>>>>> b9e63372f... feat: update implementation of https requests - - const resource: Resource = await awsEksDetector.detect({ - logger: new NoopLogger(), - }); ->>>>>>> 7d354c340... fix: use file read async instead of sync - - assert.ok(resource); - assertK8sResource(resource, { - clusterName: 'my-cluster', - }) - assertContainerResource(resource, { - id: 'bcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm', - }) - }); - - it ('should return a resource with cluster Name attribute without a container Id', async () => { - fileStub = sandbox - .stub(AwsEksDetector, 'fileAccessAsync' as any) - .resolves(''); - readStub = sandbox.stub(AwsEksDetector, 'readFileAsync' as any) - .resolves(correctCgroupData); - - isEksStub = sandbox.stub(awsEksDetector, '_isEks' as any).resolves(true); - getContainerStub = sandbox.stub(awsEksDetector, '_getContainerId' as any).resolves(''); - getClusterStub = sandbox.stub(awsEksDetector, '_getClusterName' as any).resolves(mockedClusterResponse); - - const resource: Resource = await awsEksDetector.detect({ - logger: new NoopLogger(), - }); - - assert.ok(resource); - assertContainerResource(resource, { - id: '', - }); - assertK8sResource(resource, { - clusterName: 'my-cluster', - }) - }); - - it ('should return a resource with container ID attribute without a clusterName', async () => { - fileStub = sandbox - .stub(AwsEksDetector, 'fileAccessAsync' as any) - .resolves(''); - readStub = sandbox.stub(AwsEksDetector, 'readFileAsync' as any) - .resolves(correctCgroupData); - - isEksStub = sandbox.stub(awsEksDetector, '_isEks' as any).resolves(true); - getClusterStub = sandbox.stub(awsEksDetector, '_getClusterName' as any).resolves(''); - - const resource: Resource = await awsEksDetector.detect({ - logger: new NoopLogger(), - }); - - assert.ok(resource); - assertK8sResource(resource, { - clusterName: '', - }) - assertContainerResource(resource, { - id: 'bcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm' - }); - }); - - it ('should return a resource with clusterName attribute when cgroup file does not contain valid Container ID', async () => { - fileStub = sandbox - .stub(AwsEksDetector, 'fileAccessAsync' as any) - .resolves(''); - readStub = sandbox.stub(AwsEksDetector, 'readFileAsync' as any) - .resolves(''); - - isEksStub = sandbox.stub(awsEksDetector, '_isEks' as any).resolves(true); - getClusterStub = sandbox.stub(awsEksDetector, '_getClusterName' as any).resolves(mockedClusterResponse); - - const resource: Resource = await awsEksDetector.detect({ - logger: new NoopLogger(), - }); - - assert.ok(resource); - assert.ok(resource); - assertK8sResource(resource, { - clusterName: 'my-cluster' - }) - assertContainerResource(resource, { - id: '' - }); - }); - - it ('should return a resource with clusterName attribute when cgroup file does not exist', async () => { - fileStub = sandbox - .stub(AwsEksDetector, 'fileAccessAsync' as any) - .resolves(''); - readStub = sandbox.stub(AwsEksDetector, 'readFileAsync' as any) - .rejects(errorMsg.fileNotFoundError); - - isEksStub = sandbox.stub(awsEksDetector, '_isEks' as any).resolves(true); - getClusterStub = sandbox.stub(awsEksDetector, '_getClusterName' as any).resolves(mockedClusterResponse); - - const resource: Resource = await awsEksDetector.detect({ - logger: new NoopLogger(), - }); - - assert.ok(resource); - assert.ok(resource); - assertK8sResource(resource, { - clusterName: 'my-cluster' - }) - assertContainerResource(resource, { - id: '' - }); - }); - - it ('should return an empty resource when not running on Eks', async () => { - fileStub = sandbox - .stub(AwsEksDetector, 'fileAccessAsync' as any) - .resolves(''); - - isEksStub = sandbox.stub(awsEksDetector, '_isEks' as any).resolves(false); - const resource: Resource = await awsEksDetector.detect({ - logger: new NoopLogger(), - }); - - assert.ok(resource); - assertEmptyResource(resource); - }); - - it ('should return an empty resource when k8s file does not exist', async () => { - const errorMsg = { - fileNotFoundError: new Error('cannot file k8s token file'), - }; - fileStub = sandbox - .stub(AwsEksDetector, 'fileAccessAsync' as any) - .rejects(errorMsg.fileNotFoundError); - - const resource: Resource = await awsEksDetector.detect({ - logger: new NoopLogger(), - }); - - assert.ok(resource); - assertEmptyResource(resource); - }); - - it ('should return an empty resource when containerId and clusterName are invalid', async () => { - fileStub = sandbox - .stub(AwsEksDetector, 'fileAccessAsync' as any) - .resolves(''); - readStub = sandbox.stub(AwsEksDetector, 'readFileAsync' as any) - .resolves(correctCgroupData); - - isEksStub = sandbox.stub(awsEksDetector, '_isEks' as any).resolves(true); - getContainerStub = sandbox.stub(awsEksDetector, '_getContainerId' as any).resolves(''); - getClusterStub = sandbox.stub(awsEksDetector, '_getClusterName' as any).resolves(''); - - const resource: Resource = await awsEksDetector.detect({ - logger: new NoopLogger(), - }); - - assert.ok(resource); - assertEmptyResource(resource); + scope.done(); }); -<<<<<<< HEAD -<<<<<<< HEAD - }); -}); -======= -}); -}); ->>>>>>> 4c54840bb... test: add mock tests -======= }); -}); ->>>>>>> 7d354c340... fix: use file read async instead of sync +}); \ No newline at end of file From ecabb305811f0fb3dd54c376accc7baa6f2b6765 Mon Sep 17 00:00:00 2001 From: Lo Date: Wed, 2 Dec 2020 16:43:44 -0800 Subject: [PATCH 21/26] chore: update casing consistency --- .../src/detectors/AwsEksDetector.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts index c4cae33d14d..5899ad3531a 100644 --- a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts +++ b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts @@ -185,7 +185,7 @@ export class AwsEksDetector implements Detector { /** * Establishes an HTTP connection to AWS instance document url. - * If the application is running on an Eks instance, we should be able + * If the application is running on an EKS instance, we should be able * to get back a valid JSON document. Parses that document and stores * the identity properties in a local map. */ From 05470de3372acb497d64d928255a4e0e2b9d405f Mon Sep 17 00:00:00 2001 From: Lo Date: Wed, 2 Dec 2020 16:46:43 -0800 Subject: [PATCH 22/26] chore: add spacing to end of file --- .../test/detectors/AwsEksDetector.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts index 4bf872268c7..ca0f7ed44bc 100644 --- a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts +++ b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts @@ -189,7 +189,7 @@ describe('awsEksDetector', () => { }); }); - it('should return an empty resource when not running on EKS', async () => { + it('should return an empty resource when not running on Eks', async () => { fileStub = sandbox .stub(AwsEksDetector, 'fileAccessAsync' as any) .resolves(''); @@ -321,4 +321,4 @@ describe('awsEksDetector', () => { scope.done(); }); }); -}); \ No newline at end of file +}); From 111b402104b37fdc9283f5ea9f649424acd794f2 Mon Sep 17 00:00:00 2001 From: Lo Date: Thu, 3 Dec 2020 07:03:22 -0800 Subject: [PATCH 23/26] chore: adjust naming consistency --- .../test/detectors/AwsEksDetector.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts index ca0f7ed44bc..ce350e2084a 100644 --- a/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts +++ b/packages/opentelemetry-resource-detector-aws/test/detectors/AwsEksDetector.test.ts @@ -265,7 +265,7 @@ describe('awsEksDetector', () => { describe('on unsuccesful request', () => { it('should throw when receiving error response code', async () => { - const expectedError = new Error('Eks metadata api request timed out.'); + const expectedError = new Error('EKS metadata api request timed out.'); fileStub = sandbox .stub(AwsEksDetector, 'fileAccessAsync' as any) .resolves(); From 557d68a4d8cd70495b8db0636499ca649f9bb5d9 Mon Sep 17 00:00:00 2001 From: Lo Date: Thu, 3 Dec 2020 08:22:04 -0800 Subject: [PATCH 24/26] chore: add description to getContainerID --- .../src/detectors/AwsEksDetector.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts index 5899ad3531a..29c8c88b4f9 100644 --- a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts +++ b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts @@ -155,10 +155,19 @@ export class AwsEksDetector implements Detector { } /** - * Read container ID from cgroup file - * In EKS, 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 + * Read container ID from cgroup file generated from docker which lists the full + * untruncated docker container ID at the end of each line. + * + * The predefined structure of calling /proc/self/cgroup when in a docker container has the structure: + * + * #:xxxxxx:/ + * or + * + * #:xxxxxx:/docker/64characterID + * + * This function takes advantage of that fact by just reading the 64-character ID from the end of the + * first line. In EKS, 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 async _getContainerId( From ba27742c747e538d67ae46f9908dc26cd0066880 Mon Sep 17 00:00:00 2001 From: Lo Date: Thu, 3 Dec 2020 08:31:49 -0800 Subject: [PATCH 25/26] chore: fix linting error --- .../src/detectors/AwsEksDetector.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts index 29c8c88b4f9..d18339bf911 100644 --- a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts +++ b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts @@ -157,16 +157,16 @@ export class AwsEksDetector implements Detector { /** * Read container ID from cgroup file generated from docker which lists the full * untruncated docker container ID at the end of each line. - * + * * The predefined structure of calling /proc/self/cgroup when in a docker container has the structure: - * - * #:xxxxxx:/ + * + * #:xxxxxx:/ * or - * + * * #:xxxxxx:/docker/64characterID - * + * * This function takes advantage of that fact by just reading the 64-character ID from the end of the - * first line. In EKS, even if we fail to find target file or target file does + * first line. In EKS, 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 */ From 9fcfb967fc8b77ecef45bd97bf979898d357003b Mon Sep 17 00:00:00 2001 From: Lo Date: Thu, 3 Dec 2020 08:55:22 -0800 Subject: [PATCH 26/26] chore: add space --- .../src/detectors/AwsEksDetector.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts index d18339bf911..5060b7d524a 100644 --- a/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts +++ b/packages/opentelemetry-resource-detector-aws/src/detectors/AwsEksDetector.ts @@ -161,6 +161,7 @@ export class AwsEksDetector implements Detector { * The predefined structure of calling /proc/self/cgroup when in a docker container has the structure: * * #:xxxxxx:/ + * * or * * #:xxxxxx:/docker/64characterID