| index 0bf1b7d3d..76265ecf1 100644 |
| --- a/internal/appconfig/instances_test.go |
| +++ b/internal/appconfig/instances_test.go |
| @@ -657,3 +657,102 @@ func TestSyncStillLeavesUnconfiguredAppsAlone(t *testing.T) { |
| t.Fatalf("an unconfigured app's file moved: %q %v", cur, err) |
| } |
| } |
| + |
| +// The exec half of the same delivery: one activator-spawned instance gets |
| +// its own value, its sibling does not, and the listing is paid for once. |
| +func TestSyncExecRendersTheInstanceOverlayForItsOwnRouteOnly(t *testing.T) { |
| + ctx, base, reg, dd, privB64, _ := execKit(t) |
| + priv, err := ParsePrivateKey(privB64, "test") |
| + if err != nil { |
| + t.Fatalf("ParsePrivateKey: %v", err) |
| + } |
| + if err := SetEnv(ctx, base, "bab", "titogo", map[string]string{"APP_WIDE": "yes", "TENANT": "none"}); err != nil { |
| + t.Fatalf("SetEnv: %v", err) |
| + } |
| + if _, _, err := ApplyInstance(ctx, base, "bab", "titogo", "book.example.com", |
| + map[string]Var{"TENANT": {Kind: "env", Value: "book"}}, nil); err != nil { |
| + t.Fatalf("ApplyInstance: %v", err) |
| + } |
| + for _, host := range []string{"book.example.com", "cart.example.com"} { |
| + if err := reg.Upsert(registry.Route{ |
| + Host: host, Kind: "instance", App: "titogo", Account: "bab", Status: "ready", |
| + Socket: "/run/carlos/" + host + ".sock", |
| + }); err != nil { |
| + t.Fatalf("Upsert %s: %v", host, err) |
| + } |
| + } |
| + |
| + s := newCountingStore(base) |
| + if err := SyncExec(ctx, reg, s, priv, dd, func(string) error { return nil }, "", nil); err != nil { |
| + t.Fatalf("SyncExec: %v", err) |
| + } |
| + book, err := os.ReadFile(filepath.Join(dd, "book.example.com.config.env")) |
| + if err != nil { |
| + t.Fatalf("read book config: %v", err) |
| + } |
| + if !bytes.Contains(book, []byte("TENANT=book\n")) || !bytes.Contains(book, []byte("APP_WIDE=yes\n")) { |
| + t.Fatalf("the overlaid route's config file is wrong:\n%s", book) |
| + } |
| + cart, err := os.ReadFile(filepath.Join(dd, "cart.example.com.config.env")) |
| + if err != nil { |
| + t.Fatalf("read cart config: %v", err) |
| + } |
| + if bytes.Contains(cart, []byte("book")) || !bytes.Contains(cart, []byte("TENANT=none\n")) { |
| + t.Fatalf("a sibling saw another instance's config:\n%s", cart) |
| + } |
| + if got := s.n(s.lists, InstancesConfigPrefix("bab", "titogo")); got != 1 { |
| + t.Fatalf("instance overlay listings = %d, want exactly 1 for two routes of one app", got) |
| + } |
| + if got := s.n(s.gets, InstanceConfigKey("bab", "titogo", "cart.example.com")); got != 0 { |
| + t.Fatalf("read an overlay for a host the listing did not name (%d times)", got) |
| + } |
| +} |
| + |
| +// exec_delivery is a BUNDLE-level flag and the instance layer is not one of |
| +// the bundles it is read from (the ruling at appconfig.go:66-71). A TCP-addr |
| +// route whose app has never opted in gets no file, whatever its overlay |
| +// says — a per-host delivery flag is exactly the granularity that ruling |
| +// refused, and honouring one here would be the config-that-lies hazard it |
| +// names. |
| +func TestSyncExecNeverReadsExecDeliveryFromTheInstanceLayer(t *testing.T) { |
| + ctx, s, reg, dd, privB64, _ := execKit(t) |
| + priv, err := ParsePrivateKey(privB64, "test") |
| + if err != nil { |
| + t.Fatalf("ParsePrivateKey: %v", err) |
| + } |
| + if err := SetEnv(ctx, s, "bab", "titogo", map[string]string{"APP_WIDE": "yes"}); err != nil { |
| + t.Fatalf("SetEnv: %v", err) |
| + } |
| + putInstanceRaw(t, ctx, s, "bab", "titogo", "router.example.com", &Manifest{ |
| + Schema: 1, App: "titogo", Vars: map[string]Var{"TENANT": {Kind: "env", Value: "book"}}, |
| + ExecDelivery: ExecDeliveryFile}) |
| + if err := reg.Upsert(registry.Route{ |
| + Host: "router.example.com", Kind: "instance", App: "titogo", Account: "bab", |
| + Status: "ready", Addr: "127.0.0.1:8080", |
| + }); err != nil { |
| + t.Fatalf("Upsert: %v", err) |
| + } |
| + if err := SyncExec(ctx, reg, s, priv, dd, func(string) error { return nil }, "", nil); err != nil { |
| + t.Fatalf("SyncExec: %v", err) |
| + } |
| + if _, err := os.Stat(filepath.Join(dd, "router.example.com.config.env")); !os.IsNotExist(err) { |
| + t.Fatalf("an instance overlay opted a route into file delivery: %v", err) |
| + } |
| + |
| + // Opt the APP in, and the same route gets the same overlay merged in the |
| + // ordinary way — the flag's door is the bundle, the overlay's payload is |
| + // vars. |
| + if err := SetExecDelivery(ctx, s, "bab", "titogo", ExecDeliveryFile); err != nil { |
| + t.Fatalf("SetExecDelivery: %v", err) |
| + } |
| + if err := SyncExec(ctx, reg, s, priv, dd, func(string) error { return nil }, "", nil); err != nil { |
| + t.Fatalf("SyncExec (opted in): %v", err) |
| + } |
| + cur, err := os.ReadFile(filepath.Join(dd, "router.example.com.config.env")) |
| + if err != nil { |
| + t.Fatalf("read router config: %v", err) |
| + } |
| + if !bytes.Contains(cur, []byte("TENANT=book\n")) { |
| + t.Fatalf("the overlay's vars did not land once the app opted in:\n%s", cur) |
| + } |
| +} |