--type linux (REQ-161): - internal/linux/bootstrap.go: SSH bootstrap for generic Linux workers (orcas pubkey, system user, drift-events dir; no PVE role/sudoers) - internal/cli/node.go: joinLinux function + --type linux dispatch - peer-setup kept as documented fallback UAT plan (REQ-162): - docs/uat.md: 3-host topology (lead Ubuntu + pve01 Proxmox + worker01 Ubuntu), 22 step-by-step commands, 35-claim matrix, Proxmox prerequisite + alternative 3xUbuntu path (C-48), signoff procedure UAT signoff script (REQ-163, C-47): - scripts/uat-signoff.sh: 35 idempotent read-only assertions, exit 0 iff all pass. Includes 4 critical-path assertions: job deploys to remote, ACL deny-by-default, seal/unseal round-trip, OIDC health - scripts/uat-smoke.sh: 13 CI-tested pure-CLI assertions for .coreci.yml Tests: node join --type linux test, fingerprint test updated, smoke test all 13 pass. ---ci--- project: orca phase: 12 milestone: v0.13 status: complete requirements: covered: [161, 162, 163] ---/ci---
9.0 KiB
Orca User Acceptance Testing (UAT) Plan
Version: v0.13 (production hardening round 2)
Gate: v1.0.0 production-ready tag is deferred until this UAT passes
Signoff: run scripts/uat-signoff.sh on the lead node and paste the output back
Prerequisites
Hardware
| Role | OS | Requirements |
|---|---|---|
| lead | Ubuntu 22.04 LTS | Operator laptop or VM; SSH key; orca binary (built from v0.13 tag) |
| pve01 | Proxmox VE 8/9 | Bare-metal or nested; SSH root access; orca SSH key pre-staged |
| worker01 | Ubuntu 22.04 LTS | VM or bare-metal; SSH root access; orca SSH key pre-staged |
Alternative topology (3x Ubuntu, no Proxmox)
If a Proxmox host is unavailable, run the UAT with 3x Ubuntu hosts.
Use --type linux for all remote nodes. Proxmox-specific claims
(doctor proxmox, PVE role, sudoers) are skipped in this path.
The signoff script reports exercised vs. skipped claims.
Pre-staging
-
Build orca from the v0.13 tag:
git clone https://git.cloudinit.dev/coreci/orca.git cd orca && git checkout v0.12.13 make build # binary is at bin/orca -
Generate the orca SSH keypair on the lead:
ssh-keygen -t ed25519 -f ~/.ssh/orca_ed25519 -N "" -
Pre-stage the orca public key on pve01 and worker01:
ssh-copy-id -i ~/.ssh/orca_ed25519.pub root@pve01 ssh-copy-id -i ~/.ssh/orca_ed25519.pub root@worker01 -
Pin host-key fingerprints (optional but recommended):
ssh-keyscan pve01 | ssh-keygen -lf - ssh-keyscan worker01 | ssh-keygen -lf -
Step-by-step UAT
Step 1: Initialize the cluster
export ORCA_HOME=~/orca-uat
orca init
Expected: cluster directory created, CA cert generated, localhost node registered.
Step 2: Onboard the Proxmox host
orca node join --type proxmox \
--host pve01 \
--ssh-user root \
--ssh-key ~/.ssh/orca_ed25519 \
--host-key-fingerprint SHA256:<fingerprint>
Expected: SSH bootstrap succeeds, orca user created, PVE role assigned, node registered as ready with kind=proxmox.
Step 3: Onboard the Ubuntu worker
orca node join --type linux \
--host worker01 \
--ssh-user root \
--ssh-key ~/.ssh/orca_ed25519 \
--host-key-fingerprint SHA256:<fingerprint>
Expected: SSH bootstrap succeeds, orca user created, drift-events dir created, node registered as ready with kind=linux.
Step 4: Verify nodes
orca node list
orca node list --json
Expected: 3 nodes listed (localhost + pve01 + worker01), all ready.
Step 5: Set capacity on remote nodes
orca node capacity set --node pve01 --cpu 4 --memory 8192 --disk 100000
orca node capacity set --node worker01 --cpu 2 --memory 4096 --disk 50000
orca node capacity list
Expected: capacity shown for both remote nodes.
Step 6: Create a namespace
orca ns create prod
orca ns list
Expected: prod namespace listed.
Step 7: Deploy the full stack
Deploy each service from examples/full-stack/:
orca job run examples/full-stack/web-app.md --target pve01
orca job run examples/full-stack/api.md --target pve01
orca job run examples/full-stack/worker.md --target worker01
orca job run examples/full-stack/postgres.md --target pve01
orca job run examples/full-stack/log-shipper.md --target worker01
Expected: each job is scheduled on the target, systemd unit deployed via SSH-push, job status running or complete.
Step 8: Verify deployment
orca job list
orca job list --json
Expected: all 5 jobs listed, with correct target nodes.
On each remote node:
ssh root@pve01 systemctl status 'orca-alloc-*'
ssh root@worker01 systemctl status 'orca-alloc-*'
Step 9: Verify Traefik routes
ssh root@pve01 ls /etc/traefik/dynamic/
ssh root@worker01 ls /etc/traefik/dynamic/
Expected: traefik-dynamic-*.yaml files present on nodes where jobs were deployed.
Step 10: Migrate between hosts
Migrate web-app from pve01 to worker01:
orca job migrate web-app --to worker01
Expected: job drained on pve01, rescheduled on worker01, new systemd unit deployed.
Verify:
orca job list
ssh root@worker01 systemctl status 'orca-alloc-*web-app*'
ssh root@pve01 systemctl status 'orca-alloc-*web-app*' # should be stopped
Step 11: Aggregate logs
orca logs --all-nodes --job web-app --since 5m
Expected: log entries from multiple nodes.
Step 12: ACL enforcement
orca acl grant operator-1 --namespace prod --permissions read,write
orca acl check operator-1 --namespace prod --permission read
orca acl check operator-1 --namespace prod --permission admin
Expected: read+write allowed, admin denied (not granted).
Step 13: Seal/unseal
orca cluster seal --rp-id orca.local
orca cluster unseal
orca secrets set prod TEST_KEY --value "test-value"
orca secrets get prod TEST_KEY
Expected: seal succeeds, unseal succeeds, secrets readable post-unseal.
Step 14: Audit chain
orca doctor audit
Expected: chain head reported, no tamper detected.
Step 15: Doctor modes
orca doctor modes
Expected: all file modes correct, exit 0.
Step 16: OIDC health
orca doctor oidc
Expected: Dex unit active, issuer reachable (or WARN if Dex not installed).
Step 17: Backup and restore
orca backup --out /tmp/uat-backup.tar.gz
orca restore --in /tmp/uat-backup.tar.gz --dry-run
Expected: backup succeeds, restore dry-run succeeds.
Step 18: Drift detection
orca drift show
Expected: no error (empty drift is fine).
Step 19: Transaction idempotency
orca txn apply <some-txn-dir>
orca txn apply <some-txn-dir> # re-run
Expected: second apply is idempotent (exit 5 or "already applied").
Step 20: Metrics
orca metrics --addr :9100 &
sleep 3
curl -s http://localhost:9100/metrics | grep orca_
Expected: expanded metric set present (orca_jobs_running, orca_audit_chain_head, etc.).
Step 21: Compat check
orca cluster compat-check
Expected: exit 0, all nodes compatible.
Step 22: Run the signoff script
scripts/uat-signoff.sh
Expected: UAT SIGNOFF: N/35 assertions passed, exit 0 iff N==35.
Claim Matrix
| # | Claim | UAT Step | Signoff Assertion |
|---|---|---|---|
| 1 | Cluster initializes from scratch | Step 1 | assert_orca_version |
| 2 | Proxmox host onboards via SSH | Step 2 | assert_proxmox_onboarded |
| 3 | Ubuntu worker onboards via --type linux |
Step 3 | assert_linux_worker_onboarded |
| 4 | Node list shows all nodes | Step 4 | assert_cluster_initialized |
| 5 | Capacity is set on remote nodes | Step 5 | assert_capacity_set |
| 6 | Namespace created | Step 6 | assert_namespace_created |
| 7 | Full stack deploys to remote nodes | Step 7 | assert_full_stack_running |
| 8 | Scheduler deploys to remote (not local) | Step 7 | assert_job_deploys_to_remote |
| 9 | Traefik routes present | Step 9 | assert_traefik_routes |
| 10 | Job migrates between hosts | Step 10 | assert_migrate_worked |
| 11 | Logs aggregate from multiple nodes | Step 11 | assert_logs_aggregate |
| 12 | ACL grant/check works | Step 12 | assert_acl_enforced |
| 13 | ACL deny-by-default | Step 12 | assert_acl_deny_default |
| 14 | acl.json mode 0600 | Step 12 | assert_acl_file_mode |
| 15 | Seal/unseal round-trip | Step 13 | assert_seal_unseal_roundtrip |
| 16 | Audit chain intact | Step 14 | assert_audit_chain_intact |
| 17 | Doctor modes passes | Step 15 | assert_doctor_modes |
| 18 | OIDC health check | Step 16 | assert_oidc_health |
| 19 | Backup works | Step 17 | assert_backup_restore_dryrun |
| 20 | Drift visible | Step 18 | assert_drift_visible |
| 21 | Txn idempotent | Step 19 | assert_txn_idempotent |
| 22 | Metrics expanded | Step 20 | assert_metrics_expanded |
| 23 | Compat check passes | Step 21 | assert_compat_check_passes |
| 24 | No --password in docs/examples |
— | assert_no_password_in_docs |
| 25 | Go toolchain current | — | assert_go_toolchain_current |
| 26 | cli.md matches orca --help |
— | assert_cli_md_complete |
| 27 | pprof not on all interfaces | — | assert_no_pprof_on_all_interfaces |
| 28 | WebAuthn registration requires auth | — | assert_webauthn_reg_requires_auth |
| 29 | Audit chain survives concurrency | — | assert_audit_chain_concurrent |
| 30 | Concurrent secrets no data loss | — | assert_concurrent_secrets_no_loss |
| 31 | Cache invalidated after write | — | assert_cache_invalidated_after_write |
| 32 | SQLite no lock under concurrency | — | assert_sqlite_no_lock |
| 33 | No injection in logs --job | — | assert_no_injection_in_logs |
| 34 | --type linux exists as subcommand |
Step 3 | assert_type_linux_available |
| 35 | orca status deprecated |
— | assert_status_deprecated |
Signoff procedure
- Run all steps above on the 3-host cluster
- Run
scripts/uat-signoff.shon the lead - Paste the output back to the CI agent
- The CI agent verifies
35/35 PASSand cutsv1.0.0