Skip to content

Commit 0d69a7b

Browse files
authored
add integration tests for node and java (#958)
## Overview * Adding integration tests for Java and Dotnet. These are similar to the existing Node/Python integration in that they just test very basic functionality - we get logs/traces from the lambda function. These tests are meant to be our starting point and serve as example setup for other integration tests. * Fixed how we are filtering logs to use `@lambda.request_id:{requestId}`. This makes it use log attributes instead of checking the actual log message. * Updated stack cleanup step to just execute CLI command instead of CDK command. There was a slight issue when cleaning up the Java/Dotnet stacks due to their code assets, so the CLI command was easier. * Added tag the tag `extension_integration_test: true` to all of the stacks to make it easier to cleanup stacks if it gets missed (deployed locally and forgot to clean up, pipeline cancelled before cleanup step, etc.) A follow up item is to create a lambda function to periodically run and clean up all old stacks with this tag. ## Testing * Integ tests for this PR passed. * Checked AWS account and confirmed that there are no stacks with prefix `integ-61910f24`
1 parent 32d89e5 commit 0d69a7b

File tree

17 files changed

+505
-23
lines changed

17 files changed

+505
-23
lines changed

.gitlab/templates/pipeline.yaml.tpl

Lines changed: 78 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,49 @@ signed layer bundle:
324324
- mkdir -p datadog_extension-signed-bundle-${CI_JOB_ID}
325325
- cp .layers/datadog_extension-*.zip datadog_extension-signed-bundle-${CI_JOB_ID}
326326

327+
# Integration Tests - Build Java Lambda function
328+
build java lambda:
329+
stage: integration-tests
330+
image: registry.ddbuild.io/images/docker:27.3.1
331+
tags: ["docker-in-docker:arm64"]
332+
rules:
333+
- when: on_success
334+
needs: []
335+
artifacts:
336+
expire_in: 1 hour
337+
paths:
338+
- integration-tests/lambda/base-java/target/
339+
script:
340+
- cd integration-tests/lambda/base-java
341+
- docker run --rm --platform linux/arm64
342+
-v "$(pwd)":/workspace
343+
-w /workspace
344+
maven:3.9-eclipse-temurin-21-alpine
345+
mvn clean package
346+
347+
# Integration Tests - Build .NET Lambda function
348+
build dotnet lambda:
349+
stage: integration-tests
350+
image: registry.ddbuild.io/images/docker:27.3.1
351+
tags: ["docker-in-docker:arm64"]
352+
rules:
353+
- when: on_success
354+
needs: []
355+
artifacts:
356+
expire_in: 1 hour
357+
paths:
358+
- integration-tests/lambda/base-dotnet/bin/
359+
script:
360+
- cd integration-tests/lambda/base-dotnet
361+
- docker run --rm --platform linux/arm64
362+
-v "$(pwd)":/workspace
363+
-w /workspace
364+
mcr.microsoft.com/dotnet/sdk:8.0-alpine
365+
sh -c "apk add --no-cache zip &&
366+
dotnet tool install -g Amazon.Lambda.Tools || true &&
367+
export PATH=\"\$PATH:/root/.dotnet/tools\" &&
368+
dotnet lambda package -o bin/function.zip --function-architecture arm64"
369+
327370
# Integration Tests - Publish arm64 layer with integration test prefix
328371
publish integration layer (arm64):
329372
stage: integration-tests
@@ -362,6 +405,11 @@ integration-deploy:
362405
- when: on_success
363406
needs:
364407
- publish integration layer (arm64)
408+
- build java lambda
409+
- build dotnet lambda
410+
dependencies:
411+
- build java lambda
412+
- build dotnet lambda
365413
variables:
366414
IDENTIFIER: ${CI_COMMIT_SHORT_SHA}
367415
AWS_DEFAULT_REGION: us-east-1
@@ -418,35 +466,55 @@ integration-cleanup-stacks:
418466
stage: integration-tests
419467
tags: ["arch:amd64"]
420468
image: ${CI_DOCKER_TARGET_IMAGE}:${CI_DOCKER_TARGET_VERSION}
421-
when: always
422469
rules:
423470
- when: always
424471
needs:
425-
- integration-test
472+
- job: integration-test
473+
optional: false
426474
variables:
427475
IDENTIFIER: ${CI_COMMIT_SHORT_SHA}
428476
{{ with $environment := (ds "environments").environments.sandbox }}
429477
before_script:
430478
- EXTERNAL_ID_NAME={{ $environment.external_id }} ROLE_TO_ASSUME={{ $environment.role_to_assume }} AWS_ACCOUNT={{ $environment.account }} source .gitlab/scripts/get_secrets.sh
431-
- curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
432-
- apt-get install -y nodejs
433-
- cd integration-tests
434-
- npm ci
435479
{{ end }}
436480
script:
437481
- echo "Destroying CDK stacks with identifier ${IDENTIFIER}..."
438-
- npx cdk destroy "integ-$IDENTIFIER-*" --force || echo "Failed to destroy some stacks, but continuing..."
482+
- |
483+
# Find all stacks matching the pattern using CloudFormation API
484+
STACKS=$(aws cloudformation list-stacks \
485+
--stack-status-filter CREATE_COMPLETE UPDATE_COMPLETE UPDATE_ROLLBACK_COMPLETE \
486+
--query "StackSummaries[?starts_with(StackName, 'integ-${IDENTIFIER}-')].StackName" \
487+
--output text --region us-east-1)
488+
489+
if [ -z "$STACKS" ]; then
490+
echo "No stacks found matching pattern integ-${IDENTIFIER}-*"
491+
else
492+
echo "Found stacks to delete: ${STACKS}"
493+
for STACK in $STACKS; do
494+
echo "Deleting stack ${STACK}..."
495+
aws cloudformation delete-stack --stack-name "${STACK}" --region us-east-1 || echo "Failed to delete ${STACK}, continuing..."
496+
done
497+
498+
# Wait for all deletions to complete
499+
echo "Waiting for stack deletions to complete..."
500+
for STACK in $STACKS; do
501+
echo "Waiting for ${STACK}..."
502+
aws cloudformation wait stack-delete-complete --stack-name "${STACK}" --region us-east-1 || echo "Stack ${STACK} deletion did not complete cleanly, continuing..."
503+
done
504+
505+
echo "All stacks deleted successfully"
506+
fi
439507

440508
# Integration Tests - Cleanup layer
441509
integration-cleanup-layer:
442510
stage: integration-tests
443511
tags: ["arch:amd64"]
444512
image: ${CI_DOCKER_TARGET_IMAGE}:${CI_DOCKER_TARGET_VERSION}
445-
when: always
446513
rules:
447514
- when: always
448515
needs:
449-
- integration-cleanup-stacks
516+
- job: integration-cleanup-stacks
517+
optional: false
450518
variables:
451519
IDENTIFIER: ${CI_COMMIT_SHORT_SHA}
452520
{{ with $environment := (ds "environments").environments.sandbox }}
@@ -456,7 +524,7 @@ integration-cleanup-layer:
456524
script:
457525
- echo "Deleting integration test layer with identifier ${IDENTIFIER}..."
458526
- |
459-
LAYER_NAME="Datadog-Extension-${IDENTIFIER}"
527+
LAYER_NAME="Datadog-Extension-ARM-${IDENTIFIER}"
460528
echo "Looking for layer: ${LAYER_NAME}"
461529

462530
# Get all versions of the layer

integration-tests/README.md

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,40 @@ The general flow is:
88
3. Wait for data to propagate to Datadog.
99
4. Call Datadog to get telemetry data and check the data based on test requirements.
1010

11-
For simplicity, integraiton tests are setup to only test against ARM runtimes.
11+
For simplicity, integration tests are setup to only test against ARM runtimes.
12+
13+
## Test Suites
14+
15+
### Base Tests
16+
17+
The base test suite provides basic functionality tests across all supported Lambda runtimes. Also serves as an example for other tests.
18+
19+
The base tests verify the extension can:
20+
- Collect and forward logs to Datadog
21+
- Generate and send traces with proper span structure
22+
- Detect cold starts
23+
24+
**Test Coverage:**
25+
- Lambda invocation succeeds (200 status code)
26+
- "Hello world!" log message is sent to Datadog
27+
- One trace is sent to Datadog
28+
- `aws.lambda` span exists with correct properties including `cold_start: 'true'`
29+
- `aws.lambda.cold_start` span is created
30+
- `aws.lambda.load` spand is created for python and node.
31+
32+
**Build Requirements:**
33+
34+
For Java and .NET tests, Lambda functions must be built before deployment:
35+
36+
```bash
37+
# Build Java Lambda (uses Docker)
38+
cd lambda/base-java && ./build.sh
39+
40+
# Build .NET Lambda (uses Docker)
41+
cd lambda/base-dotnet && ./build.sh
42+
```
43+
44+
These builds use Docker to ensure cross-platform compatibility and do not require local Maven or .NET SDK installation.
1245

1346
## Guidelines
1447

integration-tests/bin/app.ts

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,37 @@
11
#!/usr/bin/env node
22
import 'source-map-support/register';
33
import * as cdk from 'aws-cdk-lib';
4-
import { BaseNodeStack } from '../lib/stacks/base-node-stack';
5-
import { BasePythonStack } from '../lib/stacks/base-python-stack';
6-
import { getIdentifier } from '../tests/utils/config';
4+
import {BaseNodeStack} from '../lib/stacks/base-node-stack';
5+
import {BasePythonStack} from '../lib/stacks/base-python-stack';
6+
import {BaseJavaStack} from '../lib/stacks/base-java-stack';
7+
import {BaseDotnetStack} from '../lib/stacks/base-dotnet-stack';
8+
import {getIdentifier} from '../tests/utils/config';
79

810
const app = new cdk.App();
911

1012
const env = {
11-
account: process.env.CDK_DEFAULT_ACCOUNT || process.env.AWS_ACCOUNT_ID,
12-
region: process.env.CDK_DEFAULT_REGION || process.env.AWS_REGION || 'us-east-1',
13+
account: process.env.CDK_DEFAULT_ACCOUNT || process.env.AWS_ACCOUNT_ID,
14+
region: process.env.CDK_DEFAULT_REGION || process.env.AWS_REGION || 'us-east-1',
1315
};
1416

1517
const identifier = getIdentifier();
1618

17-
new BaseNodeStack(app, `integ-${identifier}-base-node`, {
18-
env,
19-
});
19+
const stacks = [
20+
new BaseNodeStack(app, `integ-${identifier}-base-node`, {
21+
env,
22+
}),
23+
new BasePythonStack(app, `integ-${identifier}-base-python`, {
24+
env,
25+
}),
26+
new BaseJavaStack(app, `integ-${identifier}-base-java`, {
27+
env,
28+
}),
29+
new BaseDotnetStack(app, `integ-${identifier}-base-dotnet`, {
30+
env,
31+
}),
32+
]
2033

21-
new BasePythonStack(app, `integ-${identifier}-base-python`, {
22-
env,
23-
});
34+
// Tag all stacks so we can easily clean them up
35+
stacks.forEach(stack => stack.addStackTag("extension_integration_test", "true"))
2436

2537
app.synth();
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
bin/
2+
obj/
3+
*.user
4+
*.suo
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using Amazon.Lambda.Core;
2+
using System.Collections.Generic;
3+
4+
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]
5+
6+
namespace Function
7+
{
8+
public class Handler
9+
{
10+
public Dictionary<string, object> FunctionHandler(Dictionary<string, object> input, ILambdaContext context)
11+
{
12+
context.Logger.LogLine("Hello world!");
13+
return new Dictionary<string, object>
14+
{
15+
{ "statusCode", 200 }
16+
};
17+
}
18+
}
19+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<TargetFramework>net8.0</TargetFramework>
4+
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
5+
<AWSProjectType>Lambda</AWSProjectType>
6+
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
7+
<PublishReadyToRun>true</PublishReadyToRun>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="Amazon.Lambda.Core" Version="2.2.0" />
12+
<PackageReference Include="Amazon.Lambda.Serialization.SystemTextJson" Version="2.4.0" />
13+
</ItemGroup>
14+
</Project>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#!/bin/bash
2+
set -e
3+
4+
echo "Building .NET Lambda with Docker (ARM64)..."
5+
6+
# Check if Docker is available
7+
if ! command -v docker &> /dev/null; then
8+
echo "Error: Docker is not installed or not in PATH"
9+
echo "Please install Docker: https://docs.docker.com/get-docker/"
10+
exit 1
11+
fi
12+
13+
# Get the directory of this script
14+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
15+
16+
# Clean previous build
17+
rm -rf "$SCRIPT_DIR/bin" "$SCRIPT_DIR/obj"
18+
19+
# Build and package with Docker using ARM64 platform
20+
docker run --rm --platform linux/arm64 \
21+
-v "$SCRIPT_DIR":/workspace \
22+
-w /workspace \
23+
mcr.microsoft.com/dotnet/sdk:8.0-alpine \
24+
sh -c "apk add --no-cache zip && \
25+
dotnet tool install -g Amazon.Lambda.Tools || true && \
26+
export PATH=\"\$PATH:/root/.dotnet/tools\" && \
27+
dotnet lambda package -o bin/function.zip --function-architecture arm64"
28+
29+
if [ -f "$SCRIPT_DIR/bin/function.zip" ]; then
30+
echo "✓ Build complete: bin/function.zip"
31+
ls -lh "$SCRIPT_DIR/bin/function.zip"
32+
else
33+
echo "✗ Build failed: bin/function.zip not found"
34+
exit 1
35+
fi
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
target/
2+
*.class
3+
.classpath
4+
.project
5+
.settings/
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/bin/bash
2+
set -e
3+
4+
echo "Building Java Lambda with Docker (ARM64)..."
5+
6+
# Check if Docker is available
7+
if ! command -v docker &> /dev/null; then
8+
echo "Error: Docker is not installed or not in PATH"
9+
echo "Please install Docker: https://docs.docker.com/get-docker/"
10+
exit 1
11+
fi
12+
13+
# Get the directory of this script
14+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
15+
16+
# Clean previous build
17+
rm -rf "$SCRIPT_DIR/target"
18+
19+
# Build with Docker using ARM64 platform
20+
docker run --rm --platform linux/arm64 \
21+
-v "$SCRIPT_DIR":/workspace \
22+
-w /workspace \
23+
maven:3.9-eclipse-temurin-21-alpine \
24+
mvn clean package
25+
26+
if [ -f "$SCRIPT_DIR/target/function.jar" ]; then
27+
echo "✓ Build complete: target/function.jar"
28+
ls -lh "$SCRIPT_DIR/target/function.jar"
29+
else
30+
echo "✗ Build failed: target/function.jar not found"
31+
exit 1
32+
fi
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<groupId>example</groupId>
8+
<artifactId>base-java-lambda</artifactId>
9+
<version>1.0.0</version>
10+
<packaging>jar</packaging>
11+
12+
<name>Base Java Lambda</name>
13+
<description>Base Java Lambda function for Datadog Extension integration testing</description>
14+
15+
<properties>
16+
<maven.compiler.source>21</maven.compiler.source>
17+
<maven.compiler.target>21</maven.compiler.target>
18+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
19+
</properties>
20+
21+
<dependencies>
22+
<dependency>
23+
<groupId>com.amazonaws</groupId>
24+
<artifactId>aws-lambda-java-core</artifactId>
25+
<version>1.2.3</version>
26+
</dependency>
27+
</dependencies>
28+
29+
<build>
30+
<plugins>
31+
<plugin>
32+
<groupId>org.apache.maven.plugins</groupId>
33+
<artifactId>maven-shade-plugin</artifactId>
34+
<version>3.5.0</version>
35+
<executions>
36+
<execution>
37+
<phase>package</phase>
38+
<goals>
39+
<goal>shade</goal>
40+
</goals>
41+
<configuration>
42+
<finalName>function</finalName>
43+
<createDependencyReducedPom>false</createDependencyReducedPom>
44+
</configuration>
45+
</execution>
46+
</executions>
47+
</plugin>
48+
</plugins>
49+
</build>
50+
</project>

0 commit comments

Comments
 (0)