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 "context"
5 "crypto/ed25519"
6 "os"
7 "path/filepath"
8 "testing"
9
10 "github.com/carlosframework/platform/internal/registry"
11 "github.com/carlosframework/platform/internal/release"
12 "github.com/carlosframework/platform/internal/rollout"
13)
14
15// Settings spec §4.3 through updatesPassOption: the option is built ONCE,
16// as fastPassOpts builds it, and still reads swap-failures.json at the start
17// of every pass. A build is failed for a host when the book blocks it or
18// holds the host down on it; a served build is exempt from Blocks only.
19func TestUpdatesPassOptionReadsTheBookEveryPass(t *testing.T) {
20 t.Parallel()
21 ctx := context.Background()
22 opt, data := t.TempDir(), t.TempDir()
23 store := release.NewDirStore(t.TempDir())
24 seed, pubB64, _ := release.GenerateKey()
25 priv, _ := release.ParseSigningKey(seed, "t")
26 pub, _ := release.ParsePublicKey(pubB64, "t")
27 for _, v := range []string{"v1", "v2"} {
28 m := release.Manifest{Schema: 1, App: "km", Version: v, Kind: "binary",
29 Artifacts: map[string]release.Artifact{runtimePlatform(): {}}}
30 if err := release.Ship(ctx, store, "bab", m, map[string][]byte{runtimePlatform(): []byte(v)}); err != nil {
31 t.Fatal(err)
32 }
33 if err := release.Promote(ctx, store, priv, "bab", "km", "canary/x", v); err != nil {
34 t.Fatal(err)
35 }
36 }
37 reg, err := registry.Open(filepath.Join(t.TempDir(), "r.db"))
38 if err != nil {
39 t.Fatal(err)
40 }
41 pinned := release.UpdateSettings{Updates: release.UpdatesAuto, Channel: "canary/x", Pin: "v1"}.Column()
42 add := func(host string, good string) {
43 t.Helper()
44 if err := reg.Upsert(registry.Route{Host: host, Kind: "instance", App: "km", Account: "bab", Channel: "canary/x"}); err != nil {
45 t.Fatal(err)
46 }
47 if err := reg.SetUpdateSettings(host, pinned); err != nil {
48 t.Fatal(err)
49 }
50 if err := reg.SetBuildState(host, release.BuildStateCopy{Known: true}.Column()); err != nil {
51 t.Fatal(err)
52 }
53 if good != "" {
54 if err := reg.SetGoodBuild(host, registry.GoodBuild{}, registry.GoodBuild{Version: good}); err != nil {
55 t.Fatal(err)
56 }
57 }
58 }
59 opts := []release.PassOption{updatesPassOption(data, true, func(context.Context, release.Store, ed25519.PublicKey) (*release.RootUpdateInputs, error) {
60 return nil, nil
61 })} // built once
62 pass := func() {
63 t.Helper()
64 if err := release.PassFilter(ctx, reg, store, pub, opt, nil, nil, opts...); err != nil {
65 t.Fatal(err)
66 }
67 }
68 live := func(host string) string {
69 v, _ := release.LiveVersion(opt, host)
70 return v
71 }
72
73 add("first.example.com", "")
74 pass() // no book yet: the pin installs
75 if v := live("first.example.com"); v != "v1" {
76 t.Fatalf("first live %q, want v1 with no failure records", v)
77 }
78
79 // The agent records v1 failed after the option was built.
80 book := `{"schema":1,"records":[{"account":"bab","app":"km","build":"v1","host":"first.example.com",
81 "cause":"health","mode":"canary","reason":"did not come up",
82 "held_down":["held.example.com"],"retried":["retried.example.com"]}]}`
83 if err := os.WriteFile(filepath.Join(data, rollout.FailureFile), []byte(book), 0o644); err != nil {
84 t.Fatal(err)
85 }
86 add("blocked.example.com", "") // Blocks: not served, not retried
87 add("served.example.com", "v1") // served: Blocks exempts, not held down
88 add("held.example.com", "v1") // served, but held down on v1: HeldDown
89 add("retried.example.com", "") // a host retry cleared it
90 pass()
91 for host, want := range map[string]string{
92 "blocked.example.com": "",
93 "served.example.com": "v1",
94 "held.example.com": "",
95 "retried.example.com": "v1",
96 } {
97 if v := live(host); v != want {
98 t.Fatalf("%s live %q, want %q", host, v, want)
99 }
100 }
101 for _, host := range []string{"blocked.example.com", "held.example.com"} {
102 c, err := release.ReadConsumed(opt, host)
103 if err != nil || c == nil || c.Reason != string(release.ReasonFailed) || c.Detail != "v1" {
104 t.Fatalf("%s consumed = %+v, %v; want reason failed naming v1", host, c, err)
105 }
106 }
107}
108