//go:build linux package host import ( "os" "path/filepath" "testing" "github.com/carlosframework/platform/internal/hostbox" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/hostguard" "github.com/carlosframework/platform/internal/provision" ) func TestPlan14EmptyCensusRootSibling(t *testing.T) { for _, fault := range []string{"none", "missing-root", "wrong-revision", "rename", "unclassified", "selected-host", "private-alias-substitution", "private-alias-symlink"} { t.Run(fault, func(t *testing.T) { f, _, _ := generationLaunchFixture(t) root := t.TempDir() rel := filepath.Join("instances", provision.InstanceID(f.target.Host)) path := filepath.Join(root, rel) if err := os.MkdirAll(path, 0700); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(path, "app.db"), []byte("live sibling"), 0600); err != nil { t.Fatal(err) } id, err := hostbox.CaptureInventoryIdentity(root, rel) if err != nil { t.Fatal(err) } service, err := hostbox.NewJournalService(f.journal) if err != nil { t.Fatal(err) } in := hostbox.StopInventory{Complete: true, Host: f.target.Host, Incarnation: f.target.Incarnation, InstallationStamp: f.target.Stamp, Box: f.target.Box, Enrollment: f.target.Enrollment, DirectoryRevision: "1", Entries: []hostbox.StopInventoryEntry{{Kind: "path", ManagedRoot: "data", RelativePath: rel, ArtifactID: "state", Ownership: "owner", Materialization: "materialized", Device: id.Device, Inode: id.Inode, Type: 0040000, Identity: id}}} if err := service.Inventory.WriteStopInventory(t.Context(), in); err != nil { t.Fatal(err) } reg := restartTestRegistry(t) c := hostbox.EmptyInstallationCensus{Inventory: service.Inventory, Registry: reg, Launches: f.journal, Box: f.target.Box, Enrollment: f.target.Enrollment, DirectoryRevision: "1", Roots: map[string]string{"data": root, "opt": t.TempDir(), "units": t.TempDir()}} h := ClaimRootHTTP{Launches: f.journal, Roots: c.Roots, Stop: ClaimStopHTTP{Coordinator: ClaimStopCoordinator{Guard: f.root.Guard}}} c.OtherPath = h.rootSiblingPath switch fault { case "missing-root": h.Stop.Coordinator.Guard.Root = hostguard.NewRootAuthority(&rollbackDisk{dir: t.TempDir()}) c.OtherPath = h.rootSiblingPath case "wrong-revision": if err := service.Inventory.WriteStopInventory(t.Context(), in); err != nil { t.Fatal(err) } case "rename": if err := os.Rename(path, path+"-old"); err != nil { t.Fatal(err) } if err := os.Mkdir(path, 0700); err != nil { t.Fatal(err) } case "private-alias-substitution", "private-alias-symlink": private := filepath.Join(root, ".instances", provision.InstanceID(f.target.Host)) if err := os.MkdirAll(filepath.Dir(private), 0700); err != nil { t.Fatal(err) } if fault == "private-alias-symlink" { if err := os.Symlink(path, private); err != nil { t.Fatal(err) } } else { if err := os.Mkdir(private, 0700); err != nil { t.Fatal(err) } } case "unclassified": if err := os.WriteFile(filepath.Join(root, "unknown"), []byte("x"), 0600); err != nil { t.Fatal(err) } } selected := "new.example" if fault == "selected-host" { selected = f.target.Host } obs := hostclaim.Observed{Host: selected, Claim: &hostclaim.Claim{Holder: &hostclaim.Holder{Incarnation: "new", Stamp: 1}}} if fault == "none" { c.OtherPath = nil if err := c.CheckLocal(t.Context(), obs, hostbox.RecordSelection{Host: selected, Incarnation: "new"}); err == nil { t.Fatal("unclassified sibling unexpectedly admitted") } c.OtherPath = h.rootSiblingPath } err = c.CheckLocal(t.Context(), obs, hostbox.RecordSelection{Host: selected, Incarnation: "new"}) if (err == nil) != (fault == "none") { t.Fatal("empty census", fault, err) } }) } }