4b6817381b
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
CI / coverage-report (push) Has been cancelled
CI / test-kubernetes (push) Has been cancelled
CI / should-run-thorough (push) Has been cancelled
CI / test-thorough (cloudwatch-demo) (push) Has been cancelled
CI / test-thorough (flink-ecs) (push) Has been cancelled
CI / test-thorough (upstream-lambda) (push) Has been cancelled
CI / test-thorough (prefect-ecs-fargate) (push) Has been cancelled
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Has been cancelled
Benchmark image — build + push to ECR (any adapter) / build + push (push) Has been cancelled
CI / quality (ubuntu-latest) (push) Has been cancelled
CI / test (tools-runtime) (push) Has been cancelled
CI / test (e2e-general) (push) Has been cancelled
CI / test (cli-runtime) (push) Has been cancelled
CI / test (e2e-provider-and-openclaw) (push) Has been cancelled
CI / test (integrations-and-misc) (push) Has been cancelled
Release / verify (push) Has been cancelled
Release / build-python-dist (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Has been cancelled
Release / publish-release (push) Has been cancelled
Release / publish-main-release (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Has been cancelled
Release / prepare (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Has been cancelled
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Has been cancelled
200 lines
13 KiB
Markdown
200 lines
13 KiB
Markdown
# Kubernetes Test Case — Reference Architecture
|
|
|
|
## Overview
|
|
|
|
This test case validates an end-to-end ETL pipeline running as Kubernetes Jobs, covering both the **happy path** and **failure path** (intentional schema error injection). It runs in two modes:
|
|
|
|
| Mode | Cluster | S3 | Observability | CI |
|
|
|---|---|---|---|---|
|
|
| Local (`test_local.py`) | kind (local) | Real AWS S3 | — | Yes (GitHub Actions) |
|
|
| Datadog local (`test_datadog.py`) | kind (local) | Real AWS S3 | Datadog via Helm | Manual |
|
|
| EKS (`test_eks.py`, `trigger_alert.py`) | AWS EKS | Real AWS S3 | Datadog via Helm + Lambda trigger | Manual |
|
|
|
|
---
|
|
|
|
## Architecture Diagram
|
|
|
|
```
|
|
┌──────────────────────────────────────────────────────────────────────────────┐
|
|
│ TEST RUNNER (CI / local machine) │
|
|
│ │
|
|
│ test_local.py / test_eks.py / trigger_alert.py │
|
|
│ infrastructure_sdk/local.py · infrastructure_sdk/eks.py │
|
|
└────────────────────────┬─────────────────────────────────────────────────────┘
|
|
│ orchestrates
|
|
▼
|
|
┌──────────────────────────────────────────────────────────────────────────────┐
|
|
│ KUBERNETES CLUSTER (kind locally / AWS EKS in cloud) │
|
|
│ Namespace: tracer-test │
|
|
│ │
|
|
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
|
|
│ │ Job: etl-extract (PIPELINE_STAGE=extract) │ │
|
|
│ │ Image: tracer-k8s-test:latest │ │
|
|
│ │ → reads s3://landing-bucket/<run-id>/input.json │ │
|
|
│ │ → writes s3://landing-bucket/<run-id>/extracted.json │ │
|
|
│ └──────────────────────────────┬──────────────────────────────────────────┘ │
|
|
│ │ sequential (test runner waits for job) │
|
|
│ ┌──────────────────────────────▼──────────────────────────────────────────┐ │
|
|
│ │ Job: etl-transform (PIPELINE_STAGE=transform) │ │
|
|
│ │ Happy path → validates schema, converts amount to cents │ │
|
|
│ │ Error path → REQUIRED_FIELDS contains unknown field → DomainError │ │
|
|
│ │ → reads s3://landing-bucket/<run-id>/extracted.json │ │
|
|
│ │ → writes s3://landing-bucket/<run-id>/transformed.json (happy only) │ │
|
|
│ └──────────────────────────────┬──────────────────────────────────────────┘ │
|
|
│ │ (happy path only) │
|
|
│ ┌──────────────────────────────▼──────────────────────────────────────────┐ │
|
|
│ │ Job: etl-load (PIPELINE_STAGE=load) │ │
|
|
│ │ → reads s3://landing-bucket/<run-id>/transformed.json │ │
|
|
│ │ → writes s3://processed-bucket/<run-id>/output.json │ │
|
|
│ └─────────────────────────────────────────────────────────────────────────┘ │
|
|
└──────────────────────────────────────────────────────────────────────────────┘
|
|
|
|
│ real read/write
|
|
▼
|
|
┌──────────────────────────────────────────────────────────────────────────────┐
|
|
│ AWS S3 │
|
|
│ ┌───────────────────────────────┐ ┌────────────────────────────────────┐ │
|
|
│ │ tracer-k8s-landing-* │ │ tracer-k8s-processed-* │ │
|
|
│ │ <run-id>/input.json │ │ <run-id>/output.json │ │
|
|
│ │ <run-id>/extracted.json │ └────────────────────────────────────┘ │
|
|
│ │ <run-id>/transformed.json │ │
|
|
│ └───────────────────────────────┘ │
|
|
└──────────────────────────────────────────────────────────────────────────────┘
|
|
|
|
── EKS / Datadog paths only ────────────────────────────────────────────────────
|
|
|
|
┌──────────────────────────────────────────────────────────────────────────────┐
|
|
│ AWS Lambda (trigger_lambda/handler.py) │
|
|
│ POST /trigger → happy path (valid data) │
|
|
│ POST /trigger?inject_error → error path (bad data, forces DomainError) │
|
|
│ Fronted by API Gateway │
|
|
└───────────────────────────┬──────────────────────────────────────────────────┘
|
|
│ submits kubectl jobs to EKS
|
|
▼ (same K8s jobs as above)
|
|
|
|
┌──────────────────────────────────────────────────────────────────────────────┐
|
|
│ Datadog Agent (Helm chart: datadog/datadog) │
|
|
│ • collects container logs from all pods │
|
|
│ • collects kube-state-metrics (job.failed counter) │
|
|
│ Monitors: │
|
|
│ ├── Metric alert: kubernetes_state.job.failed > 0 → @slack-devs-alerts │
|
|
│ └── Log alert: "PIPELINE_ERROR" in logs → @slack-devs-alerts │
|
|
└──────────────────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Component Descriptions
|
|
|
|
### Pipeline Code (`pipeline_code/`)
|
|
|
|
A self-contained Python ETL application packaged as a single Docker image. The active stage is selected at runtime via the `PIPELINE_STAGE` environment variable.
|
|
|
|
| Module | Role |
|
|
|---|---|
|
|
| `stages/__init__.py` | Dispatcher — routes to extract / transform / load |
|
|
| `stages/extract.py` | Reads raw JSON from S3 landing bucket, writes to staging key |
|
|
| `stages/transform.py` | Validates schema against `REQUIRED_FIELDS`, converts `amount` to cents |
|
|
| `stages/load.py` | Reads staged data, writes final record to processed bucket |
|
|
| `domain.py` | Validation + transformation business logic |
|
|
| `schemas.py` | `InputRecord` / `ProcessedRecord` dataclasses |
|
|
| `errors.py` | `PipelineError` → `DomainError` / `SystemError` hierarchy |
|
|
| `config.py` | Reads all config from environment variables |
|
|
| `adapters/s3.py` | S3 read/write with `correlation_id` + `processed_at` metadata |
|
|
|
|
The image is built from `pipeline_code/Dockerfile` (Python 3.11-slim) and tagged `tracer-k8s-test:latest`.
|
|
|
|
### Kubernetes Manifests (`k8s_manifests/`)
|
|
|
|
Plain YAML templates with `{{PLACEHOLDER}}` variables replaced at test runtime by `infrastructure_sdk/local.py`. One manifest per pipeline stage plus an error variant for transform.
|
|
|
|
| Manifest | Stage | Notes |
|
|
|---|---|---|
|
|
| `job-extract.yaml` | extract | Reads `LANDING_BUCKET` + `S3_KEY` |
|
|
| `job-transform.yaml` | transform (success) | `backoffLimit: 0` — fails fast |
|
|
| `job-transform-error.yaml` | transform (failure) | Same spec, different labels |
|
|
| `job-load.yaml` | load | Also writes `PROCESSED_BUCKET` |
|
|
| `namespace.yaml` | — | `tracer-test` namespace |
|
|
|
|
### Helm Chart (`helm/`)
|
|
|
|
A thin Helm wrapper around the same ETL job for EKS / Datadog deployments. Exposes `job.injectError` to switch between happy and error paths without maintaining two separate manifests.
|
|
|
|
### Infrastructure SDK (`infrastructure_sdk/`)
|
|
|
|
| File | Responsibility |
|
|
|---|---|
|
|
| `local.py` | kind cluster lifecycle, Docker build/load, `kubectl apply`, job polling, Datadog Helm install, monitor CRUD |
|
|
| `eks.py` | EKS cluster + node group creation/teardown, ECR push, IAM roles, API Gateway + Lambda deployment |
|
|
|
|
### Test Entry Points
|
|
|
|
| File | When to run |
|
|
|---|---|
|
|
| `test_local.py` | CI (GitHub Actions) + local dev. kind cluster, real S3. |
|
|
| `test_datadog.py` | Manual. kind + Datadog Helm. Requires `DD_API_KEY` / `DD_APP_KEY`. |
|
|
| `test_eks.py` | Manual. Full EKS deployment + teardown. Requires full AWS credentials + Datadog keys. |
|
|
| `trigger_alert.py` | Manual. Fires alerts against an already-running EKS cluster via Lambda. |
|
|
|
|
---
|
|
|
|
## Data Flow (Happy Path)
|
|
|
|
```
|
|
Test Runner
|
|
│
|
|
├─ uploads input.json to s3://landing/<run-id>/input.json
|
|
│
|
|
├─ kubectl apply job-extract
|
|
│ Job reads input.json
|
|
│ Writes extracted.json → s3://landing/<run-id>/extracted.json
|
|
│
|
|
├─ kubectl apply job-transform
|
|
│ Job reads extracted.json
|
|
│ Validates schema, converts amount*100
|
|
│ Writes transformed.json → s3://landing/<run-id>/transformed.json
|
|
│
|
|
├─ kubectl apply job-load
|
|
│ Job reads transformed.json
|
|
│ Writes output.json → s3://processed/<run-id>/output.json
|
|
│
|
|
└─ asserts output.json exists + content matches expected record
|
|
```
|
|
|
|
## Data Flow (Error / Failure Path)
|
|
|
|
```
|
|
Test Runner
|
|
│
|
|
├─ uploads malformed or schema-breaking input.json
|
|
│
|
|
├─ kubectl apply job-extract (succeeds — no validation here)
|
|
│
|
|
├─ kubectl apply job-transform-error
|
|
│ Job reads extracted.json
|
|
│ Schema validation fails → DomainError("missing required field: X")
|
|
│ Job exits non-zero → K8s Job status: Failed
|
|
│ Container logs: "PIPELINE_ERROR ..."
|
|
│
|
|
└─ asserts:
|
|
job status == Failed
|
|
(Datadog path) log alert fires within timeout
|
|
(Datadog path) metric alert fires within timeout
|
|
```
|
|
|
|
---
|
|
|
|
## Environment Variables
|
|
|
|
| Variable | Used by | Description |
|
|
|---|---|---|
|
|
| `PIPELINE_STAGE` | All stages | Selects which stage to run (`extract`/`transform`/`load`) |
|
|
| `LANDING_BUCKET` | extract, transform | S3 bucket for raw + staging data |
|
|
| `PROCESSED_BUCKET` | load | S3 bucket for final output |
|
|
| `S3_KEY` | extract | S3 key of the input file |
|
|
| `PIPELINE_RUN_ID` | All stages | Unique ID — used as S3 key prefix |
|
|
| `REQUIRED_FIELDS` | transform | Comma-separated list of required fields (error injection: add unknown field) |
|
|
| `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` | All stages | AWS credentials (injected via Job env or IAM role on EKS) |
|
|
| `DD_API_KEY` / `DD_APP_KEY` | Datadog paths | Datadog credentials |
|
|
| `INJECT_ERROR` | Helm chart path | Set to `"true"` to trigger the error path via Helm |
|