feat: added AWS Beanstalk Plugins Resource Detector (#1385)

Co-authored-by: Daniel Dyla <dyladan@users.noreply.github.com>
This commit is contained in:
Cong Zou 2020-08-24 09:25:41 -05:00 committed by GitHub
parent b2eedfb04d
commit 2a8555f71b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 214 additions and 0 deletions

View File

@ -0,0 +1,79 @@
/*
* 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,
SERVICE_RESOURCE,
ResourceDetectionConfigWithLogger,
} from '@opentelemetry/resources';
import * as fs from 'fs';
import * as util from 'util';
/**
* The AwsBeanstalkDetector can be used to detect if a process is running in AWS Elastic
* Beanstalk and return a {@link Resource} populated with data about the beanstalk
* 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 of Elastic Beanstalk plugins
*/
const DEFAULT_BEANSTALK_CONF_PATH =
'/var/elasticbeanstalk/xray/environment.conf';
const WIN_OS_BEANSTALK_CONF_PATH =
'C:\\Program Files\\Amazon\\XRay\\environment.conf';
export class AwsBeanstalkDetector implements Detector {
BEANSTALK_CONF_PATH: string;
private static readFileAsync = util.promisify(fs.readFile);
private static fileAccessAsync = util.promisify(fs.access);
constructor() {
if (process.platform === 'win32') {
this.BEANSTALK_CONF_PATH = WIN_OS_BEANSTALK_CONF_PATH;
} else {
this.BEANSTALK_CONF_PATH = DEFAULT_BEANSTALK_CONF_PATH;
}
}
async detect(config: ResourceDetectionConfigWithLogger): Promise<Resource> {
try {
await AwsBeanstalkDetector.fileAccessAsync(
this.BEANSTALK_CONF_PATH,
fs.constants.R_OK
);
const rawData = await AwsBeanstalkDetector.readFileAsync(
this.BEANSTALK_CONF_PATH,
'utf8'
);
const parsedData = JSON.parse(rawData);
return new Resource({
[SERVICE_RESOURCE.NAME]: 'elastic_beanstalk',
[SERVICE_RESOURCE.NAMESPACE]: parsedData.environment_name,
[SERVICE_RESOURCE.VERSION]: parsedData.version_label,
[SERVICE_RESOURCE.INSTANCE_ID]: parsedData.deployment_id,
});
} catch (e) {
config.logger.debug(`AwsBeanstalkDetector failed: ${e.message}`);
return Resource.empty();
}
}
}
export const awsBeanstalkDetector = new AwsBeanstalkDetector();

View File

@ -15,3 +15,4 @@
*/
export * from './AwsEc2Detector';
export * from './AwsBeanstalkDetector';

View File

@ -0,0 +1,134 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as assert from 'assert';
import * as sinon from 'sinon';
import { awsBeanstalkDetector, AwsBeanstalkDetector } from '../../src';
import {
assertEmptyResource,
assertServiceResource,
} from '@opentelemetry/resources/test/util/resource-assertions';
import { NoopLogger } from '@opentelemetry/core';
describe('BeanstalkResourceDetector', () => {
const err = new Error('failed to read config file');
const data = {
version_label: 'app-5a56-170119_190650-stage-170119_190650',
deployment_id: '32',
environment_name: 'scorekeep',
};
const noisyData = {
noise: 'noise',
version_label: 'app-5a56-170119_190650-stage-170119_190650',
deployment_id: '32',
environment_name: 'scorekeep',
};
let readStub, fileStub;
let sandbox: sinon.SinonSandbox;
beforeEach(() => {
sandbox = sinon.createSandbox();
});
afterEach(() => {
sandbox.restore();
});
it('should successfully return resource data', async () => {
fileStub = sandbox
.stub(AwsBeanstalkDetector, 'fileAccessAsync' as any)
.resolves();
readStub = sandbox
.stub(AwsBeanstalkDetector, 'readFileAsync' as any)
.resolves(JSON.stringify(data));
sandbox.stub(JSON, 'parse').returns(data);
const resource = await awsBeanstalkDetector.detect({
logger: new NoopLogger(),
});
sandbox.assert.calledOnce(fileStub);
sandbox.assert.calledOnce(readStub);
assert.ok(resource);
assertServiceResource(resource, {
name: 'elastic_beanstalk',
namespace: 'scorekeep',
version: 'app-5a56-170119_190650-stage-170119_190650',
instanceId: '32',
});
});
it('should successfully return resource data with noise', async () => {
fileStub = sandbox
.stub(AwsBeanstalkDetector, 'fileAccessAsync' as any)
.resolves();
readStub = sandbox
.stub(AwsBeanstalkDetector, 'readFileAsync' as any)
.resolves(JSON.stringify(noisyData));
sandbox.stub(JSON, 'parse').returns(noisyData);
const resource = await awsBeanstalkDetector.detect({
logger: new NoopLogger(),
});
sandbox.assert.calledOnce(fileStub);
sandbox.assert.calledOnce(readStub);
assert.ok(resource);
assertServiceResource(resource, {
name: 'elastic_beanstalk',
namespace: 'scorekeep',
version: 'app-5a56-170119_190650-stage-170119_190650',
instanceId: '32',
});
});
it('should return empty resource when failing to read file', async () => {
fileStub = sandbox
.stub(AwsBeanstalkDetector, 'fileAccessAsync' as any)
.resolves();
readStub = sandbox
.stub(AwsBeanstalkDetector, 'readFileAsync' as any)
.rejects(err);
const resource = await awsBeanstalkDetector.detect({
logger: new NoopLogger(),
});
sandbox.assert.calledOnce(fileStub);
sandbox.assert.calledOnce(readStub);
assert.ok(resource);
assertEmptyResource(resource);
});
it('should return empty resource when config file does not exist', async () => {
fileStub = sandbox
.stub(AwsBeanstalkDetector, 'fileAccessAsync' as any)
.rejects(err);
readStub = sandbox
.stub(AwsBeanstalkDetector, 'readFileAsync' as any)
.resolves(JSON.stringify(data));
const resource = await awsBeanstalkDetector.detect({
logger: new NoopLogger(),
});
sandbox.assert.calledOnce(fileStub);
sandbox.assert.notCalled(readStub);
assert.ok(resource);
assertEmptyResource(resource);
});
});