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
4 changes: 4 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -992,6 +992,8 @@ jobs:
working-directory: ${{ runner.temp }}/test-application
timeout-minutes: 7
run: ${{ matrix.build-command || 'pnpm test:build' }}
env:
SENTRY_E2E_WORKSPACE_ROOT: ${{ github.workspace }}

- name: Install Playwright
uses: ./.github/actions/install-playwright
Expand All @@ -1003,6 +1005,8 @@ jobs:
working-directory: ${{ runner.temp }}/test-application
timeout-minutes: 10
run: ${{ matrix.assert-command || 'pnpm test:assert' }}
env:
SENTRY_E2E_WORKSPACE_ROOT: ${{ github.workspace }}

- name: Upload Playwright Traces
uses: actions/upload-artifact@v5
Expand Down
2 changes: 2 additions & 0 deletions dev-packages/e2e-tests/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ async function run(): Promise<void> {
REACT_APP_E2E_TEST_DSN: dsn,
E2E_TEST_SENTRY_ORG_SLUG: process.env.E2E_TEST_SENTRY_ORG_SLUG || DEFAULT_SENTRY_ORG_SLUG,
E2E_TEST_SENTRY_PROJECT: process.env.E2E_TEST_SENTRY_PROJECT || DEFAULT_SENTRY_PROJECT,
// Pass workspace root so tests copied to temp dirs can find local packages
SENTRY_E2E_WORKSPACE_ROOT: resolve(__dirname, '../..'),
};

const env = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@ const LAYER_DIR = './node_modules/@sentry/aws-serverless/';
const DEFAULT_NODE_VERSION = '22';
export const SAM_PORT = 3001;

function resolvePackagesDir(): string {
// When running via the e2e test runner, tests are copied to a temp directory
// so we need the workspace root passed via env var
const workspaceRoot = process.env.SENTRY_E2E_WORKSPACE_ROOT;
if (workspaceRoot) {
return path.join(workspaceRoot, 'packages');
}
// Fallback for local development when running from the original location
return path.resolve(__dirname, '../../../../../packages');
}

export class LocalLambdaStack extends Stack {
sentryLayer: CfnResource;

Expand Down Expand Up @@ -67,10 +78,46 @@ export class LocalLambdaStack extends Stack {
const functionName = `${addLayer ? 'Layer' : 'Npm'}${lambdaDir}`;

if (!addLayer) {
const lambdaPath = path.resolve(functionsDir, lambdaDir);
const packageLockPath = path.join(lambdaPath, 'package-lock.json');
const nodeModulesPath = path.join(lambdaPath, 'node_modules');

// Point the dependency at the locally built packages so tests use the current workspace bits
// We need to link all @sentry/* packages that are dependencies of aws-serverless
// because otherwise npm will try to install them from the registry, where the current version is not yet published
const packagesToLink = ['aws-serverless', 'node', 'core', 'node-core', 'opentelemetry'];
const dependencies: Record<string, string> = {};

const packagesDir = resolvePackagesDir();
for (const pkgName of packagesToLink) {
const pkgDir = path.join(packagesDir, pkgName);
if (!fs.existsSync(pkgDir)) {
throw new Error(
`[LocalLambdaStack] Workspace package ${pkgName} not found at ${pkgDir}. Did you run the build?`,
);
}
const relativePath = path.relative(lambdaPath, pkgDir);
dependencies[`@sentry/${pkgName}`] = `file:${relativePath.replace(/\\/g, '/')}`;
}

console.log(`[LocalLambdaStack] Install dependencies for ${functionName}`);
const packageJson = { dependencies: { '@sentry/aws-serverless': '* || latest' } };
fs.writeFileSync(path.join(functionsDir, lambdaDir, 'package.json'), JSON.stringify(packageJson, null, 2));
execFileSync('npm', ['install', '--prefix', path.join(functionsDir, lambdaDir)], { stdio: 'inherit' });

if (fs.existsSync(packageLockPath)) {
// Prevent stale lock files from pinning the published package version
fs.rmSync(packageLockPath);
}

if (fs.existsSync(nodeModulesPath)) {
// Ensure we reinstall from the workspace instead of reusing cached dependencies
fs.rmSync(nodeModulesPath, { recursive: true, force: true });
}

const packageJson = {
dependencies,
};

fs.writeFileSync(path.join(lambdaPath, 'package.json'), JSON.stringify(packageJson, null, 2));
execFileSync('npm', ['install', '--prefix', lambdaPath], { stdio: 'inherit' });
}

new CfnResource(this, functionName, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const test = base.extend<{ testEnvironment: LocalLambdaStack; lambdaClien
console.log('[testEnvironment fixture] Setting up AWS Lambda test infrastructure');

execSync('docker network prune -f');
execSync(`docker network create --driver bridge ${DOCKER_NETWORK_NAME}`);
createDockerNetwork();

const hostIp = await getHostIp();
const app = new App();
Expand Down Expand Up @@ -71,6 +71,8 @@ export const test = base.extend<{ testEnvironment: LocalLambdaStack; lambdaClien
resolve(void 0);
}, 5000);
});

removeDockerNetwork();
}
},
{ scope: 'worker', auto: true },
Expand All @@ -88,3 +90,27 @@ export const test = base.extend<{ testEnvironment: LocalLambdaStack; lambdaClien
await use(lambdaClient);
},
});

function createDockerNetwork() {
try {
execSync(`docker network create --driver bridge ${DOCKER_NETWORK_NAME}`);
} catch (error) {
const stderr = (error as { stderr?: Buffer }).stderr?.toString() ?? '';
if (stderr.includes('already exists')) {
console.log(`[testEnvironment fixture] Reusing existing docker network ${DOCKER_NETWORK_NAME}`);
return;
}
throw error;
}
}

function removeDockerNetwork() {
try {
execSync(`docker network rm ${DOCKER_NETWORK_NAME}`);
} catch (error) {
const stderr = (error as { stderr?: Buffer }).stderr?.toString() ?? '';
if (!stderr.includes('No such network')) {
console.warn(`[testEnvironment fixture] Failed to remove docker network ${DOCKER_NETWORK_NAME}: ${stderr}`);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ test.describe('Lambda layer', () => {
data: {
'sentry.sample_rate': 1,
'sentry.source': 'custom',
'sentry.origin': 'auto.otel.aws-lambda',
'sentry.origin': 'auto.otel.aws_lambda',
'sentry.op': 'function.aws.lambda',
'cloud.account.id': '012345678912',
'faas.execution': expect.any(String),
Expand All @@ -32,7 +32,7 @@ test.describe('Lambda layer', () => {
'otel.kind': 'SERVER',
},
op: 'function.aws.lambda',
origin: 'auto.otel.aws-lambda',
origin: 'auto.otel.aws_lambda',
span_id: expect.stringMatching(/[a-f0-9]{16}/),
status: 'ok',
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
Expand Down Expand Up @@ -91,7 +91,7 @@ test.describe('Lambda layer', () => {
data: {
'sentry.sample_rate': 1,
'sentry.source': 'custom',
'sentry.origin': 'auto.otel.aws-lambda',
'sentry.origin': 'auto.otel.aws_lambda',
'sentry.op': 'function.aws.lambda',
'cloud.account.id': '012345678912',
'faas.execution': expect.any(String),
Expand All @@ -100,7 +100,7 @@ test.describe('Lambda layer', () => {
'otel.kind': 'SERVER',
},
op: 'function.aws.lambda',
origin: 'auto.otel.aws-lambda',
origin: 'auto.otel.aws_lambda',
span_id: expect.stringMatching(/[a-f0-9]{16}/),
status: 'ok',
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
Expand Down Expand Up @@ -214,7 +214,7 @@ test.describe('Lambda layer', () => {
data: {
'sentry.sample_rate': 1,
'sentry.source': 'custom',
'sentry.origin': 'auto.otel.aws-lambda',
'sentry.origin': 'auto.otel.aws_lambda',
'sentry.op': 'function.aws.lambda',
'cloud.account.id': '012345678912',
'faas.execution': expect.any(String),
Expand All @@ -223,7 +223,7 @@ test.describe('Lambda layer', () => {
'otel.kind': 'SERVER',
},
op: 'function.aws.lambda',
origin: 'auto.otel.aws-lambda',
origin: 'auto.otel.aws_lambda',
span_id: expect.stringMatching(/[a-f0-9]{16}/),
status: 'ok',
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ test.describe('NPM package', () => {
data: {
'sentry.sample_rate': 1,
'sentry.source': 'custom',
'sentry.origin': 'auto.otel.aws-lambda',
'sentry.origin': 'auto.otel.aws_lambda',
'sentry.op': 'function.aws.lambda',
'cloud.account.id': '012345678912',
'faas.execution': expect.any(String),
Expand All @@ -32,7 +32,7 @@ test.describe('NPM package', () => {
'otel.kind': 'SERVER',
},
op: 'function.aws.lambda',
origin: 'auto.otel.aws-lambda',
origin: 'auto.otel.aws_lambda',
span_id: expect.stringMatching(/[a-f0-9]{16}/),
status: 'ok',
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
Expand Down Expand Up @@ -91,7 +91,7 @@ test.describe('NPM package', () => {
data: {
'sentry.sample_rate': 1,
'sentry.source': 'custom',
'sentry.origin': 'auto.otel.aws-lambda',
'sentry.origin': 'auto.otel.aws_lambda',
'sentry.op': 'function.aws.lambda',
'cloud.account.id': '012345678912',
'faas.execution': expect.any(String),
Expand All @@ -100,7 +100,7 @@ test.describe('NPM package', () => {
'otel.kind': 'SERVER',
},
op: 'function.aws.lambda',
origin: 'auto.otel.aws-lambda',
origin: 'auto.otel.aws_lambda',
span_id: expect.stringMatching(/[a-f0-9]{16}/),
status: 'ok',
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
Expand Down
2 changes: 1 addition & 1 deletion packages/aws-serverless/src/integration/awslambda.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const instrumentAwsLambda = generateInstrumentOnce(
...options,
eventContextExtractor,
requestHook(span) {
span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto.otel.aws-lambda');
span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto.otel.aws_lambda');
span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'function.aws.lambda');
},
responseHook(_span, { err }) {
Expand Down
Loading