carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1//go:build linux
2
3package hostbox
4
5import (
6 "os"
7 "path/filepath"
8 "testing"
9
10 "github.com/carlosframework/platform/internal/hostguard"
11 "golang.org/x/sys/unix"
12)
13
14func TestLaunchIntentionRecoveryRefusesForeignChildren(t *testing.T) {
15 for _, mode := range []string{"absent", "empty", "populated", "descendant", "symlink", "parent-replaced", "reboot-absent-parent", "reboot-absent-ancestor", "reboot-reused-parent"} {
16 t.Run(mode, func(t *testing.T) {
17 ctx := t.Context()
18 root := t.TempDir()
19 parentPath := filepath.Join(root, "delegate", "parent")
20 if err := os.MkdirAll(parentPath, 0700); err != nil {
21 t.Fatal(err)
22 }
23 var st unix.Stat_t
24 if err := unix.Stat(parentPath, &st); err != nil {
25 t.Fatal(err)
26 }
27 parent := LaunchGroup{RelativePath: "delegate/parent", Device: uint64(st.Dev), Inode: st.Ino}
28 target := hostguard.FenceTarget{Host: "app.test", Incarnation: "inc", Stamp: 1, Session: 1, Box: "box", Enrollment: "enroll", BootID: "boot", ControllerID: "ctrl"}
29 j, err := OpenLaunchJournal(filepath.Join(t.TempDir(), "launch.db"))
30 if err != nil {
31 t.Fatal(err)
32 }
33 defer j.Close()
34 if err := j.Prelaunch(ctx, LaunchInventory{Target: target, ControllerPID: 42, ProcessStart: "start", Groups: []LaunchGroup{parent}, NoState: true}); err != nil {
35 t.Fatal(err)
36 }
37 r := LaunchReservation{Target: target, ID: "first:main", Role: "main", InvocationID: "first", Inputs: "inputs", Parent: parent, Group: LaunchGroup{RelativePath: "delegate/parent/role-first", InvocationID: "first"}, Executable: LaunchFile{Device: 1, Inode: 2}}
38 if err := j.IntendRole(ctx, r); err != nil {
39 t.Fatal(err)
40 }
41 childPath := filepath.Join(root, r.Group.RelativePath)
42 reboot := false
43 switch mode {
44 case "reboot-absent-ancestor":
45 reboot = true
46 if err := os.RemoveAll(filepath.Join(root, "delegate")); err != nil {
47 t.Fatal(err)
48 }
49 case "reboot-absent-parent":
50 reboot = true
51 if err := os.Remove(parentPath); err != nil {
52 t.Fatal(err)
53 }
54 case "reboot-reused-parent":
55 reboot = true
56 case "parent-replaced":
57 if err := os.Rename(parentPath, parentPath+".old"); err != nil {
58 t.Fatal(err)
59 }
60 if err := os.Mkdir(parentPath, 0700); err != nil {
61 t.Fatal(err)
62 }
63 case "symlink":
64 if err := os.Symlink(t.TempDir(), childPath); err != nil {
65 t.Fatal(err)
66 }
67 case "empty", "populated", "descendant":
68 if err := os.Mkdir(childPath, 0700); err != nil {
69 t.Fatal(err)
70 }
71 value := "populated 0\n"
72 if mode == "populated" {
73 value = "populated 1\n"
74 }
75 if err := os.WriteFile(filepath.Join(childPath, "cgroup.events"), []byte(value), 0600); err != nil {
76 t.Fatal(err)
77 }
78 if mode == "descendant" {
79 if err := os.Mkdir(filepath.Join(childPath, "unknown"), 0700); err != nil {
80 t.Fatal(err)
81 }
82 }
83 }
84 err = j.ResolveRoleIntentions(ctx, target, root, reboot)
85 accepted := mode == "absent" || mode == "empty" || mode == "reboot-absent-parent" || mode == "reboot-absent-ancestor"
86 if (err == nil) != accepted {
87 t.Fatalf("accepted=%v err=%v", accepted, err)
88 }
89 in, readErr := j.Read(ctx, target)
90 if readErr != nil {
91 t.Fatal(readErr)
92 }
93 want := "intended"
94 if accepted {
95 want = "settled"
96 }
97 if in.Reservations[0].State != want {
98 t.Fatalf("changed refused intention: %+v", in)
99 }
100 if accepted {
101 if err := j.ResolveRoleIntentions(ctx, target, root, reboot); err != nil {
102 t.Fatal("replay", err)
103 }
104 }
105 })
106 }
107}
108