//go:build linux package host import ( "context" "errors" "fmt" "net" "os" "path/filepath" "reflect" "strings" "testing" "time" "github.com/carlosframework/platform/internal/genselect" "github.com/carlosframework/platform/internal/hostbox" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/hostguard" "github.com/carlosframework/platform/internal/lease" "github.com/carlosframework/platform/internal/lease/leasetest" "github.com/carlosframework/platform/internal/provision" ) func newServingLegacyRootSetup(t *testing.T, empty bool, dataRoot ...string) (*servingRootFixture, ClaimLaunchInputs, ClaimStopCoordinator) { t.Helper() var hosts []string if len(dataRoot) > 1 { hosts = dataRoot[1:] } f, _, _, in := servingRetirementFixture(t, hosts...) if len(dataRoot) != 0 { f.guard.DataDir = dataRoot[0] in.DBPath = provision.NestedDBPath(f.guard.DataDir, f.hostName()) for _, dir := range []string{filepath.Join(f.guard.DataDir, "hostguard"), filepath.Dir(in.DBPath)} { if err := os.MkdirAll(dir, 0700); err != nil { t.Fatal(err) } } } if !empty { _, err := hostclaim.Update(t.Context(), f.claims, f.read(), func(c *hostclaim.Claim) error { c.Holder.WriterBox = "box-1" c.Holder.Location = "instances/" + c.Host return nil }) if err != nil { t.Fatal(err) } } disk := &servingAuthorityDisk{rollbackDisk: &rollbackDisk{dir: t.TempDir()}} roots := &servingAuthorityDisk{rollbackDisk: &rollbackDisk{dir: t.TempDir()}} f.guard.Root = hostguard.NewRootAuthority(roots) journals, err := hostbox.NewJournalService(f.journal) if err != nil { t.Fatal(err) } fx := &servingRootFixture{t: t, f: f, disk: disk, rootDisk: roots, db: in.DBPath, actor: hostbox.Actor{Box: "box-1", Region: "ie", Enrollment: "enrollment", Revision: 7, Binding: "7", Eligible: true}} fs := newServingParkFS(f.guard.DataDir, disk) fs.owner = uint32(os.Geteuid()) fs.mount = func(string) (bool, error) { return true, nil } fx.p = &servingParkRoot{ledger: &servingParkLedger{state: disk, fs: fs, journal: f.journal, now: func() time.Time { return f.now }}, empty: plan9D5Fixture(t).owners} f.peer = StopPeer{UID: 1001, PID: 77, ProcessStart: "edge-start", BootID: "boot", ControllerID: "controller"} coordinator := f.root(stopDrainFunc(func(context.Context, StopRequest, string) (DrainAcknowledgement, error) { t.Fatal("session zero requested drain") return DrainAcknowledgement{}, hostbox.ErrChanged })) coordinator.Kernel = &noProcessKernelFake{} coordinator.Inventory = stopInventoryFunc(func(ctx context.Context, host string) (StopInventory, error) { return journals.Inventory.ReadStopInventory(ctx, host) }) fx.p.root = ClaimRootHTTP{Roots: map[string]string{"data": f.guard.DataDir}, Journals: journals, Stop: ClaimStopHTTP{Coordinator: coordinator, Authenticate: func(context.Context, net.Conn) (StopPeer, error) { return f.peer, nil }}, InventoryActor: func(context.Context) (hostbox.Actor, error) { return fx.actor, nil }, RequireNewWork: func(context.Context) error { return nil }} fx.install(f.read()) coordinator.RetainNoProcess = fx.p.retainLegacyNoProcess if !empty { for suffix, body := range map[string]string{"": "legacy bytes", "-wal": "legacy WAL", "-shm": "legacy SHM"} { if err := os.WriteFile(fx.db+suffix, []byte(body), 0600); err != nil { t.Fatal(err) } } } left, right := net.Pipe() fx.conn = left t.Cleanup(func() { left.Close(); right.Close() }) return fx, in, coordinator } func newServingLegacyRootFixture(t *testing.T, empty bool, beforeAdmission ...func(*servingRootFixture, *ClaimStopCoordinator, StopRequest)) (*servingRootFixture, ClaimLaunchInputs) { t.Helper() fx, in, coordinator := newServingLegacyRootSetup(t, empty) f := fx.f o := f.read() h := o.Claim.Holder req := StopRequest{Region: "ie", Host: o.Host, Incarnation: h.Incarnation, InstallationStamp: h.Stamp, Box: "box-1", Enrollment: "enrollment", BootID: "boot", ControllerID: "controller", ExpectedClaimETag: o.ETag, ExpectedDirectoryRevision: "7", RosterRevision: 7} if _, err := coordinator.FenceAndStop(t.Context(), req); err != nil { t.Fatalf("no-process: %v", err) } for _, fn := range beforeAdmission { fn(fx, &coordinator, req) } admission := hostclaim.Admission{ObservedSession: 0, Box: "box-1", Region: "ie", Mode: "cold", NewGeneration: "G"} if !empty { admission.PredecessorRegion = "ie" } admitted, err := hostclaim.Update(t.Context(), f.claims, f.read(), hostclaim.AdmitWriter(admission)) if err != nil { t.Fatal(err) } fx.admitted = admitted fx.current, err = servingWriterIdentity(admitted, "box-1", "ie") if err != nil { t.Fatal(err) } fx.install(admitted) in.Admission, in.Empty = admission, empty return fx, in } func TestServingLegacyRootAuthority(t *testing.T) { for _, empty := range []bool{false, true} { t.Run(fmt.Sprint(empty), func(t *testing.T) { f, in := newServingLegacyRootFixture(t, empty) r := &claimServingArtifactRetirement{Claims: f.f.claims, Guard: f.f.guard, DataDir: f.f.guard.DataDir, Box: f.actor.Box, Region: f.actor.Region, Root: servingRootClientFixture{f}} work := servingRootWork{f.f.guard, f.admitted} if err := r.BeforeLegacyInstall(t.Context(), f.admitted, in, work); err != nil { t.Fatal(err) } id := servingRetirementID(servingArtifactIdentity{}, f.current, true) record, err := f.p.ledger.read(id) if err != nil || !record.Complete { t.Fatalf("record: %+v %v", record, err) } if record.LegacyEvidence == nil || record.LegacyEvidence.NoProcess == nil || len(record.Receipts) != 0 { t.Fatal("invented fence for session zero") } if (record.Disposition == servingParkArtifactFree) != empty { t.Fatal(record.Disposition) } if !empty { b, err := os.ReadFile(filepath.Join(f.f.guard.DataDir, "instances", ".serving-parked", id, filepath.Base(f.db))) if err != nil || string(b) != "legacy bytes" { t.Fatalf("park %q %v", b, err) } } if err := r.Recover(t.Context(), f.admitted, in); err != nil { t.Fatalf("lost completion reply: %v", err) } if err := work.With(t.Context(), in.Identity, func() error { return r.EnrollLocked(t.Context(), f.admitted, in) }); err != nil { t.Fatal(err) } }) } } func TestServingLegacyRootRefusals(t *testing.T) { for _, attack := range []string{"stop-missing", "stop-bytes", "stop-region", "first-creator", "marker", "half-tags", "tags", "peer", "census", "enrollment", "binding", "source", "main-intention", "empty-database", "empty-wal", "empty-remote", "empty-unavailable", "selector"} { t.Run(attack, func(t *testing.T) { empty := strings.HasPrefix(attack, "empty-") f, _ := newServingLegacyRootFixture(t, empty) requested := servingLegacyPredecessor{Empty: empty} switch attack { case "stop-missing": if err := os.Remove(filepath.Join(f.disk.dir, servingLegacyKey("legacy-stop", f.current))); err != nil { t.Fatal(err) } case "stop-bytes", "stop-region": s, err := f.p.ledger.legacyStop(f.current) if err != nil { t.Fatal(err) } if attack == "stop-bytes" { s.Result.Bytes = []byte("forged") } else { s.Result.Target.Region = "foreign" } if err = f.p.ledger.write(servingLegacyKey("legacy-stop", f.current), s); err != nil { t.Fatal(err) } case "first-creator": requested.Empty = true case "marker": if err := os.WriteFile(f.db+".gen", []byte("foreign"), 0600); err != nil { t.Fatal(err) } case "tags": f.tags(f.current.Stamp) case "half-tags": _, err := hostclaim.Update(t.Context(), f.f.claims, f.f.read(), func(c *hostclaim.Claim) error { rel, _ := filepath.Rel(f.f.guard.DataDir, f.db) c.Inventory = map[string]hostclaim.InventoryEntry{f.actor.Box: {Layout: "nested", Paths: map[string]uint64{rel: f.current.Stamp}}} return nil }) if err != nil { t.Fatal(err) } case "peer": f.p.root.Stop.Authenticate = func(context.Context, net.Conn) (StopPeer, error) { return StopPeer{}, ErrClaimStopIdentity } case "census": f.p.root.RequireNewWork = func(context.Context) error { return hostbox.ErrChanged } case "enrollment": f.actor.Enrollment = "changed" case "binding": f.actor.Binding = "8" case "source": if err := os.Rename(filepath.Dir(f.db), filepath.Dir(f.db)+".saved"); err != nil { t.Fatal(err) } if err := os.Mkdir(filepath.Dir(f.db), 0700); err != nil { t.Fatal(err) } case "main-intention": target := recoveryTuple(f.admitted, claimStopLocal{Actor: f.actor, BootID: "boot", ControllerID: "controller"}) if err := f.f.journal.Prelaunch(t.Context(), hostbox.LaunchInventory{Target: target, ControllerPID: 77, ProcessStart: "edge-start", NoState: true, Groups: []hostbox.LaunchGroup{{RelativePath: "carlos/parent", Device: 1, Inode: 2}}}); err != nil { t.Fatal(err) } if err := f.f.journal.IntendUnit(t.Context(), hostbox.LaunchReservation{ID: "attempt:main", Role: "main", InvocationID: "attempt", Inputs: "digest", Target: target, Group: hostbox.LaunchGroup{RelativePath: "system.slice/carlos-app@shop.service"}, Executable: hostbox.LaunchFile{Device: 1, Inode: 3}}); err != nil { t.Fatal(err) } case "empty-database", "empty-wal": suffix := "" if attack == "empty-wal" { suffix = "-wal" } if err := os.WriteFile(f.db+suffix, []byte("unexpected"), 0600); err != nil { t.Fatal(err) } case "empty-remote": fixture := plan9D5Fixture(t) fixture.bucket.Put("data", f.admitted.Claim.Holder.Location+"/unexpected", []byte("x")) f.p.empty = fixture.owners case "empty-unavailable": f.p.empty = nil case "selector": requested.Session = 1 requested.Generation = "forged" } before, _ := f.disk.Names() if _, err := f.p.parkLegacyServingDatabase(t.Context(), f.conn, f.current, requested); err == nil { t.Fatal("unsafe legacy authority accepted") } after, _ := f.disk.Names() if !reflect.DeepEqual(before, after) { t.Fatalf("refusal effects: %v -> %v", before, after) } if !empty { db := f.db if attack == "source" { db = filepath.Join(filepath.Dir(db)+".saved", filepath.Base(db)) } b, err := os.ReadFile(db) if err != nil || string(b) != "legacy bytes" { t.Fatalf("source %q %v", b, err) } } }) } } func TestServingLegacyRootContinuation(t *testing.T) { for _, point := range []string{"consumption", "intent", "rename", "parked", "retag", "lost-continuation"} { t.Run(point, func(t *testing.T) { f, _ := newServingLegacyRootFixture(t, false) original := f.current fs := f.p.ledger.fs.(*servingParkFS) rename := fs.rename if point == "rename" { once := false fs.rename = func(a int, b string, c int, d string, e uint) error { err := rename(a, b, c, d, e) if err == nil && !once { once = true return os.ErrPermission } return err } } if point == "consumption" || point == "intent" { n := 1 if point == "intent" { n = 2 } f.p.ledger.state = &servingParkFaultStore{Store: f.disk, failAt: n} } a, err := f.p.parkLegacyServingDatabase(t.Context(), f.conn, f.current, servingLegacyPredecessor{}) if point == "consumption" || point == "intent" || point == "rename" { if err == nil { t.Fatal("fault missed") } a.ID = servingRetirementID(servingArtifactIdentity{}, original, true) } else if err != nil { t.Fatal(err) } fs.rename = rename f.p.ledger.state = f.disk before, _ := f.p.ledger.read(a.ID) if point == "retag" || point == "lost-continuation" { f.tags(original.Stamp) } servingStop(t, f.f, f.f.read()) o := f.f.read() later, err := hostclaim.Update(t.Context(), f.f.claims, o, hostclaim.AdmitWriter(hostclaim.Admission{ObservedSession: o.Claim.Holder.Session, ObservedGeneration: o.Claim.Holder.Generation, Box: "box-1", Region: "ie", PredecessorRegion: "ie", Mode: "cold", NewGeneration: "G2"})) if err != nil { t.Fatal(err) } f.current, err = servingWriterIdentity(later, "box-1", "ie") if err != nil { t.Fatal(err) } f.admitted = later f.install(later) f.f.now = f.f.now.Add(time.Hour) if point == "lost-continuation" { f.p.ledger.state = &servingParkFaultStore{Store: f.disk, failAt: 1} if _, err = f.p.parkLegacyServingDatabase(t.Context(), f.conn, f.current, servingLegacyPredecessor{}); err == nil { t.Fatal("lost continuation fault missed") } f.p.ledger.state = f.disk } continued, err := f.p.parkLegacyServingDatabase(t.Context(), f.conn, f.current, servingLegacyPredecessor{}) if err != nil { t.Fatal(err) } if continued.ID != a.ID || continued.OriginalSuccessor != original || continued.Successor != f.current { t.Fatalf("relabelled: %+v", continued) } f.tags(f.current.Stamp) if err = f.p.completeServingDatabasePark(t.Context(), f.conn, a.ID, f.current); err != nil { t.Fatal(err) } after, err := f.p.ledger.read(a.ID) if err != nil || !after.Complete || len(after.Continuations) != 1 { t.Fatalf("record %+v %v", after, err) } if !before.ParkedAt.IsZero() && !before.ReapAfter.Equal(after.ReapAfter) { t.Fatal("deadline reset") } if err = f.p.completeServingDatabasePark(t.Context(), f.conn, a.ID, original); err == nil { t.Fatal("old successor completed") } }) } } func TestServingLegacyRootProductionRestoreSequence(t *testing.T) { for _, failure := range []string{"", "enrolled-refusal", "missing-marker"} { t.Run("first-restore-"+failure, func(t *testing.T) { runServingLegacyRestoreSequence(t, failure) }) } } func runServingLegacyRestoreSequence(t *testing.T, failure string) { f, in := newServingLegacyRootFixture(t, false) fixture := plan12RestoreBaseline(t, f.f) raw, err := f.f.data.GetVersion(t.Context(), f.f.bucket, fixture.Files[0].Key, fixture.Files[0].VersionID) if err != nil { t.Fatal(err) } bucket := f.f.data.(*leasetest.VersionedBucket) rootKey := strings.Replace(fixture.Files[0].Key, "/g-src/", "/", 1) bucket.Put(f.f.bucket, rootKey, []byte("older invalid snapshot")) version := bucket.Put(f.f.bucket, rootKey, raw) client := hostbox.ServingParkClient{Call: servingParkSocket(t, servingTransportRoot(t, f))} var baseline *genselect.Plan sentinels := map[string]string{filepath.Join(filepath.Dir(f.db), "files", "payload"): "live file", filepath.Join(filepath.Dir(f.db), ".env"): "environment"} for path, body := range sentinels { if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil { t.Fatal(err) } if err := os.WriteFile(path, []byte(body), 0600); err != nil { t.Fatal(err) } } var legacy servingParkRecord for _, generation := range []string{"G", "G2"} { if generation == "G2" { prior := f.current target := recoveryTuple(f.f.read(), claimStopLocal{Actor: f.actor, BootID: "boot", ControllerID: "controller"}) if err := f.f.journal.Prelaunch(t.Context(), hostbox.LaunchInventory{Target: target, ControllerPID: 77, ProcessStart: "edge-start", NoState: true, Groups: []hostbox.LaunchGroup{{RelativePath: "carlos/parent", Device: 1, Inode: 2}}}); err != nil { t.Fatal(err) } servingStop(t, f.f, f.f.read()) o := f.f.read() admission := hostclaim.Admission{ObservedSession: prior.Session, ObservedGeneration: prior.Generation, Box: "box-1", Region: "ie", PredecessorRegion: "ie", Mode: "cold", NewGeneration: generation} var err error f.admitted, err = hostclaim.Update(t.Context(), f.f.claims, o, hostclaim.AdmitWriter(admission)) if err != nil { t.Fatal(err) } f.current, err = servingWriterIdentity(f.admitted, "box-1", "ie") if err != nil { t.Fatal(err) } in.Admission = admission f.install(f.admitted) } plan, err := genselect.Select(t.Context(), f.f.data, genselect.Request{Bucket: f.f.bucket, Region: f.actor.Region, Host: f.current.Host, Holder: *f.f.read().Claim.Holder}) if err != nil { t.Fatal(err) } if plan.Generation != hostclaim.RootGeneration || len(plan.Files) != 1 || plan.Files[0].Key != rootKey || plan.Files[0].VersionID != version { t.Fatalf("selected wrong baseline: %+v", plan) } if baseline == nil { baseline = plan } else if !reflect.DeepEqual(baseline, plan) { t.Fatal("successor changed selected remote baseline") } r := &claimServingArtifactRetirement{Claims: f.f.claims, Guard: f.f.guard, DataDir: f.f.guard.DataDir, Box: f.actor.Box, Region: f.actor.Region, Root: client} work := servingRootWork{f.f.guard, f.admitted} restore := hostbox.ColdRestore{Reader: f.f.data, Bucket: f.f.bucket, Generation: generation, Work: work, BeforeLegacyInstall: func(ctx context.Context, _, _ string) error { return r.BeforeLegacyInstall(ctx, f.admitted, in, work) }, BeforeInstallLocked: func(ctx context.Context, _, _ string) error { return r.EnrollLocked(ctx, f.admitted, in) }, RetireArtifacts: func(ctx context.Context, g string) error { return r.Retire(ctx, f.admitted, in, g) }} if err := r.Recover(t.Context(), f.admitted, in); err != nil { t.Fatal(err) } if generation == "G" && failure == "enrolled-refusal" { enroll := restore.BeforeInstallLocked restore.BeforeInstallLocked = func(ctx context.Context, db, gen string) error { if err := enroll(ctx, db, gen); err != nil { return err } return os.ErrPermission } if err := restore.Install(t.Context(), f.db, plan); !errors.Is(err, os.ErrPermission) { t.Fatalf("enrollment fault: %v", err) } if _, err := os.Stat(f.db); !os.IsNotExist(err) { t.Fatalf("failed restore left database: %v", err) } } else { if err := restore.Install(t.Context(), f.db, plan); err != nil { t.Fatal(err) } if got := mwReadDB(t, f.db); got != "selected recovery baseline" { t.Fatal(got) } marker, err := hostbox.ReadGenerationMarker(f.db + ".gen") if err != nil || marker != generation { t.Fatalf("marker %q %v", marker, err) } // Reproduce the durable state after database rename but before // marker installation, with the actual restored SQLite database. if generation == "G" && failure == "missing-marker" { if err := os.Remove(f.db + ".gen"); err != nil { t.Fatal(err) } } } slot, err := f.p.ledger.slot(servingParkAuthorization{Successor: f.current}) if err != nil { t.Fatal(err) } record, err := f.p.ledger.read(slot.ID) if err != nil || !record.Authorization.Legacy { t.Fatalf("legacy displaced %+v %v", record, err) } if generation == "G" { legacy = record } else if !reflect.DeepEqual(legacy, record) { t.Fatal("never-launched restore replaced legacy") } } for suffix, want := range map[string]string{"": "legacy bytes", "-wal": "legacy WAL", "-shm": "legacy SHM"} { b, err := os.ReadFile(filepath.Join(f.f.guard.DataDir, "instances", ".serving-parked", legacy.Authorization.ID, filepath.Base(f.db)+suffix)) if err != nil || string(b) != want { t.Fatalf("legacy %q %v", b, err) } } for path, want := range sentinels { body, err := os.ReadFile(path) if err != nil || string(body) != want { t.Fatalf("unrelated artifact changed: %s %v", path, err) } } after, err := f.f.data.GetVersion(t.Context(), f.f.bucket, rootKey, version) if err != nil || string(after) != string(raw) { t.Fatal("remote baseline changed", err) } } func TestServingLegacyRootConsumedAfterExpiry(t *testing.T) { f, _ := newServingLegacyRootFixture(t, false) a, err := f.p.parkLegacyServingDatabase(t.Context(), f.conn, f.current, servingLegacyPredecessor{}) if err != nil { t.Fatal(err) } f.tags(f.current.Stamp) if err = f.p.completeServingDatabasePark(t.Context(), f.conn, a.ID, f.current); err != nil { t.Fatal(err) } f.f.now = f.f.now.Add(7 * 24 * time.Hour) if err = f.p.ledger.withNamespace(t.Context(), func() error { return f.p.ledger.reapLocked(t.Context(), a.ID) }); err != nil { t.Fatal(err) } if err = os.WriteFile(f.db, []byte("restored successor"), 0600); err != nil { t.Fatal(err) } _, err = hostclaim.Update(t.Context(), f.f.claims, f.f.read(), func(c *hostclaim.Claim) error { c.Inventory = nil; return nil }) if err != nil { t.Fatal(err) } if _, err = f.p.parkLegacyServingDatabase(t.Context(), f.conn, f.current, servingLegacyPredecessor{}); err == nil { t.Fatal("reopened legacy source") } b, err := os.ReadFile(f.db) if err != nil || string(b) != "restored successor" { t.Fatalf("source %q %v", b, err) } } func TestServingLegacyRootAdmittedPredecessor(t *testing.T) { f := newServingRootFixture(t, true) if err := os.Remove(f.db + ".gen"); err != nil { t.Fatal(err) } _, err := hostclaim.Update(t.Context(), f.f.claims, f.f.read(), func(c *hostclaim.Claim) error { c.Inventory = nil; return nil }) if err != nil { t.Fatal(err) } a, err := f.p.parkLegacyServingDatabase(t.Context(), f.conn, f.current, servingLegacyPredecessor{Session: f.prior.Session, Generation: f.prior.Generation}) if err != nil { t.Fatal(err) } f.tags(f.current.Stamp) if err = f.p.completeServingDatabasePark(t.Context(), f.conn, a.ID, f.current); err != nil { t.Fatal(err) } r, err := f.p.ledger.read(a.ID) if err != nil || !r.Complete || r.LegacyEvidence.NoProcess != nil || len(r.Receipts) != 1 { t.Fatalf("record %+v %v", r, err) } } func TestServingLegacyRootContinuationNeedsStop(t *testing.T) { f, _ := newServingLegacyRootFixture(t, false) a, err := f.p.parkLegacyServingDatabase(t.Context(), f.conn, f.current, servingLegacyPredecessor{}) if err != nil { t.Fatal(err) } original := f.current o := f.f.read() later, err := hostclaim.Update(t.Context(), f.f.claims, o, hostclaim.AdmitWriter(hostclaim.Admission{ObservedSession: o.Claim.Holder.Session, ObservedGeneration: o.Claim.Holder.Generation, Box: "box-1", Region: "ie", PredecessorRegion: "ie", Mode: "cold", NewGeneration: "G2"})) if err != nil { t.Fatal(err) } f.current, err = servingWriterIdentity(later, "box-1", "ie") if err != nil { t.Fatal(err) } f.install(later) if _, err = f.p.parkLegacyServingDatabase(t.Context(), f.conn, f.current, servingLegacyPredecessor{}); err == nil { t.Fatal("continued without stop") } r, err := f.p.ledger.read(a.ID) if err != nil || r.activeAuthorization().Successor != original || len(r.Continuations) != 0 { t.Fatalf("changed record %+v %v", r, err) } } func TestServingLegacyRootEmptyContinuationAfterConsumption(t *testing.T) { f, _ := newServingLegacyRootFixture(t, true) original := f.current f.p.ledger.state = &servingParkFaultStore{Store: f.disk, failAt: 1} if _, err := f.p.parkLegacyServingDatabase(t.Context(), f.conn, f.current, servingLegacyPredecessor{Empty: true}); err == nil { t.Fatal("consumption fault missed") } f.p.ledger.state = f.disk servingStop(t, f.f, f.f.read()) o := f.f.read() later, err := hostclaim.Update(t.Context(), f.f.claims, o, hostclaim.AdmitWriter(hostclaim.Admission{ObservedSession: o.Claim.Holder.Session, ObservedGeneration: o.Claim.Holder.Generation, Box: "box-1", Region: "ie", PredecessorRegion: "ie", Mode: "cold", NewGeneration: "G2"})) if err != nil { t.Fatal(err) } f.current, err = servingWriterIdentity(later, "box-1", "ie") if err != nil { t.Fatal(err) } f.install(later) a, err := f.p.parkLegacyServingDatabase(t.Context(), f.conn, f.current, servingLegacyPredecessor{}) if err != nil { t.Fatal(err) } if a.OriginalSuccessor != original { t.Fatal("lost first-creator identity") } f.tags(f.current.Stamp) if err = f.p.completeServingDatabasePark(t.Context(), f.conn, a.ID, f.current); err != nil { t.Fatal(err) } slot, err := f.p.ledger.slot(a) if err != nil || slot.ID != "" { t.Fatalf("empty bootstrap took retained slot: %+v %v", slot, err) } } type servingEmptyRaceReader struct { genselect.Reader during func() } func (r servingEmptyRaceReader) ListVersions(ctx context.Context, bucket, prefix string) ([]lease.ObjectVersion, error) { versions, err := r.Reader.ListVersions(ctx, bucket, prefix) if r.during != nil { r.during() } return versions, err } func TestServingLegacyRootEmptyProofClaimRace(t *testing.T) { f, _ := newServingLegacyRootFixture(t, true) f.p.empty.(*claimServingOwners).Data = servingEmptyRaceReader{Reader: f.p.empty.(*claimServingOwners).Data, during: func() { _, err := hostclaim.Update(t.Context(), f.f.claims, f.f.read(), func(c *hostclaim.Claim) error { return nil }) if err != nil { t.Fatal(err) } }} before, _ := f.disk.Names() if _, err := f.p.parkLegacyServingDatabase(t.Context(), f.conn, f.current, servingLegacyPredecessor{Empty: true}); err == nil { t.Fatal("stale first-creator proof authorized capture") } after, _ := f.disk.Names() if !reflect.DeepEqual(before, after) { t.Fatalf("stale proof wrote records: %v", after) } } func TestServingLegacyRootNoProcessRetryKeepsOriginalEvidence(t *testing.T) { for _, changedIdentity := range []bool{false, true} { t.Run(fmt.Sprint(changedIdentity), func(t *testing.T) { newServingLegacyRootFixture(t, false, func(f *servingRootFixture, c *ClaimStopCoordinator, req StopRequest) { o := f.f.read() a := servingArtifactIdentity{Host: o.Host, Box: req.Box, Region: req.Region, Incarnation: req.Incarnation} old, err := f.p.ledger.legacyStop(a) if err != nil { t.Fatal(err) } reserve := hostclaim.ReserveWakeRetryAttempt(hostclaim.WakeRetryReservation{HolderIncarnation: req.Incarnation, EpisodeID: "first-episode", Attempt: hostclaim.WakeRetryAttempt{ID: "first-attempt", Box: req.Box, Enrollment: req.Enrollment, BootID: req.BootID, ControllerID: req.ControllerID, InvocationID: "invocation", LaunchInputs: "digest"}, Now: f.f.now}) next, err := hostclaim.Update(t.Context(), f.f.claims, o, func(claim *hostclaim.Claim) error { if err := reserve(claim); err != nil { return err } if changedIdentity { claim.Holder.Location += "/" + claim.Holder.Incarnation } return nil }) if err != nil { t.Fatal(err) } f.f.peer.BootID, f.f.peer.ControllerID = "next-boot", "next-controller" req.ExpectedClaimETag, req.BootID, req.ControllerID = next.ETag, f.f.peer.BootID, f.f.peer.ControllerID _, err = c.FenceAndStop(t.Context(), req) if (err != nil) != changedIdentity { t.Fatalf("repeated no-process stop: %v (changed identity %t)", err, changedIdentity) } after, err := f.p.ledger.legacyStop(a) if err != nil || !reflect.DeepEqual(after, old) { t.Fatal("replaced original no-process evidence") } }) }) } }