Skip to content
Open
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
5 changes: 5 additions & 0 deletions .eslintrc.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

78 changes: 78 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
name: e2e

# Full instrumentation lifecycle against a live, ephemeral Lambda function.
on:
push:
branches: [main]
paths:
- "src/**"
- "e2e/**"
- "package.json"
- "yarn.lock"
- ".github/workflows/e2e.yml"
pull_request:
paths:
- "src/**"
- "e2e/**"
- "package.json"
- "yarn.lock"
- ".github/workflows/e2e.yml"
workflow_dispatch:

jobs:
lambda-e2e:
# Fork pull requests cannot use the canonical repository's OIDC identities.
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
env:
AWS_REGION: ${{ vars.AWS_REGION_E2E || 'ap-northeast-3' }}
DD_SITE: ${{ vars.DD_SITE_E2E || 'datadoghq.com' }}
steps:
- name: Checkout
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5

# Enable Corepack before setup-node so its package-manager cache step detects
# the yarn berry version from package.json instead of falling back to yarn 1.
- name: Enable Corepack
run: corepack enable

- name: Set up Node 24
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
with:
node-version: 24

- name: Install dependencies
run: yarn install --immutable

# Dedicated GitHub-OIDC role in the serverless sandbox account, scoped to deploy
# one-e2e-cdk-lambda-* via the CDK bootstrap roles. See serverless-ci/e2e/iam-infra.md.
- name: Configure AWS credentials via OIDC
uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4.3.1
with:
role-to-assume: ${{ vars.AWS_ROLE_ARN_E2E }}
aws-region: ${{ env.AWS_REGION }}

# Short-lived Datadog API + App keys via OIDC federation (dd-sts), governed by the
# policy in dd-source. No static Datadog keys are stored in this repo.
- name: Get Datadog credentials (dd-sts)
id: dd-sts
uses: DataDog/dd-sts-action@2e8187910199bd93129520183c093e19aa585c75 # v1.0.0
with:
policy: datadog-cdk-constructs-e2e

# The account is already CDK-bootstrapped; deploy reads the bootstrap version
# from SSM, so no bootstrap step is needed.
- name: Resolve account id
run: echo "CDK_DEFAULT_ACCOUNT=$(aws sts get-caller-identity --query Account --output text)" >> "$GITHUB_ENV"

- name: Run Lambda e2e suite
run: yarn test:e2e
env:
# Baked into the function at synth + used by the construct/extension.
DD_API_KEY: ${{ steps.dd-sts.outputs.api_key }}
# Used by the telemetry checker to query spans/logs.
DATADOG_API_KEY: ${{ steps.dd-sts.outputs.api_key }}
DATADOG_APP_KEY: ${{ steps.dd-sts.outputs.app_key }}
2 changes: 2 additions & 0 deletions .gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .npmignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Synced from serverless-ci/e2e/shared by the e2e-shared sync. Generated code is not
# hand-formatted here -- the source-of-truth style can't satisfy every consumer's
# prettier config, so the formatter skips these files.
e2e/helpers/exec.ts
e2e/helpers/naming.ts
e2e/helpers/lambda-telemetry-checker.ts
e2e/helpers/lambda-verifier.ts
10 changes: 10 additions & 0 deletions .projen/deps.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 12 additions & 2 deletions .projen/tasks.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 16 additions & 1 deletion .projenrc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ const project = new awscdk.AwsCdkConstructLibrary({
"esbuild",
"standard-version",
"@aws-cdk/aws-lambda-python-alpha@^2.134.0-alpha.0",
// e2e suite (runs out of e2e/, separate from the jsii-packaged library)
"vitest@^3.2.4",
"@datadog/datadog-api-client@^1.34.1",
],
gitignore: [
"*.js",
Expand All @@ -63,6 +66,8 @@ const project = new awscdk.AwsCdkConstructLibrary({
".DS_Store",
"integration_tests/cdk.out",
"integration_tests/testlib",
"e2e/.build",
"e2e/.env.local",
"bin",
"obj",
"__pycache__",
Expand All @@ -76,6 +81,7 @@ const project = new awscdk.AwsCdkConstructLibrary({
"!NOTICE",
"/scripts",
"/integration_tests",
"/e2e",
".prettierrc",
"/.ncurc.cjs",
"cdk.out/*",
Expand Down Expand Up @@ -183,6 +189,12 @@ project.github?.tryFindWorkflow("upgrade")?.file?.patch(
}),
);

project.eslint?.addLintPattern("e2e");
project.eslint?.allowDevDeps("e2e/**");
for (const helper of ["exec.ts", "lambda-telemetry-checker.ts", "lambda-verifier.ts", "naming.ts"]) {
project.eslint?.addIgnorePattern(`e2e/helpers/${helper}`);
}

const eslintConfig = project.tryFindObjectFile(".eslintrc.json")!;
eslintConfig.addOverride("extends", [
"plugin:@typescript-eslint/recommended",
Expand Down Expand Up @@ -272,7 +284,10 @@ npmScripts.addDeletionOverride("scripts.compat");
projenTasks.addDeletionOverride("scripts.test:compile");
// Replaces the removed `scripts` project option (dropped in projen 0.100/0.101).
project.addTask("check-formatting", {
exec: "prettier --check src/**/*.ts integration_tests/**/*.ts examples/**/*.ts",
exec: "prettier --check src/**/*.ts integration_tests/**/*.ts examples/**/*.ts e2e/*.ts e2e/app/**/*.ts e2e/helpers/e2e.config.ts",
});
project.addTask("test:e2e", {
exec: "tsc --noEmit -p e2e/tsconfig.json && vitest run --config e2e/vitest.config.ts e2e",
});
project.addTask("create-release", {
exec: "bash scripts/create_release.sh",
Expand Down
35 changes: 35 additions & 0 deletions e2e/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Lambda E2E tests

The suite runs the `DatadogLambda` construct against a temporary AWS Lambda function:

1. Deploy the uninstrumented function.
2. Apply the construct and verify its layers, handler, runtime, environment, and tags.
3. Invoke the function and wait for matching spans and logs in Datadog.
4. Confirm `cdk diff --fail` reports no changes.
5. Destroy the stack and verify the function is gone.

Layer and runtime versions are pinned in [`helpers/versions.ts`](helpers/versions.ts).

## Run locally

You need Node 22+, Yarn, AWS credentials for a CDK-bootstrapped account, and Datadog API and application keys for the serverless org.

```bash
aws-vault exec sso-serverless-sandbox-account-admin -- \
dd-auth --domain ddserverless.datadoghq.com -- bash -c '
export DATADOG_API_KEY="$DD_API_KEY" DATADOG_APP_KEY="$DD_APP_KEY"
yarn test:e2e
'
```

The suite defaults to `ap-northeast-3` and `datadoghq.com`. Set `AWS_REGION` or `DD_SITE` to override them.

## CI

[The E2E workflow](../.github/workflows/e2e.yml) runs when the construct or suite changes. It assumes the repository's AWS role through GitHub OIDC and obtains short-lived Datadog keys through `dd-sts`; missing credentials fail the run.

The workflow uses `AWS_ROLE_ARN_E2E`, `AWS_REGION_E2E`, and `DD_SITE_E2E` repository variables. The AWS account must be CDK-bootstrapped.

## Resource cleanup

Each run uses a unique `one-e2e-cdk-lambda-<runid>` name and stamps its run ID and creation time on the function. The test always attempts `cdk destroy`; the cross-repository sweeper removes resources left by interrupted runs.
78 changes: 78 additions & 0 deletions e2e/helpers/e2e.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Unless explicitly stated otherwise all files in this repository are licensed
* under the Apache License Version 2.0.
*
* This product includes software developed at Datadog (https://www.datadoghq.com/).
* Copyright 2026 Datadog, Inc.
*/

import { type ExpectedLayers, type LambdaVerifierConfig } from "./lambda-verifier";
import { type E2ENaming, RUN_ID_TAG_KEY } from "./naming";
import { E2E_EXTENSION_LAYER_VERSION, E2E_NODE_LAYER_VERSION } from "../app/versions";

// Repo-local config feeding the shared e2e helpers. This file is NOT synced -- it holds
// everything specific to datadog-cdk-constructs that the shared, parameterized helpers
// read through their config arguments.

export const NAMING: E2ENaming = { tool: "cdk", platform: "lambda" };

export const ENV_NAME = process.env.E2E_ENV ?? "e2e";
export const ENV_VERSION = process.env.E2E_VERSION ?? "1.0.0";

// Datadog's public layer account (commercial, non-GovCloud). The e2e defaults to ap-northeast-3.
const DD_ACCOUNT_ID = "464622532012";

// Transient cloud-provider errors safe to retry, passed as ExecOptions.retryPatterns.
export const RETRY_PATTERNS = [
"RequestTimeout",
"Throttling",
"TooManyRequests",
"Rate exceeded",
"ServiceUnavailable",
"InternalFailure",
"ResourceConflictException",
"OperationAbortedException",
"ETIMEDOUT",
"ECONNRESET",
"EAI_AGAIN",
"Connection reset",
"timed out",
"UPDATE_IN_PROGRESS",
];

// The CDK app names the function after the run-unique service name, so the deployed
// function name is the service name itself.
export const functionName = (serviceName: string): string => serviceName;

// Pinned artifact versions come from this repo's e2e/helpers/versions.ts, so a version
// mismatch blames the construct's wiring, not upstream layer drift.
export const expectedLayerArns = (region: string): ExpectedLayers => ({
node: `arn:aws:lambda:${region}:${DD_ACCOUNT_ID}:layer:Datadog-Node22-x:${E2E_NODE_LAYER_VERSION}`,
extension: `arn:aws:lambda:${region}:${DD_ACCOUNT_ID}:layer:Datadog-Extension:${E2E_EXTENSION_LAYER_VERSION}`,
});

export const verifierConfig = (site: string, runId: string): LambdaVerifierConfig => ({
functionName,
expectedLayerArns,
redirectHandler: "/opt/nodejs/node_modules/datadog-lambda-js/handler.handler",
originalHandler: "index.handler",
// The construct tags every instrumented function with its own marker tag.
toolTag: { key: "dd_cdk_construct", pattern: /.+/ },
env: {
apiKeyVars: ["DD_API_KEY", "DD_API_KEY_SECRET_ARN", "DD_KMS_API_KEY", "DD_API_KEY_SSM_ARN"],
present: [],
values: (serviceName) => ({
DD_SERVICE: serviceName,
DD_ENV: ENV_NAME,
DD_VERSION: ENV_VERSION,
DD_SITE: site,
DD_TAGS: `${RUN_ID_TAG_KEY}:${runId}`,
DD_TRACE_ENABLED: "true",
// With the extension, log collection is enabled via DD_SERVERLESS_LOGS_ENABLED;
// the construct intentionally forces DD_LOGS_INJECTION=false in this path.
DD_SERVERLESS_LOGS_ENABLED: "true",
DD_LOGS_INJECTION: "false",
DD_LAMBDA_HANDLER: "index.handler",
}),
},
});
3 changes: 3 additions & 0 deletions e2e/helpers/lambda-telemetry-checker.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading