echoapp: accept --db, so the reference tenant can boot exec-backed
The two backends spawn a tenant differently. carlos-app@.service runs `live/<host> serve` with no flags and hands the listener over as LISTEN_FDS; the activator's ExecBackend runs `live/<host> --socket <s> --db <path>` (backend_exec.go:440), and every route the provisioning pass mints carries a DBPath, so --db is always passed. echoapp declared only -socket and -addr. Exec-backed, Go's flag package rejected the unknown --db, printed usage and exited before binding anything — so the activator waited out its full 30s health timeout on a socket that would never appear, and the edge served "instance is waking; retry shortly" indefinitely. Unit-backed it was fine, which is the only way it had ever been exercised. Found on a live deployment: an instance provisioned through the documented `carlos instances enable` + `carlos instances create` flow could not boot, because the app the e2e suite treats as the reference tenant met only half the contract its own doc comment claims to honour. Accepted and ignored — echoapp keeps no state. The point is to be spawnable by BOTH backends, so this path is exercised the next time someone provisions an instance with it. Whether an app should have to accept --db at all is a separate question, filed upstream: a stateless app has no use for it, and the flag being mandatory-by-omission is what turned this into a silent 30s hang rather than a clear refusal. Build, vet and all 20 test packages clean.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 file changed,
+21
−0
testdata/echoapp/main.go+21 −0
diff --git a/testdata/echoapp/main.go b/testdata/echoapp/main.go| index ef5b2264a..4295cb70e 100644 |
| --- a/testdata/echoapp/main.go |
| +++ b/testdata/echoapp/main.go |
| @@ -2,6 +2,23 @@ |
| // contract — it accepts -socket (unix) or -addr (TCP), or inherits a |
| // systemd-activated listener (LISTEN_FDS, the carlos-app@.socket contract), |
| // answers GET /healthz with "ok", and echoes its BUILD env everywhere else. |
| +// |
| +// It also accepts -db and ignores it. The two backends spawn a tenant |
| +// DIFFERENTLY: carlos-app@.service runs `live/<host> serve` with no flags |
| +// (the listener arrives as LISTEN_FDS), while the activator's ExecBackend |
| +// runs `live/<host> --socket <s> --db <path>` (backend_exec.go). An app that |
| +// declares only -socket/-addr therefore works unit-backed and dies instantly |
| +// exec-backed — Go's flag package rejects the unknown --db, prints usage and |
| +// exits before binding anything, so the activator waits out its 30s health |
| +// timeout on a socket that will never appear and the edge serves "instance |
| +// is waking; retry shortly" forever. |
| +// |
| +// That is exactly what happened on a live deployment: an instance |
| +// provisioned through `carlos instances create` could not boot, because this |
| +// tenant — the one the e2e suite treats as the reference app — only ever met |
| +// the unit-backed half of the contract it claims to honour. echoapp keeps no |
| +// state, so the flag is accepted and discarded; the point is to be spawnable |
| +// by BOTH backends. |
| package main |
| import ( |
| @@ -16,6 +33,10 @@ import ( |
| func main() { |
| socket := flag.String("socket", "", "unix socket to listen on") |
| addr := flag.String("addr", "", "TCP host:port to listen on") |
| + // Accepted and ignored: the activator always passes it (every route the |
| + // provisioning pass mints carries a DBPath), and a tenant that cannot |
| + // PARSE it never reaches its listener. echoapp keeps no state. |
| + _ = flag.String("db", "", "instance database path (accepted, unused: echoapp is stateless)") |
| flag.Parse() |
| mux := http.NewServeMux() |