//go:build linux && rootrunner package host import ( "context" "encoding/json" "os" "path/filepath" "testing" "time" "github.com/carlosframework/platform/internal/rootparticipation" "github.com/carlosframework/platform/internal/rootstate" ) func TestPlan14RootMigrationLedgerExclusionRootRunner(t *testing.T) { dir := generationRootRunnerDir(t) state, err := rootstate.Open(filepath.Join(dir, "exec"), true, false) if err != nil { t.Fatal(err) } defer state.Close() m, _, _ := migrationFixture(t) m.execution = state if err := m.run(t.Context()); err != nil { t.Fatal(err) } c, err := (rootparticipation.Ledger{State: state}).Census() if err != nil { t.Fatal(err) } c.Revision = 42 c.LaunchRevision = 17 raw, _ := json.Marshal(c) if err := state.Write("census.json", raw); err != nil { t.Fatal(err) } if err := m.run(t.Context()); err != nil { t.Fatal(err) } after, _ := state.Read("census.json") if string(raw) != string(after) { t.Fatal("completed revision reset") } unlock, err := state.Lock(t.Context(), "transaction.lock") if err != nil { t.Fatal(err) } done := make(chan error, 1) go func() { done <- m.census(true) }() select { case err := <-done: unlock() t.Fatal("migration bypassed ledger exclusion", err) case <-time.After(50 * time.Millisecond): } c.Pending = "acquisition" raw, _ = json.Marshal(c) if err := state.Write("census.json", raw); err != nil { unlock() t.Fatal(err) } unlock() select { case err := <-done: if err == nil { t.Fatal("migration erased pending acquisition") } case <-time.After(3 * time.Second): t.Fatal("migration lock stuck") } after, _ = state.Read("census.json") if string(raw) != string(after) { t.Fatal("pending census changed") } } func TestPlan14RootMigrationUnarmedRootRunner(t *testing.T) { opt := generationRootRunnerDir(t) m, _, _ := migrationFixture(t) m.guardReady = func(context.Context) error { return verifyRootMigrationFloors(opt, rootFloorIdentity{"deploy", "box", "en"}) } if m.run(t.Context()) == nil { t.Fatal("missing signed guard accepted") } if _, err := m.execution.Read("census.json"); !os.IsNotExist(err) { t.Fatal("census preceded guard", err) } } func TestPlan14ExecutionHostileCensusRootRunner(t *testing.T) { dir := generationRootRunnerDir(t) parent := filepath.Join(dir, "authority") path := filepath.Join(parent, "exec") s, err := rootstate.Open(path, true, false) if err != nil { t.Fatal(err) } c := rootparticipation.Census{Schema: 1, Box: "box", Enrollment: "en", Migration: 1, Revision: 1, LaunchRevision: 1, Complete: true} raw, _ := json.Marshal(c) if err := s.Write("census.json", raw); err != nil { t.Fatal(err) } s.Close() check := func() error { s, e := rootstate.Open(path, false, false) if e != nil { return e } defer s.Close() return (rootparticipation.Ledger{State: s, VerifyLaunches: func(context.Context, uint64) error { return nil }}).Idle(t.Context()) } if err := check(); err != nil { t.Fatal("positive", err) } for _, hostile := range []string{parent, filepath.Join(path, "census.json")} { if err := os.Chown(hostile, 65534, 65534); err != nil { t.Fatal(err) } if check() == nil { t.Fatal("hostile authority granted idle") } if err := os.Chown(hostile, 0, 0); err != nil { t.Fatal(err) } if err := check(); err != nil { t.Fatal("restored root authority refused", err) } } }