//go:build linux package host import ( "context" "errors" "os" "path/filepath" "testing" "time" "github.com/carlosframework/platform/internal/instancectl" ) func TestServingParkMaintenanceExpiryAndPressure(t *testing.T) { for _, mode := range []string{"healthy", "expiry", "pressure", "unknown", "incomplete", "replacement"} { t.Run(mode, func(t *testing.T) { f := newServingParkDiskFixture(t) a, receipts := f.evidence(1, true, "") f.put("", "retained database") if _, err := f.park(a, receipts); err != nil { t.Fatal(err) } if mode != "incomplete" { f.complete(a) } if mode == "expiry" { f.now = f.now.Add(7 * 24 * time.Hour) } if mode == "replacement" { path := f.path(a, "") if err := os.Rename(path, path+"-original"); err != nil { t.Fatal(err) } if err := os.WriteFile(path, []byte("foreign"), 0600); err != nil { t.Fatal(err) } } calls := 0 room := func() error { calls++ if mode == "unknown" { return os.ErrPermission } if mode == "pressure" || mode == "incomplete" || mode == "replacement" { return instancectl.ErrDiskFull } return nil } err := f.ledger.maintainParks(t.Context(), room) if mode == "unknown" || mode == "replacement" { if err == nil { t.Fatal("expected refusal") } } else if err != nil { t.Fatal(err) } r, err := f.ledger.read(a.ID) if err != nil { t.Fatal(err) } want := mode == "expiry" || mode == "pressure" if r.Closed != want { t.Fatalf("closed=%v want=%v", r.Closed, want) } if mode == "pressure" && (r.Cause != "pressure" || calls < 2) { t.Fatal("pressure not remeasured", r.Cause, calls) } if !want { if _, err := os.Stat(f.path(a, "")); err != nil { t.Fatal("retained bytes lost", err) } } }) } } func TestServingParkMaintenanceCancellation(t *testing.T) { f := newServingParkDiskFixture(t) ctx, cancel := context.WithCancel(t.Context()) cancel() if err := f.ledger.maintainParks(ctx, func() error { t.Fatal("cancelled pass measured disk"); return nil }); !errors.Is(err, context.Canceled) { t.Fatal(err) } } type servingMaintenanceWait struct { servingParkAuthority entered chan struct{} } func (s servingMaintenanceWait) maintainServingParks(ctx context.Context, _ string) (string, error) { close(s.entered) <-ctx.Done() return "", ctx.Err() } func TestServingParkMaintenanceShutdownJoins(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() s := &servingCloseStore{} entered := make(chan struct{}) p := &servingParkRuntime{authority: servingMaintenanceWait{entered: entered}, state: s, ctx: ctx, cancel: cancel, idle: make(chan struct{})} p.Start(t.Context()) select { case <-entered: case <-time.After(time.Second): t.Fatal("maintenance did not start") } closeCtx, done := context.WithTimeout(t.Context(), time.Second) defer done() if err := p.Close(closeCtx); err != nil || !s.closed.Load() { t.Fatal("maintenance did not join", err) } } func TestServingCapacityFromAgentIdentity(t *testing.T) { data := t.TempDir() if err := os.Mkdir(filepath.Join(data, "instances"), 0700); err != nil { t.Fatal(err) } if err := claimServingRoom(data); err != nil { t.Fatal(err) } other := t.TempDir() if err := os.Symlink(other, filepath.Join(data, "alias")); err != nil { t.Fatal(err) } if err := claimServingRoom(filepath.Join(data, "alias")); err == nil { t.Fatal("symlink data root accepted") } }