289e5cf6e1
P02 — operator-facing documentation (REQ-091, REQ-092, REQ-093; gate C-22). docs/cli.md (REQ-091): - Full CLI command/flag reference: every command/subcommand with synopsis, flag tables (name/type/default/description), one-line examples. - Global flags (--json, --system, --config, --no-deprecation-warnings). - Output modes (text/json/watch), env vars, exit codes. - Deprecated surface callout boxes (daemon, cert, node-join-mTLS, HCL jobspec) pointing to v0.11 removal. docs/jobspec.md (REQ-092): - Markdown frontmatter schema reference: all top-level keys, block reference (runtime/ports/env-secrets/volumes/restart/update/service/ health/lifecycle/constraints/affinity/tasks), kinds matrix (Job/Service/DaemonSet required vs allowed), CEL subset grammar, body byte-exact preservation (R-015), deprecated HCL callout. docs/ingress.md (REQ-093): - Traefik ingress reference: service->Traefik mapping (D-175), R-007 socket-vs-TCP-bind semantics, generated YAML shape (routers/services/ healthCheck), atomic reload (C-10), drain (weight:0), TLS (certResolver, trust domain, step-ca), worked-example pointer to examples/full-stack/, v0.11 forward limitations. All factual claims grounded in live codebase (gate C-22). Cross-links verified to resolve. ---ci--- project: orca phase: 2 milestone: v0.10 status: execute ---/ci---
442 lines
13 KiB
Markdown
442 lines
13 KiB
Markdown
# Orca Jobspec Reference
|
|
|
|
This document is the complete reference for the Orca jobspec format —
|
|
the Markdown-with-frontmatter specification that describes workloads.
|
|
|
|
> **Canonical format (v0.9)**: Orca uses Markdown with YAML frontmatter
|
|
> as the canonical jobspec format (R-013/R-014). The legacy HCL format
|
|
> is supported via an adapter during the migration window but is
|
|
> deprecated (see [HCL jobspec](#deprecated-hcl-jobspec) below).
|
|
|
|
## File formats
|
|
|
|
The `orca job run` command dispatches by file extension:
|
|
|
|
| Extension | Parser | Body |
|
|
|-----------|--------|------|
|
|
| `.md` | `ParseMarkdown` (canonical) | Verbatim after closing `---` (R-015 byte-exact) |
|
|
| `.yaml` / `.yml` | `parseYAMLFile` | Whole file as frontmatter; body empty |
|
|
| `.hcl` | `ParseHCL` (legacy adapter) | Empty (deprecated) |
|
|
|
|
## Minimal example
|
|
|
|
```yaml
|
|
---
|
|
kind: Job
|
|
name: my-job
|
|
runtime:
|
|
one_of: process
|
|
command: /bin/echo hello
|
|
---
|
|
# My Job
|
|
|
|
This body is preserved byte-exact and carried to the target node.
|
|
```
|
|
|
|
## Top-level keys
|
|
|
|
| Key | Type | Default | Required | Notes |
|
|
|-----|------|---------|----------|-------|
|
|
| `orca-spec-version` | string | `""` | no | Free-form version tag (e.g. `"1"`) |
|
|
| `kind` | enum | — | **yes** | One of `Job`, `Service`, `DaemonSet` |
|
|
| `name` | string | — | **yes** | Workload name (trimmed, non-empty) |
|
|
| `count` | int | `1` | no | Job: must be 1; Service: ≥1; DaemonSet: not allowed |
|
|
| `runtime` | block | nil | see kinds | Runtime block (or per-task runtimes in a task group) |
|
|
| `ports` | block list | nil | Service: **yes** | Array of port mappings |
|
|
| `env` | block map | nil | no | Environment variables |
|
|
| `secrets` | inline/block list | nil | no | Secret names (resolution in v0.11) |
|
|
| `volumes` | block list | nil | no | Volume mounts |
|
|
| `restart` | block | nil | Service/DaemonSet: **yes** | Restart policy |
|
|
| `update` | block | nil | Service: **yes** | Update strategy |
|
|
| `service` | block | nil | no | Traefik route definition (implied for Service; not allowed for Job/DaemonSet) |
|
|
| `health` | block | nil | Service: **yes** | Health check |
|
|
| `lifecycle` | block | nil | no | Pre-stop / post-start hooks |
|
|
| `constraints` | list | nil | no | CEL expressions (node selection) |
|
|
| `affinity` | block list | nil | no | Co-location / anti-affinity rules |
|
|
| `tasks` | block list | nil | no | Task group (multi-process alloc) |
|
|
| `timeout` | duration string | `""` | no | Job timeout |
|
|
| `schedule` | block | nil | DaemonSet: **yes** | Schedule mode |
|
|
|
|
## Kinds
|
|
|
|
### `Job`
|
|
|
|
A one-shot batch task. Runs once and exits.
|
|
|
|
- `count` must be 1 (or unset). Use `Service` for replicas.
|
|
- `service` block is **not allowed** (no Traefik route for Jobs).
|
|
- `restart` optional (defaults to `never` / `on-failure`).
|
|
- `timeout` optional.
|
|
|
|
**Example**:
|
|
```yaml
|
|
---
|
|
kind: Job
|
|
name: data-migration
|
|
runtime:
|
|
one_of: process
|
|
command: /usr/bin/python3 migrate.py
|
|
timeout: 300s
|
|
env:
|
|
DB_URL: postgres://localhost/mydb
|
|
---
|
|
```
|
|
|
|
### `Service`
|
|
|
|
A long-running, load-balanced workload with a Traefik route.
|
|
|
|
- `count` ≥ 1 (number of replicas).
|
|
- `ports` required (at least one).
|
|
- `restart` required; `mode` one of `service`, `on-failure`, `never`.
|
|
- `update` required; `strategy` one of `rolling`, `canary`, `blue-green`.
|
|
- `runtime` required (or a task group with per-task runtimes).
|
|
- `health` required (Traefik routing requires health checks).
|
|
- `service` block optional (implied for Service; use for `bind` override).
|
|
- `service.bind` if present must be a valid IP (`127.0.0.1` = TCP opt-in;
|
|
default = Unix socket).
|
|
|
|
**Example**:
|
|
```yaml
|
|
---
|
|
kind: Service
|
|
name: web
|
|
count: 3
|
|
runtime:
|
|
one_of: process
|
|
command: /usr/bin/httpd
|
|
ports:
|
|
- name: http
|
|
port: 8080
|
|
restart:
|
|
mode: service
|
|
attempts: 5
|
|
delay: 2s
|
|
update:
|
|
strategy: rolling
|
|
max_parallel: 1
|
|
health:
|
|
check_type: http
|
|
interval: 5s
|
|
timeout: 1s
|
|
unhealthy_threshold: 2
|
|
constraints:
|
|
- node.role == "web"
|
|
---
|
|
```
|
|
|
|
### `DaemonSet`
|
|
|
|
A workload that runs on every matching node.
|
|
|
|
- `schedule` required; `mode` one of `every-node`, `matching`, `mandatory`.
|
|
- `ports` **not allowed** (no Traefik route by default).
|
|
- `count` **not allowed** (implicit = matching nodes).
|
|
- `restart` required.
|
|
|
|
**Example**:
|
|
```yaml
|
|
---
|
|
kind: DaemonSet
|
|
name: log-shipper
|
|
schedule:
|
|
mode: every-node
|
|
runtime:
|
|
one_of: process
|
|
command: /usr/bin/fluent-bit
|
|
restart:
|
|
mode: service
|
|
---
|
|
```
|
|
|
|
## Block reference
|
|
|
|
### `runtime`
|
|
|
|
The runtime backend for the workload.
|
|
|
|
| Field | Key | Type | Default | Notes |
|
|
|-------|-----|------|---------|-------|
|
|
| `one_of` | `one_of` | string | — | Runtime type (see below) |
|
|
| `image` | `image` | string | `""` | Container image (for `podman`) |
|
|
| `command` | `command` | string | — | ExecStart command |
|
|
|
|
**Supported runtime types** (`one_of`):
|
|
|
|
| Type | Description | Requires |
|
|
|------|-------------|----------|
|
|
| `process` | Direct process execution via systemd (default) | systemd on target |
|
|
| `wasm` / `wasmtime` | WASM via wasmtime CLI (apt-installed on peer, SSH exec) | wasmtime on target |
|
|
| `podman` | Container via podman | podman on target |
|
|
| `pve-vm` | Proxmox VM via `qm` | Proxmox node |
|
|
| `pve-ct` | Proxmox container via `pct` | Proxmox node |
|
|
| `proxmox` | Alias for Proxmox runtime | Proxmox node |
|
|
|
|
An empty/missing `Runtime` or `OneOf` is runtime-agnostic (always fits
|
|
the runtime axis in the scheduler).
|
|
|
|
### `ports`
|
|
|
|
Array of port mappings. Required for `Service`.
|
|
|
|
| Field | Key | Type | Default | Notes |
|
|
|-------|-----|------|---------|-------|
|
|
| `name` | `name` | string | — | Port name (used in socket path) |
|
|
| `port` | `port` | int | — | Container port |
|
|
| `host_port` | `host_port` | int | `0` | Host port |
|
|
| `protocol` | `protocol` | string | `""` | Protocol (e.g. `tcp`) |
|
|
| `host_ip` | `host_ip` | string | `""` | Host IP |
|
|
|
|
**Example**:
|
|
```yaml
|
|
ports:
|
|
- name: http
|
|
port: 8080
|
|
host_port: 80
|
|
protocol: tcp
|
|
- name: https
|
|
port: 8443
|
|
host_port: 443
|
|
```
|
|
|
|
### `env`
|
|
|
|
Environment variables. Scalar values or secret references.
|
|
|
|
```yaml
|
|
env:
|
|
FOO: bar
|
|
BAZ: "qux"
|
|
SECRET_REF:
|
|
from: "secret:db-password"
|
|
INLINE: {from: "secret:token"}
|
|
```
|
|
|
|
> Secret resolution (`from: "secret:..."`) lands in v0.11-P03. The
|
|
> parser stores the reference; the emitter will emit
|
|
> `EnvironmentFile=`/`LoadCredential=` in v0.11.
|
|
|
|
### `secrets`
|
|
|
|
List of secret names. Inline array or block list.
|
|
|
|
```yaml
|
|
secrets: ["db-password", "api-token"]
|
|
# or
|
|
secrets:
|
|
- db-password
|
|
- api-token
|
|
```
|
|
|
|
### `volumes`
|
|
|
|
Array of volume mounts.
|
|
|
|
| Field | Key | Type | Default | Notes |
|
|
|-------|-----|------|---------|-------|
|
|
| `name` | `name` | string | — | Volume name |
|
|
| `type` | `type` | string | — | Volume type (e.g. `host`) |
|
|
| `source` | `source` | string | — | Source path (or `replicate:<peer>,<peer>` for Syncthing) |
|
|
| `target` | `target` | string | — | Mount target |
|
|
| `read_only` | `read_only` | bool | `false` | Read-only mount (`true`/`yes`/`on`/`1`) |
|
|
|
|
**Example**:
|
|
```yaml
|
|
volumes:
|
|
- name: data
|
|
type: host
|
|
source: /data
|
|
target: /data
|
|
read_only: true
|
|
```
|
|
|
|
### `restart`
|
|
|
|
Restart policy.
|
|
|
|
| Field | Key | Type | Default | Notes |
|
|
|-------|-----|------|---------|-------|
|
|
| `mode` | `mode` | enum | — | `never`, `on-failure`, `service` |
|
|
| `attempts` / `max_retries` | `attempts` or `max_retries` | int | `0` | Max retries (both keys accepted) |
|
|
| `delay` | `delay` | duration string | `""` | Retry delay (e.g. `2s`) |
|
|
|
|
### `update`
|
|
|
|
Update strategy. Required for `Service`.
|
|
|
|
| Field | Key | Type | Default | Notes |
|
|
|-------|-----|------|---------|-------|
|
|
| `strategy` | `strategy` | enum | — | `rolling`, `canary`, `blue-green` |
|
|
| `max_surge` | `max_surge` | int | `0` | Max surge |
|
|
| `max_parallel` | `max_parallel` | int | `1` (clamped to `count`) | Max parallel updates |
|
|
| `min_healthy_time` | `min_healthy_time` | duration | `""` | Min time healthy before next batch |
|
|
| `healthy_deadline` | `healthy_deadline` | duration | `""` | Deadline for health |
|
|
| `canary` | `canary` | int or `"<n>%"` | — | Canary size (int count or percentage) |
|
|
| `auto_promote` | `auto_promote` | bool | `false` | Auto-promote canary (`true`/`yes`/`on`/`1`) |
|
|
|
|
**Strategies**:
|
|
- **rolling**: batches of `max_parallel`, each batch waits for healthy.
|
|
- **canary**: canary batch first, then `promote` (manual or `auto_promote`), then remaining in `max_parallel` batches.
|
|
- **blue-green**: all new allocs start in parallel, wait healthy, then `cutover`.
|
|
|
|
> Transactional update execution lands in v0.11-P10. The current
|
|
> emitter computes the plan; execution is a v0.11 deliverable.
|
|
|
|
### `service`
|
|
|
|
Traefik route definition. Implied for `Service`; not allowed for
|
|
`Job`/`DaemonSet`. See [docs/ingress.md](ingress.md) for details.
|
|
|
|
| Field | Key | Type | Default | Notes |
|
|
|-------|-----|------|---------|-------|
|
|
| `name` | `name` | string | — | Service name |
|
|
| `port` | `port` | int | — | Service port |
|
|
| `bind` | `bind` | string (IP) | `""` | Bind mode: empty = Unix socket (default); `127.0.0.1` = TCP opt-in (R-007) |
|
|
| `host` | `host` | string | `""` | Host (stored, not yet consumed by emitter) |
|
|
| `route_id` | `route_id` | string | `""` | Route ID (stored, not yet consumed by emitter) |
|
|
|
|
### `health`
|
|
|
|
Health check. Required for `Service`.
|
|
|
|
| Field | Key | Type | Default | Notes |
|
|
|-------|-----|------|---------|-------|
|
|
| `check_type` | `check_type` | string | — | Check type (e.g. `http`) |
|
|
| `interval` | `interval` | duration string | — | Check interval (e.g. `5s`) |
|
|
| `timeout` | `timeout` | duration string | — | Check timeout |
|
|
| `unhealthy_threshold` | `unhealthy_threshold` | int | `0` | Failures before unhealthy |
|
|
|
|
Maps to Traefik `healthCheck` stanza (`path: /healthz`).
|
|
|
|
### `lifecycle`
|
|
|
|
Lifecycle hooks. Maps to systemd `ExecStartPost` / `ExecStop`.
|
|
|
|
| Field | Key | Type | Default | systemd mapping |
|
|
|-------|-----|------|---------|-----------------|
|
|
| `post_start` | `post_start` | string list | nil | `ExecStartPost=` (runs after main starts) |
|
|
| `pre_stop` | `pre_stop` | string list | nil | `ExecStop=` (runs before kill) |
|
|
|
|
**Example**:
|
|
```yaml
|
|
lifecycle:
|
|
pre_stop:
|
|
- /bin/sh -c 'sleep 5'
|
|
- /usr/local/bin/drain.sh
|
|
post_start:
|
|
- /usr/local/bin/warm-cache.sh
|
|
```
|
|
|
|
### `constraints`
|
|
|
|
CEL-subset expressions for node selection. Inline array or block list.
|
|
|
|
```yaml
|
|
constraints:
|
|
- node.role == "web"
|
|
- region == "us"
|
|
# or inline
|
|
constraints: ['node.role == "web"', 'region == "us"']
|
|
```
|
|
|
|
**CEL subset grammar** (hand-rolled, no CEL dependency):
|
|
- Node attributes: `node.hostname`, `node.kind`, `node.cpus`,
|
|
`node.memory`, `node.tags`, `node.runtimes`
|
|
- Bare identifiers: equivalent to `node.<name>`
|
|
- Literals: string (`"..."`), int
|
|
- Comparisons: `==`, `!=`, `>=`, `<=`, `>`, `<`
|
|
- Membership: `in`, `not in`
|
|
- Boolean: `and`, `or`, `not`, parentheses
|
|
- Anything outside the subset returns an error (node skipped, not
|
|
silently mis-evaluated)
|
|
|
|
### `affinity`
|
|
|
|
Co-location / anti-affinity rules.
|
|
|
|
```yaml
|
|
affinity:
|
|
- target: zone == "a"
|
|
weight: 80
|
|
- target: web
|
|
weight: -50 # anti-affinity (negative weight)
|
|
```
|
|
|
|
- `target`: CEL expression or bare workload name (for name-based
|
|
co-location).
|
|
- `weight`: positive = co-locate, negative = anti-affinity.
|
|
- Affinity is a **hint** (not a gate); evaluation failures are ignored.
|
|
|
|
### `tasks` (task group)
|
|
|
|
Multi-process alloc (P06). When `tasks` is non-empty, the alloc runs
|
|
multiple processes, each as its own systemd unit, grouped under a
|
|
systemd target.
|
|
|
|
```yaml
|
|
tasks:
|
|
- name: app
|
|
runtime:
|
|
one_of: process
|
|
command: /usr/bin/httpd -f
|
|
env:
|
|
LOG_LEVEL: debug
|
|
- name: sidecar
|
|
runtime:
|
|
one_of: wasm
|
|
command: /bin/wasm-runner sidecar.wasm
|
|
```
|
|
|
|
- A task with no `runtime:` inherits the top-level `spec.Runtime`.
|
|
- Each task can have its own `env:` overlay.
|
|
- `command` falls back: `task.Command` → `task.Runtime.Command` →
|
|
`spec.Runtime.Command`.
|
|
- Task names must be unique within the group.
|
|
|
|
## Kinds matrix
|
|
|
|
| Feature | Job | Service | DaemonSet |
|
|
|---------|-----|---------|-----------|
|
|
| `count` | must be 1 | ≥ 1 | not allowed |
|
|
| `ports` | optional | **required** | not allowed |
|
|
| `service` block | not allowed | optional (implied) | not allowed |
|
|
| `restart` | optional | **required** | **required** |
|
|
| `update` | optional | **required** | optional |
|
|
| `health` | optional | **required** | optional |
|
|
| `runtime` | optional | **required** (or task group) | optional |
|
|
| `schedule` | optional | optional | **required** |
|
|
| `tasks` | optional | optional | optional |
|
|
| Traefik route | no | yes (implied) | no (by default) |
|
|
|
|
## Body semantics
|
|
|
|
The body after the closing `---` is preserved **byte-exact** (R-015) —
|
|
including trailing newlines, CRLF, BOM in body, and `---` inside code
|
|
fences. The body is carried verbatim to the target node. It is not
|
|
interpreted as commands/scripts by the parser today.
|
|
|
|
## Deprecated: HCL jobspec
|
|
|
|
The legacy HCL jobspec format is supported via an adapter during the
|
|
migration window. It is deprecated in v0.9 and will be removed in
|
|
v0.11.
|
|
|
|
```hcl
|
|
job "hello-orca" {
|
|
}
|
|
|
|
task "greet" {
|
|
command = "/bin/echo"
|
|
args = ["hello", "from", "orca"]
|
|
}
|
|
```
|
|
|
|
The adapter converts this to a `*WorkloadSpec{Kind: "Job", Name:
|
|
"hello-orca", Count: 1, Runtime: {OneOf: "process", Command:
|
|
"/bin/echo"}}`. Use `.md` for all new jobspecs.
|
|
|
|
## See also
|
|
|
|
- [docs/cli.md](cli.md) — CLI reference (including `orca job run`)
|
|
- [docs/ingress.md](ingress.md) — Traefik ingress configuration
|
|
- [examples/full-stack/](../examples/full-stack/) — Full-stack example jobspecs |