docs(01-infrastructure-and-deployment): create phase plan
This commit is contained in:
parent
909c28cf4b
commit
d57185e262
3 changed files with 413 additions and 2 deletions
|
|
@ -38,7 +38,10 @@ Decimal phases appear between their surrounding integers in numeric order.
|
|||
2. The Terragrunt stack applies cleanly from a fresh checkout with no manual cluster intervention
|
||||
3. The NFS volume is mounted inside the running pod and a file written to it survives a pod restart
|
||||
4. Woodpecker CI pipeline exists and triggers on push to the service's directory
|
||||
**Plans**: TBD
|
||||
**Plans**: 2 plans
|
||||
Plans:
|
||||
- [ ] 01-01-PLAN.md — Create FastAPI backend app, Dockerfile, and build/push Docker image
|
||||
- [ ] 01-02-PLAN.md — Update Terraform deployment, apply stack, verify NFS, add CI pipeline
|
||||
|
||||
### Phase 2: F1 Schedule Subsystem
|
||||
**Goal**: The system automatically fetches the full F1 race calendar and serves it as structured data — users can see all sessions for the current season with correct times.
|
||||
|
|
@ -125,7 +128,7 @@ Phases execute in numeric order: 1 → 2 → 3 → 4 → 5 → 6 → 7 → 8
|
|||
|
||||
| Phase | Plans Complete | Status | Completed |
|
||||
|-------|----------------|--------|-----------|
|
||||
| 1. Infrastructure and Deployment | 0/TBD | Not started | - |
|
||||
| 1. Infrastructure and Deployment | 0/2 | Planning complete | - |
|
||||
| 2. F1 Schedule Subsystem | 0/TBD | Not started | - |
|
||||
| 3. Extractor Framework and First Site | 0/TBD | Not started | - |
|
||||
| 4. Stream Health and Fallback | 0/TBD | Not started | - |
|
||||
|
|
|
|||
173
.planning/phases/01-infrastructure-and-deployment/01-01-PLAN.md
Normal file
173
.planning/phases/01-infrastructure-and-deployment/01-01-PLAN.md
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
---
|
||||
phase: 01-infrastructure-and-deployment
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- stacks/f1-stream/files/backend/main.py
|
||||
- stacks/f1-stream/files/backend/requirements.txt
|
||||
- stacks/f1-stream/files/Dockerfile
|
||||
- stacks/f1-stream/files/redeploy.sh
|
||||
autonomous: true
|
||||
requirements:
|
||||
- DEPL-01
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "A Docker image viktorbarzin/f1-stream:v2.0.0 exists and can be pulled"
|
||||
- "The image starts a FastAPI server on port 8000 that responds to GET /health with 200"
|
||||
- "The image is based on python:3.13-slim-bookworm and runs without errors"
|
||||
artifacts:
|
||||
- path: "stacks/f1-stream/files/backend/main.py"
|
||||
provides: "FastAPI app with health endpoint"
|
||||
contains: "/health"
|
||||
- path: "stacks/f1-stream/files/backend/requirements.txt"
|
||||
provides: "Python dependencies"
|
||||
contains: "fastapi"
|
||||
- path: "stacks/f1-stream/files/Dockerfile"
|
||||
provides: "Multi-stage Docker build for Python FastAPI"
|
||||
contains: "python:3.13-slim-bookworm"
|
||||
- path: "stacks/f1-stream/files/redeploy.sh"
|
||||
provides: "Build, push, restart script"
|
||||
contains: "docker build"
|
||||
key_links:
|
||||
- from: "stacks/f1-stream/files/Dockerfile"
|
||||
to: "stacks/f1-stream/files/backend/main.py"
|
||||
via: "COPY backend/ into image"
|
||||
pattern: "COPY.*backend"
|
||||
- from: "stacks/f1-stream/files/Dockerfile"
|
||||
to: "stacks/f1-stream/files/backend/requirements.txt"
|
||||
via: "pip install requirements"
|
||||
pattern: "pip install.*requirements"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Create a minimal FastAPI backend application with a health endpoint and build a Docker image for it. This replaces the existing Go-based f1-stream application with the new Python/FastAPI stack.
|
||||
|
||||
Purpose: Provide a deployable container image that the Terraform stack (Plan 02) will reference. The health endpoint proves the service is running correctly.
|
||||
Output: Docker image `viktorbarzin/f1-stream:v2.0.0` pushed to Docker Hub, containing a working FastAPI server.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@/Users/viktorbarzin/.claude/get-shit-done/workflows/execute-plan.md
|
||||
@/Users/viktorbarzin/.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/STATE.md
|
||||
@.planning/research/STACK.md
|
||||
|
||||
# Existing files to replace/modify:
|
||||
@stacks/f1-stream/files/Dockerfile
|
||||
@stacks/f1-stream/files/redeploy.sh
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Create FastAPI backend application with health endpoint</name>
|
||||
<files>stacks/f1-stream/files/backend/main.py, stacks/f1-stream/files/backend/requirements.txt</files>
|
||||
<action>
|
||||
Create the directory `stacks/f1-stream/files/backend/`.
|
||||
|
||||
Create `stacks/f1-stream/files/backend/requirements.txt` with pinned versions:
|
||||
```
|
||||
fastapi==0.132.0
|
||||
uvicorn[standard]
|
||||
```
|
||||
|
||||
Create `stacks/f1-stream/files/backend/main.py` with a minimal FastAPI application:
|
||||
- Import FastAPI
|
||||
- Create app instance with title "F1 Streams"
|
||||
- Add `GET /health` endpoint that returns `{"status": "ok"}`
|
||||
- Add `GET /` root endpoint that returns `{"service": "f1-streams", "version": "2.0.0"}`
|
||||
- Add an `if __name__ == "__main__"` block that runs uvicorn on host 0.0.0.0 port 8000
|
||||
|
||||
This is intentionally minimal -- just enough to prove the deployment works. Later phases will add schedule, extractor, and proxy routes.
|
||||
|
||||
Do NOT add any other dependencies or routes beyond the health check and root. Keep it simple.
|
||||
</action>
|
||||
<verify>
|
||||
Run `python3 -c "import ast; ast.parse(open('stacks/f1-stream/files/backend/main.py').read()); print('Syntax OK')"` to verify the Python file is valid.
|
||||
Verify requirements.txt exists and contains fastapi and uvicorn.
|
||||
</verify>
|
||||
<done>
|
||||
`backend/main.py` exists with a valid FastAPI app that has `/health` and `/` endpoints. `requirements.txt` lists fastapi and uvicorn.
|
||||
</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Create Dockerfile and build/push the container image</name>
|
||||
<files>stacks/f1-stream/files/Dockerfile, stacks/f1-stream/files/redeploy.sh</files>
|
||||
<action>
|
||||
Replace the existing Go Dockerfile at `stacks/f1-stream/files/Dockerfile` with a Python-based Dockerfile:
|
||||
|
||||
```dockerfile
|
||||
FROM python:3.13-slim-bookworm
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY backend/requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY backend/ ./backend/
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
CMD ["uvicorn", "backend.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||
```
|
||||
|
||||
Key points:
|
||||
- Single-stage build (no build stage needed for Python -- interpreted language)
|
||||
- Use `python:3.13-slim-bookworm` as base (from stack research)
|
||||
- Install deps first for Docker layer caching
|
||||
- Expose port 8000 (FastAPI default, different from old Go app's 8080)
|
||||
- Run via uvicorn pointing to `backend.main:app`
|
||||
|
||||
Update `stacks/f1-stream/files/redeploy.sh`:
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
docker build -t viktorbarzin/f1-stream:v2.0.0 -t viktorbarzin/f1-stream:latest .
|
||||
docker push viktorbarzin/f1-stream:v2.0.0
|
||||
docker push viktorbarzin/f1-stream:latest
|
||||
kubectl -n f1-stream rollout restart deployment f1-stream
|
||||
```
|
||||
|
||||
Then build and push the image by running the redeploy script from the `stacks/f1-stream/files/` directory. Only run the docker build and push steps (skip the kubectl rollout -- that happens after Terraform apply in Plan 02).
|
||||
|
||||
Build command: `cd stacks/f1-stream/files && docker build -t viktorbarzin/f1-stream:v2.0.0 -t viktorbarzin/f1-stream:latest . && docker push viktorbarzin/f1-stream:v2.0.0 && docker push viktorbarzin/f1-stream:latest`
|
||||
|
||||
IMPORTANT: The old Go application files (main.go, go.mod, go.sum, internal/, node_modules/, package.json, package-lock.json, index.html, static/) should be removed from `stacks/f1-stream/files/` since they are no longer needed. Keep only: Dockerfile, redeploy.sh, and backend/.
|
||||
</action>
|
||||
<verify>
|
||||
Run `docker images | grep f1-stream` to confirm the image was built.
|
||||
Run `docker run --rm -d -p 18000:8000 --name f1-test viktorbarzin/f1-stream:v2.0.0 && sleep 2 && curl -s http://localhost:18000/health && docker stop f1-test` to verify the container starts and the health endpoint responds.
|
||||
</verify>
|
||||
<done>
|
||||
Docker image `viktorbarzin/f1-stream:v2.0.0` is built, pushed to Docker Hub, and serves a 200 response on GET /health when run locally.
|
||||
</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
1. `docker images | grep f1-stream` shows the v2.0.0 tag
|
||||
2. Running the image locally and curling /health returns `{"status": "ok"}`
|
||||
3. The old Go files have been removed from `stacks/f1-stream/files/`
|
||||
4. Only Dockerfile, redeploy.sh, and backend/ remain in the files directory
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Docker image viktorbarzin/f1-stream:v2.0.0 exists on Docker Hub
|
||||
- The image runs a FastAPI server on port 8000 with a working /health endpoint
|
||||
- Old Go application files are cleaned up
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/01-infrastructure-and-deployment/01-01-SUMMARY.md`
|
||||
</output>
|
||||
235
.planning/phases/01-infrastructure-and-deployment/01-02-PLAN.md
Normal file
235
.planning/phases/01-infrastructure-and-deployment/01-02-PLAN.md
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
---
|
||||
phase: 01-infrastructure-and-deployment
|
||||
plan: 02
|
||||
type: execute
|
||||
wave: 2
|
||||
depends_on:
|
||||
- "01-01"
|
||||
files_modified:
|
||||
- stacks/f1-stream/main.tf
|
||||
- .woodpecker/f1-stream.yml
|
||||
autonomous: true
|
||||
requirements:
|
||||
- DEPL-01
|
||||
- DEPL-02
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "A request to https://f1.viktorbarzin.me/health returns HTTP 200 with JSON {status: ok}"
|
||||
- "The Terragrunt stack applies cleanly with no errors"
|
||||
- "A file written to /data inside the pod survives a pod restart"
|
||||
- "Woodpecker CI pipeline triggers on push for the f1-stream directory"
|
||||
artifacts:
|
||||
- path: "stacks/f1-stream/main.tf"
|
||||
provides: "Kubernetes deployment, service, ingress, TLS for f1-stream"
|
||||
contains: "viktorbarzin/f1-stream:v2.0.0"
|
||||
- path: ".woodpecker/f1-stream.yml"
|
||||
provides: "CI pipeline for f1-stream service"
|
||||
contains: "f1-stream"
|
||||
key_links:
|
||||
- from: "stacks/f1-stream/main.tf"
|
||||
to: "Docker Hub viktorbarzin/f1-stream:v2.0.0"
|
||||
via: "kubernetes_deployment image reference"
|
||||
pattern: "viktorbarzin/f1-stream:v2.0.0"
|
||||
- from: "stacks/f1-stream/main.tf"
|
||||
to: "NFS /mnt/main/f1-stream"
|
||||
via: "inline NFS volume mount"
|
||||
pattern: "/mnt/main/f1-stream"
|
||||
- from: "stacks/f1-stream/main.tf"
|
||||
to: "modules/kubernetes/ingress_factory"
|
||||
via: "ingress module call"
|
||||
pattern: "ingress_factory"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Update the Terraform stack to deploy the new Python/FastAPI container, verify NFS mount persistence, and add a Woodpecker CI pipeline. This completes Phase 1 by making the service live on the cluster and reachable at its public URL.
|
||||
|
||||
Purpose: The service must be running on the Kubernetes cluster, reachable at f1.viktorbarzin.me, with NFS storage mounted and CI/CD in place -- ready for application development in Phase 2.
|
||||
Output: Live deployment at f1.viktorbarzin.me, NFS-backed persistent storage, Woodpecker CI pipeline.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@/Users/viktorbarzin/.claude/get-shit-done/workflows/execute-plan.md
|
||||
@/Users/viktorbarzin/.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/STATE.md
|
||||
@.planning/phases/01-infrastructure-and-deployment/01-01-SUMMARY.md
|
||||
|
||||
# Key reference files:
|
||||
@stacks/f1-stream/main.tf
|
||||
@stacks/f1-stream/terragrunt.hcl
|
||||
@.woodpecker/build-cli.yml
|
||||
@.woodpecker/default.yml
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Update Terraform deployment for Python/FastAPI and verify NFS mount</name>
|
||||
<files>stacks/f1-stream/main.tf</files>
|
||||
<action>
|
||||
Modify `stacks/f1-stream/main.tf` to update the deployment for the new Python/FastAPI application:
|
||||
|
||||
1. **Change the container image** from `viktorbarzin/f1-stream:v1.3.1` to `viktorbarzin/f1-stream:v2.0.0`
|
||||
|
||||
2. **Change the container port** from 8080 to 8000 (FastAPI/uvicorn default)
|
||||
|
||||
3. **Update the service target_port** from 8080 to 8000
|
||||
|
||||
4. **Remove old Go-specific environment variables** that are no longer needed:
|
||||
- Remove `WEBAUTHN_RPID`
|
||||
- Remove `WEBAUTHN_ORIGIN`
|
||||
- Remove `WEBAUTHN_DISPLAY_NAME`
|
||||
- Remove `HEADLESS_EXTRACT_ENABLED`
|
||||
- Remove `TURN_URL`
|
||||
- Remove `TURN_SHARED_SECRET`
|
||||
- Remove `TURN_INTERNAL_URL`
|
||||
|
||||
5. **Remove unused variables** from the top of the file:
|
||||
- Remove `variable "coturn_turn_secret"` (was for WebRTC/TURN)
|
||||
- Remove `variable "public_ip"` (was for TURN URL)
|
||||
- Keep `variable "tls_secret_name"` and `variable "nfs_server"` (still needed)
|
||||
|
||||
6. **Keep the NFS volume mount** exactly as-is -- it already follows the inline NFS pattern:
|
||||
```hcl
|
||||
volume {
|
||||
name = "data"
|
||||
nfs {
|
||||
server = var.nfs_server
|
||||
path = "/mnt/main/f1-stream"
|
||||
}
|
||||
}
|
||||
```
|
||||
The volume_mount at `/data` stays the same.
|
||||
|
||||
7. **Update resource limits** for Python:
|
||||
```hcl
|
||||
resources {
|
||||
limits = {
|
||||
cpu = "500m"
|
||||
memory = "256Mi"
|
||||
}
|
||||
requests = {
|
||||
cpu = "50m"
|
||||
memory = "64Mi"
|
||||
}
|
||||
}
|
||||
```
|
||||
Python/FastAPI with uvicorn needs less CPU than Go+Chromium but similar memory.
|
||||
|
||||
8. **Keep everything else unchanged**: namespace, service, tls_secret module, ingress module.
|
||||
|
||||
After editing, apply the Terraform stack:
|
||||
```bash
|
||||
cd stacks/f1-stream && terragrunt apply --non-interactive
|
||||
```
|
||||
|
||||
Wait for the deployment to roll out:
|
||||
```bash
|
||||
kubectl --kubeconfig $(pwd)/config -n f1-stream rollout status deployment/f1-stream --timeout=120s
|
||||
```
|
||||
|
||||
Verify the pod is running:
|
||||
```bash
|
||||
kubectl --kubeconfig $(pwd)/config -n f1-stream get pods
|
||||
```
|
||||
|
||||
Verify the health endpoint responds through the public URL:
|
||||
```bash
|
||||
curl -s https://f1.viktorbarzin.me/health
|
||||
```
|
||||
|
||||
Verify NFS mount persistence by writing a test file, restarting the pod, and reading it back:
|
||||
```bash
|
||||
POD=$(kubectl --kubeconfig $(pwd)/config -n f1-stream get pods -l app=f1-stream -o jsonpath='{.items[0].metadata.name}')
|
||||
kubectl --kubeconfig $(pwd)/config -n f1-stream exec $POD -- sh -c 'echo "nfs-test-$(date +%s)" > /data/test-file.txt && cat /data/test-file.txt'
|
||||
kubectl --kubeconfig $(pwd)/config -n f1-stream rollout restart deployment/f1-stream
|
||||
kubectl --kubeconfig $(pwd)/config -n f1-stream rollout status deployment/f1-stream --timeout=120s
|
||||
NEW_POD=$(kubectl --kubeconfig $(pwd)/config -n f1-stream get pods -l app=f1-stream -o jsonpath='{.items[0].metadata.name}')
|
||||
kubectl --kubeconfig $(pwd)/config -n f1-stream exec $NEW_POD -- cat /data/test-file.txt
|
||||
```
|
||||
The test file should contain the same content after the pod restart.
|
||||
</action>
|
||||
<verify>
|
||||
1. `terragrunt apply` exits with 0 (no errors)
|
||||
2. `kubectl get pods -n f1-stream` shows 1/1 Running
|
||||
3. `curl -s https://f1.viktorbarzin.me/health` returns `{"status":"ok"}`
|
||||
4. NFS persistence test passes (file survives pod restart)
|
||||
</verify>
|
||||
<done>
|
||||
The f1-stream deployment is running on the cluster with the new Python/FastAPI image, reachable at https://f1.viktorbarzin.me/health, and the NFS volume at /data persists data across pod restarts.
|
||||
</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Create Woodpecker CI pipeline for f1-stream</name>
|
||||
<files>.woodpecker/f1-stream.yml</files>
|
||||
<action>
|
||||
Create `.woodpecker/f1-stream.yml` following the pattern from `build-cli.yml`:
|
||||
|
||||
```yaml
|
||||
when:
|
||||
event: push
|
||||
path:
|
||||
include:
|
||||
- "stacks/f1-stream/files/**"
|
||||
|
||||
clone:
|
||||
git:
|
||||
image: woodpeckerci/plugin-git
|
||||
settings:
|
||||
attempts: 5
|
||||
backoff: 10s
|
||||
|
||||
steps:
|
||||
- name: build-image
|
||||
image: woodpeckerci/plugin-docker-buildx
|
||||
settings:
|
||||
username: "viktorbarzin"
|
||||
password:
|
||||
from_secret: dockerhub-pat
|
||||
repo: viktorbarzin/f1-stream
|
||||
dockerfile: stacks/f1-stream/files/Dockerfile
|
||||
context: stacks/f1-stream/files
|
||||
auto_tag: true
|
||||
```
|
||||
|
||||
Key differences from the default pipeline:
|
||||
- **Path filter**: Only triggers when files under `stacks/f1-stream/files/` change (the application code)
|
||||
- **Builds and pushes the Docker image** using the same `woodpeckerci/plugin-docker-buildx` pattern as build-cli.yml
|
||||
- **Docker context** points to the `stacks/f1-stream/files/` directory where the Dockerfile lives
|
||||
- Does NOT run Terragrunt apply (that is done manually or by the default pipeline for the platform stack)
|
||||
</action>
|
||||
<verify>
|
||||
Verify the YAML is valid: `python3 -c "import yaml; yaml.safe_load(open('.woodpecker/f1-stream.yml')); print('YAML OK')"`
|
||||
Verify the file exists and references f1-stream correctly.
|
||||
</verify>
|
||||
<done>
|
||||
Woodpecker CI pipeline file exists at `.woodpecker/f1-stream.yml`, configured to build and push the Docker image when files under `stacks/f1-stream/files/` change.
|
||||
</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
1. `curl -s https://f1.viktorbarzin.me/health` returns `{"status":"ok"}`
|
||||
2. `cd stacks/f1-stream && terragrunt plan --non-interactive` shows no changes (stack is clean)
|
||||
3. NFS test file written before pod restart is readable after pod restart
|
||||
4. `.woodpecker/f1-stream.yml` exists and is valid YAML
|
||||
5. `kubectl --kubeconfig $(pwd)/config -n f1-stream get pods` shows 1/1 Running
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- The service is live at https://f1.viktorbarzin.me and responds with 200 on /health
|
||||
- Terragrunt stack applies cleanly with no manual cluster intervention
|
||||
- NFS volume mount at /data persists data across pod restarts
|
||||
- Woodpecker CI pipeline exists for automated image builds
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/01-infrastructure-and-deployment/01-02-SUMMARY.md`
|
||||
</output>
|
||||
Loading…
Add table
Add a link
Reference in a new issue