// Command zddprobe is the probe app for the zero-downtime deploy roll notes // (docs/superpowers/plans/2026-10-07-zero-downtime-deploys-6-roll-notes.md). // It serves HTTP on a unix socket. Every request sleeps 50 ms, so requests are // in flight across a swap, then answers "ok ". SIGTERM or SIGINT // drains in-flight requests, waiting at most 15 s, and exits 0. // // The platform starts an exec-backed instance as // ` --socket --db `, and `carlos deploy`'s vet starts it as // ` -socket `. The probe takes both. It keeps no data, so --db // is accepted and ignored. // // On a platform launch, the one that passes --db, the probe checks before // it listens what a real app may rely on (zero-downtime spec rev 19, rule // 5), and exits 1 with a line on stderr if either is wrong: CARLOS_HOST // must be set, and the file name of --socket must be the flag-off one, // app.sock (nested) or $CARLOS_HOST.sock (flat). A generation launch that // named its socket anything else crash-looped a tenant whose framework // reads its host from that name (amadan #272); the probe ignored the name // and passed. Vet's boot passes no --db, no CARLOS_HOST and a socket name // of its own choosing, so it skips the check and the probe serves. (The // flag package reads -db and --db alike, so --db being given is the test.) // // version is set at build time with -ldflags "-X main.version=...". // // With ZDDPROBE_TRACE=1 the probe writes one line, "serving", to stdout as a // request enters the handler, so a test can tell a request is in flight // before it signals. Without it the probe writes nothing to stdout. package main import ( "context" "errors" "flag" "fmt" "io" "net" "net/http" "os" "os/signal" "path/filepath" "syscall" "time" ) var version = "dev" func main() { sigs := make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGTERM, syscall.SIGINT) if err := run(os.Args[1:], sigs); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } // checkLaunch is rule 5's check of a platform launch: host (CARLOS_HOST) // is set, and sock's file name is app.sock or .sock, the names a // flag-off launch gives. func checkLaunch(sock, host string) error { if host == "" { return errors.New("zddprobe: CARLOS_HOST is empty; the platform sets it on every instance launch") } if base := filepath.Base(sock); base != "app.sock" && base != host+".sock" { return fmt.Errorf("zddprobe: --socket file name is %q; want app.sock or %s.sock, the flag-off names", base, host) } return nil } func run(args []string, stop <-chan os.Signal) error { fs := flag.NewFlagSet("zddprobe", flag.ContinueOnError) fs.SetOutput(os.Stderr) sock := fs.String("socket", "", "unix socket to serve on") fs.String("db", "", "accepted for the platform's argv and ignored") if err := fs.Parse(args); err != nil { return err } if *sock == "" { return errors.New("zddprobe: --socket is required") } platform := false fs.Visit(func(f *flag.Flag) { platform = platform || f.Name == "db" }) if platform { if err := checkLaunch(*sock, os.Getenv("CARLOS_HOST")); err != nil { return err } } _ = os.Remove(*sock) l, err := net.Listen("unix", *sock) if err != nil { return err } trace := os.Getenv("ZDDPROBE_TRACE") == "1" mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { if trace { fmt.Fprintln(os.Stdout, "serving") } time.Sleep(50 * time.Millisecond) // keep requests in flight across the switch _, _ = io.WriteString(w, "ok "+version+"\n") }) srv := &http.Server{Handler: mux} drained := make(chan error, 1) go func() { <-stop ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() drained <- srv.Shutdown(ctx) }() if err := srv.Serve(l); err != nil && !errors.Is(err, http.ErrServerClosed) { return err } return <-drained }