//go:build linux package host import ( "os" "path/filepath" "strings" "testing" "time" "github.com/carlosframework/platform/internal/hostbox" "github.com/carlosframework/platform/internal/hostguard" "github.com/carlosframework/platform/internal/instancectl" "github.com/carlosframework/platform/internal/release" ) func TestPlan14RetiredGenerationBoundary(t *testing.T) { for _, mode := range []string{"retired", "active sibling", "live installation", "pending pin", "open pin", "named", "legacy process", "open writers", "pending unit", "foreign receipt", "bad receipt", "barrier", "missing installation", "missing launches", "missing reservation"} { t.Run(mode, func(t *testing.T) { target := hostguard.FenceTarget{Host: "shop.example", Incarnation: "inc", Stamp: 3, Session: 4, Box: "box", Enrollment: "en", BootID: "boot", ControllerID: "controller", FenceID: "fence", Op: "delete", ObligationID: "park", RosterRevision: 1} at := time.Now().UTC() s := StopReceipt{Kind: "stopped", DirectoryRevision: testReceiptNumeral(target), Host: target.Host, Incarnation: target.Incarnation, InstallationStamp: target.Stamp, WriterSession: target.Session, Box: target.Box, Enrollment: target.Enrollment, BootID: target.BootID, ControllerID: target.ControllerID, FenceID: target.FenceID, OperationID: target.Op, ObligationID: target.ObligationID, EvidenceAt: at} view := hostguard.FenceView{Host: target.Host, Installation: hostguard.RootInstallation{Host: target.Host, Incarnation: target.Incarnation, Stamp: target.Stamp}, Retired: []hostguard.Captured{{Incarnation: target.Incarnation, Stamp: target.Stamp}}, Receipts: []hostguard.FenceReceipt{{Target: target, Bytes: mustMarshalStopReceipt(s), EvidenceAt: at}}} inv := generationInventory{Installations: []hostguard.FenceView{view}, Launches: []hostbox.LaunchInventory{{Target: target, WritersClosed: true, Reservations: []hostbox.LaunchReservation{{Target: target, State: hostbox.UnitSettled, Group: hostbox.LaunchGroup{RelativePath: instancectl.ClaimUnitControlGroup(target.Host)}}}}}} records := []release.GenerationReservation{{Binding: release.GenerationBinding{Target: generationOwner(target)}, Closed: true, Retirement: strings.Repeat("a", 64)}} switch mode { case "active sibling": sibling := hostguard.FenceView{Host: "sibling.example", Installation: hostguard.RootInstallation{Host: "sibling.example", Incarnation: "sibling", Stamp: 1}} inv.Installations = append(inv.Installations, sibling) inv.Launches = append(inv.Launches, hostbox.LaunchInventory{Target: hostguard.FenceTarget{Host: "sibling.example", Incarnation: "sibling", Stamp: 1}}) records = append(records, release.GenerationReservation{Binding: release.GenerationBinding{Target: release.GenerationOwner{Host: "sibling.example", Incarnation: "sibling", Stamp: 1}}}) case "live installation": inv.Installations[0].Retired = nil case "pending pin": records[0].Retirement = "" case "open pin": records[0].Closed = false case "named": records[0].Binding.Unit = "console.service" case "legacy process": records[0].Process.PID = 10 case "open writers": inv.Launches[0].WritersClosed = false case "pending unit": inv.Launches[0].Reservations[0].State = hostbox.UnitIntended case "foreign receipt": inv.Installations[0].Receipts[0].Target.Session++ case "bad receipt": inv.Installations[0].Receipts[0].Bytes = []byte("{}") case "barrier": inv.Installations[0].Barrier = &target case "missing installation": inv.Installations = nil case "missing launches": inv.Launches = nil case "missing reservation": records = nil } if got := retiredGenerationSnapshot(t.Context(), inv, records); got != (mode == "retired" || mode == "active sibling") { t.Fatalf("boundary = %v", got) } }) } } func TestPlan14RetiredGenerationGroups(t *testing.T) { root := filepath.Join(t.TempDir(), "groups") if err := emptyGenerationGroups(t.Context(), root); err != nil { t.Fatal(err) } if err := os.MkdirAll(filepath.Join(root, "unknown"), 0700); err != nil { t.Fatal(err) } for _, group := range []string{root, filepath.Join(root, "unknown")} { if err := os.WriteFile(filepath.Join(group, "cgroup.procs"), nil, 0600); err != nil { t.Fatal(err) } } if err := emptyGenerationGroups(t.Context(), root); err != nil { t.Fatal(err) } path := filepath.Join(root, "unknown", "cgroup.procs") if err := os.WriteFile(path, []byte("42\n"), 0600); err != nil { t.Fatal(err) } if err := emptyGenerationGroups(t.Context(), root); err == nil { t.Fatal("unrecorded managed process ignored") } if err := os.Remove(path); err != nil { t.Fatal(err) } if err := emptyGenerationGroups(t.Context(), root); err == nil { t.Fatal("missing kernel evidence accepted") } } func TestPlan14GenerationEarlyBootStart(t *testing.T) { for _, start := range []string{"0", "1", "-1", "not-a-number", "18446744073709551616"} { got, err := generationStart([]byte("1 (init) S " + strings.Repeat("0 ", 18) + start)) if start == "0" || start == "1" { if err != nil || got != start { t.Fatalf("start %s: %s %v", start, got, err) } } else if err == nil { t.Fatalf("invalid start accepted: %s", start) } } }