diff --git a/.craft.yml b/.craft.yml index a6444038bb8d..0b7e5e83be39 100644 --- a/.craft.yml +++ b/.craft.yml @@ -2,20 +2,16 @@ minVersion: '0.23.1' changelogPolicy: simple preReleaseCommand: bash scripts/craft-pre-release.sh targets: - # - # Deactivated for now. This needs to be reactivated if the new Sentry Lambda Extension is deployed to production. - # (ask Anton Pirker if you have questions.) - # - # - name: aws-lambda-layer - # includeNames: /^sentry-node-serverless-\d+.\d+.\d+(-(beta|alpha)\.\d+)?\.zip$/ - # layerName: SentryNodeServerlessSDK - # compatibleRuntimes: - # - name: node - # versions: - # - nodejs10.x - # - nodejs12.x - # - nodejs14.x - # license: MIT + - name: aws-lambda-layer + includeNames: /^sentry-node-serverless-\d+.\d+.\d+(-(beta|alpha)\.\d+)?\.zip$/ + layerName: SentryNodeServerlessSDK + compatibleRuntimes: + - name: node + versions: + - nodejs10.x + - nodejs12.x + - nodejs14.x + license: MIT - name: gcs includeNames: /.*\.js.*$/ bucket: sentry-js-sdk diff --git a/packages/serverless/README.md b/packages/serverless/README.md index c0ac6f7659aa..0f61fae3afb9 100644 --- a/packages/serverless/README.md +++ b/packages/serverless/README.md @@ -63,7 +63,6 @@ Another and much simpler way to integrate Sentry to your AWS Lambda function is - `SENTRY_DSN`: `your dsn`. - `SENTRY_TRACES_SAMPLE_RATE`: a number between 0 and 1 representing the chance a transaction is sent to Sentry. For more information, see [docs](https://docs.sentry.io/platforms/node/guides/aws-lambda/configuration/options/#tracesSampleRate). - ### Google Cloud Functions To use this SDK, call `Sentry.GCPFunction.init(options)` at the very beginning of your JavaScript file. diff --git a/packages/serverless/src/awslambda-auto.ts b/packages/serverless/src/awslambda-auto.ts index ac63f117063f..ac048cde5aed 100644 --- a/packages/serverless/src/awslambda-auto.ts +++ b/packages/serverless/src/awslambda-auto.ts @@ -6,7 +6,11 @@ if (lambdaTaskRoot) { if (!handlerString) { throw Error(`LAMBDA_TASK_ROOT is non-empty(${lambdaTaskRoot}) but _HANDLER is not set`); } - Sentry.AWSLambda.init(); + + Sentry.AWSLambda.init({ + _invokedByLambdaLayer: true, + }); + Sentry.AWSLambda.tryPatchHandler(lambdaTaskRoot, handlerString); } else { throw Error('LAMBDA_TASK_ROOT environment variable is not set'); diff --git a/packages/serverless/src/awslambda.ts b/packages/serverless/src/awslambda.ts index fe785e061b9e..bd5c8ced25af 100644 --- a/packages/serverless/src/awslambda.ts +++ b/packages/serverless/src/awslambda.ts @@ -11,7 +11,7 @@ import { } from '@sentry/node'; import { extractTraceparentData } from '@sentry/tracing'; import { Integration } from '@sentry/types'; -import { isString, logger, parseBaggageSetMutability } from '@sentry/utils'; +import { dsnFromString, dsnToString, isString, logger, parseBaggageSetMutability } from '@sentry/utils'; // NOTE: I have no idea how to fix this right now, and don't want to waste more time, as it builds just fine — Kamil // eslint-disable-next-line import/no-unresolved import { Context, Handler } from 'aws-lambda'; @@ -57,10 +57,39 @@ export interface WrapperOptions { export const defaultIntegrations: Integration[] = [...Sentry.defaultIntegrations, new AWSServices({ optional: true })]; +/** + * Changes a Dsn to point to the `relay` server running in the Lambda Extension. + * + * This is only used by the serverless integration for AWS Lambda. + * + * @param originalDsn The original Dsn of the customer. + * @returns Dsn pointing to Lambda extension. + */ +function extensionRelayDSN(originalDsn: string | undefined): string | undefined { + if (originalDsn === undefined) { + return undefined; + } + + const dsn = dsnFromString(originalDsn); + dsn.host = 'localhost'; + dsn.port = '3000'; + dsn.protocol = 'http'; + + return dsnToString(dsn); +} + +interface AWSLambdaOptions extends Sentry.NodeOptions { + /** + * Internal field that is set to `true` when init() is called by the Sentry AWS Lambda layer. + * + */ + _invokedByLambdaLayer?: boolean; +} + /** * @see {@link Sentry.init} */ -export function init(options: Sentry.NodeOptions = {}): void { +export function init(options: AWSLambdaOptions = {}): void { if (options.defaultIntegrations === undefined) { options.defaultIntegrations = defaultIntegrations; } @@ -78,6 +107,12 @@ export function init(options: Sentry.NodeOptions = {}): void { version: Sentry.SDK_VERSION, }; + // If invoked by the Sentry Lambda Layer point the SDK to the Lambda Extension (inside the layer) instead of the host + // specified in the DSN + if (options._invokedByLambdaLayer) { + options.dsn = extensionRelayDSN(options.dsn); + } + Sentry.init(options); Sentry.addGlobalEventProcessor(serverlessEventProcessor); } diff --git a/packages/serverless/tsconfig.json b/packages/serverless/tsconfig.json index 7fccf7258ead..05c709778aa0 100644 --- a/packages/serverless/tsconfig.json +++ b/packages/serverless/tsconfig.json @@ -5,6 +5,6 @@ "compilerOptions": { // package-specific options - "target": "ES2018", + "target": "ES2018" } } diff --git a/packages/utils/src/dsn.ts b/packages/utils/src/dsn.ts index 35c3f0d5d141..a364c47d5d6b 100644 --- a/packages/utils/src/dsn.ts +++ b/packages/utils/src/dsn.ts @@ -32,7 +32,7 @@ export function dsnToString(dsn: DsnComponents, withPassword: boolean = false): * @param str A Dsn as string * @returns Dsn as DsnComponents */ -function dsnFromString(str: string): DsnComponents { +export function dsnFromString(str: string): DsnComponents { const match = DSN_REGEX.exec(str); if (!match) { @@ -106,24 +106,3 @@ export function makeDsn(from: DsnLike): DsnComponents { validateDsn(components); return components; } - -/** - * Changes a Dsn to point to the `relay` server running in the Lambda Extension. - * - * This is only used by the serverless integration for AWS Lambda. - * - * @param originalDsn The original Dsn of the customer. - * @returns Dsn pointing to Lambda extension. - */ -export function extensionRelayDSN(originalDsn: string | undefined): string | undefined { - if (originalDsn === undefined) { - return undefined; - } - - const dsn = dsnFromString(originalDsn); - dsn.host = 'localhost'; - dsn.port = '3000'; - dsn.protocol = 'http'; - - return dsnToString(dsn); -} diff --git a/scripts/aws-deploy-local-layer.sh b/scripts/aws-deploy-local-layer.sh index b86dcd344d78..a979ddbbdcbe 100755 --- a/scripts/aws-deploy-local-layer.sh +++ b/scripts/aws-deploy-local-layer.sh @@ -10,7 +10,7 @@ set -euo pipefail -# Cleanup +# Remove old distribution directories and zip files. echo "Preparing local directories for new build..." rm -rf dist-serverless/ rm -rf ./packages/serverless/build @@ -37,12 +37,36 @@ echo "Done copying Lambda layer in ./packages/serverless/build/aws/dist-serverle # is building the Lambda layer in production! # see: https://github.com/getsentry/action-build-aws-lambda-extension/blob/main/action.yml#L23-L40 -# Adding Sentry Lambda extension to Lambda layer -echo "Adding Sentry Lambda extension to Lambda layer in ./dist-serverless..." +echo "Downloading relay..." +# Make directory (if not existing) +mkdir -p dist-serverless/relay +# Download releay from release registry to dist-serverless/relay/relay +curl -0 --silent \ + --output dist-serverless/relay/relay \ + "$(curl -s https://release-registry.services.sentry.io/apps/relay/latest | jq -r .files.\"relay-Linux-x86_64\".url)" +# Make file executable +chmod +x dist-serverless/relay/relay +echo "Done downloading relay." + +echo "Creating start script..." +# Make directory (if not existing) mkdir -p dist-serverless/extensions -curl -0 --silent --output dist-serverless/extensions/sentry-lambda-extension $(curl -s https://release-registry.services.sentry.io/apps/sentry-lambda-extension/latest | jq -r .files.\"sentry-lambda-extension\".url) +# Create 'sentry-lambda-extension' script that starts relay. +# The file has to have exactly this name, because the executable files of +# Lambda extensions need to have same file name as the name that they use +# to register with AWS Lambda environment +cat > dist-serverless/extensions/sentry-lambda-extension << EOT +#!/bin/bash +set -euo pipefail +exec /opt/relay/relay run \ + --mode=proxy \ + --shutdown-timeout=2 \ + --upstream-dsn="\$SENTRY_DSN" \ + --aws-runtime-api="\$AWS_LAMBDA_RUNTIME_API" +EOT +# Make script executable chmod +x dist-serverless/extensions/sentry-lambda-extension -echo "Done adding Sentry Lambda extension to Lambda layer in ./dist-serverless." +echo "Done creating start script." # Zip Lambda layer and included Lambda extension echo "Zipping Lambda layer and included Lambda extension..."