carlos / platform Public

Sidecar isolation Task 8 (2/4): ConfigEnvPath learns the worker shape (D8)

nestedRoute decides a sidecar row by its database path alone, with no
socket, the way Classify does (D2), so a nested worker's .config.env lands
in its instance directory instead of flat; a sidecar row carrying a socket
stays flat, as foreign. SyncExec's fenced skip already covered worker
hosts through the journal predicate; pinned alongside.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev f327c2f5e516eacf7a0151f11e6516e850047fe2 parent 3d8ce33
2 files changed, +94 −3
  • internal/appconfig/syncexec.go +11 −3
  • internal/appconfig/syncexec_test.go +83 −0
diff --git a/internal/appconfig/syncexec.go b/internal/appconfig/syncexec.go
index 1caa1b368..726583050 100644
--- a/internal/appconfig/syncexec.go
+++ b/internal/appconfig/syncexec.go
@@ -63,14 +63,22 @@ const configEnvSuffix = ".config.env"
// test activator.Classify makes, spelled out here rather than imported so
// this package need not take a dependency on internal/activator for one
// comparison.
+//
+// A native worker row has no socket in either layout (sidecar isolation
+// D2, D8): for it the database path alone decides, and a sidecar row that
+// carries a socket is not a shape this platform produced, so it is not
+// nested here any more than Classify calls it nested.
func nestedRoute(dataDir string, rt registry.Route) bool {
+ if rt.IsSidecar() {
+ return rt.Socket == "" && rt.DBPath == provision.NestedDBPath(dataDir, rt.Host)
+ }
return rt.DBPath == provision.NestedDBPath(dataDir, rt.Host) && rt.Socket == provision.NestedSocket(rt.Host)
}
// ConfigEnvPath is where a route's rendered bundle lands: inside the
-// instance directory for a nested row (isolated-exec design §7), flat for
-// everything else, including opted-in addr routes whose supervisor reads
-// the flat path.
+// instance directory for a nested row (isolated-exec design §7), a nested
+// worker row included (sidecar isolation D8), flat for everything else,
+// including opted-in addr routes whose supervisor reads the flat path.
func ConfigEnvPath(dataDir string, rt registry.Route) string {
if nestedRoute(dataDir, rt) {
return filepath.Join(provision.InstanceDir(dataDir, rt.Host), rt.Host+configEnvSuffix)
diff --git a/internal/appconfig/syncexec_test.go b/internal/appconfig/syncexec_test.go
index ef8cbc533..e320147b8 100644
--- a/internal/appconfig/syncexec_test.go
+++ b/internal/appconfig/syncexec_test.go
@@ -2735,3 +2735,86 @@ func TestSyncExecWithRouteFilter(t *testing.T) {
t.Fatalf("bounced %v, want only shop.example.com", bounced)
}
}
+
+// TestConfigEnvPathNestedWorker pins sidecar isolation D8's config rule: a
+// native worker row has no socket in either layout, so its rendered bundle
+// lands in its instance directory when its database path alone is the
+// nested default, flat when it is the flat default, and flat (never
+// nested) for a sidecar row carrying a socket, which is not a shape the
+// platform produced and which Classify calls foreign.
+func TestConfigEnvPathNestedWorker(t *testing.T) {
+ dd := t.TempDir()
+ host := "bot.shop.abc123" + provision.SidecarSuffix
+ nested := filepath.Join(provision.InstanceDir(dd, host), host+".config.env")
+ flat := filepath.Join(dd, host+".config.env")
+ for _, c := range []struct {
+ name string
+ rt registry.Route
+ want string
+ }{
+ {"nested worker", registry.Route{Host: host, Kind: "sidecar", DBPath: provision.NestedDBPath(dd, host)}, nested},
+ {"flat worker", registry.Route{Host: host, Kind: "sidecar", DBPath: provision.DefaultDBPath(dd, host)}, flat},
+ {"worker with no database path", registry.Route{Host: host, Kind: "sidecar"}, flat},
+ {"sidecar row carrying a socket at the nested database", registry.Route{Host: host, Kind: "sidecar", DBPath: provision.NestedDBPath(dd, host), Socket: provision.NestedSocket(host)}, flat},
+ {"instance row at the nested database alone is not nested", registry.Route{Host: host, Kind: "instance", DBPath: provision.NestedDBPath(dd, host)}, flat},
+ } {
+ if got := ConfigEnvPath(dd, c.rt); got != c.want {
+ t.Errorf("%s: ConfigEnvPath = %q, want %q", c.name, got, c.want)
+ }
+ }
+}
+
+// TestSyncExecNestedWorkerBundleAndFencedSkip is D8's config rule through
+// SyncExec itself: a nested worker row's bundle is written inside its
+// instance directory and nowhere flat, and the fenced skip (the layout
+// pass's journal predicate) covers a worker host exactly as it covers an
+// instance host, writing nothing at either path for it.
+func TestSyncExecNestedWorkerBundleAndFencedSkip(t *testing.T) {
+ ctx, s, reg, dd, priv, _ := execKit(t)
+ pk, err := ParsePrivateKey(priv, "test")
+ if err != nil {
+ t.Fatalf("ParsePrivateKey: %v", err)
+ }
+ if err := SetEnv(ctx, s, "bab", "eleven", map[string]string{"LOG_LEVEL": "debug"}); err != nil {
+ t.Fatalf("SetEnv: %v", err)
+ }
+ worker := "bot.eleven.abc123" + provision.SidecarSuffix
+ fenced := "cron.eleven.abc123" + provision.SidecarSuffix
+ for _, h := range []string{worker, fenced} {
+ if err := os.MkdirAll(provision.InstanceDir(dd, h), 0o755); err != nil {
+ t.Fatalf("mkdir instance dir: %v", err)
+ }
+ if err := reg.Upsert(registry.Route{Host: h, Kind: "sidecar", App: "eleven", Account: "bab",
+ Status: "ready", Args: `["work"]`, DBPath: provision.NestedDBPath(dd, h)}); err != nil {
+ t.Fatalf("Upsert: %v", err)
+ }
+ }
+ var bounced []string
+ bounce := func(h string) error { bounced = append(bounced, h); return nil }
+ skip := func(h string) bool { return h == fenced }
+ if err := SyncExec(ctx, reg, s, pk, dd, bounce, "", nil, SkipFenced(skip)); err != nil {
+ t.Fatalf("SyncExec: %v", err)
+ }
+ nestedPath := filepath.Join(provision.InstanceDir(dd, worker), worker+".config.env")
+ got, err := os.ReadFile(nestedPath)
+ if err != nil || string(got) != "LOG_LEVEL=debug\n" {
+ t.Fatalf("nested worker bundle = %q, %v", got, err)
+ }
+ if _, err := os.Stat(filepath.Join(dd, worker+".config.env")); !os.IsNotExist(err) {
+ t.Fatalf("a nested worker's config also landed at the flat path, stat err = %v", err)
+ }
+ for _, path := range []string{
+ filepath.Join(provision.InstanceDir(dd, fenced), fenced+".config.env"),
+ filepath.Join(dd, fenced+".config.env"),
+ } {
+ if _, err := os.Lstat(path); !os.IsNotExist(err) {
+ t.Errorf("a fenced worker's bundle was written at %s (err %v)", path, err)
+ }
+ }
+ // A sidecar is never bounced by this pass (inScope's doc: nothing dials
+ // a worker back up; carlos restart pairs the stop with a wake), nested
+ // or flat.
+ if len(bounced) != 0 {
+ t.Fatalf("bounced = %v, want nothing bounced for worker rows", bounced)
+ }
+}