Skip to content

Commit

Permalink
feat(dynamodb): added endpoint filtering (#1484)
Browse files Browse the repository at this point in the history
refs INSTA-16323
  • Loading branch information
aryamohanan authored Dec 13, 2024
1 parent ce5c9d8 commit 93e4023
Show file tree
Hide file tree
Showing 11 changed files with 370 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const { expect } = require('chai');
const { fail } = expect;
const supportedVersion = require('@instana/core').tracing.supportedVersion;
const config = require('../../../../../../../core/test/config');
const { retry, stringifyItems, delay } = require('../../../../../../../core/test/test_util');
const { retry, stringifyItems, delay, expectAtLeastOneMatching } = require('../../../../../../../core/test/test_util');
const ProcessControls = require('../../../../../test_util/ProcessControls');
const globalAgent = require('../../../../../globalAgent');
const {
Expand Down Expand Up @@ -252,4 +252,84 @@ mochaSuiteFn('tracing/cloud/aws-sdk/v2/dynamodb', function () {
});
});
});
describe('when ignore-endpoints enabled via tracing config', async () => {
let appControls;
before(async () => {
appControls = new ProcessControls({
dirname: __dirname,
useGlobalAgent: true,
env: {
AWS_DYNAMODB_TABLE_NAME: tableName,
INSTANA_IGNORE_ENDPOINTS: 'dynamodb:list'
}
});
await appControls.startAndWaitForAgentConnection();
});

beforeEach(async () => {
await agentControls.clearReceivedTraceData();
});

after(async () => {
await appControls.stop();
});

afterEach(async () => {
await appControls.clearIpcMessages();
});

const requestMethod = getNextCallMethod();
it('should ignore spans for configured ignore endpoints(listTables)', async function () {
const apiPath = `/listTables/${requestMethod}`;

await appControls.sendRequest({
method: 'GET',
path: `${apiPath}`
});
await delay(1000);
const spans = await agentControls.getSpans();
// 1 x http entry span
// 1 x http client span
expect(spans.length).to.equal(2);
spans.forEach(span => {
expect(span.n).not.to.equal('dynamodb');
});
expectAtLeastOneMatching(spans, [
span => expect(span.n).to.equal('node.http.server'),
span => expect(span.data.http.method).to.equal('GET')
]);
expectAtLeastOneMatching(spans, [
span => expect(span.n).to.equal('node.http.client'),
span => expect(span.data.http.method).to.equal('GET')
]);
});
it('should not ignore spans for endpoints that are not in the ignore list', async () => {
const apiPath = `/scan/${requestMethod}`;

await appControls.sendRequest({
method: 'GET',
path: `${apiPath}`
});
await delay(1000);
const spans = await agentControls.getSpans();

// 1 x http entry span
// 1 x http client span
// 1 x dynamodb span
expect(spans.length).to.equal(3);

expectAtLeastOneMatching(spans, [
span => expect(span.n).to.equal('dynamodb'),
span => expect(span.data.dynamodb.op).to.equal('scan')
]);
expectAtLeastOneMatching(spans, [
span => expect(span.n).to.equal('node.http.server'),
span => expect(span.data.http.method).to.equal('GET')
]);
expectAtLeastOneMatching(spans, [
span => expect(span.n).to.equal('node.http.client'),
span => expect(span.data.http.method).to.equal('GET')
]);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const { expect } = require('chai');
const { fail } = expect;
const supportedVersion = require('@instana/core').tracing.supportedVersion;
const config = require('@instana/core/test/config');
const { retry, stringifyItems, delay } = require('@instana/core/test/test_util');
const { retry, stringifyItems, delay, expectAtLeastOneMatching } = require('@instana/core/test/test_util');
const ProcessControls = require('../../../../../test_util/ProcessControls');
const globalAgent = require('../../../../../globalAgent');
const {
Expand Down Expand Up @@ -342,6 +342,149 @@ function start(version, requestMethod, reducedTestSuite = false) {
});
});

describe('ignore-endpoints:', function () {
describe('when ignore-endpoints is enabled via agent configuration', () => {
const { AgentStubControls } = require('../../../../../apps/agentStubControls');
const customAgentControls = new AgentStubControls();
let controls;
const tableName = createTableName();
before(async () => {
await customAgentControls.startAgent({
ignoreEndpoints: { dynamodb: ['listTables'] }
});

controls = new ProcessControls({
agentControls: customAgentControls,
appPath: path.join(__dirname, './app'),
env: {
AWS_DYNAMODB_TABLE_NAME: tableName,
AWS_SDK_CLIENT_DYNAMODB_REQUIRE: version
}
});
await controls.startAndWaitForAgentConnection();
});

beforeEach(async () => {
await customAgentControls.clearReceivedTraceData();
});

after(async () => {
await customAgentControls.stopAgent();
await controls.stop();
});
after(() => cleanup(tableName));

it('should ignore dynamodb spans for ignored endpoints (listTables)', async () => {
const apiPath = `/listTables/${requestMethod}`;

await controls.sendRequest({
method: 'GET',
path: `${apiPath}`
});
await delay(1000);
const spans = await customAgentControls.getSpans();
// 1 x http entry span
// 1 x http client span
expect(spans.length).to.equal(2);
spans.forEach(span => {
expect(span.n).not.to.equal('dynamodb');
});
expectAtLeastOneMatching(spans, [
span => expect(span.n).to.equal('node.http.server'),
span => expect(span.data.http.method).to.equal('GET')
]);
expectAtLeastOneMatching(spans, [
span => expect(span.n).to.equal('node.http.client'),
span => expect(span.data.http.method).to.equal('GET')
]);
});
});
describe('ignore-endpoints enabled via tracing config', async () => {
const tableName = createTableName();
let appControls;

before(async () => {
appControls = new ProcessControls({
useGlobalAgent: true,
appPath: path.join(__dirname, './app'),
env: {
AWS_DYNAMODB_TABLE_NAME: tableName,
AWS_SDK_CLIENT_DYNAMODB_REQUIRE: version,
INSTANA_IGNORE_ENDPOINTS: 'dynamodb:listTables'
}
});
await appControls.startAndWaitForAgentConnection();
});

beforeEach(async () => {
await agentControls.clearReceivedTraceData();
});

after(async () => {
await appControls.stop();
});

afterEach(async () => {
await appControls.clearIpcMessages();
});

after(() => cleanup(tableName));

it('should ignore spans for configured ignore endpoints(listTables)', async function () {
const apiPath = `/listTables/${requestMethod}`;

await appControls.sendRequest({
method: 'GET',
path: `${apiPath}`
});
await delay(1000);
const spans = await agentControls.getSpans();
// 1 x http entry span
// 1 x http client span
expect(spans.length).to.equal(2);
spans.forEach(span => {
expect(span.n).not.to.equal('dynamodb');
});
expectAtLeastOneMatching(spans, [
span => expect(span.n).to.equal('node.http.server'),
span => expect(span.data.http.method).to.equal('GET')
]);
expectAtLeastOneMatching(spans, [
span => expect(span.n).to.equal('node.http.client'),
span => expect(span.data.http.method).to.equal('GET')
]);
});
it('should not ignore spans for endpoints that are not in the ignore list', async () => {
const apiPath = `/createTable/${requestMethod}`;

await appControls.sendRequest({
method: 'GET',
path: `${apiPath}`
});
await delay(1000);
const spans = await agentControls.getSpans();

// 1 x http entry span
// 1 x http client span
// 1 x dynamodb span
expect(spans.length).to.equal(3);

expectAtLeastOneMatching(spans, [
span => expect(span.n).to.equal('dynamodb'),
span => expect(span.data.dynamodb.op).to.equal('createTable')
]);
expectAtLeastOneMatching(spans, [
span => expect(span.n).to.equal('node.http.server'),
span => expect(span.data.http.method).to.equal('GET')
]);
expectAtLeastOneMatching(spans, [
span => expect(span.n).to.equal('node.http.client'),
span => expect(span.data.http.method).to.equal('GET')
]);
});
});
});

function verify(controls, response, apiPath, operation, withError, tableName) {
return retry(
() =>
Expand Down
5 changes: 3 additions & 2 deletions packages/core/src/tracing/backend_mappers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@

'use strict';

const mapper = require('./mapper');
module.exports = {
get transform() {
return (/** @type {import('../../core').InstanaBaseSpan} */ span) => {
try {
return require(`./${span.n}_mapper`).transform(span);
} catch {
return mapper.transform(span);
} catch (error) {
return span;
}
};
Expand Down
61 changes: 61 additions & 0 deletions packages/core/src/tracing/backend_mappers/mapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* (c) Copyright IBM Corp. 2024
*/

'use strict';

/**
* @typedef {Object<string, string>} FieldMapping
* @typedef {Object<string, FieldMapping>} FieldMappings
*/

/**
* Field mappings for different span types.
*
* This FieldMappings defines how internal span fields are mapped to backend fields
* for various span types (e.g., dynamodb, redis).
*
* As new span types needs to add, simply add their respective mappings
* following the same format (e.g., 'internal-field': 'backend-field').
*
* @type {FieldMappings}
*/
const fieldMappings = {
dynamodb: {
/// Maps internal field 'operation' to backend field 'op'
operation: 'op'
},
redis: {
operation: 'command'
}
};

/**
* Transforms span data fields to match the backend format.
*
* @param {import('../../core').InstanaBaseSpan} span
* @returns {import('../../core').InstanaBaseSpan} The transformed span.
*/
module.exports.transform = span => {
const spanName = span.n;
const mappings = fieldMappings[spanName];
// If no mappings exist for the span name or the span data, return the original span
if (!mappings || !span.data[spanName]) return span;

Object.keys(span.data[spanName]).forEach(internalField => {
// Only proceed if there's a mapping for the internal field in the current span type
if (internalField in mappings) {
const backendField = mappings[internalField];

if (backendField) {
span.data[spanName][backendField] = span.data[spanName][internalField];
delete span.data[spanName][internalField];
} else {
// If backendField is falsy, remove the internalField from span data
delete span.data[spanName][internalField];
}
}
});

return span;
};
29 changes: 0 additions & 29 deletions packages/core/src/tracing/backend_mappers/redis_mapper.js

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ class InstanaAWSDynamoDB extends InstanaAWSProduct {
buildSpanData(ctx, operation, params) {
const operationInfo = operationsInfo[operation];
const spanData = {
op: operationInfo.op,
operation: operationInfo.op,
region: ctx.config.region
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class InstanaAWSDynamoDB extends InstanaAWSProduct {

buildSpanData(operation, params) {
const spanData = {
op: this.convertOperationName(operation)
operation: this.convertOperationName(operation)
};

if (params && params.TableName) {
Expand Down
Loading

0 comments on commit 93e4023

Please sign in to comment.