Skip to content
Merged
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
23 changes: 22 additions & 1 deletion .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,36 @@ on:
workflow_dispatch: {}

jobs:
discover:
name: Discover E2E Tests
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Find all e2e-tests directories
id: set-matrix
run: |
# Find all directories with e2e-tests and convert to JSON array
LIBRARIES=$(find drift/instrumentation -type d -name "e2e-tests" \
| sed 's|drift/instrumentation/||' | sed 's|/e2e-tests||' | sort \
| jq -R -s -c 'split("\n") | map(select(length > 0))')

echo "Found libraries with e2e-tests: $LIBRARIES"
echo "matrix=$LIBRARIES" >> $GITHUB_OUTPUT

e2e:
name: E2E Tests - ${{ matrix.library }}
needs: discover
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
max-parallel: 6
matrix:
library: [flask, fastapi, django, redis, requests, httpx, psycopg, psycopg2, urllib3]
library: ${{ fromJSON(needs.discover.outputs.matrix) }}
steps:
- name: Checkout
uses: actions/checkout@v4
Expand Down
5 changes: 5 additions & 0 deletions drift/instrumentation/aiohttp/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"""aiohttp HTTP client instrumentation."""

from .instrumentation import AiohttpInstrumentation, RequestDroppedByTransform

__all__ = ["AiohttpInstrumentation", "RequestDroppedByTransform"]
28 changes: 28 additions & 0 deletions drift/instrumentation/aiohttp/e2e-tests/.tusk/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
version: 1

service:
id: "aiohttp-e2e-test-id"
name: "aiohttp-e2e-test"
port: 8000
start:
command: "python src/app.py"
readiness_check:
command: "curl -f http://localhost:8000/health"
timeout: 45s
interval: 5s

tusk_api:
url: "http://localhost:8000"

test_execution:
concurrent_limit: 10
batch_size: 10
timeout: 30s

recording:
sampling_rate: 1.0
export_spans: false

replay:
enable_telemetry: false

22 changes: 22 additions & 0 deletions drift/instrumentation/aiohttp/e2e-tests/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
FROM python-e2e-base:latest

# Copy SDK source for editable install
COPY . /sdk

# Copy test files
COPY drift/instrumentation/aiohttp/e2e-tests /app

WORKDIR /app

# Install dependencies (requirements.txt uses -e /sdk for SDK)
RUN pip install -q -r requirements.txt

# Make entrypoint executable
RUN chmod +x entrypoint.py

# Create .tusk directories
RUN mkdir -p /app/.tusk/traces /app/.tusk/logs

# Run entrypoint
ENTRYPOINT ["python", "entrypoint.py"]

20 changes: 20 additions & 0 deletions drift/instrumentation/aiohttp/e2e-tests/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
services:
app:
build:
context: ../../../..
dockerfile: drift/instrumentation/aiohttp/e2e-tests/Dockerfile
args:
- TUSK_CLI_VERSION=${TUSK_CLI_VERSION:-latest}
environment:
- PORT=8000
- TUSK_ANALYTICS_DISABLED=1
- PYTHONUNBUFFERED=1
working_dir: /app
volumes:
# Mount SDK source for hot reload (no rebuild needed for SDK changes)
- ../../../..:/sdk
# Mount app source for development
- ./src:/app/src
# Mount .tusk folder to persist traces
- ./.tusk:/app/.tusk

65 changes: 65 additions & 0 deletions drift/instrumentation/aiohttp/e2e-tests/entrypoint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/usr/bin/env python3
"""
E2E Test Entrypoint for aiohttp Instrumentation

This script orchestrates the full e2e test lifecycle:
1. Setup: Install dependencies
2. Record: Start app in RECORD mode, execute requests
3. Test: Run Tusk CLI tests
4. Teardown: Cleanup and return exit code
"""

import sys
from pathlib import Path

# Add SDK to path for imports
sys.path.insert(0, "/sdk")

from drift.instrumentation.e2e_common.base_runner import Colors, E2ETestRunnerBase


class AiohttpE2ETestRunner(E2ETestRunnerBase):
"""E2E test runner for aiohttp instrumentation."""

def __init__(self):
import os

port = int(os.getenv("PORT", "8000"))
super().__init__(app_port=port)

def check_socket_instrumentation_warnings(self):
"""Override to skip socket instrumentation check for aiohttp.

aiohttp uses aiohappyeyeballs internally for connection management,
which makes low-level socket calls that trigger the socket instrumentation
warnings during REPLAY mode. This is expected behavior - the main
aiohttp instrumentation is working correctly (intercepting _request).

We skip this check because:
1. aiohappyeyeballs is an internal implementation detail of aiohttp
2. Our instrumentation correctly intercepts at the _request level
3. The socket calls from aiohappyeyeballs are a known limitation
"""
self.log("=" * 50, Colors.BLUE)
self.log("Checking for Instrumentation Warnings", Colors.BLUE)
self.log("=" * 50, Colors.BLUE)

self.log(
"⚠ Skipping socket instrumentation check for aiohttp (aiohappyeyeballs makes expected socket calls)",
Colors.YELLOW,
)

# Just verify trace files exist
traces_dir = Path(".tusk/traces")
trace_files = list(traces_dir.glob("*.jsonl")) if traces_dir.exists() else []
if trace_files:
self.log(f"✓ Found {len(trace_files)} trace file(s).", Colors.GREEN)
else:
self.log("✗ ERROR: No trace files found!", Colors.RED)
self.exit_code = 1


if __name__ == "__main__":
runner = AiohttpE2ETestRunner()
exit_code = runner.run()
sys.exit(exit_code)
5 changes: 5 additions & 0 deletions drift/instrumentation/aiohttp/e2e-tests/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-e /sdk
Flask>=3.1.2
aiohttp>=3.9.0
requests>=2.32.5

65 changes: 65 additions & 0 deletions drift/instrumentation/aiohttp/e2e-tests/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/bin/bash

# Exit on error
set -e

# Accept optional port parameter (default: 8000)
APP_PORT=${1:-8000}
export APP_PORT

# Generate unique docker compose project name
# Get the instrumentation name (parent directory of e2e-tests)
TEST_NAME="$(basename "$(dirname "$(pwd)")")"
PROJECT_NAME="python-${TEST_NAME}-${APP_PORT}"

# Colors for output
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'

echo -e "${BLUE}========================================${NC}"
echo -e "${BLUE}Running Python E2E Test: ${TEST_NAME}${NC}"
echo -e "${BLUE}Port: ${APP_PORT}${NC}"
echo -e "${BLUE}========================================${NC}"
echo ""

# Cleanup function
cleanup() {
echo ""
echo -e "${YELLOW}Cleaning up containers...${NC}"
docker compose -p "$PROJECT_NAME" down -v 2>/dev/null || true
}

# Register cleanup on exit
trap cleanup EXIT

# Build containers
echo -e "${BLUE}Building containers...${NC}"
docker compose -p "$PROJECT_NAME" build --no-cache

# Run the test container
echo -e "${BLUE}Starting test...${NC}"
echo ""

# Run container and capture exit code (always use port 8000 inside container)
# Disable set -e temporarily to capture exit code
set +e
docker compose -p "$PROJECT_NAME" run --rm app
EXIT_CODE=$?
set -e

echo ""
if [ $EXIT_CODE -eq 0 ]; then
echo -e "${GREEN}========================================${NC}"
echo -e "${GREEN}Test passed!${NC}"
echo -e "${GREEN}========================================${NC}"
else
echo -e "${RED}========================================${NC}"
echo -e "${RED}Test failed with exit code ${EXIT_CODE}${NC}"
echo -e "${RED}========================================${NC}"
fi

exit $EXIT_CODE

Loading