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 && rootrunner
2
3package host
4
5import (
6 "context"
7 "crypto/sha256"
8 "encoding/hex"
9 "os"
10 "os/exec"
11 "path/filepath"
12 "strconv"
13 "strings"
14 "syscall"
15 "testing"
16 "time"
17
18 "github.com/carlosframework/platform/internal/registry"
19 "github.com/carlosframework/platform/internal/release"
20 "github.com/carlosframework/platform/internal/rootstate"
21)
22
23func TestPlan14GenerationRunningTenantPinRootRunner(t *testing.T) {
24 dir := os.Getenv("PLAN14_E43_RUNNING_CHILD")
25 if dir == "" {
26 dir = generationRootRunnerDir(t)
27 binary, err := os.Executable()
28 if err != nil {
29 t.Fatal(err)
30 }
31 cmd := exec.CommandContext(t.Context(), "unshare", "--mount", "--pid", "--fork", "--mount-proc", binary, "-test.run=^TestPlan14GenerationRunningTenantPinRootRunner$", "-test.v", "-test.timeout=2m")
32 cmd.Dir = dir
33 cmd.Env = append(os.Environ(), "PLAN14_E43_RUNNING_CHILD="+dir)
34 if out, err := cmd.CombinedOutput(); err != nil {
35 t.Fatalf("running tenant pin: %v\n%s", err, out)
36 }
37 return
38 }
39 t.Setenv("CARLOS_OPT_DIR", dir)
40 if err := os.Chmod(dir, 0755); err != nil {
41 t.Fatal(err)
42 }
43 body, err := os.ReadFile("/bin/sleep")
44 if err != nil {
45 t.Fatal(err)
46 }
47 for _, v := range []string{"v1", "v2", "v3"} {
48 if err := release.StageArtifact(dir, "bab", v, "shop", body); err != nil {
49 t.Fatal(err)
50 }
51 old := time.Now().Add(-48 * time.Hour)
52 if err := os.Chtimes(release.VersionPath(dir, "bab", v, "shop"), old, old); err != nil {
53 t.Fatal(err)
54 }
55 }
56 identity := func(path string) rootstate.FileIdentity {
57 t.Helper()
58 f, err := os.Open(path)
59 if err != nil {
60 t.Fatal(err)
61 }
62 defer f.Close()
63 id, err := rootstate.Identity(f)
64 if err != nil {
65 t.Fatal(err)
66 }
67 return id
68 }
69 store, err := rootstate.Open(filepath.Join(dir, "generations"), true, false)
70 if err != nil {
71 t.Fatal(err)
72 }
73 defer store.Close()
74 ledger := &release.RootGenerations{Store: store}
75 x := &release.ReleaseExclusion{Open: func() (rootstate.Store, error) { return rootstate.Open(filepath.Join(dir, "release"), true, false) }}
76 boot, err := (generationKernel{proc: "/proc"}).boot()
77 if err != nil {
78 t.Fatal(err)
79 }
80 start, err := claimLaunchProcessStart("/proc", os.Getpid())
81 if err != nil {
82 t.Fatal(err)
83 }
84 sum := sha256.Sum256(body)
85 reserve := func(v string) release.GenerationReservation {
86 t.Helper()
87 path := release.VersionPath(dir, "bab", v, "shop")
88 b := release.GenerationBinding{Binding: release.HistoryBinding{Host: "shop.example", Account: "bab", App: "shop"}, Target: release.GenerationOwner{Host: "shop.example", Incarnation: "fixture", Stamp: 1, Session: 1, Box: "box", Enrollment: "en", BootID: boot, ControllerID: "controller"}, Installation: "fixture", Record: "apps/bab/shop/instances/shop.example.json", RecordETag: "fixture", Attempt: v, Inputs: "fixture", Invocation: v, ControllerPID: os.Getpid(), ControllerStart: start, Version: v, Digest: hex.EncodeToString(sum[:]), Path: path, Tree: filepath.Dir(path), Executable: identity(path), TreeIdentity: identity(filepath.Dir(path))}
89 var r release.GenerationReservation
90 if err := x.With(t.Context(), func(context.Context) error { var err error; r, err = ledger.Reserve(b); return err }); err != nil {
91 t.Fatal(err)
92 }
93 return r
94 }
95 v1, v3 := reserve("v1"), reserve("v3")
96 child := exec.Command(v1.Binding.Path, "120")
97 child.SysProcAttr = &syscall.SysProcAttr{Credential: &syscall.Credential{Uid: 65534, Gid: 65534}}
98 child.Dir = "/"
99 if err := child.Start(); err != nil {
100 t.Fatal(err)
101 }
102 defer func() { child.Process.Kill(); child.Wait() }()
103 pid := strconv.Itoa(child.Process.Pid)
104 deadline := time.Now().Add(5 * time.Second)
105 for {
106 actual, err := os.Readlink("/proc/" + pid + "/exe")
107 if err == nil && actual == v1.Binding.Path {
108 break
109 }
110 if time.Now().After(deadline) {
111 t.Fatalf("tenant never executed staged build: %s %v", actual, err)
112 }
113 time.Sleep(time.Millisecond)
114 }
115 status, err := os.ReadFile("/proc/" + pid + "/status")
116 if err != nil || !strings.Contains(string(status), "Uid:\t65534\t65534\t65534\t65534") {
117 t.Fatalf("tenant uid: %s %v", status, err)
118 }
119 source := generationPinSource{open: func() (rootstate.Store, error) {
120 return rootstate.Open(filepath.Join(dir, "generations"), false, false)
121 }, verify: func(ctx context.Context, _ []release.GenerationReservation) error {
122 _, _, err := productionGenerationKernelCensus(ctx)
123 return err
124 }}
125 rows := fakeLister{routes: []registry.Route{{Host: "shop.example", Account: "bab", App: "shop", Version: "v2"}}}
126 legacy := filepath.Join(dir, "zdd")
127 if err := os.Mkdir(legacy, 0755); err != nil {
128 t.Fatal(err)
129 }
130 check := func(label string) {
131 t.Helper()
132 if err := x.With(t.Context(), func(ctx context.Context) error {
133 if pins, err := source.StagedPins(ctx, dir); err == nil || pins != nil {
134 t.Fatalf("%s: uncontained tenant certified", label)
135 }
136 sweepStagedVersions(rows, dir, filepath.Join(legacy, "generations.json"), time.Hour, time.Now(), source)
137 return nil
138 }); err != nil {
139 t.Fatal(err)
140 }
141 for _, r := range []release.GenerationReservation{v1, v3} {
142 got, err := ledger.Read(r.Binding)
143 if err != nil || got != r {
144 t.Fatalf("%s: reservation changed: %v", label, err)
145 }
146 raw, err := os.ReadFile(r.Binding.Path)
147 if err != nil || sha256.Sum256(raw) != sum || identity(r.Binding.Path) != r.Binding.Executable {
148 t.Fatalf("%s: pinned build changed: %v", label, err)
149 }
150 }
151 }
152 for _, file := range []string{"generations.json", "generations.seq"} {
153 if err := os.WriteFile(filepath.Join(legacy, file), []byte("{}"), 0644); err != nil {
154 t.Fatal(err)
155 }
156 }
157 check("empty service publication")
158 for _, file := range []string{"generations.json", "generations.seq"} {
159 if err := os.Remove(filepath.Join(legacy, file)); err != nil {
160 t.Fatal(err)
161 }
162 }
163 check("deleted service publication")
164 if err := os.Rename(legacy, legacy+".old"); err != nil {
165 t.Fatal(err)
166 }
167 check("renamed service publication parent")
168}
169