diff --git a/.github/workflows/system_tests.yml b/.github/workflows/system_tests.yml new file mode 100644 index 000000000..b61725b1a --- /dev/null +++ b/.github/workflows/system_tests.yml @@ -0,0 +1,38 @@ +name: System Tests + +on: + push: + branches: + - main + pull_request: + branches: + - "**" + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Build layer + run: | + ARCH=amd64 PYTHON_VERSION=3.13 ./scripts/build_layers.sh + + - uses: actions/upload-artifact@v4 + with: + path: .layers/datadog_lambda_py-amd64-3.13.zip + name: binaries + + system-tests: + needs: + - build + uses: DataDog/system-tests/.github/workflows/system-tests.yml@main + secrets: inherit + permissions: + contents: read + packages: write + with: + library: python_lambda + binaries_artifact: binaries + scenarios_groups: appsec + skip_empty_scenarios: true diff --git a/.github/workflows/update_deps.yml b/.github/workflows/update_deps.yml index 33a524b25..15a3ac665 100644 --- a/.github/workflows/update_deps.yml +++ b/.github/workflows/update_deps.yml @@ -25,18 +25,17 @@ jobs: - name: Set up Python uses: actions/setup-python@v4 with: - python-version: 3.7 + python-version: 3.13 - name: Update Dependencies run: | - curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python - - source $HOME/.poetry/env - poetry build + pip install poetry poetry update - name: Create Pull Request uses: peter-evans/create-pull-request@v3 with: + token: ${{ steps.generate_token.outputs.token }} commit-message: update dependencies title: Update Dependencies body: | diff --git a/Dockerfile b/Dockerfile index 02910a783..8da968fb2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,6 +7,16 @@ ARG runtime RUN mkdir -p /build/python/lib/$runtime/site-packages WORKDIR /build +# Install newer version of GCC on AL2 +RUN set -eux; \ + if command -v yum >/dev/null 2>&1; then \ + yum -y install git gcc10 gcc10-c++; \ + cd /usr/bin; \ + rm gcc && ln -s gcc10-gcc gcc; \ + rm g++ && ln -s gcc10-g++ g++; \ + rm cc && ln -s gcc10-cc cc; \ + fi + # Add Rust compiler which is needed to build dd-trace-py from source RUN curl https://sh.rustup.rs -sSf | \ sh -s -- --default-toolchain stable -y @@ -21,12 +31,16 @@ RUN pip install --no-cache-dir . -t ./python/lib/$runtime/site-packages RUN rm -rf ./python/lib/$runtime/site-packages/botocore* RUN rm -rf ./python/lib/$runtime/site-packages/setuptools RUN rm -rf ./python/lib/$runtime/site-packages/jsonschema/tests +RUN rm -f ./python/lib/$runtime/site-packages/ddtrace/appsec/_iast/_ast/iastpatch*.so RUN rm -f ./python/lib/$runtime/site-packages/ddtrace/appsec/_iast/_taint_tracking/*.so RUN rm -f ./python/lib/$runtime/site-packages/ddtrace/appsec/_iast/_stacktrace*.so # _stack_v2 may not exist for some versions of ddtrace (e.g. under python 3.13) RUN rm -f ./python/lib/$runtime/site-packages/ddtrace/internal/datadog/profiling/stack_v2/_stack_v2.*.so -# remove *.dist-info directories except any entry_points.txt files -RUN find ./python/lib/$runtime/site-packages/*.dist-info -not -name "entry_points.txt" -type f -delete +# remove *.dist-info directories except any entry_points.txt files and METADATA files required for Appsec Software Composition Analysis +RUN find ./python/lib/$runtime/site-packages/*.dist-info \ + -type f \ + ! \( -name 'entry_points.txt' -o -name 'METADATA' \) \ + -delete RUN find ./python/lib/$runtime/site-packages -type d -empty -delete # Remove requests and dependencies @@ -59,5 +73,11 @@ RUN find ./python/lib/$runtime/site-packages/ddtrace -name \*.h -delete RUN find ./python/lib/$runtime/site-packages/ddtrace -name \*.hpp -delete RUN find ./python/lib/$runtime/site-packages/ddtrace -name \*.pyx -delete +# Strip debug symbols using strip -g for all .so files in ddtrace. This is to +# reduce the size when ddtrace is built from sources. The release wheels are +# already stripped of debug symbols. We should revisit this when serverless +# benchmark uses pre-built wheels instead of building from sources. +RUN find ./python/lib/$runtime/site-packages/ddtrace -name "*.so" -exec strip -g {} \; + FROM scratch COPY --from=builder /build/python / diff --git a/ci/input_files/build.yaml.tpl b/ci/input_files/build.yaml.tpl index 769f87f5c..8624a8060 100644 --- a/ci/input_files/build.yaml.tpl +++ b/ci/input_files/build.yaml.tpl @@ -1,8 +1,11 @@ +{{- $e2e_region := "us-west-2" -}} + stages: - build - test - sign - publish + - e2e .python-before-script: &python-before-script - pip install virtualenv @@ -56,11 +59,11 @@ check-layer-size ({{ $runtime.name }}-{{ $runtime.arch }}): stage: test tags: ["arch:amd64"] image: registry.ddbuild.io/images/docker:20.10 - needs: + needs: - build-layer ({{ $runtime.name }}-{{ $runtime.arch }}) dependencies: - build-layer ({{ $runtime.name }}-{{ $runtime.arch }}) - script: + script: - PYTHON_VERSION={{ $runtime.python_version }} ARCH={{ $runtime.arch }} ./scripts/check_layer_size.sh lint python: @@ -69,7 +72,7 @@ lint python: image: registry.ddbuild.io/images/mirror/python:{{ $runtime.image }} cache: &{{ $runtime.name }}-{{ $runtime.arch }}-cache before_script: *python-before-script - script: + script: - source venv/bin/activate - ./scripts/check_format.sh @@ -79,7 +82,7 @@ unit-test ({{ $runtime.name }}-{{ $runtime.arch }}): image: registry.ddbuild.io/images/mirror/python:{{ $runtime.image }} cache: &{{ $runtime.name }}-{{ $runtime.arch }}-cache before_script: *python-before-script - script: + script: - source venv/bin/activate - pytest -vv @@ -87,7 +90,7 @@ integration-test ({{ $runtime.name }}-{{ $runtime.arch }}): stage: test tags: ["arch:amd64"] image: registry.ddbuild.io/images/docker:20.10-py3 - needs: + needs: - build-layer ({{ $runtime.name }}-{{ $runtime.arch }}) dependencies: - build-layer ({{ $runtime.name }}-{{ $runtime.arch }}) @@ -132,16 +135,22 @@ sign-layer ({{ $runtime.name }}-{{ $runtime.arch }}): - LAYER_FILE=datadog_lambda_py-{{ $runtime.arch}}-{{ $runtime.python_version }}.zip ./scripts/sign_layers.sh prod {{ range $environment_name, $environment := (ds "environments").environments }} +{{ $dotenv := print $runtime.name "_" $runtime.arch "_" $environment_name ".env" }} publish-layer-{{ $environment_name }} ({{ $runtime.name }}-{{ $runtime.arch }}): stage: publish tags: ["arch:amd64"] image: registry.ddbuild.io/images/docker:20.10-py3 rules: + - if: '"{{ $environment_name }}" == "sandbox" && $REGION == "{{ $e2e_region }}" && "{{ $runtime.arch }}" == "amd64"' + when: on_success - if: '"{{ $environment_name }}" == "sandbox"' when: manual allow_failure: true - if: '$CI_COMMIT_TAG =~ /^v.*/' + artifacts: + reports: + dotenv: {{ $dotenv }} needs: {{ if or (eq $environment_name "prod") }} - sign-layer ({{ $runtime.name }}-{{ $runtime.arch}}) @@ -166,7 +175,7 @@ publish-layer-{{ $environment_name }} ({{ $runtime.name }}-{{ $runtime.arch }}): before_script: - EXTERNAL_ID_NAME={{ $environment.external_id }} ROLE_TO_ASSUME={{ $environment.role_to_assume }} AWS_ACCOUNT={{ $environment.account }} source ./ci/get_secrets.sh script: - - STAGE={{ $environment_name }} PYTHON_VERSION={{ $runtime.python_version }} ARCH={{ $runtime.arch }} ./ci/publish_layers.sh + - STAGE={{ $environment_name }} PYTHON_VERSION={{ $runtime.python_version }} ARCH={{ $runtime.arch }} DOTENV={{ $dotenv }} ./ci/publish_layers.sh {{- end }} @@ -232,3 +241,56 @@ signed layer bundle: - rm -rf datadog_lambda_py-signed-bundle-${CI_JOB_ID} - mkdir -p datadog_lambda_py-signed-bundle-${CI_JOB_ID} - cp .layers/datadog_lambda_py-*.zip datadog_lambda_py-signed-bundle-${CI_JOB_ID} + +e2e-test: + stage: e2e + trigger: + project: DataDog/serverless-e2e-tests + strategy: depend + variables: + LANGUAGES_SUBSET: python + # These env vars are inherited from the dotenv reports of the publish-layer jobs + {{- range (ds "runtimes").runtimes }} + {{- if eq .arch "amd64" }} + {{- $version := print (.name | strings.Trim "python") }} + PYTHON_{{ $version }}_VERSION: $PYTHON_{{ $version }}_VERSION + {{- end }} + {{- end }} + needs: {{ range (ds "runtimes").runtimes }} + {{- if eq .arch "amd64" }} + - "publish-layer-sandbox ({{ .name }}-{{ .arch }}): [{{ $e2e_region }}]" + {{- end }} + {{- end }} + +e2e-test-status: + stage: e2e + image: registry.ddbuild.io/images/docker:20.10-py3 + tags: ["arch:amd64"] + timeout: 3h + script: | + GITLAB_API_TOKEN=$(aws ssm get-parameter --region us-east-1 --name "ci.${CI_PROJECT_NAME}.serverless-e2e-gitlab-token" --with-decryption --query "Parameter.Value" --out text) + URL="${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/pipelines/${CI_PIPELINE_ID}/bridges" + echo "Fetching E2E job status from: $URL" + while true; do + RESPONSE=$(curl -s --header "PRIVATE-TOKEN: ${GITLAB_API_TOKEN}" "$URL") + E2E_JOB_STATUS=$(echo "$RESPONSE" | jq -r '.[] | select(.name=="e2e-test") | .downstream_pipeline.status') + echo -n "E2E job status: $E2E_JOB_STATUS, " + if [ "$E2E_JOB_STATUS" == "success" ]; then + echo "✅ E2E tests completed successfully" + exit 0 + elif [ "$E2E_JOB_STATUS" == "failed" ]; then + echo "❌ E2E tests failed" + exit 1 + elif [ "$E2E_JOB_STATUS" == "running" ]; then + echo "⏳ E2E tests are still running, retrying in 1 minute..." + elif [ "$E2E_JOB_STATUS" == "canceled" ]; then + echo "🚫 E2E tests were canceled" + exit 1 + elif [ "$E2E_JOB_STATUS" == "skipped" ]; then + echo "⏭️ E2E tests were skipped" + exit 0 + else + echo "❓ Unknown E2E test status: $E2E_JOB_STATUS, retrying in 1 minute..." + fi + sleep 60 + done diff --git a/ci/publish_layers.sh b/ci/publish_layers.sh index 58257bf11..9654582a2 100755 --- a/ci/publish_layers.sh +++ b/ci/publish_layers.sh @@ -199,6 +199,8 @@ fi while [ $latest_version -lt $VERSION ]; do latest_version=$(publish_layer $REGION $layer $aws_cli_python_version_key $layer_path) printf "[$REGION] Published version $latest_version for layer $layer in region $REGION\n" + latest_arn=$(aws lambda get-layer-version --layer-name $layer --version-number $latest_version --region $REGION --query 'LayerVersionArn' --output text) + printf "[$REGION] Published arn $latest_arn\n" # This shouldn't happen unless someone manually deleted the latest version, say 28, and # then tries to republish 28 again. The published version would actually be 29, because @@ -209,4 +211,10 @@ while [ $latest_version -lt $VERSION ]; do fi done +if [ -n "$DOTENV" ]; then + printf "[$REGION] Exporting layer version to $DOTENV file...\n" + echo "PYTHON_${PYTHON_VERSION/./}_VERSION=$latest_arn" >> "$DOTENV" + cat "$DOTENV" +fi + printf "[$REGION] Finished publishing layers...\n\n" diff --git a/datadog_lambda/__init__.py b/datadog_lambda/__init__.py index f319d2ed8..2034d3cbf 100644 --- a/datadog_lambda/__init__.py +++ b/datadog_lambda/__init__.py @@ -3,10 +3,11 @@ if os.environ.get("DD_INSTRUMENTATION_TELEMETRY_ENABLED") is None: - os.environ["DD_INSTRUMENTATION_TELEMETRY_ENABLED"] = "false" + # Telemetry is required for Appsec Software Composition Analysis + os.environ["DD_INSTRUMENTATION_TELEMETRY_ENABLED"] = os.environ.get( + "DD_APPSEC_ENABLED", "false" + ) -if os.environ.get("DD_API_SECURITY_ENABLED") is None: - os.environ["DD_API_SECURITY_ENABLED"] = "False" initialize_cold_start_tracing() diff --git a/datadog_lambda/asm.py b/datadog_lambda/asm.py index 31f750d82..7bd8272f8 100644 --- a/datadog_lambda/asm.py +++ b/datadog_lambda/asm.py @@ -73,17 +73,16 @@ def asm_start_request( route: Optional[str] = None if event_source.event_type == EventTypes.ALB: - headers = event.get("headers") - multi_value_request_headers = event.get("multiValueHeaders") - if multi_value_request_headers: - request_headers = _to_single_value_headers(multi_value_request_headers) - else: - request_headers = headers or {} - raw_uri = event.get("path") - parsed_query = event.get("multiValueQueryStringParameters") or event.get( - "queryStringParameters" - ) + + if event_source.subtype == EventSubtypes.ALB: + request_headers = event.get("headers", {}) + parsed_query = event.get("queryStringParameters") + if event_source.subtype == EventSubtypes.ALB_MULTI_VALUE_HEADERS: + request_headers = _to_single_value_headers( + event.get("multiValueHeaders", {}) + ) + parsed_query = event.get("multiValueQueryStringParameters") elif event_source.event_type == EventTypes.LAMBDA_FUNCTION_URL: request_headers = event.get("headers", {}) @@ -226,15 +225,27 @@ def get_asm_blocked_response( content_type = blocked.get("content-type", "application/json") content = http_utils._get_blocked_template(content_type) - response_headers = { - "content-type": content_type, - } - if "location" in blocked: - response_headers["location"] = blocked["location"] - - return { + response = { "statusCode": blocked.get("status_code", 403), - "headers": response_headers, "body": content, "isBase64Encoded": False, } + + needs_multi_value_headers = event_source.equals( + EventTypes.ALB, EventSubtypes.ALB_MULTI_VALUE_HEADERS + ) + + if needs_multi_value_headers: + response["multiValueHeaders"] = { + "content-type": [content_type], + } + if "location" in blocked: + response["multiValueHeaders"]["location"] = [blocked["location"]] + else: + response["headers"] = { + "content-type": content_type, + } + if "location" in blocked: + response["headers"]["location"] = blocked["location"] + + return response diff --git a/datadog_lambda/trigger.py b/datadog_lambda/trigger.py index bbd0d027a..e0c3c4fa1 100644 --- a/datadog_lambda/trigger.py +++ b/datadog_lambda/trigger.py @@ -54,6 +54,10 @@ class EventSubtypes(_stringTypedEnum): WEBSOCKET = "websocket" HTTP_API = "http-api" + ALB = "alb" # regular alb + # ALB with the multi-value headers option checked on the target group + ALB_MULTI_VALUE_HEADERS = "alb-multi-value-headers" + class _EventSource: """ @@ -133,7 +137,12 @@ def parse_event_source(event: dict) -> _EventSource: event_source.subtype = EventSubtypes.WEBSOCKET if request_context and request_context.get("elb"): - event_source = _EventSource(EventTypes.ALB) + if "multiValueHeaders" in event: + event_source = _EventSource( + EventTypes.ALB, EventSubtypes.ALB_MULTI_VALUE_HEADERS + ) + else: + event_source = _EventSource(EventTypes.ALB, EventSubtypes.ALB) if event.get("awslogs"): event_source = _EventSource(EventTypes.CLOUDWATCH_LOGS) @@ -288,7 +297,7 @@ def extract_http_tags(event): """ Extracts HTTP facet tags from the triggering event """ - http_tags = {} + http_tags = {"span.kind": "server"} # Safely get request_context and ensure it's a dictionary request_context = event.get("requestContext") diff --git a/datadog_lambda/version.py b/datadog_lambda/version.py index 7fb332f27..ac6691b07 100644 --- a/datadog_lambda/version.py +++ b/datadog_lambda/version.py @@ -1 +1 @@ -__version__ = "8.113.0" +__version__ = "8.114.0" diff --git a/datadog_lambda/wrapper.py b/datadog_lambda/wrapper.py index 494556257..51e5bcfac 100644 --- a/datadog_lambda/wrapper.py +++ b/datadog_lambda/wrapper.py @@ -53,6 +53,9 @@ asm_start_request, get_asm_blocked_response, ) + from ddtrace.internal.appsec.product import start + + start() if config.profiling_enabled: from ddtrace.profiling import profiler @@ -281,6 +284,34 @@ def _before(self, event, context): def _after(self, event, context): try: status_code = extract_http_status_code_tag(self.trigger_tags, self.response) + + if self.span: + if config.appsec_enabled and not self.blocking_response: + asm_start_response( + self.span, + status_code, + self.event_source, + response=self.response, + ) + self.blocking_response = get_asm_blocked_response(self.event_source) + + if self.blocking_response: + status_code = str(self.blocking_response.get("statusCode")) + if config.capture_payload_enabled and self.response: + tag_object( + self.span, "function.blocked_response", self.response + ) + self.response = self.blocking_response + + if config.capture_payload_enabled: + tag_object(self.span, "function.request", event) + tag_object(self.span, "function.response", self.response) + + if status_code: + self.span.set_tag("http.status_code", status_code) + + self.span.finish() + if status_code: self.trigger_tags["http.status_code"] = status_code mark_trace_as_error_for_5xx_responses(context, status_code, self.span) @@ -295,25 +326,6 @@ def _after(self, event, context): if should_trace_cold_start: trace_ctx = tracer.current_trace_context() - if self.span: - if config.capture_payload_enabled: - tag_object(self.span, "function.request", event) - tag_object(self.span, "function.response", self.response) - - if status_code: - self.span.set_tag("http.status_code", status_code) - - if config.appsec_enabled and not self.blocking_response: - asm_start_response( - self.span, - status_code, - self.event_source, - response=self.response, - ) - self.blocking_response = get_asm_blocked_response(self.event_source) - - self.span.finish() - if self.inferred_span: if status_code: self.inferred_span.set_tag("http.status_code", status_code) diff --git a/pyproject.toml b/pyproject.toml index 1ef66fd54..ae46ce3d3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "datadog_lambda" -version = "8.113.0" +version = "8.114.0" description = "The Datadog AWS Lambda Library" authors = ["Datadog, Inc. "] license = "Apache-2.0" diff --git a/scripts/build_layers.sh b/scripts/build_layers.sh index a0d6ee39d..23941b7a0 100755 --- a/scripts/build_layers.sh +++ b/scripts/build_layers.sh @@ -61,7 +61,7 @@ function docker_build_zip { # between different python runtimes. temp_dir=$(mktemp -d) docker buildx build -t datadog-lambda-python-${arch}:$1 . --no-cache \ - --build-arg image=public.ecr.aws/docker/library/python:$1 \ + --build-arg image=public.ecr.aws/sam/build-python$1:1 \ --build-arg runtime=python$1 \ --platform linux/${arch} \ --progress=plain \ diff --git a/scripts/check_layer_size.sh b/scripts/check_layer_size.sh index 0b835cb04..27988e858 100755 --- a/scripts/check_layer_size.sh +++ b/scripts/check_layer_size.sh @@ -8,8 +8,8 @@ # Compares layer size to threshold, and fails if below that threshold set -e -MAX_LAYER_COMPRESSED_SIZE_KB=$(expr 8 \* 1024) -MAX_LAYER_UNCOMPRESSED_SIZE_KB=$(expr 21 \* 1024) +MAX_LAYER_COMPRESSED_SIZE_KB=$(expr 17 \* 1024 / 2) # 8704 KB +MAX_LAYER_UNCOMPRESSED_SIZE_KB=$(expr 22 \* 1024) # 22528 KB LAYER_FILES_PREFIX="datadog_lambda_py" diff --git a/tests/event_samples/application-load-balancer-mutivalue-headers.json b/tests/event_samples/application-load-balancer-multivalue-headers.json similarity index 96% rename from tests/event_samples/application-load-balancer-mutivalue-headers.json rename to tests/event_samples/application-load-balancer-multivalue-headers.json index 6d446d15c..a35ca5023 100644 --- a/tests/event_samples/application-load-balancer-mutivalue-headers.json +++ b/tests/event_samples/application-load-balancer-multivalue-headers.json @@ -6,7 +6,7 @@ }, "httpMethod": "GET", "path": "/lambda", - "queryStringParameters": { + "multiValueQueryStringParameters": { "query": "1234ABCD" }, "multiValueHeaders": { diff --git a/tests/integration/snapshots/logs/async-metrics_python310.log b/tests/integration/snapshots/logs/async-metrics_python310.log index 66351a990..d72afba39 100644 --- a/tests/integration/snapshots/logs/async-metrics_python310.log +++ b/tests/integration/snapshots/logs/async-metrics_python310.log @@ -104,6 +104,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", + "span.kind": "server", "http.url": "https://xxxx.execute-api.us-east-2.amazonaws.com/", "http.url_details.path": "/Prod/", "http.method": "GET", @@ -642,6 +643,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX$default", + "span.kind": "server", "http.url": "https://xxxx.execute-api.eu-west-1.amazonaws.com/", "http.url_details.path": "/httpapi/get", "http.method": "GET", @@ -1479,6 +1481,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", + "span.kind": "server", "http.url": "https://xxxx.execute-api.eu-west-1.amazonaws.com/", "http.status_code": "200" }, diff --git a/tests/integration/snapshots/logs/async-metrics_python311.log b/tests/integration/snapshots/logs/async-metrics_python311.log index 8e19e239d..dac9c4277 100644 --- a/tests/integration/snapshots/logs/async-metrics_python311.log +++ b/tests/integration/snapshots/logs/async-metrics_python311.log @@ -104,6 +104,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", + "span.kind": "server", "http.url": "https://xxxx.execute-api.us-east-2.amazonaws.com/", "http.url_details.path": "/Prod/", "http.method": "GET", @@ -642,6 +643,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX$default", + "span.kind": "server", "http.url": "https://xxxx.execute-api.eu-west-1.amazonaws.com/", "http.url_details.path": "/httpapi/get", "http.method": "GET", @@ -1479,6 +1481,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", + "span.kind": "server", "http.url": "https://xxxx.execute-api.eu-west-1.amazonaws.com/", "http.status_code": "200" }, diff --git a/tests/integration/snapshots/logs/async-metrics_python312.log b/tests/integration/snapshots/logs/async-metrics_python312.log index 9879d8754..c2b624ff3 100644 --- a/tests/integration/snapshots/logs/async-metrics_python312.log +++ b/tests/integration/snapshots/logs/async-metrics_python312.log @@ -104,6 +104,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", + "span.kind": "server", "http.url": "https://xxxx.execute-api.us-east-2.amazonaws.com/", "http.url_details.path": "/Prod/", "http.method": "GET", @@ -642,6 +643,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX$default", + "span.kind": "server", "http.url": "https://xxxx.execute-api.eu-west-1.amazonaws.com/", "http.url_details.path": "/httpapi/get", "http.method": "GET", @@ -1479,6 +1481,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", + "span.kind": "server", "http.url": "https://xxxx.execute-api.eu-west-1.amazonaws.com/", "http.status_code": "200" }, diff --git a/tests/integration/snapshots/logs/async-metrics_python313.log b/tests/integration/snapshots/logs/async-metrics_python313.log index 92bb83efe..9a5e763f9 100644 --- a/tests/integration/snapshots/logs/async-metrics_python313.log +++ b/tests/integration/snapshots/logs/async-metrics_python313.log @@ -104,6 +104,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", + "span.kind": "server", "http.url": "https://xxxx.execute-api.us-east-2.amazonaws.com/", "http.url_details.path": "/Prod/", "http.method": "GET", @@ -642,6 +643,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX$default", + "span.kind": "server", "http.url": "https://xxxx.execute-api.eu-west-1.amazonaws.com/", "http.url_details.path": "/httpapi/get", "http.method": "GET", @@ -1479,6 +1481,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", + "span.kind": "server", "http.url": "https://xxxx.execute-api.eu-west-1.amazonaws.com/", "http.status_code": "200" }, diff --git a/tests/integration/snapshots/logs/async-metrics_python38.log b/tests/integration/snapshots/logs/async-metrics_python38.log index 6c5021469..9e6370b5b 100644 --- a/tests/integration/snapshots/logs/async-metrics_python38.log +++ b/tests/integration/snapshots/logs/async-metrics_python38.log @@ -104,6 +104,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", + "span.kind": "server", "http.url": "https://xxxx.execute-api.us-east-2.amazonaws.com/", "http.url_details.path": "/Prod/", "http.method": "GET", @@ -642,6 +643,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX$default", + "span.kind": "server", "http.url": "https://xxxx.execute-api.eu-west-1.amazonaws.com/", "http.url_details.path": "/httpapi/get", "http.method": "GET", @@ -1479,6 +1481,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", + "span.kind": "server", "http.url": "https://xxxx.execute-api.eu-west-1.amazonaws.com/", "http.status_code": "200" }, diff --git a/tests/integration/snapshots/logs/async-metrics_python39.log b/tests/integration/snapshots/logs/async-metrics_python39.log index 42bdaeb84..70f7a59f1 100644 --- a/tests/integration/snapshots/logs/async-metrics_python39.log +++ b/tests/integration/snapshots/logs/async-metrics_python39.log @@ -104,6 +104,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", + "span.kind": "server", "http.url": "https://xxxx.execute-api.us-east-2.amazonaws.com/", "http.url_details.path": "/Prod/", "http.method": "GET", @@ -642,6 +643,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX$default", + "span.kind": "server", "http.url": "https://xxxx.execute-api.eu-west-1.amazonaws.com/", "http.url_details.path": "/httpapi/get", "http.method": "GET", @@ -1479,6 +1481,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", + "span.kind": "server", "http.url": "https://xxxx.execute-api.eu-west-1.amazonaws.com/", "http.status_code": "200" }, diff --git a/tests/integration/snapshots/logs/sync-metrics_python310.log b/tests/integration/snapshots/logs/sync-metrics_python310.log index 201ca82a7..7d504bdcb 100644 --- a/tests/integration/snapshots/logs/sync-metrics_python310.log +++ b/tests/integration/snapshots/logs/sync-metrics_python310.log @@ -84,6 +84,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", + "span.kind": "server", "http.url": "https://xxxx.execute-api.us-east-2.amazonaws.com/", "http.url_details.path": "/Prod/", "http.method": "GET", @@ -679,6 +680,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX$default", + "span.kind": "server", "http.url": "https://xxxx.execute-api.eu-west-1.amazonaws.com/", "http.url_details.path": "/httpapi/get", "http.method": "GET", @@ -1611,6 +1613,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", + "span.kind": "server", "http.url": "https://xxxx.execute-api.eu-west-1.amazonaws.com/", "http.status_code": "200" }, diff --git a/tests/integration/snapshots/logs/sync-metrics_python311.log b/tests/integration/snapshots/logs/sync-metrics_python311.log index 564db4a94..aa242652d 100644 --- a/tests/integration/snapshots/logs/sync-metrics_python311.log +++ b/tests/integration/snapshots/logs/sync-metrics_python311.log @@ -84,6 +84,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", + "span.kind": "server", "http.url": "https://xxxx.execute-api.us-east-2.amazonaws.com/", "http.url_details.path": "/Prod/", "http.method": "GET", @@ -679,6 +680,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX$default", + "span.kind": "server", "http.url": "https://xxxx.execute-api.eu-west-1.amazonaws.com/", "http.url_details.path": "/httpapi/get", "http.method": "GET", @@ -1611,6 +1613,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", + "span.kind": "server", "http.url": "https://xxxx.execute-api.eu-west-1.amazonaws.com/", "http.status_code": "200" }, diff --git a/tests/integration/snapshots/logs/sync-metrics_python312.log b/tests/integration/snapshots/logs/sync-metrics_python312.log index 6010a3641..99471eaf8 100644 --- a/tests/integration/snapshots/logs/sync-metrics_python312.log +++ b/tests/integration/snapshots/logs/sync-metrics_python312.log @@ -84,6 +84,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", + "span.kind": "server", "http.url": "https://xxxx.execute-api.us-east-2.amazonaws.com/", "http.url_details.path": "/Prod/", "http.method": "GET", @@ -679,6 +680,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX$default", + "span.kind": "server", "http.url": "https://xxxx.execute-api.eu-west-1.amazonaws.com/", "http.url_details.path": "/httpapi/get", "http.method": "GET", @@ -1611,6 +1613,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", + "span.kind": "server", "http.url": "https://xxxx.execute-api.eu-west-1.amazonaws.com/", "http.status_code": "200" }, diff --git a/tests/integration/snapshots/logs/sync-metrics_python313.log b/tests/integration/snapshots/logs/sync-metrics_python313.log index 21d033620..e53aae508 100644 --- a/tests/integration/snapshots/logs/sync-metrics_python313.log +++ b/tests/integration/snapshots/logs/sync-metrics_python313.log @@ -84,6 +84,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", + "span.kind": "server", "http.url": "https://xxxx.execute-api.us-east-2.amazonaws.com/", "http.url_details.path": "/Prod/", "http.method": "GET", @@ -679,6 +680,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX$default", + "span.kind": "server", "http.url": "https://xxxx.execute-api.eu-west-1.amazonaws.com/", "http.url_details.path": "/httpapi/get", "http.method": "GET", @@ -1611,6 +1613,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", + "span.kind": "server", "http.url": "https://xxxx.execute-api.eu-west-1.amazonaws.com/", "http.status_code": "200" }, diff --git a/tests/integration/snapshots/logs/sync-metrics_python38.log b/tests/integration/snapshots/logs/sync-metrics_python38.log index a68289046..9cbf45648 100644 --- a/tests/integration/snapshots/logs/sync-metrics_python38.log +++ b/tests/integration/snapshots/logs/sync-metrics_python38.log @@ -84,6 +84,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", + "span.kind": "server", "http.url": "https://xxxx.execute-api.us-east-2.amazonaws.com/", "http.url_details.path": "/Prod/", "http.method": "GET", @@ -679,6 +680,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX$default", + "span.kind": "server", "http.url": "https://xxxx.execute-api.eu-west-1.amazonaws.com/", "http.url_details.path": "/httpapi/get", "http.method": "GET", @@ -1611,6 +1613,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", + "span.kind": "server", "http.url": "https://xxxx.execute-api.eu-west-1.amazonaws.com/", "http.status_code": "200" }, diff --git a/tests/integration/snapshots/logs/sync-metrics_python39.log b/tests/integration/snapshots/logs/sync-metrics_python39.log index 6a8d692e9..cc0407c1b 100644 --- a/tests/integration/snapshots/logs/sync-metrics_python39.log +++ b/tests/integration/snapshots/logs/sync-metrics_python39.log @@ -84,6 +84,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", + "span.kind": "server", "http.url": "https://xxxx.execute-api.us-east-2.amazonaws.com/", "http.url_details.path": "/Prod/", "http.method": "GET", @@ -679,6 +680,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX$default", + "span.kind": "server", "http.url": "https://xxxx.execute-api.eu-west-1.amazonaws.com/", "http.url_details.path": "/httpapi/get", "http.method": "GET", @@ -1611,6 +1613,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", + "span.kind": "server", "http.url": "https://xxxx.execute-api.eu-west-1.amazonaws.com/", "http.status_code": "200" }, diff --git a/tests/test_asm.py b/tests/test_asm.py index 7a5e6c560..1e11b102d 100644 --- a/tests/test_asm.py +++ b/tests/test_asm.py @@ -8,6 +8,7 @@ get_asm_blocked_response, ) from datadog_lambda.trigger import ( + EventSubtypes, EventTypes, _EventSource, extract_trigger_tags, @@ -34,7 +35,7 @@ ), ( "application_load_balancer_multivalue_headers", - "application-load-balancer-mutivalue-headers.json", + "application-load-balancer-multivalue-headers.json", "72.12.164.125", "/lambda?query=1234ABCD", "GET", @@ -111,7 +112,7 @@ ), ( "application_load_balancer_multivalue_headers", - "application-load-balancer-mutivalue-headers.json", + "application-load-balancer-multivalue-headers.json", { "statusCode": 404, "multiValueHeaders": { @@ -397,6 +398,25 @@ def test_get_asm_blocked_response_blocked( response = get_asm_blocked_response(event_source) assert response["statusCode"] == expected_status assert response["headers"] == expected_headers + assert "multiValueHeaders" not in response + + +@patch("datadog_lambda.asm.get_blocked") +def test_get_asm_blocked_response_blocked_multi_value_headers( + mock_get_blocked, +): + # HTML blocking response + mock_get_blocked.return_value = { + "status_code": 401, + "type": "html", + "content-type": "text/html", + } + + event_source = _EventSource(EventTypes.ALB, EventSubtypes.ALB_MULTI_VALUE_HEADERS) + response = get_asm_blocked_response(event_source) + assert response["statusCode"] == 401 + assert response["multiValueHeaders"] == {"content-type": ["text/html"]} + assert "headers" not in response @patch("datadog_lambda.asm.get_blocked") diff --git a/tests/test_trigger.py b/tests/test_trigger.py index 151039372..182e61d8e 100644 --- a/tests/test_trigger.py +++ b/tests/test_trigger.py @@ -5,6 +5,7 @@ from datadog_lambda.trigger import ( EventSubtypes, + EventTypes, parse_event_source, get_event_source_arn, extract_trigger_tags, @@ -117,6 +118,22 @@ def test_event_source_application_load_balancer(self): event_source = parse_event_source(event) event_source_arn = get_event_source_arn(event_source, event, ctx) self.assertEqual(event_source.to_string(), event_sample_source) + self.assertEqual(event_source.subtype, EventSubtypes.ALB) + self.assertEqual( + event_source_arn, + "arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/lambda-xyz/123abc", + ) + + def test_event_source_application_load_balancer_multi_value_headers(self): + event_sample_source = "application-load-balancer-multivalue-headers" + test_file = event_samples + event_sample_source + ".json" + with open(test_file, "r") as event: + event = json.load(event) + ctx = get_mock_context() + event_source = parse_event_source(event) + event_source_arn = get_event_source_arn(event_source, event, ctx) + self.assertEqual(event_source.event_type, EventTypes.ALB) + self.assertEqual(event_source.subtype, EventSubtypes.ALB_MULTI_VALUE_HEADERS) self.assertEqual( event_source_arn, "arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/lambda-xyz/123abc", @@ -305,6 +322,7 @@ def test_extract_trigger_tags_api_gateway(self): "http.url_details.path": "/prod/path/to/resource", "http.method": "POST", "http.route": "/{proxy+}", + "span.kind": "server", }, ) @@ -324,6 +342,7 @@ def test_extract_trigger_tags_api_gateway_non_proxy(self): "http.url_details.path": "/dev/http/get", "http.method": "GET", "http.route": "/http/get", + "span.kind": "server", }, ) @@ -340,6 +359,7 @@ def test_extract_trigger_tags_api_gateway_websocket_connect(self): "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "arn:aws:apigateway:us-west-1::/restapis/p62c47itsb/stages/dev", "http.url": "https://p62c47itsb.execute-api.eu-west-1.amazonaws.com/", + "span.kind": "server", }, ) @@ -356,6 +376,7 @@ def test_extract_trigger_tags_api_gateway_websocket_default(self): "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "arn:aws:apigateway:us-west-1::/restapis/p62c47itsb/stages/dev", "http.url": "https://p62c47itsb.execute-api.eu-west-1.amazonaws.com/", + "span.kind": "server", }, ) @@ -372,6 +393,7 @@ def test_extract_trigger_tags_api_gateway_websocket_disconnect(self): "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "arn:aws:apigateway:us-west-1::/restapis/p62c47itsb/stages/dev", "http.url": "https://p62c47itsb.execute-api.eu-west-1.amazonaws.com/", + "span.kind": "server", }, ) @@ -390,6 +412,7 @@ def test_extract_trigger_tags_api_gateway_http_api(self): "http.url": "https://x02yirxc7a.execute-api.eu-west-1.amazonaws.com/", "http.url_details.path": "/httpapi/get", "http.method": "GET", + "span.kind": "server", "http.route": "/httpapi/get", }, ) @@ -408,6 +431,7 @@ def test_extract_trigger_tags_application_load_balancer(self): "function_trigger.event_source_arn": "arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/lambda-xyz/123abc", "http.url_details.path": "/lambda", "http.method": "GET", + "span.kind": "server", }, ) @@ -569,7 +593,12 @@ def test_extract_http_tags_with_invalid_request_context(self): http_tags = extract_http_tags(event) # Should still extract valid tags from the event self.assertEqual( - http_tags, {"http.url_details.path": "/test", "http.method": "GET"} + http_tags, + { + "span.kind": "server", + "http.url_details.path": "/test", + "http.method": "GET", + }, ) def test_extract_http_tags_with_invalid_apigateway_http(self): @@ -582,7 +611,7 @@ def test_extract_http_tags_with_invalid_apigateway_http(self): } http_tags = extract_http_tags(event) # Should not raise an exception - self.assertEqual(http_tags, {}) + self.assertEqual(http_tags, {"span.kind": "server"}) def test_extract_http_tags_with_invalid_headers(self): from datadog_lambda.trigger import extract_http_tags @@ -591,7 +620,7 @@ def test_extract_http_tags_with_invalid_headers(self): event = {"headers": "not_a_dict"} http_tags = extract_http_tags(event) # Should not raise an exception - self.assertEqual(http_tags, {}) + self.assertEqual(http_tags, {"span.kind": "server"}) def test_extract_http_tags_with_invalid_route(self): from datadog_lambda.trigger import extract_http_tags @@ -600,7 +629,7 @@ def test_extract_http_tags_with_invalid_route(self): event = {"routeKey": 12345} # Not a string http_tags = extract_http_tags(event) # Should not raise an exception - self.assertEqual(http_tags, {}) + self.assertEqual(http_tags, {"span.kind": "server"}) class ExtractHTTPStatusCodeTag(unittest.TestCase): diff --git a/tests/test_wrapper.py b/tests/test_wrapper.py index e07b5ca91..0083b3faa 100644 --- a/tests/test_wrapper.py +++ b/tests/test_wrapper.py @@ -690,6 +690,9 @@ def setUp(self): self.mock_get_asm_blocking_response = patcher.start() self.addCleanup(patcher.stop) + with open("tests/event_samples/api-gateway.json") as f: + self.api_gateway_request = json.loads(f.read()) + self.fake_blocking_response = { "statusCode": "403", "headers": { @@ -706,7 +709,7 @@ def test_blocking_before(self): lambda_handler = wrapper.datadog_lambda_wrapper(mock_handler) - response = lambda_handler({}, get_mock_context()) + response = lambda_handler(self.api_gateway_request, get_mock_context()) self.assertEqual(response, self.fake_blocking_response) mock_handler.assert_not_called() @@ -715,27 +718,31 @@ def test_blocking_before(self): self.mock_asm_start_request.assert_called_once() self.mock_asm_start_response.assert_not_called() + assert lambda_handler.span.get_tag("http.status_code") == "403" + def test_blocking_during(self): self.mock_get_asm_blocking_response.return_value = None - @wrapper.datadog_lambda_wrapper def lambda_handler(event, context): self.mock_get_asm_blocking_response.return_value = ( self.fake_blocking_response ) raise wrapper.BlockingException() - response = lambda_handler({}, get_mock_context()) + lambda_handler = wrapper.datadog_lambda_wrapper(lambda_handler) + + response = lambda_handler(self.api_gateway_request, get_mock_context()) self.assertEqual(response, self.fake_blocking_response) self.mock_asm_set_context.assert_called_once() self.mock_asm_start_request.assert_called_once() self.mock_asm_start_response.assert_not_called() + assert lambda_handler.span.get_tag("http.status_code") == "403" + def test_blocking_after(self): self.mock_get_asm_blocking_response.return_value = None - @wrapper.datadog_lambda_wrapper def lambda_handler(event, context): self.mock_get_asm_blocking_response.return_value = ( self.fake_blocking_response @@ -745,13 +752,17 @@ def lambda_handler(event, context): "body": "This should not be returned", } - response = lambda_handler({}, get_mock_context()) + lambda_handler = wrapper.datadog_lambda_wrapper(lambda_handler) + + response = lambda_handler(self.api_gateway_request, get_mock_context()) self.assertEqual(response, self.fake_blocking_response) self.mock_asm_set_context.assert_called_once() self.mock_asm_start_request.assert_called_once() self.mock_asm_start_response.assert_called_once() + assert lambda_handler.span.get_tag("http.status_code") == "403" + def test_no_blocking_appsec_disabled(self): os.environ["DD_APPSEC_ENABLED"] = "false" @@ -764,14 +775,17 @@ def test_no_blocking_appsec_disabled(self): "body": "This should be returned", } - @wrapper.datadog_lambda_wrapper def lambda_handler(event, context): return expected_response - response = lambda_handler({}, get_mock_context()) + lambda_handler = wrapper.datadog_lambda_wrapper(lambda_handler) + + response = lambda_handler(self.api_gateway_request, get_mock_context()) self.assertEqual(response, expected_response) self.mock_get_asm_blocking_response.assert_not_called() self.mock_asm_set_context.assert_not_called() self.mock_asm_start_request.assert_not_called() self.mock_asm_start_response.assert_not_called() + + assert lambda_handler.span.get_tag("http.status_code") == "200"