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 "os"
6 "testing"
7 "time"
8
9 "github.com/carlosframework/platform/internal/hostwrite"
10 "github.com/carlosframework/platform/internal/release"
11)
12
13func TestPlan14RecoveryKnownRefusal(t *testing.T) {
14 for _, mode := range []string{"active", "failed", "inactive"} {
15 for _, kind := range []string{release.RefusalNotUp, release.RefusalWrongProgram, release.RefusalNotProven} {
16 for _, change := range []string{"same", "retry", "unsigned", "late-refusal"} {
17 t.Run(mode+"/"+kind+"/"+change, func(t *testing.T) {
18 e, u, c, source, pub := signedRecoveryFixture(t)
19 rt, err := e.root.reg.Get(observedHost)
20 if err != nil {
21 t.Fatal(err)
22 }
23 h := seedHostRootHistory(t, *rt)
24 b := release.HistoryBinding{Host: rt.Host, Account: rt.Account, App: rt.App}
25 p, err := release.Channel(t.Context(), e.store, pub, rt.Account, rt.App, rt.Channel)
26 if err != nil {
27 t.Fatal(err)
28 }
29 at := p.PromotedAt
30 if change == "retry" {
31 at = at.Add(-time.Second)
32 }
33 ref := release.Refusal{Host: rt.Host, App: rt.App, Version: p.Version, Kind: kind, PointerPromotedAt: at, RefusedAt: at}
34 add := func() {
35 if err := h.AddRefusal(b, ref); err != nil {
36 t.Fatal(err)
37 }
38 }
39 if change != "late-refusal" {
40 add()
41 }
42 c.history = func() (*release.RootHistoryPass, error) { return &release.RootHistoryPass{History: h}, nil }
43 n := namedUnitRecovery{source: source, pub: pub, readLive: os.Readlink}
44 if change == "unsigned" {
45 n.pub = make(ed25519.PublicKey, ed25519.PublicKeySize)
46 }
47 if change == "late-refusal" {
48 n.writes = hostwrite.Config{Enabled: true, Selector: namedRecoveryWork{before: add}}
49 }
50 if mode != "active" {
51 u.props.ActiveState, u.props.SubState, u.props.MainPID = mode, mode, 0
52 }
53 handled, err := n.recover(t.Context(), rt.Host, c, func(string) string { return "5s" }, e.root.reg)
54 accepted := change == "retry" && kind == release.RefusalNotUp
55 live, liveErr := release.LiveVersion(u.opt, rt.Host)
56 want := "v1"
57 effects := 0
58 if accepted {
59 want = "v2"
60 if mode == "active" {
61 effects = 1
62 }
63 }
64 if !handled || (err == nil) != accepted || liveErr != nil || live != want || len(u.effects) != effects {
65 t.Fatalf("handled=%v error=%v live=%s effects=%v", handled, err, live, u.effects)
66 }
67 refs, err := h.Refusals(b)
68 if err != nil {
69 t.Fatal(err)
70 }
71 if accepted && len(refs) != 0 {
72 t.Fatalf("verified retry retained D1: %+v", refs)
73 }
74 if !accepted && len(refs) != 1 {
75 t.Fatalf("refused target cleared evidence: %+v", refs)
76 }
77 })
78 }
79 }
80 }
81}
82
83func TestPlan14RecoveryOriginalRefusalWithUnknownHistory(t *testing.T) {
84 e, u, c, source, pub := signedRecoveryFixture(t)
85 old, err := c.host(t.Context(), observedHost)
86 if err != nil {
87 t.Fatal(err)
88 }
89 p, err := release.Channel(t.Context(), e.store, pub, old.Route.Account, old.Route.App, old.Route.Channel)
90 if err != nil {
91 t.Fatal(err)
92 }
93 disk, err := c.attempts()
94 if err != nil {
95 t.Fatal(err)
96 }
97 b := release.HistoryBinding{Host: old.Route.Host, Account: old.Route.Account, App: old.Route.App}
98 a, err := captureRollback(disk, old, c.now(), unitSettleTimeout(func(string) string { return "5s" }), release.VersionPath(u.opt, b.Account, p.Version, b.App), c.identity, historyRollbackLink{Binding: b, Attempt: release.HistoryAttempt{ID: "failed", Source: old.Source, Version: p.Version, SHA256: "digest", Platform: release.AdoptPlatform(), Started: c.now(), PromotedAt: p.PromotedAt}, Independent: true})
99 if err != nil {
100 t.Fatal(err)
101 }
102 a.Failed, a.Settled = true, true
103 if err := writeRollbackState(disk, recoveryResultKey(a), a); err != nil {
104 t.Fatal(err)
105 }
106 delete(disk.(*rollbackMemory).files, rollbackJournalKey(b.Host))
107 c.history = func() (*release.RootHistoryPass, error) { return nil, os.ErrPermission }
108 n := namedUnitRecovery{source: source, pub: pub, readLive: os.Readlink}
109 handled, err := n.recover(t.Context(), b.Host, c, func(string) string { return "5s" }, e.root.reg)
110 live, _ := release.LiveVersion(u.opt, b.Host)
111 if !handled || err == nil || live != "v1" || len(u.effects) != 0 {
112 t.Fatalf("retained failure ignored: handled=%v err=%v live=%s effects=%v", handled, err, live, u.effects)
113 }
114}
115