package hostfloor import ( "context" "encoding/json" "os" "path/filepath" "sync" "testing" "github.com/carlosframework/platform/internal/rootparticipation" ) type floorParticipationDisk struct { dir string mu sync.Mutex beforeWrite func(string) } func (d *floorParticipationDisk) Read(n string) ([]byte, error) { return os.ReadFile(filepath.Join(d.dir, n)) } func (d *floorParticipationDisk) Write(n string, b []byte) error { if d.beforeWrite != nil { d.beforeWrite(n) } return Durable(filepath.Join(d.dir, n), b, 0600) } func (d *floorParticipationDisk) Remove(n string) error { return RemoveDurable(filepath.Join(d.dir, n)) } func (d *floorParticipationDisk) Names() ([]string, error) { entries, err := os.ReadDir(d.dir) var out []string for _, e := range entries { out = append(out, e.Name()) } return out, err } func (d *floorParticipationDisk) Lock(context.Context, string) (func(), error) { d.mu.Lock() return d.mu.Unlock, nil } func (d *floorParticipationDisk) Close() error { return nil } func floorParticipationFixture(t *testing.T) *rootparticipation.Ledger { t.Helper() d := &floorParticipationDisk{dir: t.TempDir()} raw, _ := json.Marshal(rootparticipation.Census{Schema: 1, Box: "box", Enrollment: "en", Migration: 1, Revision: 1, LaunchRevision: 1, Complete: true}) if err := d.Write("census.json", raw); err != nil { t.Fatal(err) } return &rootparticipation.Ledger{State: d, VerifyLaunches: func(context.Context, uint64) error { return nil }, ExcludeLaunches: func(ctx context.Context, fn func(context.Context) error) error { return fn(ctx) }} } func TestPlan14ExecutionFloorUsesRootCensus(t *testing.T) { d := Disk{Dir: t.TempDir(), Participation: floorParticipationFixture(t)} data := t.TempDir() if err := d.Participation.State.Write("shop.example.json", []byte("{\"Schema\":1,\"Status\":\"lost\"}")); err != nil { t.Fatal(err) } if err := d.Begin(t.Context(), "update", data); err == nil { t.Fatal("missing agent directory cleared root obligation") } if err := d.Participation.State.Remove("shop.example.json"); err != nil { t.Fatal(err) } d.Participation.VerifyLaunches = nil if err := d.Begin(t.Context(), "update", data); err == nil { t.Fatal("missing kernel completeness permitted update") } d.Participation.VerifyLaunches = func(context.Context, uint64) error { return nil } if err := d.Begin(t.Context(), "update", data); err != nil { t.Fatal(err) } if err := d.Participation.Acquire(t.Context(), func() error { return nil }); err == nil { t.Fatal("floor update admitted acquisition") } } func TestPlan14ExecutionFloorUpdateRace(t *testing.T) { for _, first := range []string{"acquisition", "update"} { t.Run(first, func(t *testing.T) { d := Disk{Dir: t.TempDir(), Participation: floorParticipationFixture(t)} state := d.Participation.State.(*floorParticipationDisk) entered, release := make(chan struct{}), make(chan struct{}) target := "census.json" if first == "update" { target = "update.json" } var once sync.Once state.beforeWrite = func(name string) { if name == target { once.Do(func() { close(entered); <-release }) } } acquisition := func() error { return d.Participation.Acquire(t.Context(), func() error { return state.Write("shop.example.json", []byte(`{"Schema":1,"Status":"pending"}`)) }) } data := t.TempDir() update := func() error { return d.Begin(t.Context(), "update", data) } a, b := acquisition, update if first == "update" { a, b = update, acquisition } one, two := make(chan error, 1), make(chan error, 1) go func() { one <- a() }() <-entered go func() { two <- b() }() mirror := filepath.Join(data, ".hostexec") if err := os.Mkdir(mirror, 0700); err != nil { t.Fatal(err) } if err := os.Remove(mirror); err != nil { t.Fatal(err) } close(release) if err := <-one; err != nil { t.Fatal(err) } if err := <-two; err == nil { t.Fatal("floor update and acquisition both admitted") } }) } }