//go:build linux && rootrunner package host import ( "context" "crypto/ed25519" "encoding/base64" "encoding/json" "github.com/carlosframework/platform/internal/moves" "github.com/carlosframework/platform/internal/release" "github.com/carlosframework/platform/internal/rootstate" "github.com/carlosframework/platform/internal/seeds" "os" "path/filepath" "testing" ) func TestPlan14RootSeedsFloorRootRunner(t *testing.T) { for _, prev := range []bool{false, true} { t.Run(map[bool]string{false: "absent", true: "present"}[prev], func(t *testing.T) { opt := generationRootRunnerDir(t) dd := filepath.Join(opt, "data") if err := os.Mkdir(dd, 0755); err != nil { t.Fatal(err) } id := rootFloorIdentity{"deploy", "box", "en"} pub, key, _ := ed25519.GenerateKey(nil) t.Setenv("CARLOS_RELEASE_PUBKEY", base64.StdEncoding.EncodeToString(pub)) t.Setenv("CARLOS_DEPLOYMENT_BUCKET", "deploy") rootFloorSignRunnerBytes(t, opt, systemBinaryPath(opt), "v2.0.0", []byte("#!/bin/sh\nexit 0\n"), key) if prev { rootFloorSignRunnerBytes(t, opt, systemPrevPath(opt), "v2.0.0", []byte("#!/bin/sh\nexit 0 #prev\n"), key) } identities, e := rootstate.Open(filepath.Join(opt, "system", "guard", "identities"), false, true) if e != nil { t.Fatal(e) } names, e := identities.Names() if e != nil { t.Fatal(e) } for _, name := range names { raw, e := identities.Read(name) if e != nil { t.Fatal(e) } var p release.SystemPointer if release.VerifyRootEnvelope(pub, raw, &p) != nil { t.Fatal("identity") } p.Current.SeedsProtocol = 1 wire, e := release.SignRootEnvelope(release.RootSigner(key), p) if e != nil { t.Fatal(e) } if e := identities.Write(name, wire); e != nil { t.Fatal(e) } } identities.Close() f, err := openRootProtocolFloors(rootSeedsFloorDir(opt), id, true) if err != nil { t.Fatal(err) } defer f.close() a := moves.Floor{Min: 1, MinRelease: "v1.0.0"} b := moves.Floor{Min: 1, MinRelease: "v2.0.0"} if err := f.reconstruct(t.Context(), rootFloorReconstruction{Identity: id, Floor: &a, Source: "root history", Digest: floorTestDigest, Authorization: floorTestAuthorization(t, f)}); err != nil { t.Fatal(err) } 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 }} if err := j.judge(t.Context()); err != nil { t.Fatal(err) } store := release.NewDirStore(t.TempDir()) raw, _ := json.Marshal(seeds.Floor{Min: b.Min, MinRelease: b.MinRelease}) if err := os.MkdirAll(filepath.Join(dd, ".moves"), 0755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(dd, seeds.FloorFile), raw, 0644); err != nil { t.Fatal(err) } wire, err := release.SignRootEnvelope(release.RootSigner(key), rootSeedsPublication{Schema: 1, Deployment: id.Deployment, Purpose: seeds.FloorKey, Floor: b}) if err != nil { t.Fatal(err) } if _, err := store.Put(t.Context(), seeds.FloorKey, wire, ""); err != nil { t.Fatal(err) } initial, err := release.SignRootEnvelope(release.RootSigner(key), rootSeedsPublication{Schema: 1, Deployment: id.Deployment, Purpose: seeds.FloorKey, Floor: a}) if err != nil { t.Fatal(err) } if err := acceptRootSeedsPublication(t.Context(), f, initial); err != nil { t.Fatal(err) } edge := edgesFile{Box: id.Box, MovesFloorState: "accepted"} if got := withSeedsFields(edge, dd, opt); got.SeedsFloorMinRelease != a.MinRelease { t.Fatal("mirror advertised ahead", got) } if err := refreshRootSeedsFloor(t.Context(), store, opt, dd, id); err != nil { t.Fatal(err) } if got := withSeedsFields(edge, dd, opt); got.SeedsFloorState == "accepted" { t.Fatal("stale verdict accepted", got) } if err := j.judge(t.Context()); err != nil { t.Fatal(err) } if got := withSeedsFields(edge, dd, opt); got.SeedsFloorState != "accepted" || got.SeedsFloorMinRelease != b.MinRelease { t.Fatal("no root acceptance", got) } if err := os.RemoveAll(dd); err != nil { t.Fatal(err) } aplier := systemApplier{optDir: opt, dataDir: dd, rootMoves: func() (rootFloorIdentity, bool, error) { return id, true, nil }} if aplier.seedsActivationRefusal("v1.0.0") == nil { t.Fatal("erased mirror permitted downgrade") } if err := aplier.seedsActivationRefusal("v2.0.0"); err != nil { t.Fatal(err) } // The distinct moves namespace cannot inherit a seeds acceptance. if _, err := readRootFloorProjection(opt); !os.IsNotExist(err) { t.Fatal("moves namespace created", err) } }) } }