carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1// echoapp is the e2e/vet test tenant: a minimal app honoring the platform
2// contract — it accepts -socket (unix) or -addr (TCP), or inherits a
3// systemd-activated listener (LISTEN_FDS, the carlos-app@.socket contract),
4// answers GET /healthz with "ok", and echoes its BUILD env everywhere else.
5//
6// It also accepts -db and ignores it. The two backends spawn a tenant
7// DIFFERENTLY: carlos-app@.service runs `live/<host> serve` with no flags
8// (the listener arrives as LISTEN_FDS), while the activator's ExecBackend
9// runs `live/<host> --socket <s> --db <path>` (backend_exec.go). An app that
10// declares only -socket/-addr therefore works unit-backed and dies instantly
11// exec-backed — Go's flag package rejects the unknown --db, prints usage and
12// exits before binding anything, so the activator waits out its 30s health
13// timeout on a socket that will never appear and the edge serves "instance
14// is waking; retry shortly" forever.
15//
16// That is exactly what happened on a live deployment: an instance
17// provisioned through `carlos instances create` could not boot, because this
18// tenant — the one the e2e suite treats as the reference app — only ever met
19// the unit-backed half of the contract it claims to honour. echoapp keeps no
20// state, so the flag is accepted and discarded; the point is to be spawnable
21// by BOTH backends.
22package main
23
24import (
25 "flag"
26 "fmt"
27 "net"
28 "net/http"
29 "os"
30 "strconv"
31)
32
33func main() {
34 socket := flag.String("socket", "", "unix socket to listen on")
35 addr := flag.String("addr", "", "TCP host:port to listen on")
36 // Accepted and ignored: the activator always passes it (every route the
37 // provisioning pass mints carries a DBPath), and a tenant that cannot
38 // PARSE it never reaches its listener. echoapp keeps no state.
39 _ = flag.String("db", "", "instance database path (accepted, unused: echoapp is stateless)")
40 flag.Parse()
41
42 mux := http.NewServeMux()
43 mux.HandleFunc("/healthz", func(w http.ResponseWriter, _ *http.Request) {
44 fmt.Fprint(w, "ok")
45 })
46 mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) {
47 fmt.Fprintf(w, "echoapp BUILD=%s", os.Getenv("BUILD"))
48 })
49
50 ln, err := listen(*socket, *addr)
51 if err != nil {
52 fmt.Fprintln(os.Stderr, err)
53 os.Exit(1)
54 }
55 if err := http.Serve(ln, mux); err != nil {
56 fmt.Fprintln(os.Stderr, err)
57 os.Exit(1)
58 }
59}
60
61// listen picks the app's listener, activation first: when systemd (or a test
62// playing systemd) passed a pre-bound socket — LISTEN_FDS=1, the fd at 3 —
63// serve on it and never touch any path. That is the carlos-app@.socket
64// contract: the file is root-bound and carlos-owned so only the edge can
65// connect; the tenant just inherits the fd. Otherwise bind -socket (unix)
66// or -addr (TCP) ourselves.
67func listen(socket, addr string) (net.Listener, error) {
68 if n, _ := strconv.Atoi(os.Getenv("LISTEN_FDS")); n >= 1 {
69 // systemd sets LISTEN_PID to our pid post-fork; accept it unset for
70 // harnesses that exec us directly and can't know the pid in advance.
71 if pid := os.Getenv("LISTEN_PID"); pid == "" || pid == strconv.Itoa(os.Getpid()) {
72 return net.FileListener(os.NewFile(3, "listen-fd"))
73 }
74 }
75 network, address := "tcp", addr
76 if socket != "" {
77 network, address = "unix", socket
78 os.Remove(socket)
79 }
80 return net.Listen(network, address)
81}
82