carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package host
2
3import (
4 "crypto/ed25519"
5 "encoding/base64"
6 "os"
7 "path/filepath"
8 "testing"
9
10 "github.com/carlosframework/platform/internal/provision"
11 "github.com/carlosframework/platform/internal/release"
12)
13
14func signedRecoveryFixture(t *testing.T) (*proofRootEnv, *plan14RestartUnits, rootRestartChecks, *proofBindingSource, ed25519.PublicKey) {
15 t.Helper()
16 e, rt, u, c, _ := plan14AdoptionFixture(t)
17 pub, priv, err := ed25519.GenerateKey(nil)
18 if err != nil {
19 t.Fatal(err)
20 }
21 t.Setenv("CARLOS_RELEASE_PUBKEY", base64.StdEncoding.EncodeToString(pub))
22 if err := e.store.Delete(t.Context(), release.ChannelKey(rt.Account, rt.App, rt.Channel)); err != nil {
23 t.Fatal(err)
24 }
25 for _, ver := range []string{"v1", "v2"} {
26 if err := release.Promote(t.Context(), e.store, priv, rt.Account, rt.App, rt.Channel, ver); err != nil {
27 t.Fatal(err)
28 }
29 }
30 source := &proofBindingSource{signedRecovery: true, store: e.store, sys: u, optDir: u.opt, openFile: os.Open, unitDirs: []string{filepath.Dir(u.props.FragmentPath)}}
31 c.resolve, c.host, c.rollback, c.prepareRollback, c.recovered = source.resolve, source.forHost, source.rollback, source.prepareRollback, source.recovered
32 return e, u, c, source, pub
33}
34
35func TestPlan14SignedRestartWithoutHealthHistory(t *testing.T) {
36 e, u, c, _, _ := signedRecoveryFixture(t)
37 rt, err := e.root.reg.Get(observedHost)
38 if err != nil {
39 t.Fatal(err)
40 }
41 rec, _, err := provision.FindRecordByHost(t.Context(), e.store, rt.Host)
42 if err != nil {
43 t.Fatal(err)
44 }
45 rec.Pin = "v1"
46 plan14PutRecord(t, e.store, *rec)
47 err = checkedUnitRestart(u, u.opt, func(string) string { return "5s" }, nil, c)(*rt)
48 if err != nil || len(u.effects) != 1 {
49 t.Fatalf("signed restart depended on health history: %v %v", err, u.effects)
50 }
51}
52
53func TestPlan14CapturedRollbackWithoutHealthHistory(t *testing.T) {
54 t.Parallel()
55 e, rt, u, c, _ := plan14AdoptionFixture(t)
56 before, err := c.host(t.Context(), rt.Host)
57 if err != nil {
58 t.Fatal(err)
59 }
60 disk, err := c.attempts()
61 if err != nil {
62 t.Fatal(err)
63 }
64 a, err := captureRollback(disk, before, c.now(), unitSettleTimeout(func(string) string { return "5s" }), release.VersionPath(u.opt, rt.Account, "v2", rt.App), c.identity)
65 if err != nil {
66 t.Fatal(err)
67 }
68 a.Failed = true
69 if err := writeRollbackState(disk, rollbackJournalKey(rt.Host), a); err != nil {
70 t.Fatal(err)
71 }
72 source := &proofBindingSource{store: e.store, sys: u, optDir: u.opt, openFile: os.Open, unitDirs: []string{filepath.Dir(u.props.FragmentPath)}}
73 c.rollback, c.prepareRollback, c.recovered = source.rollback, source.prepareRollback, source.recovered
74 u.props.ActiveState, u.props.SubState, u.props.MainPID = "failed", "failed", 0
75 if err := rollbackUnitRestart(e.root.reg, u, u.opt, func(string) string { return "5s" }, c)(rt.Host); err != nil {
76 t.Fatal(err)
77 }
78 if len(u.effects) != 2 || u.exe != release.VersionPath(u.opt, rt.Account, "v1", rt.App) {
79 t.Fatalf("rollback blocked: %v", u.effects)
80 }
81}
82
83func TestPlan14SignedRestartRequiresTrustedLiveLink(t *testing.T) {
84 e, u, c, _, _ := signedRecoveryFixture(t)
85 rt, err := e.root.reg.Get(observedHost)
86 if err != nil {
87 t.Fatal(err)
88 }
89 c.readLive = func(string) (string, error) { return "", os.ErrPermission }
90 if err := checkedUnitRestart(u, u.opt, func(string) string { return "5s" }, nil, c)(*rt); err == nil || len(u.effects) != 0 {
91 t.Fatalf("untrusted pointer restarted: %v %v", err, u.effects)
92 }
93 if productionRootRestartChecks(u, u.opt).readLive == nil {
94 t.Fatal("production restart has no checked pointer reader")
95 }
96}
97