package host import ( "context" "crypto/sha256" "encoding/hex" "github.com/carlosframework/platform/internal/rootparticipation" "github.com/carlosframework/platform/internal/rootstate" "io" "os" "strings" "testing" "time" "github.com/carlosframework/platform/internal/hostbox" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/hostguard" "github.com/carlosframework/platform/internal/instancectl" "github.com/carlosframework/platform/internal/provision" "github.com/carlosframework/platform/internal/release" ) func TestPlan14FreshInstallRegistration(t *testing.T) { t.Parallel() for _, mode := range []string{"new", "writer", "relocation", "landing", "store-failure", "replay", "deleted-history"} { t.Run(mode, func(t *testing.T) { kind, phase := hostclaim.KindServing, "" if mode == "relocation" { kind, phase = hostclaim.KindRelocating, hostclaim.PhaseInstalling } if mode == "landing" { kind, phase = hostclaim.KindLanding, provision.LandChecking } f, root, q := plan14InstallRegistration(t, kind, phase) if mode == "writer" { o, err := hostclaim.Update(t.Context(), f.claims, f.read(), func(c *hostclaim.Claim) error { c.Holder.WriterBox = "box-1"; c.Holder.Session = 1; return nil }) if err != nil { t.Fatal(err) } q.Target.ClaimETag, q.Target.Session = o.ETag, 1 q = plan14PreparedInventory(t, root, q) } disk := &rollbackDisk{dir: t.TempDir()} history := &release.RootHistory{Store: disk} calls := 0 root.history = func(ctx context.Context, fn func(*release.RootHistory) error) error { calls++ if mode == "store-failure" { return os.ErrPermission } return fn(history) } out, err := root.commitRootInventory(t.Context(), q) if mode == "store-failure" { if err == nil { t.Fatal("registration ignored history failure") } if _, err = root.Stop.Coordinator.Guard.Capture(q.Target.Host); err == nil { t.Fatal("registered before release exclusion") } return } if err != nil { t.Fatal(err) } if out.Revision != 1 || calls != 1 { t.Fatalf("out=%+v calls=%d", out, calls) } in, err := root.Stop.Coordinator.Guard.Root.View(t.Context(), q.Target.Host, hostguard.Captured{Incarnation: q.Target.Incarnation, Stamp: q.Target.Stamp}) if err != nil { t.Fatal(err) } b := release.HistoryBinding{Host: in.Installation.Host, Account: in.Installation.Account, App: in.Installation.App} id := rootHistoryInstallation(in.Installation) if mode == "replay" || mode == "deleted-history" { if mode == "deleted-history" { names, e := disk.Names() if e != nil { t.Fatal(e) } for _, name := range names { if e = disk.Remove(name); e != nil { t.Fatal(e) } } } if _, err = root.commitRootInventory(t.Context(), q); err != nil { t.Fatal(err) } } fresh, err := history.FreshInstallation(b, id) want := mode == "new" || mode == "replay" if want && (err != nil || !fresh) { t.Fatalf("new creation not recorded: %v %v", fresh, err) } if !want && err == nil && fresh { t.Fatal("old installation became fresh") } }) } } func TestPlan14FreshInstallLaunchObservation(t *testing.T) { for _, mode := range []string{"ready", "not-ready", "history-failure", "record-failure"} { t.Run(mode, func(t *testing.T) { f := newLaunchFixture(t, servingOp()) f.claims.set(func(c *hostclaim.Claim) { c.Holder.Account = "bab" c.Holder.App = "app" c.Holder.Key = "record" c.Holder.Admissions = []hostclaim.AdmissionRecord{{Box: f.target.Box, Region: f.target.Region, Session: f.target.Session, Generation: c.Holder.Generation}} }) f.root.Guard.Root = hostguard.NewRootAuthority(&rollbackDisk{dir: t.TempDir()}) in := hostguard.RootInstallation{Host: f.target.Host, Account: "bab", App: "app", Record: "record", Incarnation: f.target.Incarnation, Stamp: f.target.Stamp, Box: f.target.Box, Enrollment: f.target.Enrollment, Migration: 1, InventoryRevision: 1} if err := f.root.Guard.EstablishRoot(t.Context(), in, func(*hostclaim.Claim) error { return nil }); err != nil { t.Fatal(err) } h := &release.RootHistory{Store: &rollbackDisk{dir: t.TempDir()}} b := release.HistoryBinding{Host: in.Host, Account: in.Account, App: in.App} if err := h.RecordCreation(b, rootHistoryInstallation(in)); err != nil { t.Fatal(err) } f.root.history = func(ctx context.Context, fn func(*release.RootHistory) error) error { if mode == "history-failure" { return os.ErrPermission } return fn(h) } observed := 0 f.root.installedProgram = func(context.Context, hostbox.RoleLaunchRequest, instancectl.ClaimUnitStarted, ClaimLaunchObserved) (string, error) { observed++ if mode == "record-failure" { return "", os.ErrPermission } return "actual/program", nil } if mode == "not-ready" { f.kernel.observeErr = os.ErrPermission } _, err := f.root.Serve(t.Context(), f.peer, f.req) if (err == nil) != (mode == "ready") { t.Fatalf("launch: %v", err) } programs, e := h.Programs(b) if e != nil { t.Fatal(e) } if mode == "ready" { if len(programs) != 1 || programs[0] != "actual/program" { t.Fatalf("programs: %v", programs) } } else if len(programs) != 0 { t.Fatal("unobserved program recorded", programs) } fresh, e := h.FreshInstallation(b, rootHistoryInstallation(in)) if e != nil || fresh != (mode == "history-failure") { t.Fatalf("fresh after attempt: %v %v", fresh, e) } if mode == "not-ready" && observed != 0 { t.Fatal("recorded unready launch") } }) } } func TestPlan14InstalledLaunchProgramEvidence(t *testing.T) { path, err := os.Executable() if err != nil { t.Fatal(err) } file, err := os.Open(path) if err != nil { t.Fatal(err) } id, err := rootstate.Identity(file) if err != nil { t.Fatal(err) } raw, err := io.ReadAll(file) file.Close() if err != nil { t.Fatal(err) } digest := sha256.Sum256(raw) for _, mode := range []string{"observed", "staged-only", "digest", "inode", "process", "unit", "authorization"} { t.Run(mode, func(t *testing.T) { f := newLaunchFixture(t, servingOp()) f.req.Executable = path f.req.Spec.Digest = hex.EncodeToString(digest[:]) f.req.Reservation.Executable = hostbox.LaunchFile{Device: id.Device, Inode: id.Inode} seen := f.kernel.observed seen.Executable = f.req.Reservation.Executable seen.Running = seen.Executable f.kernel.observed = seen switch mode { case "staged-only": seen.TenantPID = 0 f.kernel.observed = seen case "digest": f.req.Spec.Digest = strings.Repeat("0", 64) case "inode": f.req.Reservation.Executable.Inode++ case "process": f.kernel.observed.TenantStart = "successor" case "unit": f.units.sameErr = os.ErrPermission case "authorization": f.root.ValidateExecutable = func(string, string, string) error { return os.ErrPermission } } program, err := f.root.rootInstalledProgram(t.Context(), f.req, f.units.started, seen) if mode == "observed" { if err != nil || program != release.FileProgram(path) { t.Fatalf("program=%q: %v", program, err) } } else if err == nil { t.Fatal("accepted", mode) } }) } } func TestPlan14FreshInstallCanonicalIdentity(t *testing.T) { t.Parallel() for _, mode := range []string{"canonical", "account", "app", "host", "record", "census", "inventory", "service-files"} { t.Run(mode, func(t *testing.T) { f := newRestartConsumptionFixture(t) rt, err := f.e.root.reg.Get(f.claim.Host) if err != nil { t.Fatal(err) } id, err := f.p.source.creation(t.Context(), *rt) if err != nil || id == "" { t.Fatal(id, err) } switch mode { case "account": rt.Account = "bap" case "app": rt.App = "other" case "host": rt.Host = "other.example" case "record": rec, err := provision.GetRecord(t.Context(), f.e.store, rt.Account, rt.App, rt.Host) if err != nil { t.Fatal(err) } rec.CreatedAt = rec.CreatedAt.Add(time.Second) plan14PutRecord(t, f.e.store, *rec) case "census": f.p.source.census = func() (rootparticipation.Census, error) { return rootparticipation.Census{}, os.ErrPermission } case "inventory": if err := f.p.source.state.Remove(rt.Host + ".json"); err != nil { t.Fatal(err) } case "service-files": if err := f.e.root.reg.Delete(rt.Host); err != nil { t.Fatal(err) } rt.Caps = "host-role" rt.GoodVersion = "invented" rt.Unit = "forged.service" } got, err := f.p.source.creation(t.Context(), *rt) if mode == "canonical" || mode == "service-files" { if err != nil || got != id { t.Fatalf("canonical identity changed: %q %v", got, err) } } else if err == nil { t.Fatal("accepted", mode) } }) } }