Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import {
DetectedResourceAttributes,
} from '@opentelemetry/resources';
import {
CLOUDPLATFORMVALUES_GCP_KUBERNETES_ENGINE,
CLOUDPLATFORMVALUES_GCP_CLOUD_RUN,
CLOUDPROVIDERVALUES_GCP,
SEMRESATTRS_CLOUD_ACCOUNT_ID,
SEMRESATTRS_CLOUD_AVAILABILITY_ZONE,
Expand All @@ -34,6 +36,10 @@ import {
SEMRESATTRS_K8S_CLUSTER_NAME,
SEMRESATTRS_K8S_NAMESPACE_NAME,
SEMRESATTRS_K8S_POD_NAME,
SEMRESATTRS_FAAS_NAME,
SEMRESATTRS_FAAS_INSTANCE,
SEMRESATTRS_FAAS_VERSION,
SEMRESATTRS_CLOUD_PLATFORM,
} from '@opentelemetry/semantic-conventions';

/**
Expand All @@ -54,19 +60,29 @@ class GcpDetector implements ResourceDetector {
*/
private _getAttributes(): DetectedResourceAttributes {
const isAvail = gcpMetadata.isAvailable();

const instanceId = this._getInstanceId(isAvail);

const attributes: DetectedResourceAttributes = {
[SEMRESATTRS_CLOUD_PROVIDER]: (async () => {
return (await isAvail) ? CLOUDPROVIDERVALUES_GCP : undefined;
})(),
[SEMRESATTRS_CLOUD_ACCOUNT_ID]: this._getProjectId(isAvail),
[SEMRESATTRS_HOST_ID]: this._getInstanceId(isAvail),
[SEMRESATTRS_HOST_ID]: instanceId,
[SEMRESATTRS_HOST_NAME]: this._getHostname(isAvail),
[SEMRESATTRS_CLOUD_AVAILABILITY_ZONE]: this._getZone(isAvail),
};

// Add resource attributes for Cloud Run.
if (process.env.K_SERVICE) {
attributes[SEMRESATTRS_CLOUD_PLATFORM] = CLOUDPLATFORMVALUES_GCP_CLOUD_RUN;
attributes[SEMRESATTRS_FAAS_NAME] = process.env.K_SERVICE;
attributes[SEMRESATTRS_FAAS_VERSION] = process.env.K_REVISION;
attributes[SEMRESATTRS_FAAS_INSTANCE] = instanceId;
}

// Add resource attributes for K8s.
if (process.env.KUBERNETES_SERVICE_HOST) {
attributes[SEMRESATTRS_CLOUD_PLATFORM] = CLOUDPLATFORMVALUES_GCP_KUBERNETES_ENGINE;
attributes[SEMRESATTRS_K8S_CLUSTER_NAME] = this._getClusterName(isAvail);
attributes[SEMRESATTRS_K8S_NAMESPACE_NAME] = (async () => {
return (await isAvail) ? process.env.NAMESPACE : undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,15 @@ import {
assertContainerResource,
assertEmptyResource,
} from '@opentelemetry/contrib-test-utils';
import { detectResources } from '@opentelemetry/resources';
import { detectResources, Resource } from '@opentelemetry/resources';
import * as assert from 'assert';
import {
CLOUDPLATFORMVALUES_GCP_CLOUD_RUN,
SEMRESATTRS_CLOUD_PLATFORM,
SEMRESATTRS_FAAS_INSTANCE,
SEMRESATTRS_FAAS_NAME,
SEMRESATTRS_FAAS_VERSION,
} from '@opentelemetry/semantic-conventions';

const HEADERS = {
[HEADER_NAME.toLowerCase()]: HEADER_VALUE,
Expand All @@ -43,6 +51,34 @@ const ZONE_PATH = BASE_PATH + '/instance/zone';
const CLUSTER_NAME_PATH = BASE_PATH + '/instance/attributes/cluster-name';
const HOSTNAME_PATH = BASE_PATH + '/instance/hostname';

const assertFaasResource = (
resource: Resource,
validations: {
name?: string;
instance?: string;
version?: string;
}
) => {
if (validations.name) {
assert.strictEqual(
resource.attributes[SEMRESATTRS_FAAS_NAME],
validations.name
);
}
if (validations.instance) {
assert.strictEqual(
resource.attributes[SEMRESATTRS_FAAS_INSTANCE],
validations.instance
);
}
if (validations.version) {
assert.strictEqual(
resource.attributes[SEMRESATTRS_FAAS_VERSION],
validations.version
);
}
};

describe('gcpDetector', () => {
describe('.detect', () => {
before(() => {
Expand All @@ -55,6 +91,8 @@ describe('gcpDetector', () => {
delete process.env.NAMESPACE;
delete process.env.CONTAINER_NAME;
delete process.env.HOSTNAME;
delete process.env.K_SERVICE;
delete process.env.K_REVISION;
});

beforeEach(() => {
Expand All @@ -64,6 +102,8 @@ describe('gcpDetector', () => {
delete process.env.NAMESPACE;
delete process.env.CONTAINER_NAME;
delete process.env.HOSTNAME;
delete process.env.K_SERVICE;
delete process.env.K_REVISION;
});

it('should return resource with GCP metadata', async () => {
Expand Down Expand Up @@ -181,5 +221,47 @@ describe('gcpDetector', () => {
await resource.waitForAsyncAttributes?.();
assertEmptyResource(resource);
});

it('should populate Cloud Run attributes when K_SERVICE is set', async () => {
process.env.K_SERVICE = 'my-cloud-run-service';
process.env.K_REVISION = 'my-cloud-run-revision';

const scope = nock(HOST_ADDRESS)
.get(INSTANCE_PATH)
.reply(200, {}, HEADERS)
.get(INSTANCE_ID_PATH)
.reply(200, () => '4520031799277581759', HEADERS)
.get(PROJECT_ID_PATH)
.reply(200, () => 'my-project-id', HEADERS)
.get(ZONE_PATH)
.reply(200, () => 'project/zone/my-zone', HEADERS)
.get(HOSTNAME_PATH)
.reply(200, () => 'dev.my-project.local', HEADERS);
const secondaryScope = nock(SECONDARY_HOST_ADDRESS)
.get(INSTANCE_PATH)
.reply(200, {}, HEADERS);

const resource = detectResources({ detectors: [gcpDetector] });
await resource.waitForAsyncAttributes?.();

secondaryScope.done();
scope.done();

assert.strictEqual(resource.attributes[SEMRESATTRS_CLOUD_PLATFORM],CLOUDPLATFORMVALUES_GCP_CLOUD_RUN)
assertCloudResource(resource, {
provider: 'gcp',
accountId: 'my-project-id',
zone: 'my-zone',
});
assertHostResource(resource, {
id: '4520031799277581759',
name: 'dev.my-project.local',
});
assertFaasResource(resource, {
name: 'my-cloud-run-service',
version: 'my-cloud-run-revision',
instance: '4520031799277581759',
})
});
});
});