A production-ready mock providing 100% compatible Ansible Automation Platform REST API endpoints for development, testing, and integration on Red Hat OpenShift.
- Key Features
- AAP API Endpoints
- Quick Start
- Log Upload & Replay
- OpenShift Deployment
- Logging Architecture
- Configuration
- Troubleshooting
- 🔌 100% Compatible REST API:
/api/v2/jobs/,/api/v2/job_events/,/api/v2/stdout/ - 📊 Real Job Events: Parsed from actual AAP logs
- 🔄 Pagination: Standard AAP pagination
- Drop-in Replacement: Point existing apps to this service - no code changes needed
- 📤 Upload Real AAP Logs: Multi-format auto-detection (JSON, raw Ansible, system logs)
- ⚡ Log Replay: Stream logs for Grafana Alloy/Promtail collection
- 🔄 Multi-File Replay: Replay all files with
"id_or_path": "all" - 🎯 Stdout Logging: AAP logs go to stdout → Kubernetes → Alloy → Loki (zero config!)
✅ JSON Event Logs • Raw Ansible Output • AAP System Logs • AWX/Tower Logs • Structured Format
Example Files: See /examples/ directory (8.5KB - 169KB real AAP logs)
✅ Health Checks • Persistent Storage • Security (non-root) • Helm Charts • OpenShift Native
| Endpoint | Description |
|---|---|
/api/v2/jobs/ |
List all jobs with pagination |
/api/v2/jobs/{id}/ |
Job details and status |
/api/v2/jobs/{id}/job_events/ |
Job events stream (most important) |
/api/v2/jobs/{id}/stdout/ |
Job stdout output |
/api/v2/job_templates/ |
Job templates |
| Endpoint | Description |
|---|---|
/api/logs/upload |
Upload AAP log → auto-creates job |
/api/logs/replay |
Stream logs (for Alloy/Promtail) |
/api/replay/stop |
Stop active replay |
/api/status |
Current replay status |
/healthz |
Health check |
# Run locally
./run-local.sh
# Upload a log file
curl -F "file=@examples/demo-job-complex.log" http://localhost:8080/api/logs/upload
# Check AAP API
curl http://localhost:8080/api/v2/jobs/ | jq .
# Start replay
curl -X POST http://localhost:8080/api/logs/replay \
-H 'Content-Type: application/json' \
-d '{
"source": "uploaded",
"id_or_path": "latest",
"mode": "file",
"rate_lines_per_sec": 50
}'# Build (for x86_64 clusters)
podman build --platform linux/amd64 -t quay.io/ecosystem-appeng/aap-mock:latest .
# Push
podman push quay.io/ecosystem-appeng/aap-mock:latestWhere to Get AAP Logs:
- AAP Web UI: Jobs → Download → Events/Output
- AAP CLI:
awx jobs stdout <job-id> - Log Files:
/var/log/tower/or/var/log/awx/ - Ansible Runner: Raw
ansible-playbookoutput
Upload:
curl -F "file=@your-aap-log.log" http://HOST/api/logs/upload# Replay latest uploaded file
curl -X POST http://HOST/api/logs/replay \
-H 'Content-Type: application/json' \
-d '{
"source": "uploaded",
"id_or_path": "latest",
"mode": "file",
"rate_lines_per_sec": 50,
"loop": false
}'
# Replay ALL uploaded files
curl -X POST http://HOST/api/logs/replay \
-H 'Content-Type: application/json' \
-d '{
"source": "uploaded",
"id_or_path": "all",
"mode": "file",
"rate_lines_per_sec": 80
}'
# Stop replay
curl -X POST http://HOST/api/replay/stop| Parameter | Default | Description |
|---|---|---|
source |
- | "uploaded", "auto-loaded", or "generated" |
id_or_path |
- | "latest", "all", UUID, or filename |
mode |
"file" |
"file", "otlp", or "both" |
rate_lines_per_sec |
20 |
Replay speed |
loop |
false |
Continuous replay |
jitter_ms |
100 |
Random delay between lines |
# Deploy
helm upgrade --install aap-mock ./chart/aap-mock \
--namespace alm-infra-final --create-namespace \
--set image.repository=quay.io/ecosystem-appeng/aap-mock \
--set image.tag=latest \
--set image.pullPolicy=Always
# Get route URL
oc get route aap-mock -n alm-infra-final -o jsonpath='{.spec.host}'
# Monitor deployment
oc rollout status deployment/aap-mock -n alm-infra-final
# View logs
oc logs -f -l app.kubernetes.io/name=aap-mock -n alm-infra-final# Development
helm upgrade --install aap-mock ./chart/aap-mock \
-n dev-namespace \
-f environments/values-dev.yaml
# Production
helm upgrade --install aap-mock ./chart/aap-mock \
-n prod-namespace \
-f environments/values-prod.yamlNamespace: Use helm --namespace <name> to deploy to any namespace. Default charts use aap-mock namespace.
# Get pod name
POD=$(oc get pods -l app.kubernetes.io/name=aap-mock -n alm-infra-final -o name | head -n1)
# Copy single file
oc cp your-log.log $POD:/app/sample-logs/ -n alm-infra-final
# Copy directory contents
oc cp examples/. $POD:/app/sample-logs/ -n alm-infra-final
# Refresh to detect new files
curl -X POST http://YOUR_ROUTE/api/auto-loaded/refresh# Uninstall
helm uninstall aap-mock -n alm-infra-final
# Verify cleanup
oc get all,pvc -n alm-infra-final | grep aap-mockThe app uses two separate log streams for clean Kubernetes log collection:
| Log Stream | Destination | Purpose |
|---|---|---|
| AAP Mock Logs | stdout → Kubernetes |
Mock AAP job logs (collected by Alloy/Promtail) |
| Application Logs | /var/log/aap-mock/app.log + stdout (with [APP] prefix) |
Debugging/diagnostics |
Zero Configuration: Grafana Alloy/Promtail automatically collect from pod stdout - no PVC mounts needed!
Clean Separation: Application diagnostics don't pollute your mock AAP data.
Real-Time: Logs appear in Loki immediately.
# All logs
oc logs -f -l app.kubernetes.io/name=aap-mock -n alm-infra-final
# Only AAP mock logs (what Alloy collects)
oc logs -f -l app.kubernetes.io/name=aap-mock -n alm-infra-final | grep -v "^\[APP\]"
# Only application diagnostics
oc logs -f -l app.kubernetes.io/name=aap-mock -n alm-infra-final | grep "^\[APP\]"Basic (Zero Config):
loki.source.kubernetes "pods" {
targets = discovery.kubernetes.pods.targets
forward_to = [loki.write.default.receiver]
}Advanced (with label extraction):
loki.process "aap_mock" {
forward_to = [loki.write.default.receiver]
stage.match {
selector = "{app_kubernetes_io_name=\"aap-mock\"}"
stage.regex {
expression = "(?P<timestamp>\\S+) (?P<level>\\S+) \\[(?P<job_context>[^\\]]+)\\] (?P<message>.*)"
}
stage.labels {
values = {
level = "",
job_context = "",
}
}
}
}# All AAP mock logs
{app_kubernetes_io_name="aap-mock"}
# Only AAP data (exclude app diagnostics)
{app_kubernetes_io_name="aap-mock"} !~ "\\[APP\\]"
# Errors only
{app_kubernetes_io_name="aap-mock"} |~ "ERROR"
# Specific job
{app_kubernetes_io_name="aap-mock"} |~ "\\[job_123"
| Variable | Default | Description |
|---|---|---|
PORT |
8080 |
HTTP server port |
PYTHONUNBUFFERED |
1 |
Python output buffering |
Key Configuration Options (see chart/aap-mock/values.yaml):
image:
repository: quay.io/ecosystem-appeng/aap-mock
tag: latest
pullPolicy: Always
persistence:
data:
enabled: true
size: 1Gi
logs:
enabled: true
size: 1Gi
resources:
limits:
memory: 512Mi
cpu: 500m
route:
enabled: true # OpenShift RouteEnvironment-Specific Values: See environments/values-dev.yaml and environments/values-prod.yaml
/
├── main.py # FastAPI application
├── Dockerfile # Container build
├── README.md # This file
├── run-local.sh # Local dev script
├── sample-logs/ # Auto-loaded logs (empty by default)
├── examples/ # Example AAP logs
├── chart/aap-mock/ # Helm chart
├── environments/ # Environment-specific configs
└── openshift/ # Manual deployment manifests
Pod not starting:
# Check logs
oc logs -l app.kubernetes.io/name=aap-mock -n alm-infra-final --tail=50
# Check events
oc get events -n alm-infra-final --sort-by='.lastTimestamp'Route not accessible:
# Verify route
oc get route aap-mock -n alm-infra-final
# Test from within cluster
oc exec -n alm-infra-final $POD -- curl -s http://localhost:8000/healthzLogs not in Loki:
# Check replay is active
curl http://YOUR_ROUTE/api/status
# Verify stdout output
oc logs -l app.kubernetes.io/name=aap-mock -n alm-infra-final --tail=20
# Check Alloy is running
oc get pods -n monitoring -l app=alloyFiles not auto-loading:
# List files in sample-logs
oc exec $POD -- ls -la /app/sample-logs/
# Check permissions
oc exec $POD -- ls -ld /app/sample-logs/
# Trigger refresh
curl -X POST http://YOUR_ROUTE/api/auto-loaded/refreshVolume conflicts (RWO PVCs):
# Scale down to release PVC
oc scale deployment/aap-mock --replicas=0 -n alm-infra-final
# Wait for pods to terminate
oc wait --for=delete pod -l app.kubernetes.io/name=aap-mock -n alm-infra-final --timeout=60s
# Scale back up
oc scale deployment/aap-mock --replicas=1 -n alm-infra-final# Application health
curl http://YOUR_ROUTE/healthz
# Get API root
curl http://YOUR_ROUTE/api/v2/ | jq 'keys'
# Check auto-loaded files
curl http://YOUR_ROUTE/api/auto-loaded | jq .
# Check replay status
curl http://YOUR_ROUTE/api/status | jq .View application logs (not AAP mock data):
oc logs -l app.kubernetes.io/name=aap-mock | grep "^\[APP\]"Access log files directly:
POD=$(oc get pods -l app.kubernetes.io/name=aap-mock -o name | head -n1)
oc exec $POD -- tail -f /var/log/aap-mock/app.logTest AAP API compatibility:
# List jobs
curl http://YOUR_ROUTE/api/v2/jobs/ | jq '.results[] | {id, name, status}'
# Get job events
curl http://YOUR_ROUTE/api/v2/jobs/1/job_events/ | jq '.results[] | {event, host, task}'
# Get job stdout
curl http://YOUR_ROUTE/api/v2/jobs/1/stdout/ | jq -r '.content' | head -20- API Documentation:
http://YOUR_ROUTE/docs(FastAPI auto-generated) - Example Log Files:
/examples/directory - Helm Chart:
./chart/aap-mock/ - Environment Configs:
./environments/
- 📖 Documentation: This README
- 🐛 Issues: Check pod logs and events
- 💡 Examples: See
/examples/for real AAP log samples
# 1. Deploy
helm upgrade --install aap-mock ./chart/aap-mock -n alm-infra-final --create-namespace
# 2. Get URL
HOST=$(oc get route aap-mock -n alm-infra-final -o jsonpath='{.spec.host}')
# 3. Upload log
curl -F "file=@examples/demo-job-complex.log" "http://$HOST/api/logs/upload"
# 4. Verify AAP API
curl "http://$HOST/api/v2/jobs/" | jq '.count'
# 5. Start replay
curl -X POST "http://$HOST/api/logs/replay" \
-H "Content-Type: application/json" \
-d '{"source":"uploaded","id_or_path":"latest","mode":"file","rate_lines_per_sec":50,"loop":true}'
# 6. Watch logs (what Alloy collects)
oc logs -f -l app.kubernetes.io/name=aap-mock -n alm-infra-final | grep -v "^\[APP\]"
# 7. Check in Loki/Grafana
# Query: {app_kubernetes_io_name="aap-mock"} !~ "\\[APP\\]"🎯 Built for: Development teams needing AAP API compatibility without AAP licensing costs
🏗️ Platform: Red Hat OpenShift / Kubernetes
🔧 Stack: Python 3.9+, FastAPI, Helm
📦 Container: quay.io/ecosystem-appeng/aap-mock:latest