| 1 | //go:build linux && rootrunner |
| 2 | |
| 3 | package host |
| 4 | |
| 5 | import ( |
| 6 | "bytes" |
| 7 | "context" |
| 8 | "encoding/base64" |
| 9 | "encoding/json" |
| 10 | "os" |
| 11 | "path/filepath" |
| 12 | "testing" |
| 13 | |
| 14 | "github.com/carlosframework/platform/internal/hostfloor" |
| 15 | "github.com/carlosframework/platform/internal/release" |
| 16 | ) |
| 17 | |
| 18 | // Fix round 1, I2, as real root: a box with a root participation census |
| 19 | // (leo's kind) and an unarmed guard, updated from v1.3.0 to v1.4.0 by the |
| 20 | // real apply on the census branch, then armed for guard-arm by |
| 21 | // install-channel --policy on that same branch. The release before is |
| 22 | // replaced, carlos.prev's record names the release, the identity is kept, |
| 23 | // and the root moves floor's judgement ran over the new carlos.prev. |
| 24 | func TestRootInstallChannelPolicyCensusBox(t *testing.T) { |
| 25 | scratch := generationRootRunnerDir(t) |
| 26 | t.Setenv("TMPDIR", scratch) |
| 27 | f := &channelFixture{applyFixture: newApplyFixture(t, "stable", "v1.3.0")} |
| 28 | for _, dir := range []string{f.a.optDir, f.a.dataDir, f.a.etcDir} { |
| 29 | for path := dir; path != scratch && path != "/" && path != filepath.Dir(scratch); path = filepath.Dir(path) { |
| 30 | if err := os.Chmod(path, 0755); err != nil { |
| 31 | t.Fatal(err) |
| 32 | } |
| 33 | } |
| 34 | } |
| 35 | t.Setenv("CARLOS_RELEASE_PUBKEY", base64.StdEncoding.EncodeToString(f.pub)) |
| 36 | t.Setenv("CARLOS_DEPLOYMENT_BUCKET", "bucket") |
| 37 | id := rootFloorIdentity{Deployment: "bucket", Box: "box", Enrollment: "en"} |
| 38 | f.a.rootMoves = func() (rootFloorIdentity, bool, error) { return id, true, nil } |
| 39 | old := []byte("#!/bin/sh\necho carlos v1.3.0\n") |
| 40 | rootFloorSignRunnerBytes(t, f.a.optDir, systemBinaryPath(f.a.optDir), "v1.3.0", old, f.priv) |
| 41 | f.body = []byte("#!/bin/sh\necho carlos v1.4.0\n") |
| 42 | applyChannelUpdate(t, f, "v1.4.0", f.body, true) |
| 43 | if cur, prev := f.slots(t); !bytes.Equal(cur, f.body) || !bytes.Equal(prev, old) { |
| 44 | t.Fatalf("the census update left %q and %q", cur, prev) |
| 45 | } |
| 46 | f.in = channelInstall{a: f.a, bucket: "bucket", fetchTo: func(ctx context.Context, s release.Store, key, dst, sha string) error { |
| 47 | f.fetches++ |
| 48 | return release.FetchToFile(ctx, s, key, dst, systemArtifactLimit, sha) |
| 49 | }, own: func(string) error { return nil }} |
| 50 | f.in.policy = signedPolicy(t, f.priv, "bucket", hostfloor.Floor{Min: 1, MinRelease: "v1.4.0"}) |
| 51 | f.in.identity = f.identityOf(t, "v1.4.0") |
| 52 | got, err := f.in.run(context.Background()) |
| 53 | if err != nil || !got.Replaced { |
| 54 | t.Fatalf("install-channel --policy on a census box: %+v %v", got, err) |
| 55 | } |
| 56 | if cur, prev := f.slots(t); !bytes.Equal(cur, f.body) || !bytes.Equal(prev, f.body) || !prevRecorded(f.a.optDir, "v1.4.0") || f.fetches != 0 { |
| 57 | t.Fatalf("slots %q %q", cur, prev) |
| 58 | } |
| 59 | key, _ := release.IdentityKey("v1.4.0") |
| 60 | published, _, err := f.store.Get(context.Background(), key) |
| 61 | if err != nil { |
| 62 | t.Fatal(err) |
| 63 | } |
| 64 | if held, err := os.ReadFile(channelIdentityPath(f.a.optDir)); err != nil || !bytes.Equal(held, published) { |
| 65 | t.Fatal("identity", err) |
| 66 | } |
| 67 | floors, err := openRootMovesFloors(f.a.optDir, id, false) |
| 68 | if err != nil { |
| 69 | t.Fatal("the census box's root floor state", err) |
| 70 | } |
| 71 | defer floors.close() |
| 72 | raw, err := floors.public.Read("prev.json") |
| 73 | if err != nil { |
| 74 | t.Fatal("the root moves floor did not judge the new carlos.prev", err) |
| 75 | } |
| 76 | var verdict rootFloorVerdict |
| 77 | if err := json.Unmarshal(raw, &verdict); err != nil || verdict.Identity != id || verdict.PrevAbsent || verdict.Prev.Version != "v1.4.0" || verdict.Prev.Digest != hostfloor.Digest(f.body) { |
| 78 | t.Fatalf("the judgement is not of the replaced carlos.prev (v1.4.0): %s %v", raw, err) |
| 79 | } |
| 80 | if again, err := f.in.run(context.Background()); err != nil || !again.Already { |
| 81 | t.Fatalf("rerun %+v %v", again, err) |
| 82 | } |
| 83 | t.Log("PASS install-channel --policy on a census box: root moves branch, mandatory record, identity kept") |
| 84 | } |
| 85 | |