//go:build unix package hostguard import ( "bytes" "context" "errors" "github.com/carlosframework/platform/internal/hostclaim" "testing" "time" ) func TestPlan14Fix2LegacyLockCannotStallRoot(t *testing.T) { f, target, captured := fenceFixture(t) f.obs.Claim.Holder.Account, f.obs.Claim.Holder.App = "bab", "console" f.g.Root = NewRootAuthority(&rootTestStore{dir: t.TempDir()}) in := RootInstallation{Host: target.Host, Incarnation: captured.Incarnation, Stamp: captured.Stamp, Account: "bab", App: "console", Record: "record", Box: target.Box, Enrollment: target.Enrollment, Migration: 1} if err := f.g.EstablishRoot(t.Context(), in, allow); err != nil { t.Fatal(err) } unlock, err := acquire(t.Context(), f.g.LockDir, target.Host) if err != nil { t.Fatal(err) } defer func() { if unlock != nil { unlock() } }() before, err := f.g.Root.store.Read(target.Host + ".json") if err != nil { t.Fatal(err) } ctx, cancel := context.WithTimeout(t.Context(), time.Second) defer cancel() if _, err := f.g.PendingFences(ctx); err != nil { t.Fatal("read waited for legacy lock", err) } if _, err := f.g.BeginFence(ctx, target); err == nil { t.Fatal("mutation crossed held legacy lock") } r, err := f.g.Root.read(target.Host) if err != nil || r.quarantined { t.Fatalf("honest contention quarantined: %+v %v", r, err) } after, err := f.g.Root.store.Read(target.Host + ".json") if err != nil || !bytes.Equal(before, after) { t.Fatal("contention wrote durable state", err) } if _, err := f.g.Root.View(ctx, target.Host, captured); err != nil { t.Fatal("contention fenced running installation", err) } unlock() unlock = nil if _, err := f.g.BeginFence(ctx, target); err != nil { t.Fatal("released contention did not retry", err) } } func TestPlan14Fix2PendingIntentCanBeAbandoned(t *testing.T) { f, target, captured := fenceFixture(t) f.obs.Claim.Holder.Account, f.obs.Claim.Holder.App = "bab", "console" f.g.Root = NewRootAuthority(&rootTestStore{dir: t.TempDir()}) in := RootInstallation{Host: target.Host, Incarnation: captured.Incarnation, Stamp: captured.Stamp, Account: "bab", App: "console", Record: "record", Box: target.Box, Enrollment: target.Enrollment, Migration: 1, InventoryRevision: 1} intent := []byte("pending") failure := errors.New("commit failed") if err := f.g.CommitRootInventory(t.Context(), in, intent, allow, nil, func(RootInstallation) error { return failure }); !errors.Is(err, failure) { t.Fatal(err) } if err := f.g.AbandonRootInventory(t.Context(), target.Host, intent, func(*hostclaim.Claim) bool { return false }, unlanded); !errors.Is(err, ErrFenced) { t.Fatal("abandoned committable intent", err) } if err := f.g.AbandonRootInventory(t.Context(), target.Host, intent, func(*hostclaim.Claim) bool { return true }, unlanded); err != nil { t.Fatal(err) } pending, err := f.g.Root.PendingInstallations() if err != nil || len(pending) != 0 { t.Fatal(pending, err) } if _, err := f.g.Root.View(t.Context(), target.Host, captured); !errors.Is(err, ErrFenced) { t.Fatal("abandon discarded deny", err) } if err := f.g.CommitRootInventory(t.Context(), in, []byte("new"), allow, nil, func(RootInstallation) error { return nil }); err != nil { t.Fatal("fresh intent refused", err) } if _, err := f.g.Root.View(t.Context(), target.Host, captured); err != nil { t.Fatal(err) } } func TestPlan14Fix2SupersededColdBarrierAbandoned(t *testing.T) { f, prior, captured := fenceFixture(t) f.obs.Claim.Holder.Account, f.obs.Claim.Holder.App = "bab", "console" f.g.Root = NewRootAuthority(&rootTestStore{dir: t.TempDir()}) in := RootInstallation{Host: prior.Host, Incarnation: captured.Incarnation, Stamp: captured.Stamp, Account: "bab", App: "console", Record: "record", Box: prior.Box, Enrollment: prior.Enrollment, Migration: 1} if err := f.g.EstablishRoot(t.Context(), in, allow); err != nil { t.Fatal(err) } f.obs.Claim.Op.Kind = hostclaim.KindMoving if _, err := f.g.BeginFence(t.Context(), prior); err != nil { t.Fatal(err) } if err := f.g.CompleteFence(t.Context(), prior, []byte("receipt"), time.Now(), func(context.Context, FenceTarget, []byte, time.Time) error { return nil }); err != nil { t.Fatal(err) } barrier := prior barrier.FenceID = "barrier" barrier.Stamp = 17 barrier.Session = 17 f.obs.Claim.Holder.Stamp, f.obs.Claim.Holder.Session = 17, 17 if err := f.g.BeginColdHandover(t.Context(), prior, barrier, allow); err != nil { t.Fatal(err) } if err := f.g.AbandonColdHandover(t.Context(), barrier); !errors.Is(err, ErrFenced) { t.Fatal("abandoned live admission", err) } f.obs.Claim.Holder.Stamp, f.obs.Claim.Holder.Session = 18, 18 if err := f.g.AbandonColdHandover(t.Context(), barrier); err != nil { t.Fatal(err) } r, err := f.g.Root.read(prior.Host) if err != nil || r.barrier != nil || len(r.FenceJournal) != 1 || r.FenceJournal[0].Target != prior { t.Fatalf("barrier/receipt: %+v %v", r, err) } }