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 "encoding/json"
7 "os"
8 "sort"
9 "strconv"
10 "strings"
11 "testing"
12
13 "github.com/carlosframework/platform/internal/lease/leasetest"
14)
15
16type lifecycleRemoteCheckpoint struct {
17 Boot, Snapshot string
18 Objects []seedRebootObject
19}
20
21func lifecycleSaveBucket(t *testing.T, model *leasetest.VersionedBucket) {
22 t.Helper()
23 boot, err := os.ReadFile("/proc/sys/kernel/random/boot_id")
24 lifecycleCheck(t, "checkpoint boot", err)
25 saved := lifecycleRemoteCheckpoint{Boot: strings.TrimSpace(string(boot)), Snapshot: model.Snapshot()}
26 versions, err := model.ListVersions(t.Context(), "data", "")
27 lifecycleCheck(t, "checkpoint versions", err)
28 for _, version := range versions {
29 object := seedRebootObject{Version: version}
30 if !version.DeleteMarker {
31 object.Body, err = model.GetVersion(t.Context(), "data", version.Key, version.VersionID)
32 lifecycleCheck(t, "checkpoint bytes", err)
33 }
34 saved.Objects = append(saved.Objects, object)
35 }
36 raw, err := json.Marshal(saved)
37 lifecycleCheck(t, "checkpoint JSON", err)
38 lifecycleCheck(t, "checkpoint remote service", os.WriteFile(lifecycleHome+"/remote.json", raw, 0600))
39}
40
41func lifecycleRestoreBucket(t *testing.T, model *leasetest.VersionedBucket) {
42 t.Helper()
43 raw, err := os.ReadFile(lifecycleHome + "/remote.json")
44 lifecycleCheck(t, "read remote checkpoint", err)
45 var saved lifecycleRemoteCheckpoint
46 lifecycleCheck(t, "decode remote checkpoint", json.Unmarshal(raw, &saved))
47 boot, err := os.ReadFile("/proc/sys/kernel/random/boot_id")
48 lifecycleCheck(t, "read reboot identity", err)
49 if saved.Boot == "" || saved.Boot == strings.TrimSpace(string(boot)) {
50 t.Fatal("lifecycle runner did not reboot")
51 }
52 sort.Slice(saved.Objects, func(i, j int) bool {
53 a, _ := strconv.Atoi(strings.TrimPrefix(saved.Objects[i].Version.VersionID, "v"))
54 b, _ := strconv.Atoi(strings.TrimPrefix(saved.Objects[j].Version.VersionID, "v"))
55 return a < b
56 })
57 next := 1
58 for _, object := range saved.Objects {
59 n, err := strconv.Atoi(strings.TrimPrefix(object.Version.VersionID, "v"))
60 lifecycleCheck(t, "version number", err)
61 for next < n {
62 id := model.Put("data", "checkpoint-gap", nil)
63 lifecycleCheck(t, "preserve version gap", model.DeleteVersion(t.Context(), "data", "checkpoint-gap", id))
64 next++
65 }
66 var id string
67 if object.Version.DeleteMarker {
68 id = model.BareDelete("data", object.Version.Key)
69 } else {
70 id = model.Put("data", object.Version.Key, object.Body)
71 }
72 if id != object.Version.VersionID {
73 t.Fatal("remote version identity changed", id, object.Version.VersionID)
74 }
75 next++
76 }
77 if model.Snapshot() != saved.Snapshot {
78 t.Fatal("remote state changed across reboot")
79 }
80}
81