package host import ( "context" "encoding/json" "errors" "os" "path/filepath" "testing" "time" "github.com/carlosframework/platform/internal/activator" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/provision" "github.com/carlosframework/platform/internal/registry" "github.com/carlosframework/platform/internal/release" ) func plan14ProofFixture(t *testing.T) (*proofRootEnv, registry.Route, provision.Record, *proofBindingSource) { t.Helper() e := newProofRootEnv(t, provePolicy) rec := provision.Record{Schema: 1, Host: observedHost, Account: "bab", App: "console", Kind: "instance", Unit: "console.service", Addr: "127.0.0.1:4000", Channel: "canary/console", Pin: "v1", Health: &provision.Health{Path: "/ready"}} plan14PutRecord(t, e.store, rec) health, err := json.Marshal(rec.Health.WithDefaults()) if err != nil { t.Fatal(err) } rt := registry.Route{Host: rec.Host, Account: rec.Account, App: rec.App, Kind: rec.Kind, Unit: rec.Unit, Addr: rec.Addr, Health: string(health), Channel: rec.Channel, Status: "ready"} if err := e.root.reg.Upsert(rt); err != nil { t.Fatal(err) } unitsDir := t.TempDir() fragment := filepath.Join(unitsDir, rec.Unit) if err := os.WriteFile(fragment, []byte("[Service]\nExecStart="+release.LiveLink(e.root.obs.optDir, rec.Host)+"\n"), 0644); err != nil { t.Fatal(err) } f := e.root.obs.sys.(*fakeUnits) f.shows[0].Name, f.shows[0].FragmentPath = rec.Unit, fragment f.exe = map[int]string{7: release.VersionPath(e.root.obs.optDir, rec.Account, rec.Pin, rec.App)} source := &proofBindingSource{store: e.store, sys: f, optDir: e.root.obs.optDir, openFile: os.Open, unitDirs: []string{unitsDir}} e.root.obs.resolve = source.resolve e.root.obs.ready = func(registry.Route) func() error { return func() error { return nil } } return e, rt, rec, source } func plan14PutRecord(t *testing.T, s release.Store, rec provision.Record) { t.Helper() key := provision.RecordKey(rec.Account, rec.App, rec.Host) _, etag, err := s.Get(context.Background(), key) if err != nil && !errors.Is(err, os.ErrNotExist) { t.Fatal(err) } body, err := json.Marshal(rec) if err != nil { t.Fatal(err) } if _, err := s.Put(context.Background(), key, body, etag); err != nil { t.Fatal(err) } } func TestPlan14ProofObservationRejectsForgedRegistry(t *testing.T) { t.Parallel() mutations := map[string]func(*registry.Route){ "account": func(r *registry.Route) { r.Account = "bap" }, "app": func(r *registry.Route) { r.App = "other" }, "host": func(r *registry.Route) { r.Host = "other.example.com" }, "unit": func(r *registry.Route) { r.Unit = "other.service" }, "socket": func(r *registry.Route) { r.Socket = "/run/forged.sock" }, "addr": func(r *registry.Route) { r.Addr = "" }, "addrs": func(r *registry.Route) { r.Addrs = "127.0.0.1:1" }, "health nil": func(r *registry.Route) { r.Health = "" }, "health disabled": func(r *registry.Route) { r.Health = "null" }, "health alternate": func(r *registry.Route) { r.Health = `{"path":"/forged"}` }, "all upstreams empty": func(r *registry.Route) { r.Addr, r.Addrs, r.Socket, r.Health = "", "", "", "" }, } for name, mutate := range mutations { t.Run(name, func(t *testing.T) { e, rt, _, _ := plan14ProofFixture(t) mutate(&rt) if err := e.root.reg.Delete(observedHost); err != nil { t.Fatal(err) } if err := e.root.reg.Upsert(rt); err != nil { t.Fatal(err) } if got := e.root.observeAndRecord(rt, 15*time.Minute); got.err == nil { t.Fatal("forged route recorded as healthy") } e.root.run(context.Background()) if err := e.root.obs.recordSettled(e.bake)(rt); err == nil { t.Fatal("forged settled route accepted") } keys, err := e.store.List(context.Background(), "apps/") if err != nil || len(keys) != 1 { t.Fatalf("unexpected proof writes: %v, %v", keys, err) } for _, acct := range []string{"bab", "bap"} { for _, app := range []string{"console", "other"} { if got := release.ReadHealthy(e.bake, acct, app); len(got) != 0 { t.Fatalf("forged health %s/%s: %+v", acct, app, got) } } } }) } t.Run("canonical failing readiness", func(t *testing.T) { e, rt, _, _ := plan14ProofFixture(t) e.root.obs.ready = func(r registry.Route) func() error { if r.Addr == "" { return nil } return func() error { return os.ErrPermission } } e.root.run(context.Background()) if e.root.obs.recordSettled(e.bake)(rt) == nil || len(release.ReadHealthy(e.bake, "bab", "console")) != 0 || e.proof(t) != nil { t.Fatal("failed canonical readiness authorized health") } }) t.Run("canonical success", func(t *testing.T) { e, rt, _, _ := plan14ProofFixture(t) e.root.run(context.Background()) if err := e.root.obs.recordSettled(e.bake)(rt); err != nil { t.Fatal(err) } if len(release.ReadHealthy(e.bake, "bab", "console")) != 1 || e.proof(t) == nil { t.Fatal("canonical healthy build was not recorded and proved") } }) } func TestPlan14ProofBindingChangesAfterObservation(t *testing.T) { t.Parallel() e, _, rec, _ := plan14ProofFixture(t) obs := e.root.recordLive(context.Background()) rec.Health.Path = "/changed" plan14PutRecord(t, e.store, rec) e.root.mintAndLog(context.Background(), obs) if e.proof(t) != nil { t.Fatal("stale binding minted a proof") } rt, err := e.root.reg.Get(rec.Host) if err != nil { t.Fatal(err) } health, err := json.Marshal(rec.Health.WithDefaults()) if err != nil { t.Fatal(err) } rt.Health = string(health) if err := e.root.reg.Upsert(*rt); err != nil { t.Fatal(err) } e.root.run(context.Background()) if e.proof(t) == nil { t.Fatal("new successful observation did not mint") } t.Run("etag only", func(t *testing.T) { e, _, rec, _ := plan14ProofFixture(t) obs := e.root.recordLive(context.Background()) rec.RecordSeq++ plan14PutRecord(t, e.store, rec) e.root.obs.ready = func(registry.Route) func() error { return func() error { return os.ErrPermission } } e.root.mintAndLog(context.Background(), obs) if e.proof(t) != nil { t.Fatal("changed ETag reused cached observation") } }) } func TestPlan14OrdinaryInstanceBinding(t *testing.T) { t.Parallel() e, rt, rec, source := plan14ProofFixture(t) rec.Addr, rec.Unit = "", "" plan14PutRecord(t, e.store, rec) rt.Addr, rt.Unit, rt.Socket, rt.Backing = "", "", provision.DefaultSocket(rec.Host), registry.BackingUnit if err := e.root.reg.Upsert(rt); err != nil { t.Fatal(err) } u := source.sys.(*fakeUnits) u.shows[0].Name = activator.UnitName(rec.Host) if got := e.root.observeAndRecord(rt, 0); got.err == nil { t.Fatal("ordinary record recorded root health") } if got := release.ReadHealthy(e.bake, rt.Account, rt.App); len(got) != 0 { t.Fatalf("ordinary health: %+v", got) } rt.Unit = "other.service" if got := e.root.observeAndRecord(rt, 0); got.err == nil { t.Fatal("forged ordinary unit accepted") } rec.Unit = "other.service" plan14PutRecord(t, e.store, rec) if _, err := source.forHost(context.Background(), rt.Host); err == nil { t.Fatal("record overrides ordinary naming") } } func TestPlan14RootRestartDispatchFromRecord(t *testing.T) { t.Parallel() e, rt, rec, source := plan14ProofFixture(t) rec.Unit = "" rt.Unit = "" plan14PutRecord(t, e.store, rec) needs, err := source.needsRestart(context.Background(), rt) if err != nil || needs { t.Fatalf("unmanaged proxy restart: %v %v", needs, err) } rec.Unit = "console.service" plan14PutRecord(t, e.store, rec) if _, err := source.needsRestart(context.Background(), rt); err == nil { t.Fatal("cleared candidate unit suppressed authoritative restart") } rt.Unit = rec.Unit needs, err = source.needsRestart(context.Background(), rt) if err != nil || !needs { t.Fatalf("named service skipped: %v %v", needs, err) } } func TestPlan14ProofClaimBinding(t *testing.T) { t.Parallel() e, rt, rec, s := plan14ProofFixture(t) observed := hostclaim.Observed{Host: rec.Host, ETag: "claim-one", Claim: &hostclaim.Claim{Host: rec.Host, Holder: &hostclaim.Holder{Key: provision.RecordKey(rec.Account, rec.App, rec.Host), Account: rec.Account, App: rec.App, Incarnation: "inc", Stamp: 1, Session: 1, WriterBox: "box"}, Op: hostclaim.Op{Kind: hostclaim.KindServing, Exec: "box"}}} s.claim = func(context.Context, string) (hostclaim.Observed, error) { return observed, nil } s.box = func(context.Context) (string, error) { return "box", nil } cached := e.root.recordLive(context.Background()) if cached.byHost[rt.Host].err != nil { t.Fatal(cached.byHost[rt.Host].err) } observed.Claim.Holder.App = "other" e.root.mintAndLog(context.Background(), cached) if e.proof(t) != nil { t.Fatal("changed holder minted proof") } if _, err := s.forHost(context.Background(), rt.Host); err == nil { t.Fatal("cross-app holder accepted") } observed.Claim.Holder.App = rec.App observed.Claim.Holder.WriterBox = "other-box" if _, err := s.forHost(context.Background(), rt.Host); err == nil { t.Fatal("foreign writer accepted") } }