Look at what a container mounts directly, accept a pre-upgrade label, and write the genesis secret without a newline

Review of the first cut found four things.

A directory mounted into a container is no longer looked inside, not even
for the files this host wrote there. The controller records every
provider's received and contributions file as a plain file under a mounted
directory, so folding those in would have recreated the route proxy — which
re-reads its routes live, by design — on every route change, and killed
every provisioner sidecar, which polls what it receives, mid-reconcile on
every grant. Whether a service reads a file under its directory once or
watches it is the service's; restart-on is how a module says "once", and it
stays the opt-in. Env-files and files mounted directly remain by content.

Genesis wrote the superuser secret as `value\n`; `secret accept` strips the
line ending by design, so the postgres module declared `value` — and with
a mounted file's content in the spec, phase three would have recreated the
store it meant to adopt in place, with the temporary control plane
connected to it. Genesis now writes the value alone. readCredentialFile
tolerated both endings already. Pinned with the bytes the genesis code
path writes, then the module's declaration of the same container: it must
reconcile.

A container carrying a label from before the host folded in what it reads
is accepted rather than recreated, when that label matches the spec as it
used to be computed: what it reads is recorded then, a change is caught
from that record from the next apply on, and the label is renewed at the
next genuine recreate. Recreating them all would have been a restart storm
across the mesh in declaration order, the store first. The trade-off is
stated in the code: a container already stale at upgrade time is not
caught, and could not have been either way.

The record of what a container read is looked up by its name when its
declared id has none — the bundle's `store` becomes `postgres.server` for
the same container — so a change on the day it is adopted still names the
file. The by-target lookup takes the most recently applied record, since
the bundle's record for the same target is never removed by the mesh's.

novox/hq 04-ISSUES/103
This commit is contained in:
2026-09-23 23:40:27 +02:00
parent c60228e719
commit 982b84310e
6 changed files with 329 additions and 63 deletions
+14 -13
View File
@@ -18,7 +18,6 @@ import (
"os"
"path/filepath"
"sort"
"strings"
"time"
)
@@ -209,24 +208,26 @@ func (s State) Recorded(kind, target string) bool {
return false
}
// FilesUnder returns every file this host has a record of writing at a path or beneath it, of any
// origin and under any id, in path order.
// At returns what this host has a record of putting at a target of this kind, under any id and of
// any origin — Recorded, with the record.
//
// By path rather than by id because a container names what it reads by path: the id a file was
// declared under may change — the bundle's, then a module's, for the same file — and the container
// reading it does not care which.
func (s State) FilesUnder(path string) []Applied {
var out []Applied
// By target rather than by id because the id a thing was declared under may change while the thing
// does not: the bundle's `store` becomes a module's `postgres.server` for the same container, and
// the file that container reads is the same file under either id. Both ids may then hold a record
// for the one target — the bundle's is never removed by the mesh's declaration — and the most
// recently applied is the one that says what is there now.
func (s State) At(kind, target string) (Applied, bool) {
var latest Applied
found := false
for _, r := range s.Resources {
if r.Type != "file" {
if r.Type != kind || r.Target != target {
continue
}
if r.Target == path || strings.HasPrefix(r.Target, strings.TrimSuffix(path, "/")+"/") {
out = append(out, r)
if !found || r.AppliedAt.After(latest.AppliedAt) {
latest, found = r, true
}
}
sort.Slice(out, func(i, j int) bool { return out[i].Target < out[j].Target })
return out
return latest, found
}
// HeldAt returns what is held under a resource id.