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 "context"
7 "crypto/ed25519"
8 "encoding/base64"
9 "encoding/json"
10 "github.com/carlosframework/platform/internal/moves"
11 "github.com/carlosframework/platform/internal/release"
12 "github.com/carlosframework/platform/internal/rootstate"
13 "github.com/carlosframework/platform/internal/seeds"
14 "os"
15 "path/filepath"
16 "testing"
17)
18
19func TestPlan14RootSeedsFloorRootRunner(t *testing.T) {
20 for _, prev := range []bool{false, true} {
21 t.Run(map[bool]string{false: "absent", true: "present"}[prev], func(t *testing.T) {
22 opt := generationRootRunnerDir(t)
23 dd := filepath.Join(opt, "data")
24 if err := os.Mkdir(dd, 0755); err != nil {
25 t.Fatal(err)
26 }
27 id := rootFloorIdentity{"deploy", "box", "en"}
28 pub, key, _ := ed25519.GenerateKey(nil)
29 t.Setenv("CARLOS_RELEASE_PUBKEY", base64.StdEncoding.EncodeToString(pub))
30 t.Setenv("CARLOS_DEPLOYMENT_BUCKET", "deploy")
31 rootFloorSignRunnerBytes(t, opt, systemBinaryPath(opt), "v2.0.0", []byte("#!/bin/sh\nexit 0\n"), key)
32 if prev {
33 rootFloorSignRunnerBytes(t, opt, systemPrevPath(opt), "v2.0.0", []byte("#!/bin/sh\nexit 0 #prev\n"), key)
34 }
35 identities, e := rootstate.Open(filepath.Join(opt, "system", "guard", "identities"), false, true)
36 if e != nil {
37 t.Fatal(e)
38 }
39 names, e := identities.Names()
40 if e != nil {
41 t.Fatal(e)
42 }
43 for _, name := range names {
44 raw, e := identities.Read(name)
45 if e != nil {
46 t.Fatal(e)
47 }
48 var p release.SystemPointer
49 if release.VerifyRootEnvelope(pub, raw, &p) != nil {
50 t.Fatal("identity")
51 }
52 p.Current.SeedsProtocol = 1
53 wire, e := release.SignRootEnvelope(release.RootSigner(key), p)
54 if e != nil {
55 t.Fatal(e)
56 }
57 if e := identities.Write(name, wire); e != nil {
58 t.Fatal(e)
59 }
60 }
61 identities.Close()
62 f, err := openRootProtocolFloors(rootSeedsFloorDir(opt), id, true)
63 if err != nil {
64 t.Fatal(err)
65 }
66 defer f.close()
67 a := moves.Floor{Min: 1, MinRelease: "v1.0.0"}
68 b := moves.Floor{Min: 1, MinRelease: "v2.0.0"}
69 if err := f.reconstruct(t.Context(), rootFloorReconstruction{Identity: id, Floor: &a, Source: "root history", Digest: floorTestDigest, Authorization: floorTestAuthorization(t, f)}); err != nil {
70 t.Fatal(err)
71 }
72 j := rootFloorJudge{floors: f, current: systemBinaryPath(opt), prev: systemPrevPath(opt), binary: func(path string) (rootFloorBinary, error) { return readRootSeedsBinary(opt, path) }, idle: func(context.Context) error { return nil }}
73 if err := j.judge(t.Context()); err != nil {
74 t.Fatal(err)
75 }
76 store := release.NewDirStore(t.TempDir())
77 raw, _ := json.Marshal(seeds.Floor{Min: b.Min, MinRelease: b.MinRelease})
78 if err := os.MkdirAll(filepath.Join(dd, ".moves"), 0755); err != nil {
79 t.Fatal(err)
80 }
81 if err := os.WriteFile(filepath.Join(dd, seeds.FloorFile), raw, 0644); err != nil {
82 t.Fatal(err)
83 }
84 wire, err := release.SignRootEnvelope(release.RootSigner(key), rootSeedsPublication{Schema: 1, Deployment: id.Deployment, Purpose: seeds.FloorKey, Floor: b})
85 if err != nil {
86 t.Fatal(err)
87 }
88 if _, err := store.Put(t.Context(), seeds.FloorKey, wire, ""); err != nil {
89 t.Fatal(err)
90 }
91 initial, err := release.SignRootEnvelope(release.RootSigner(key), rootSeedsPublication{Schema: 1, Deployment: id.Deployment, Purpose: seeds.FloorKey, Floor: a})
92 if err != nil {
93 t.Fatal(err)
94 }
95 if err := acceptRootSeedsPublication(t.Context(), f, initial); err != nil {
96 t.Fatal(err)
97 }
98 edge := edgesFile{Box: id.Box, MovesFloorState: "accepted"}
99 if got := withSeedsFields(edge, dd, opt); got.SeedsFloorMinRelease != a.MinRelease {
100 t.Fatal("mirror advertised ahead", got)
101 }
102 if err := refreshRootSeedsFloor(t.Context(), store, opt, dd, id); err != nil {
103 t.Fatal(err)
104 }
105 if got := withSeedsFields(edge, dd, opt); got.SeedsFloorState == "accepted" {
106 t.Fatal("stale verdict accepted", got)
107 }
108 if err := j.judge(t.Context()); err != nil {
109 t.Fatal(err)
110 }
111 if got := withSeedsFields(edge, dd, opt); got.SeedsFloorState != "accepted" || got.SeedsFloorMinRelease != b.MinRelease {
112 t.Fatal("no root acceptance", got)
113 }
114 if err := os.RemoveAll(dd); err != nil {
115 t.Fatal(err)
116 }
117 aplier := systemApplier{optDir: opt, dataDir: dd, rootMoves: func() (rootFloorIdentity, bool, error) { return id, true, nil }}
118 if aplier.seedsActivationRefusal("v1.0.0") == nil {
119 t.Fatal("erased mirror permitted downgrade")
120 }
121 if err := aplier.seedsActivationRefusal("v2.0.0"); err != nil {
122 t.Fatal(err)
123 }
124 // The distinct moves namespace cannot inherit a seeds acceptance.
125 if _, err := readRootFloorProjection(opt); !os.IsNotExist(err) {
126 t.Fatal("moves namespace created", err)
127 }
128 })
129 }
130}
131