# Full-Stack Example with Ingress This directory contains a complete multi-service stack deployed with Orca, including Traefik ingress configuration. Each file is a valid Orca jobspec (`.md` frontmatter) that passes the v0.9 parser and schema validators. ## Stack overview | File | Kind | Runtime | Ingress | Description | |------|------|---------|---------|-------------| | `web-app.md` | Service | process | Unix socket (default) | Frontend HTTP server, 3 replicas, rolling update | | `api.md` | Service | process | TCP `127.0.0.1:9090` (R-007 opt-in) | Backend API, 2 replicas, canary update | | `worker.md` | Job | process | none | One-shot batch worker with lifecycle hooks | | `log-shipper.md` | Service | process | Unix socket (metrics) | Log shipper on a dedicated node | | `postgres.md` | Service | process | Unix socket | Database with volume replication, blue-green update | ## Rendered artifacts The `rendered/` directory shows what Orca generates on the target nodes when you submit these jobspecs: | File | Description | |------|-------------| | `traefik-dynamic-web-app.yaml` | Traefik dynamic config for the web-app Service | | `traefik-dynamic-api.yaml` | Traefik dynamic config for the api Service (TCP bind) | | `systemd-web-app.service` | Systemd unit for the web-app alloc | | `systemd-api.service` | Systemd unit for the api alloc (with TCP bind marker) | | `systemd-log-shipper.service` | Systemd unit for the log-shipper alloc | ## Walkthrough ### Prerequisites - Orca installed (`orca version` works) - 2+ Linux nodes reachable over SSH (for multi-node scheduling) - Traefik installed on the lead node (watches `/etc/traefik/dynamic/`) ### Step 1: Initialize the cluster ```bash # On the operator laptop orca init ``` This creates `~/.orca/` (or `/root/.orca` with `--system`), bootstraps the CA, generates the server cert, auto-detects the OS, and registers a localhost node. ### Step 2: Join remote nodes ```bash # Join a Proxmox node (v0.9 canonical SSH-push path) orca node join --type proxmox --host 192.168.1.100 --ssh-user root # Join a second node ORCA_PROXMOX_PASSWORD=secret orca node join --type proxmox --host 192.168.1.101 ``` ### Step 3: Declare node capacity The CLI-side scheduler uses capacity declarations for bin-packing: ```bash orca node capacity set --cpu 4000 --memory 8192 --disk 100000 --node 192.168.1.100 orca node capacity set --cpu 4000 --memory 8192 --disk 100000 --node 192.168.1.101 ``` ### Step 4: Create a namespace ```bash orca ns create prod --parent _defaults ``` This creates `~/.orca/prod/` with `db/`, `jobs/`, `alloc/`, and `ns.md`. ### Step 5: Submit the stack ```bash orca job run web-app.md orca job run api.md orca job run worker.md orca job run log-shipper.md orca job run postgres.md ``` Each `orca job run` parses the `.md` jobspec, validates it against the schema, schedules it via the CLI-side bin-packing scheduler, and generates the systemd + Traefik artifacts on the target node via SSH-push. ### Step 6: Observe placements ```bash orca job list --watch # Output: # ID NAME STATUS EXIT # abc-123... web-app running 0 # def-456... api running 0 # ghi-789... worker complete 0 # jkl-012... log-shipper running 0 # mno-345... postgres running 0 ``` ### Step 7: Inspect rendered artifacts After submission, the target nodes have: ``` /etc/systemd/system/orca-v1-web-app.service # systemd unit /etc/systemd/system/orca-v1-api.service # systemd unit (TCP bind) /etc/traefik/dynamic/orca-web-app.yaml # Traefik dynamic config /etc/traefik/dynamic/orca-api.yaml # Traefik dynamic config /run/orca/alloc-web-app-0/port-http.sock # Unix socket (R-007 default) ``` See the `rendered/` directory in this example for the exact file contents. ### Step 8: Verify ingress Traefik watches `/etc/traefik/dynamic/` and atomically reloads when a file changes (write-tmp + rename, gate C-10). The web-app is reachable at `https:///web-app` and the API at `https:///api`. Health checks (`/healthz` on each backend) ensure Traefik only routes to healthy instances. ### Step 9: Drain and rollback To drain a service (stop traffic, keep the workload running): ```bash # Orca writes a Traefik config with weight:0 on every backend # (RenderDrain). Traefik stops sending traffic. ``` To roll back, re-submit the normal jobspec — Orca writes the non-drained Traefik config and Traefik resumes routing. ## Ingress model See [docs/ingress.md](../../docs/ingress.md) for the full Traefik ingress reference. Key points: - `kind: Service` **implies** a Traefik route (D-175). - Default bind is a **Unix socket** at `/run/orca/alloc-/port-.sock` (R-007). - `service.bind: 127.0.0.1` opts in to **TCP** (loopback only). - One Traefik dynamic file per Service at `/etc/traefik/dynamic/orca-.yaml`. - Atomic reload via write-tmp + rename (gate C-10). - Drain sets `weight: 0` per backend. ## Validation All jobspecs in this directory are validated by a Go test: ```bash go test ./examples/full-stack/ -v -run TestExamplesValidate ``` This test parses each `.md` file with `jobspec.ParseFile` and validates it against `schema.ValidatorFor(kind)` — ensuring every field used in the examples exists in the current `WorkloadSpec` struct and passes the per-kind validators (gate C-20). ## v0.11 forward The following are not yet implemented in v0.9 and will land in v0.11: - **DaemonSet `schedule:` block**: the parser does not yet populate the `schedule:` frontmatter block (v0.9 parser gap). The `log-shipper` example uses `kind: Service` with `count: 1` and a `node.role` constraint as a workaround. - **Secret resolution**: `env: { KEY: { from: "secret:..." } }` is parsed but not resolved to `EnvironmentFile=`/`LoadCredential=` until v0.11-P03. - **Transactional update execution**: the `update:` block's plan is computed but not executed transactionally until v0.11-P10. - **Socket activation**: real socket unit files land in v0.11-P08.