carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package release
2
3import (
4 "context"
5 "fmt"
6 "os"
7 "path/filepath"
8 "testing"
9 "time"
10
11 "github.com/carlosframework/platform/internal/registry"
12)
13
14func TestPlan14RootHistoryMissingManifest(t *testing.T) {
15 h, _, b := rootHistoryFixture(t)
16 seedRootBake(t, h, b, time.Now().UTC().Add(-time.Hour))
17 st := NewDirStore(t.TempDir())
18 priv, pub := testKeys(t)
19 ctx := t.Context()
20 opt := t.TempDir()
21 reg := testRegistry(t)
22 addRoute(t, reg, b.Host, b.App, "canary/x", false)
23 shipRelease(t, st, b.Account, b.App, "new", adoptPlatform, []byte("new"))
24 if err := Promote(ctx, st, priv, b.Account, b.App, "canary/x", "new"); err != nil {
25 t.Fatal(err)
26 }
27 if err := StageArtifact(opt, b.Account, "new", b.App, []byte("new")); err != nil {
28 t.Fatal(err)
29 }
30 if err := st.Delete(ctx, ManifestKey(b.Account, b.App, "new")); err != nil {
31 t.Fatal(err)
32 }
33 deps := &RootHistoryPass{History: h, Resolve: func(context.Context, registry.Route) (HistoryBinding, string, error) { return b, "record", nil }, Installed: func(context.Context, registry.Route, string) (string, error) { return "", nil }}
34 effects := 0
35 if err := Pass(ctx, reg, st, pub, opt, func(string) error { effects++; return nil }, WithRootHistory(deps)); err == nil || effects != 0 {
36 t.Fatalf("missing manifest: %v, effects=%d", err, effects)
37 }
38 if live, _ := LiveVersion(opt, b.Host); live != "" {
39 t.Fatal("missing manifest changed live link")
40 }
41 if a, err := h.Pending(b); err != nil || a != nil {
42 t.Fatalf("unprepared attempt: %+v %v", a, err)
43 }
44}
45
46func TestPlan14RootHistoryPassRefusesUnknown(t *testing.T) {
47 for _, state := range []string{"missing", "programs", "refusals", "pending", "sync", "binding"} {
48 t.Run(state, func(t *testing.T) {
49 h, disk, b := rootHistoryFixture(t)
50 st := NewDirStore(t.TempDir())
51 priv, pub := testKeys(t)
52 opt, legacy := t.TempDir(), t.TempDir()
53 reg := testRegistry(t)
54 addRoute(t, reg, b.Host, b.App, "canary/x", false)
55 shipRelease(t, st, b.Account, b.App, "new", adoptPlatform, []byte("new"))
56 if err := Promote(context.Background(), st, priv, b.Account, b.App, "canary/x", "new"); err != nil {
57 t.Fatal(err)
58 }
59 r, err := h.read(b)
60 if err != nil {
61 t.Fatal(err)
62 }
63 switch state {
64 case "programs":
65 r.Programs.Complete = false
66 case "refusals":
67 r.Refusals.Complete = false
68 case "pending":
69 r.Attempt = &HistoryAttempt{ID: "pending", Version: "new", Source: "record", Started: time.Now()}
70 }
71 if err := h.write(r); err != nil {
72 t.Fatal(err)
73 }
74 if state == "missing" {
75 if err := os.Remove(filepath.Join(disk.dir, historyKey(b))); err != nil {
76 t.Fatal(err)
77 }
78 }
79 if state == "sync" {
80 disk.fail = 1
81 }
82 effects := 0
83 deps := &RootHistoryPass{History: h, Installed: func(context.Context, registry.Route, string) (string, error) { return "", nil }, Resolve: func(context.Context, registry.Route) (HistoryBinding, string, error) {
84 if state == "binding" {
85 return b, "", os.ErrPermission
86 }
87 return b, "record", nil
88 }}
89 err = Pass(context.Background(), reg, st, pub, opt, func(string) error { effects++; return nil }, BakeDir(legacy), WithRootHistory(deps))
90 if err == nil || effects != 0 {
91 t.Fatalf("unknown %s: err=%v effects=%d", state, err, effects)
92 }
93 if v, _ := LiveVersion(opt, b.Host); v != "" {
94 t.Fatalf("unknown history moved link to %s", v)
95 }
96 })
97 }
98}
99
100func TestPlan14RootHistoryPassRetainsRefusal(t *testing.T) {
101 for _, attack := range []string{"delete", "rename", "recovered", "signed-retry"} {
102 t.Run(attack, func(t *testing.T) {
103 h, _, b := rootHistoryFixture(t)
104 seedRootBake(t, h, b, time.Now().UTC().Add(-time.Hour))
105 st := NewDirStore(t.TempDir())
106 priv, pub := testKeys(t)
107 opt, legacy := t.TempDir(), t.TempDir()
108 reg := testRegistry(t)
109 addRoute(t, reg, b.Host, b.App, "canary/x", false)
110 shipRelease(t, st, b.Account, b.App, "new", adoptPlatform, []byte("new"))
111 at := time.Now().UTC().Truncate(time.Second)
112 writeBackdatedPointer(t, st, priv, b.Account, b.App, "canary/x", "new", at)
113 ref := Refusal{Host: b.Host, App: b.App, Version: "new", PointerPromotedAt: at, RefusedAt: at, Reason: "failed"}
114 if err := h.AddRefusal(b, ref); err != nil {
115 t.Fatal(err)
116 }
117 if err := WriteRefusal(legacy, ref); err != nil {
118 t.Fatal(err)
119 }
120 switch attack {
121 case "rename":
122 if err := os.Rename(RefusalDir(legacy), filepath.Join(legacy, "replaced")); err != nil {
123 t.Fatal(err)
124 }
125 case "recovered":
126 ref.Recovered, ref.Version = true, "old"
127 if err := WriteRefusal(legacy, ref); err != nil {
128 t.Fatal(err)
129 }
130 case "delete", "signed-retry":
131 if err := ClearRefusal(legacy, b.Host); err != nil {
132 t.Fatal(err)
133 }
134 }
135 if attack == "signed-retry" {
136 if err := st.Delete(context.Background(), ChannelKey(b.Account, b.App, "canary/x")); err != nil {
137 t.Fatal(err)
138 }
139 writeBackdatedPointer(t, st, priv, b.Account, b.App, "canary/x", "new", at.Add(time.Second))
140 }
141 effects := 0
142 deps := &RootHistoryPass{History: h,
143 Resolve: func(context.Context, registry.Route) (HistoryBinding, string, error) { return b, "record", nil },
144 Installed: func(context.Context, registry.Route, string) (string, error) { return "", nil },
145 }
146 err := Pass(context.Background(), reg, st, pub, opt, func(string) error {
147 r, err := h.read(b)
148 if err != nil || r.Attempt == nil {
149 t.Fatal("restart without durable intent", err)
150 }
151 effects++
152 return nil
153 }, BakeDir(legacy), WithRootHistory(deps))
154 if attack == "signed-retry" {
155 if err != nil || effects != 1 {
156 t.Fatalf("signed retry refused: %v, %d", err, effects)
157 }
158 if v, _ := LiveVersion(opt, b.Host); v != "new" {
159 t.Fatal("retry did not adopt")
160 }
161 } else if effects != 0 {
162 t.Fatalf("%s retried failed promotion", attack)
163 }
164 refs, readErr := h.Refusals(b)
165 want := 1
166 if attack == "signed-retry" {
167 want = 0
168 }
169 if readErr != nil || len(refs) != want {
170 t.Fatalf("lost retained refusal: %v, %v", refs, readErr)
171 }
172 })
173 }
174}
175
176func TestPlan14RootHistoryPassRejectsProgramForgery(t *testing.T) {
177 good, bad := tinyProgram(t, "old/program"), tinyProgram(t, "foreign/program")
178 for _, attack := range []string{"foreign-history", "empty-root", "same-program", "failed-observation", "changed-binding"} {
179 t.Run(attack, func(t *testing.T) {
180 h, _, b := rootHistoryFixture(t)
181 seedRootBake(t, h, b, time.Now().UTC().Add(-time.Hour))
182 st := NewDirStore(t.TempDir())
183 priv, pub := testKeys(t)
184 opt, legacy := t.TempDir(), t.TempDir()
185 reg := testRegistry(t)
186 addRoute(t, reg, b.Host, b.App, "canary/x", false)
187 if err := StageArtifact(opt, b.Account, "old", b.App, good); err != nil {
188 t.Fatal(err)
189 }
190 if err := PointLive(opt, b.Host, b.Account, "old", b.App); err != nil {
191 t.Fatal(err)
192 }
193 binary := bad
194 if attack == "same-program" || attack == "failed-observation" || attack == "changed-binding" {
195 binary = good
196 }
197 shipRelease(t, st, b.Account, b.App, "new", adoptPlatform, binary)
198 if err := Promote(context.Background(), st, priv, b.Account, b.App, "canary/x", "new"); err != nil {
199 t.Fatal(err)
200 }
201 if err := RecordProgram(legacy, b.Host, "foreign/program"); err != nil {
202 t.Fatal(err)
203 }
204 if attack == "empty-root" {
205 r, err := h.read(b)
206 if err != nil {
207 t.Fatal(err)
208 }
209 r.Programs.Values = nil
210 if err := h.write(r); err != nil {
211 t.Fatal(err)
212 }
213 }
214 reads, effects := 0, 0
215 deps := &RootHistoryPass{History: h,
216 Resolve: func(context.Context, registry.Route) (HistoryBinding, string, error) {
217 reads++
218 if attack == "changed-binding" && reads >= 3 {
219 return b, "changed-record", nil
220 }
221 return b, "record", nil
222 },
223 Installed: func(context.Context, registry.Route, string) (string, error) {
224 if attack == "failed-observation" {
225 return "", os.ErrPermission
226 }
227 return "old/program", nil
228 },
229 }
230 err := Pass(context.Background(), reg, st, pub, opt, func(string) error { effects++; return nil }, BakeDir(legacy), WithRootHistory(deps))
231 v, _ := LiveVersion(opt, b.Host)
232 switch attack {
233 case "same-program":
234 if err != nil || v != "new" || effects != 1 {
235 t.Fatalf("valid adoption failed: %s %d %v", v, effects, err)
236 }
237 case "failed-observation":
238 if err == nil {
239 t.Fatal("unobserved install reported success")
240 }
241 if _, err := h.Refusals(b); err == nil {
242 t.Fatal("unverified attempt settled")
243 }
244 default:
245 if v != "old" || effects != 0 {
246 t.Fatalf("%s adopted: %s %d %v", attack, v, effects, err)
247 }
248 }
249 })
250 }
251}
252
253func TestPlan14RootHistoryProgramChangeNeedsSignedManifest(t *testing.T) {
254 binary := tinyProgram(t, "foreign/program")
255 for _, signed := range []bool{false, true} {
256 t.Run(fmt.Sprint(signed), func(t *testing.T) {
257 h, _, b := rootHistoryFixture(t)
258 seedRootBake(t, h, b, time.Now().UTC().Add(-time.Hour))
259 st := NewDirStore(t.TempDir())
260 priv, pub := testKeys(t)
261 ctx := context.Background()
262 opt, legacy := t.TempDir(), t.TempDir()
263 reg := testRegistry(t)
264 addRoute(t, reg, b.Host, b.App, "canary/x", false)
265 m := Manifest{App: b.App, Version: "new", Kind: "binary", ProgramChange: true, Artifacts: map[string]Artifact{adoptPlatform: {}}}
266 if err := Ship(ctx, st, b.Account, m, map[string][]byte{adoptPlatform: binary}); err != nil {
267 t.Fatal(err)
268 }
269 if signed {
270 if err := WriteDigestMarker(ctx, st, RootSigner(priv), b.Account, "test"); err != nil {
271 t.Fatal(err)
272 }
273 }
274 if err := Promote(ctx, st, priv, b.Account, b.App, "canary/x", "new"); err != nil {
275 t.Fatal(err)
276 }
277 deps := &RootHistoryPass{History: h, Resolve: func(context.Context, registry.Route) (HistoryBinding, string, error) { return b, "record", nil }, Installed: func(context.Context, registry.Route, string) (string, error) { return "foreign/program", nil }}
278 effects := 0
279 err := Pass(ctx, reg, st, pub, opt, func(string) error { effects++; return nil }, BakeDir(legacy), WithRootHistory(deps))
280 if signed && (err != nil || effects != 1) {
281 t.Fatalf("signed consent refused: %v %d", err, effects)
282 }
283 if !signed && effects != 0 {
284 t.Fatal("unsigned manifest field granted program change")
285 }
286 })
287 }
288}
289
290func TestPlan14RootHistoryPassRetainsBake(t *testing.T) {
291 for _, elapsed := range []bool{false, true} {
292 t.Run(fmt.Sprint(elapsed), func(t *testing.T) {
293 h, _, b := rootHistoryFixture(t)
294 start := time.Now().UTC().Add(-time.Minute)
295 if elapsed {
296 start = start.Add(-121 * time.Hour)
297 }
298 seedRootBake(t, h, b, start)
299 clock, err := h.LoadBake([]HistoryBinding{b}, start)
300 if err != nil {
301 t.Fatal(err)
302 }
303 clock.Observe(b.Account, b.App, "stable", "new", start)
304 clock.Production = map[string]*prodMemory{prodKey(b.Account, b.App): {WasProduction: true}}
305 clock.Holds = map[string]*holdMemory{channelClockKey(b.Account, b.App, "stable"): {Window: 96 * time.Hour, SetAt: start}}
306 if err := clock.Save(); err != nil {
307 t.Fatal(err)
308 }
309 st := NewDirStore(t.TempDir())
310 priv, pub := testKeys(t)
311 opt, legacy := t.TempDir(), t.TempDir()
312 reg := testRegistry(t)
313 addRoute(t, reg, b.Host, b.App, "stable", false)
314 shipRelease(t, st, b.Account, b.App, "new", adoptPlatform, []byte("new"))
315 writeBackdatedPointer(t, st, priv, b.Account, b.App, "stable", "new", start)
316 forged := LoadFirstSeen(FirstSeenPath(legacy))
317 forged.Observe(b.Account, b.App, "stable", "new", start.Add(-1000*time.Hour))
318 if err := forged.Save(); err != nil {
319 t.Fatal(err)
320 }
321 deps := &RootHistoryPass{History: h, Resolve: func(context.Context, registry.Route) (HistoryBinding, string, error) { return b, "record", nil }, Installed: func(context.Context, registry.Route, string) (string, error) { return "", nil }}
322 effects := 0
323 err = Pass(context.Background(), reg, st, pub, opt, func(string) error { effects++; return nil }, BakeDir(legacy), WithRootHistory(deps))
324 if err != nil {
325 t.Fatal(err)
326 }
327 if elapsed && effects != 1 {
328 t.Fatal("root-observed interval never admitted adoption")
329 }
330 if !elapsed && effects != 0 {
331 t.Fatal("legacy clock shortened root hold")
332 }
333 })
334 }
335}
336