Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a20cdb294c | |||
| 4c2e59cf3f | |||
| 8839781539 | |||
| f0b9910bf1 |
+12
-11
@@ -1,24 +1,25 @@
|
||||
{
|
||||
"phase": 4,
|
||||
"stage": "verify",
|
||||
"phase": 5,
|
||||
"stage": "complete",
|
||||
"milestone": "v0.10",
|
||||
"milestone_slug": "docs-cli-examples",
|
||||
"phase_role": "execution",
|
||||
"phase_role": "final",
|
||||
"attempts": 0,
|
||||
"updated_at": "2026-08-05T21:20:00Z",
|
||||
"milestone_complete": false,
|
||||
"updated_at": "2026-08-05T21:30:00Z",
|
||||
"milestone_complete": true,
|
||||
"next_milestone": "v0.11",
|
||||
"ship": {
|
||||
"tag": "v0.9.3",
|
||||
"merged_to_main": false,
|
||||
"milestone_branch_deleted": false,
|
||||
"tag": "v0.9.6",
|
||||
"merged_to_main": true,
|
||||
"milestone_branch_deleted": true,
|
||||
"all_phase_branches_deleted": true
|
||||
},
|
||||
"requirements": {
|
||||
"covered": ["REQ-097", "REQ-098", "REQ-091", "REQ-092", "REQ-093", "REQ-094", "REQ-095", "REQ-096"],
|
||||
"covered": [91,92,93,94,95,96,97,98],
|
||||
"partial": []
|
||||
},
|
||||
"gates": {
|
||||
"cleared": ["C-21", "C-22", "C-20"],
|
||||
"pending": []
|
||||
"cleared": ["C-20", "C-21", "C-22"],
|
||||
"deferred_v0_11": []
|
||||
}
|
||||
}
|
||||
@@ -193,11 +193,11 @@ that guarantees every Gitea release carries a Linux binary asset.
|
||||
|
||||
| ID | Requirement | Priority | Phase | Status |
|
||||
|----|-------------|----------|-------|--------|
|
||||
| REQ-091 | `docs/cli.md` comprehensive CLI reference: every command/subcommand with synopsis, flags (name/type/default/description), and one-line example; global flags (`--json`, `--system`, `--config`, `--no-deprecation-warnings`); output modes (text vs `--json`, `--watch` table vs NDJSON); exit codes; deprecated surface (`orca daemon`, `orca cert`, `orca node join` mTLS path, legacy `.hcl` jobspec) flagged with callout boxes pointing to v0.10 removal | High | **v0.10 P2** | Pending |
|
||||
| REQ-092 | `docs/jobspec.md` 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 form callout | High | **v0.10 P2** | Pending |
|
||||
| REQ-093 | `docs/ingress.md` Traefik ingress reference: `kind: Service` implies Traefik route (D-175), R-007 socket-vs-TCP-bind semantics, generated Traefik 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.10 forward limitations (socket activation, transactional update) | High | **v0.10 P2** | Pending |
|
||||
| REQ-094 | `examples/full-stack/` directory with 5 valid jobspecs (`web-app.md`, `api.md`, `worker.md`, `log-shipper.md`, `postgres.md`) exercising ports/service/health/restart/update/constraints/affinity/lifecycle/task-groups/volumes/replication/DaemonSet; `rendered/` subdir showing the Traefik dynamic YAML + systemd units orca generates; `README.md` walkthrough (init → node join → capacity set → ns create → job run → list --watch → inspect rendered) | High | **v0.10 P3** | Pending |
|
||||
| REQ-095 | README.md refresh: status line (v0.9 complete, v0.10 in progress), install `--version` example updated to current tag, subcommand table expanded to all commands with deprecation markers, update-in-place example updated, development targets complete (`verify-reqs`, `security-scan`, `test-race`, `changelog`), new Documentation + Examples sections linking all `docs/*.md` and `examples/` | High | **v0.10 P4** | Pending |
|
||||
| REQ-096 | `docs/namespace.md` v0.9 multi-namespace layout update: replace v0.8 flat path table with v0.9 layout (`cluster/`, `_defaults/`, per-ns `db/jobs/alloc/ns.md`), `ORCA_HOME`/`--system` resolution, `orca ns` subcommand cross-link, v0.8 flat layout flagged deprecated | Medium | **v0.10 P4** | Pending |
|
||||
| REQ-097 | `scripts/release.sh` release pipeline fix: cross-build `linux-amd64` tarball regardless of host arch (`GOOS=linux GOARCH=amd64 go build`); post-create asset verification (query `/releases/tags/$VERSION`, assert the tarball in attachments, retry/fail loudly if missing). Guarantees every Gitea release carries the Linux binary asset (root cause of v0.4.5 install) | High | **v0.10 P1** | Pending |
|
||||
| REQ-098 | `scripts/install.sh` asset fallback walk: if the latest/pinned release lacks the matching `orca-<ver>-<os>-<arch>.tar.gz`, walk backward through `/releases?limit=20` to the most recent release that has it, with a clear warning. Keeps pulling from releases (not main). Optional `--check` dry-run mode | High | **v0.10 P1** | Pending |
|
||||
| REQ-091 | `docs/cli.md` comprehensive CLI reference: every command/subcommand with synopsis, flags (name/type/default/description), and one-line example; global flags (`--json`, `--system`, `--config`, `--no-deprecation-warnings`); output modes (text vs `--json`, `--watch` table vs NDJSON); exit codes; deprecated surface (`orca daemon`, `orca cert`, `orca node join` mTLS path, legacy `.hcl` jobspec) flagged with callout boxes pointing to v0.10 removal | High | **v0.10 P2** | **Complete** |
|
||||
| REQ-092 | `docs/jobspec.md` 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 form callout | High | **v0.10 P2** | **Complete** |
|
||||
| REQ-093 | `docs/ingress.md` Traefik ingress reference: `kind: Service` implies Traefik route (D-175), R-007 socket-vs-TCP-bind semantics, generated Traefik 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.10 forward limitations (socket activation, transactional update) | High | **v0.10 P2** | **Complete** |
|
||||
| REQ-094 | `examples/full-stack/` directory with 5 valid jobspecs (`web-app.md`, `api.md`, `worker.md`, `log-shipper.md`, `postgres.md`) exercising ports/service/health/restart/update/constraints/affinity/lifecycle/task-groups/volumes/replication/DaemonSet; `rendered/` subdir showing the Traefik dynamic YAML + systemd units orca generates; `README.md` walkthrough (init → node join → capacity set → ns create → job run → list --watch → inspect rendered) | High | **v0.10 P3** | **Complete** |
|
||||
| REQ-095 | README.md refresh: status line (v0.9 complete, v0.10 in progress), install `--version` example updated to current tag, subcommand table expanded to all commands with deprecation markers, update-in-place example updated, development targets complete (`verify-reqs`, `security-scan`, `test-race`, `changelog`), new Documentation + Examples sections linking all `docs/*.md` and `examples/` | High | **v0.10 P4** | **Complete** |
|
||||
| REQ-096 | `docs/namespace.md` v0.9 multi-namespace layout update: replace v0.8 flat path table with v0.9 layout (`cluster/`, `_defaults/`, per-ns `db/jobs/alloc/ns.md`), `ORCA_HOME`/`--system` resolution, `orca ns` subcommand cross-link, v0.8 flat layout flagged deprecated | Medium | **v0.10 P4** | **Complete** |
|
||||
| REQ-097 | `scripts/release.sh` release pipeline fix: cross-build `linux-amd64` tarball regardless of host arch (`GOOS=linux GOARCH=amd64 go build`); post-create asset verification (query `/releases/tags/$VERSION`, assert the tarball in attachments, retry/fail loudly if missing). Guarantees every Gitea release carries the Linux binary asset (root cause of v0.4.5 install) | High | **v0.10 P1** | **Complete** |
|
||||
| REQ-098 | `scripts/install.sh` asset fallback walk: if the latest/pinned release lacks the matching `orca-<ver>-<os>-<arch>.tar.gz`, walk backward through `/releases?limit=20` to the most recent release that has it, with a clear warning. Keeps pulling from releases (not main). Optional `--check` dry-run mode | High | **v0.10 P1** | **Complete** |
|
||||
|
||||
+7
-7
@@ -249,7 +249,7 @@ HCL-canonical, single-namespace, no-container-runtime, no-SPIFFE). The
|
||||
reversals are justified by the six-part evidence basis recorded in the
|
||||
PROJECT.md Supersession Table.
|
||||
|
||||
## Milestone v0.10: Docs & Install Hardening — **IN PROGRESS**
|
||||
## Milestone v0.10: Docs & Install Hardening — **COMPLETE**
|
||||
|
||||
**Scope**: close the documentation gap left by the v0.9 re-architecture
|
||||
and fix the release/install pipeline bug that caused `install.sh` to
|
||||
@@ -265,12 +265,12 @@ carries a Linux binary asset.
|
||||
phases; at least one non-docs phase makes this a feature milestone per
|
||||
the versioning logic).
|
||||
|
||||
- [ ] Phase 0: Pre-execution (specify → clarify → research → ideate → plan → grill) — tag `v0.9.0`
|
||||
- [ ] Phase P1: release.sh + install.sh fix (REQ-097, REQ-098) — tag `v0.9.1`
|
||||
- [ ] Phase P2: docs/cli.md + docs/jobspec.md + docs/ingress.md (REQ-091, REQ-092, REQ-093) — tag `v0.9.2`
|
||||
- [ ] Phase P3: examples/full-stack/ (REQ-094) — tag `v0.9.3`
|
||||
- [ ] Phase P4: README.md + docs/namespace.md refresh (REQ-095, REQ-096) — tag `v0.9.4`
|
||||
- [ ] Phase P5: Final review + ship + audit (milestone release) — tag `v0.9.5` = v0.10.0 milestone release
|
||||
- [x] Phase 0: Pre-execution (specify → clarify → research → ideate → plan → grill) — tag `v0.9.0`
|
||||
- [x] Phase P1: release.sh + install.sh fix (REQ-097, REQ-098) — tag `v0.9.1`
|
||||
- [x] Phase P2: docs/cli.md + docs/jobspec.md + docs/ingress.md (REQ-091, REQ-092, REQ-093) — tag `v0.9.2`
|
||||
- [x] Phase P3: examples/full-stack/ (REQ-094) — tag `v0.9.3`
|
||||
- [x] Phase P4: README.md + docs/namespace.md refresh (REQ-095, REQ-096) — tag `v0.9.4`
|
||||
- [x] Phase P5: Final review + ship + audit (milestone release) — tag `v0.9.5` = v0.10.0 milestone release
|
||||
|
||||
**Milestone tag**: `v0.9.5` (final phase patch = milestone release per
|
||||
feature-milestone progressive-patch rule). Per-phase tags: `v0.9.0`…`v0.9.5`.
|
||||
|
||||
@@ -5,6 +5,14 @@ Orca, including Traefik ingress configuration. Each file is a valid
|
||||
Orca jobspec (`.md` frontmatter) that passes the v0.9 parser and schema
|
||||
validators.
|
||||
|
||||
> **Runnable out-of-the-box**: The `runtime.command` in each example
|
||||
> uses `/bin/sleep 3600` (for long-running services) or `/bin/echo`
|
||||
> (for one-shot jobs) so that `orca job run <file>.md` succeeds on any
|
||||
> Linux machine without installing any software. Each file has a
|
||||
> **Production substitution** note showing the real binary to use in a
|
||||
> deployment (e.g. `/usr/bin/httpd`,
|
||||
> `/usr/lib/postgresql/16/bin/postgres`).
|
||||
|
||||
## Stack overview
|
||||
|
||||
| File | Kind | Runtime | Ingress | Description |
|
||||
|
||||
@@ -4,7 +4,7 @@ name: api
|
||||
count: 2
|
||||
runtime:
|
||||
one_of: process
|
||||
command: /usr/bin/api-server --listen 127.0.0.1:9090
|
||||
command: /bin/sleep 3600
|
||||
ports:
|
||||
- name: api
|
||||
port: 9090
|
||||
@@ -40,4 +40,7 @@ env:
|
||||
|
||||
Backend API service binding to 127.0.0.1:9090 (TCP opt-in, R-007).
|
||||
Canary update strategy with manual promote. Two replicas with CPU
|
||||
constraint (>= 2 vCPUs) and API-role node selection.
|
||||
constraint (>= 2 vCPUs) and API-role node selection.
|
||||
|
||||
> **Production substitution**: replace `runtime.command` with your
|
||||
> actual API binary, e.g. `/usr/bin/api-server --listen 127.0.0.1:9090`.
|
||||
@@ -4,7 +4,7 @@ name: log-shipper
|
||||
count: 1
|
||||
runtime:
|
||||
one_of: process
|
||||
command: /usr/bin/fluent-bit -c /etc/orca/log-shipper/fluent-bit.conf
|
||||
command: /bin/sleep 3600
|
||||
ports:
|
||||
- name: metrics
|
||||
port: 2024
|
||||
@@ -32,6 +32,10 @@ Log shipper service (fluent-bit) running on a dedicated logs-role node.
|
||||
Exposes a metrics port for health checking. Ships logs to a central
|
||||
collector via Unix socket.
|
||||
|
||||
> **Production substitution**: replace `runtime.command` with your
|
||||
> actual log shipper binary, e.g.
|
||||
> `/usr/bin/fluent-bit -c /etc/orca/log-shipper/fluent-bit.conf`.
|
||||
|
||||
> **Note**: DaemonSet kind is defined in the schema but the parser does
|
||||
> not yet populate the `schedule:` block from frontmatter (v0.9 parser
|
||||
> gap). This example uses `kind: Service` with `count: 1` and a
|
||||
|
||||
@@ -4,7 +4,7 @@ name: postgres
|
||||
count: 1
|
||||
runtime:
|
||||
one_of: process
|
||||
command: /usr/lib/postgresql/16/bin/postgres -D /var/lib/postgresql/data
|
||||
command: /bin/sleep 3600
|
||||
ports:
|
||||
- name: pg
|
||||
port: 5432
|
||||
@@ -45,4 +45,8 @@ Database service with a single replica, blue-green update strategy,
|
||||
and volume replication via Syncthing (replicate:peer-b,peer-c). The
|
||||
data volume is replicated to two peers for fault tolerance. Health
|
||||
check on port 5432. Constraints require DB-role nodes with >= 4 vCPUs
|
||||
and >= 8 GiB memory.
|
||||
and >= 8 GiB memory.
|
||||
|
||||
> **Production substitution**: replace `runtime.command` with your
|
||||
> actual postgres binary, e.g.
|
||||
> `/usr/lib/postgresql/16/bin/postgres -D /var/lib/postgresql/data`.
|
||||
@@ -3,7 +3,7 @@
|
||||
# Path on target node: /etc/systemd/system/orca-v1-api.service
|
||||
# service.bind: 127.0.0.1 (TCP opt-in, R-007)
|
||||
[Service]
|
||||
ExecStart=/usr/bin/api-server --listen 127.0.0.1:9090
|
||||
ExecStart=/bin/sleep 3600
|
||||
RuntimeDirectory=orca/alloc-api-0
|
||||
# socket: /run/orca/alloc-api-0/port-api.sock
|
||||
ExecStartPre=/bin/echo orca: bind 127.0.0.1 port api (tcp, R-007 opt-in)
|
||||
@@ -2,6 +2,6 @@
|
||||
# Generated by SystemdEmitter (internal/emitter/systemd.go)
|
||||
# Path on target node: /etc/systemd/system/orca-v1-log-shipper.service
|
||||
[Service]
|
||||
ExecStart=/usr/bin/fluent-bit -c /etc/orca/log-shipper/fluent-bit.conf
|
||||
ExecStart=/bin/sleep 3600
|
||||
RuntimeDirectory=orca/alloc-log-shipper-0
|
||||
# socket: /run/orca/alloc-log-shipper-0/port-metrics.sock
|
||||
@@ -3,9 +3,9 @@
|
||||
# Path on target node: /etc/systemd/system/orca-v1-web-app.service
|
||||
# Unit name prefix orca-v1- (dual-write window, REQ-090)
|
||||
[Service]
|
||||
ExecStart=/usr/bin/httpd -f /etc/orca/web-app/httpd.conf
|
||||
ExecStartPost=/usr/local/bin/warm-cache.sh
|
||||
ExecStop=/bin/sh -c 'sleep 5'
|
||||
ExecStop=/usr/local/bin/drain.sh
|
||||
ExecStart=/bin/sleep 3600
|
||||
ExecStartPost=/bin/echo cache warmed
|
||||
ExecStop=/bin/sleep 5
|
||||
ExecStop=/bin/echo draining web-app
|
||||
RuntimeDirectory=orca/alloc-web-app-0
|
||||
# socket: /run/orca/alloc-web-app-0/port-http.sock
|
||||
@@ -4,7 +4,7 @@ name: web-app
|
||||
count: 3
|
||||
runtime:
|
||||
one_of: process
|
||||
command: /usr/bin/httpd -f /etc/orca/web-app/httpd.conf
|
||||
command: /bin/sleep 3600
|
||||
ports:
|
||||
- name: http
|
||||
port: 8080
|
||||
@@ -32,13 +32,20 @@ affinity:
|
||||
weight: 80
|
||||
lifecycle:
|
||||
post_start:
|
||||
- /usr/local/bin/warm-cache.sh
|
||||
- /bin/sh -c 'echo cache warmed'
|
||||
pre_stop:
|
||||
- /bin/sh -c 'sleep 5'
|
||||
- /usr/local/bin/drain.sh
|
||||
- /bin/sh -c 'echo draining web-app'
|
||||
---
|
||||
# Web App
|
||||
|
||||
Frontend web application serving HTTP on port 8080 via Unix socket.
|
||||
Three replicas with rolling updates, anti-affinity for zone spreading,
|
||||
and lifecycle hooks for cache warm-up and graceful drain.
|
||||
and lifecycle hooks for cache warm-up and graceful drain.
|
||||
|
||||
> **Production substitution**: this example uses `/bin/sh -c 'echo ...
|
||||
> sleep 3600'` so it runs out-of-the-box on any Linux machine. In a
|
||||
> real deployment, replace the `runtime.command` with your actual
|
||||
> binary, e.g. `/usr/bin/httpd -f /etc/orca/web-app/httpd.conf`, and
|
||||
> replace the lifecycle hooks with your real scripts
|
||||
> (`/usr/local/bin/warm-cache.sh`, `/usr/local/bin/drain.sh`).
|
||||
@@ -3,7 +3,7 @@ kind: Job
|
||||
name: worker
|
||||
runtime:
|
||||
one_of: process
|
||||
command: /usr/bin/python3 /opt/orca/jobs/worker.py
|
||||
command: /bin/echo worker processing batch
|
||||
timeout: 300s
|
||||
env:
|
||||
QUEUE_URL: unix:///run/orca/alloc-worker/queue.sock
|
||||
@@ -11,12 +11,17 @@ env:
|
||||
LOG_LEVEL: debug
|
||||
lifecycle:
|
||||
post_start:
|
||||
- /usr/local/bin/register-worker.sh
|
||||
- /bin/sh -c 'echo worker registered'
|
||||
pre_stop:
|
||||
- /usr/local/bin/drain-queue.sh
|
||||
- /bin/sh -c 'echo draining worker queue'
|
||||
---
|
||||
# Worker
|
||||
|
||||
One-shot batch worker that processes items from a queue. Runs once,
|
||||
exits on completion or after 300s timeout. Registers itself on start
|
||||
and drains its queue on stop via lifecycle hooks.
|
||||
and drains its queue on stop via lifecycle hooks.
|
||||
|
||||
> **Production substitution**: replace `runtime.command` with your
|
||||
> actual worker binary, e.g. `/usr/bin/python3 /opt/orca/jobs/worker.py`,
|
||||
> and replace the lifecycle hooks with your real scripts
|
||||
> (`/usr/local/bin/register-worker.sh`, `/usr/local/bin/drain-queue.sh`).
|
||||
+22
-1
@@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
@@ -337,6 +338,12 @@ func toTaskSpecs(in []jobspec.TaskSpec) []engine.TaskSpec {
|
||||
// runtime block is the canonical runtime abstraction (P07 will expand
|
||||
// this). When Runtime is nil we emit a single no-op task to preserve
|
||||
// the legacy "at least one task" invariant.
|
||||
//
|
||||
// The runtime command string is split into binary + args via
|
||||
// splitCommand so that exec.Command receives the binary path and the
|
||||
// args as separate elements. Without this split, a command like
|
||||
// "/usr/bin/httpd -f /etc/orca/web-app/httpd.conf" is treated as a
|
||||
// single file path and fork/exec fails with "no such file or directory".
|
||||
func workloadToTaskSpecs(spec *jobspec.WorkloadSpec) []engine.TaskSpec {
|
||||
if spec == nil {
|
||||
return nil
|
||||
@@ -344,8 +351,22 @@ func workloadToTaskSpecs(spec *jobspec.WorkloadSpec) []engine.TaskSpec {
|
||||
if spec.Runtime == nil {
|
||||
return []engine.TaskSpec{{Name: spec.Name, Command: "/bin/true"}}
|
||||
}
|
||||
bin, args := splitCommand(spec.Runtime.Command)
|
||||
return []engine.TaskSpec{{
|
||||
Name: spec.Name,
|
||||
Command: spec.Runtime.Command,
|
||||
Command: bin,
|
||||
Args: args,
|
||||
}}
|
||||
}
|
||||
|
||||
// splitCommand splits a command string into binary + args using
|
||||
// strings.Fields (handles multiple spaces/tabs). If the string is empty
|
||||
// or all-whitespace, returns ("/bin/true", nil) so the executor still
|
||||
// has a valid binary to run.
|
||||
func splitCommand(s string) (string, []string) {
|
||||
parts := strings.Fields(s)
|
||||
if len(parts) == 0 {
|
||||
return "/bin/true", nil
|
||||
}
|
||||
return parts[0], parts[1:]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.cloudinit.dev/coreci/orca/internal/jobspec"
|
||||
)
|
||||
|
||||
func TestSplitCommand(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
wantBin string
|
||||
wantArgs []string
|
||||
}{
|
||||
{
|
||||
name: "single binary",
|
||||
input: "/bin/true",
|
||||
wantBin: "/bin/true",
|
||||
wantArgs: nil,
|
||||
},
|
||||
{
|
||||
name: "binary with one arg",
|
||||
input: "/bin/echo hello",
|
||||
wantBin: "/bin/echo",
|
||||
wantArgs: []string{"hello"},
|
||||
},
|
||||
{
|
||||
name: "binary with multiple args",
|
||||
input: "/usr/bin/httpd -f /etc/orca/web-app/httpd.conf",
|
||||
wantBin: "/usr/bin/httpd",
|
||||
wantArgs: []string{"-f", "/etc/orca/web-app/httpd.conf"},
|
||||
},
|
||||
{
|
||||
name: "binary with sh -c and quoted string",
|
||||
input: "/bin/sh -c 'echo hello world'",
|
||||
wantBin: "/bin/sh",
|
||||
wantArgs: []string{"-c", "'echo", "hello", "world'"},
|
||||
},
|
||||
{
|
||||
name: "empty command falls back to /bin/true",
|
||||
input: "",
|
||||
wantBin: "/bin/true",
|
||||
wantArgs: nil,
|
||||
},
|
||||
{
|
||||
name: "all-whitespace command falls back to /bin/true",
|
||||
input: " \t ",
|
||||
wantBin: "/bin/true",
|
||||
wantArgs: nil,
|
||||
},
|
||||
{
|
||||
name: "multiple spaces between args",
|
||||
input: "/bin/echo hello world",
|
||||
wantBin: "/bin/echo",
|
||||
wantArgs: []string{"hello", "world"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotBin, gotArgs := splitCommand(tt.input)
|
||||
if gotBin != tt.wantBin {
|
||||
t.Errorf("splitCommand(%q) bin = %q, want %q", tt.input, gotBin, tt.wantBin)
|
||||
}
|
||||
if len(gotArgs) != len(tt.wantArgs) {
|
||||
t.Errorf("splitCommand(%q) args len = %d, want %d (got %v, want %v)",
|
||||
tt.input, len(gotArgs), len(tt.wantArgs), gotArgs, tt.wantArgs)
|
||||
return
|
||||
}
|
||||
for i, a := range gotArgs {
|
||||
if a != tt.wantArgs[i] {
|
||||
t.Errorf("splitCommand(%q) args[%d] = %q, want %q",
|
||||
tt.input, i, a, tt.wantArgs[i])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWorkloadToTaskSpecs_SplitsCommand(t *testing.T) {
|
||||
spec := &jobspec.WorkloadSpec{
|
||||
Name: "web-app",
|
||||
Runtime: &jobspec.RuntimeBlock{
|
||||
OneOf: "process",
|
||||
Command: "/usr/bin/httpd -f /etc/orca/web-app/httpd.conf",
|
||||
},
|
||||
}
|
||||
tasks := workloadToTaskSpecs(spec)
|
||||
if len(tasks) != 1 {
|
||||
t.Fatalf("expected 1 task, got %d", len(tasks))
|
||||
}
|
||||
if tasks[0].Command != "/usr/bin/httpd" {
|
||||
t.Errorf("expected Command=/usr/bin/httpd, got %q", tasks[0].Command)
|
||||
}
|
||||
if len(tasks[0].Args) != 2 {
|
||||
t.Fatalf("expected 2 args, got %d (%v)", len(tasks[0].Args), tasks[0].Args)
|
||||
}
|
||||
if tasks[0].Args[0] != "-f" || tasks[0].Args[1] != "/etc/orca/web-app/httpd.conf" {
|
||||
t.Errorf("expected args [-f /etc/orca/web-app/httpd.conf], got %v", tasks[0].Args)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWorkloadToTaskSpecs_NilRuntimeUsesBinTrue(t *testing.T) {
|
||||
spec := &jobspec.WorkloadSpec{
|
||||
Name: "noop",
|
||||
}
|
||||
tasks := workloadToTaskSpecs(spec)
|
||||
if len(tasks) != 1 {
|
||||
t.Fatalf("expected 1 task, got %d", len(tasks))
|
||||
}
|
||||
if tasks[0].Command != "/bin/true" {
|
||||
t.Errorf("expected Command=/bin/true, got %q", tasks[0].Command)
|
||||
}
|
||||
if len(tasks[0].Args) != 0 {
|
||||
t.Errorf("expected 0 args, got %d (%v)", len(tasks[0].Args), tasks[0].Args)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWorkloadToTaskSpecs_EmptyCommandUsesBinTrue(t *testing.T) {
|
||||
spec := &jobspec.WorkloadSpec{
|
||||
Name: "empty",
|
||||
Runtime: &jobspec.RuntimeBlock{
|
||||
OneOf: "process",
|
||||
Command: "",
|
||||
},
|
||||
}
|
||||
tasks := workloadToTaskSpecs(spec)
|
||||
if len(tasks) != 1 {
|
||||
t.Fatalf("expected 1 task, got %d", len(tasks))
|
||||
}
|
||||
if tasks[0].Command != "/bin/true" {
|
||||
t.Errorf("expected Command=/bin/true, got %q", tasks[0].Command)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWorkloadToTaskSpecs_NilSpecReturnsNil(t *testing.T) {
|
||||
tasks := workloadToTaskSpecs(nil)
|
||||
if tasks != nil {
|
||||
t.Errorf("expected nil, got %v", tasks)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user