Skip to content

Commit 8f9fda2

Browse files
feat: get dash0 token from secret manager (#44)
1 parent f5a29b0 commit 8f9fda2

15 files changed

Lines changed: 652 additions & 85 deletions

File tree

Cargo.lock

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ tracing-subscriber = { version = "0.3", features = ["fmt", "json", "env-filter"]
2323
hex = "0.4.3"
2424
base64 = "0.22"
2525
regex = "1.12.2"
26+
hmac = "0.12"
2627

2728
[features]
2829
default = []

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,16 @@ See the release page for the latest ARNs of the extension layers for each runtim
2222

2323
* `DASH0_ENDPOINT` - The integration endpoint for you organization in Dash0, i.e. `https://ingress.eu-west-1.aws.dash0.com:4318`.
2424

25-
* `DASH0_TOKEN` - The API token for your Dash0 project.
25+
* Either `DASH0_TOKEN` or `DASH0_TOKEN_SECRET_ARN` must be set (see below).
2626

2727
### Optional
2828

29+
* `DASH0_TOKEN` - The API token for your Dash0 project. Either this or `DASH0_TOKEN_SECRET_ARN` must be set.
30+
31+
* `DASH0_TOKEN_SECRET_ARN` - The ARN of an AWS Secrets Manager secret containing the Dash0 API token. Either this or `DASH0_TOKEN` must be set. If both are set, `DASH0_TOKEN` takes precedence.
32+
33+
* `DASH0_TOKEN_SECRET_KEY` - The JSON key within the secret to extract the token from. Required when the secret stored in `DASH0_TOKEN_SECRET_ARN` is a JSON object. If not set, the entire secret value is used as the token.
34+
2935
* `DASH0_DISABLE_AUTO_INSTRUMENTATION` - Auto-instrumentation can be turned off by this environment variable, which will result in creating synthetic traces by the extension for all invocations.
3036

3137
* `DASH0_SEND_ON_INVOCATION_END` - The extension has two modes of sending to the backend, either on invocation end or on the next invocation. The default is `true`. Sending on invocation end will increase the billed duration of the lambda, but not the response time. Sending on next invocation will decrease the billed duration since the sending will take place in parallel of the regular execution, but might delay the sending up to 7 minutes in case of last invocation in the container.

integration-tests/iac/lib/integration-tests-stack.ts

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import * as lambda from 'aws-cdk-lib/aws-lambda';
44
import * as ecr_assets from 'aws-cdk-lib/aws-ecr-assets';
55
import * as iam from 'aws-cdk-lib/aws-iam';
66
import * as logs from 'aws-cdk-lib/aws-logs';
7+
import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
78
import * as cr from 'aws-cdk-lib/custom-resources';
89
import * as lambdaNodejs from 'aws-cdk-lib/aws-lambda-nodejs';
910
import * as path from 'path';
@@ -39,6 +40,7 @@ interface SubStackProps extends cdk.NestedStackProps {
3940
layer: lambda.ILayerVersion;
4041
logGroup: logs.ILogGroup;
4142
prefix: string;
43+
dash0TokenSecretArn?: string;
4244
}
4345

4446
function createLambdas(
@@ -48,8 +50,9 @@ function createLambdas(
4850
role: iam.Role,
4951
logGroup: logs.ILogGroup,
5052
prefix: string,
51-
overrides?: { handler?: string; code?: lambda.Code; memorySize?: number }
53+
overrides?: { handler?: string; code?: lambda.Code; memorySize?: number; dash0TokenSecretArn?: string }
5254
) {
55+
const latestRuntime = runtimes[runtimes.length - 1];
5356
for (const runtime of runtimes) {
5457
for (const architecture of [lambda.Architecture.X86_64, lambda.Architecture.ARM_64]) {
5558
for (const invocationEnd of ["true", "false"]) {
@@ -64,13 +67,20 @@ function createLambdas(
6467
continue;
6568
}
6669
const runtimeName = runtime.name.replace(/\./g, '-');
70+
const useSecretManager = overrides?.dash0TokenSecretArn
71+
&& runtime === latestRuntime
72+
&& scenario === "success";
6773
const environment: any = {
6874
AWS_LAMBDA_EXEC_WRAPPER: "/opt/wrapper",
69-
DASH0_TOKEN: process.env.DASH0_DEV_API_TOKEN!,
7075
DASH0_ENDPOINT: "https://ingress.eu-west-1.aws.dash0-dev.com:4318",
7176
DASH0_EXTENSION_LOG_LEVEL: "info",
7277
DASH0_SEND_ON_INVOCATION_END: invocationEnd,
7378
};
79+
if (useSecretManager) {
80+
environment["DASH0_TOKEN_SECRET_ARN"] = overrides!.dash0TokenSecretArn!;
81+
} else {
82+
environment["DASH0_TOKEN"] = process.env.DASH0_DEV_API_TOKEN!;
83+
}
7484
if (traced === "false") {
7585
environment["DASH0_DISABLE_AUTO_INSTRUMENTATION"] = "true";
7686
}
@@ -122,6 +132,7 @@ class PythonStack extends cdk.NestedStack {
122132

123133
createLambdas(this, runtimes, props.layer, props.role, props.logGroup, props.prefix, {
124134
code: createPythonCode(),
135+
dash0TokenSecretArn: props.dash0TokenSecretArn,
125136
});
126137

127138
// Dependency conflict test lambdas
@@ -174,7 +185,9 @@ class NodeStack extends cdk.NestedStack {
174185
lambda.Runtime.NODEJS_24_X,
175186
];
176187

177-
createLambdas(this, runtimes, props.layer, props.role, props.logGroup, props.prefix);
188+
createLambdas(this, runtimes, props.layer, props.role, props.logGroup, props.prefix, {
189+
dash0TokenSecretArn: props.dash0TokenSecretArn,
190+
});
178191

179192
for (const runtime of runtimes) {
180193
const runtimeName = runtime.name.replace(/\./g, '-');
@@ -226,6 +239,7 @@ class NodeStack extends cdk.NestedStack {
226239
loggingFormat: lambda.LoggingFormat.TEXT,
227240
});
228241
}
242+
229243
}
230244
}
231245

@@ -247,6 +261,7 @@ class JavaStack extends cdk.NestedStack {
247261
handler: 'org.example.HelloHandler::handleRequest',
248262
code: javaCode,
249263
memorySize: 512,
264+
dash0TokenSecretArn: props.dash0TokenSecretArn,
250265
};
251266
const runtimes = [lambda.Runtime.JAVA_25, lambda.Runtime.JAVA_21, lambda.Runtime.JAVA_17];
252267

@@ -364,25 +379,35 @@ export class IntegrationTestsStack extends cdk.Stack {
364379
retention: logs.RetentionDays.ONE_DAY,
365380
});
366381

382+
const dash0TokenSecret = new secretsmanager.Secret(this, 'Dash0TokenSecret', {
383+
secretName: `${prefix}dash0-token-secret`,
384+
secretStringValue: cdk.SecretValue.unsafePlainText(process.env.DASH0_DEV_API_TOKEN!),
385+
removalPolicy: cdk.RemovalPolicy.DESTROY,
386+
});
387+
dash0TokenSecret.grantRead(role);
388+
367389
new PythonStack(this, 'PythonStack', {
368390
role,
369391
layer: pythonLayer,
370392
logGroup: sharedLogGroup,
371393
prefix,
394+
dash0TokenSecretArn: dash0TokenSecret.secretArn,
372395
});
373396

374397
new NodeStack(this, 'NodeStack', {
375398
role,
376399
layer: nodeLayer,
377400
logGroup: sharedLogGroup,
378401
prefix,
402+
dash0TokenSecretArn: dash0TokenSecret.secretArn,
379403
});
380404

381405
new JavaStack(this, 'JavaStack', {
382406
role,
383407
layer: javaLayer,
384408
logGroup: sharedLogGroup,
385409
prefix,
410+
dash0TokenSecretArn: dash0TokenSecret.secretArn,
386411
});
387412

388413
new ManualStack(this, 'ManualStack', {

integration-tests/tests/src/test-python-success.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ const verifySuccessInvocation = async (functionName: string, invocationEnd: bool
2323

2424
// Verify DASH0_TOKEN is masked in resource attributes
2525
const resourceAttributes = getAttributesMap(resource.attributes);
26-
expect(resourceAttributes['process.environment_variable.DASH0_TOKEN'].stringValue).toEqual('****');
26+
if (!functionName.includes("python3-14")) {
27+
expect(resourceAttributes['process.environment_variable.DASH0_TOKEN'].stringValue).toEqual('****');
28+
}
2729

2830
let httpSpanId: string | undefined = undefined;
2931
if (traced) {

opt/java/opentelemetry-java-distro/custom/src/main/java/io/dash0/javaagent/Dash0Configurator.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,6 @@ private SdkTracerProviderBuilder tracerProviderCustomizer(
8080

8181
private Map<String, String> propertiesCustomizer(ConfigProperties originalCfg) {
8282
String accessToken = originalCfg.getString(DASH0_TOKEN);
83-
if (Strings.isBlank(accessToken)) {
84-
LOGGER.warning(
85-
"Dash0 token not provided (env var 'DASH0_TOKEN' not set); no data will be sent.");
86-
return Collections.emptyMap();
87-
}
8883

8984
Map<String, String> customizedCfg = new HashMap<>();
9085

opt/node/distro/src/bootstrap.ts

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -128,13 +128,7 @@ export const init = async (): Promise<Dash0SdkInitialization> => {
128128

129129
logger.debug(`Instrumented modules: ${instrumentedModules.join(', ')}`);
130130

131-
const dashToken = process.env.DASH0_TOKEN;
132-
133-
if (!dashToken) {
134-
logger.warn(
135-
'The Dash0 token is not available (the "DASH0_TOKEN" environment variable is not set): no telemetry will be sent.'
136-
);
137-
}
131+
const dashToken = process.env.DASH0_TOKEN || '';
138132

139133
const infrastructureDetectors = [
140134
envDetector,
@@ -162,23 +156,21 @@ export const init = async (): Promise<Dash0SdkInitialization> => {
162156
);
163157
}
164158

165-
if (dashToken) {
166-
const otlpTraceExporter = new OTLPTraceExporter({
167-
url: traceEndpoint,
168-
headers: {
169-
Authorization: `Bearer ${dashToken.trim()}`,
170-
},
171-
});
159+
const otlpTraceExporter = new OTLPTraceExporter({
160+
url: traceEndpoint,
161+
headers: {
162+
Authorization: `Bearer ${dashToken.trim()}`,
163+
},
164+
});
172165

173-
spanProcessors.push(
174-
new BatchSpanProcessor(otlpTraceExporter, {
175-
// The maximum queue size. After the size is reached spans are dropped.
176-
maxQueueSize: 1000,
177-
// The maximum batch size of every export. It must be smaller or equal to maxQueueSize.
178-
maxExportBatchSize: 100,
179-
})
180-
);
181-
}
166+
spanProcessors.push(
167+
new BatchSpanProcessor(otlpTraceExporter, {
168+
// The maximum queue size. After the size is reached spans are dropped.
169+
maxQueueSize: 1000,
170+
// The maximum batch size of every export. It must be smaller or equal to maxQueueSize.
171+
maxExportBatchSize: 100,
172+
})
173+
);
182174

183175
// Create providers with processors
184176
const tracerProvider = new NodeTracerProvider({

opt/node/distro/src/distro.test.ts

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -74,36 +74,6 @@ describe('Distro initialization', () => {
7474

7575
});
7676

77-
describe('without the DASH0_TOKEN environment variable set', () => {
78-
test('should not initialize the OTLPTraceExporter', async () => {
79-
await jest.isolateModulesAsync(async () => {
80-
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
81-
jest.mock('@opentelemetry/exporter-trace-otlp-http');
82-
83-
const { init } = jest.requireActual('./distro');
84-
await init;
85-
86-
expect(OTLPTraceExporter).not.toHaveBeenCalled();
87-
});
88-
});
89-
90-
describe('with the DASH0_DEBUG_SPANDUMP variable set', () => {
91-
test('should initialize the FileSpanExporter', async () => {
92-
await jest.isolateModulesAsync(async () => {
93-
process.env.DASH0_DEBUG_SPANDUMP = '/dev/stdout';
94-
95-
jest.mock('./exporters');
96-
97-
const { init } = jest.requireActual('./distro');
98-
await init;
99-
100-
const { FileSpanExporter } = require('./exporters');
101-
expect(FileSpanExporter).toHaveBeenCalledWith('/dev/stdout');
102-
});
103-
});
104-
});
105-
});
106-
10777
describe('NodeTracerProvider should be initialize with span limit according to environment variables or default', () => {
10878
beforeEach(() => {
10979
process.env = { ...ORIGINAL_PROCESS_ENV };

0 commit comments

Comments
 (0)