//go:build linux && rootrunner package host import ( "context" "crypto/ed25519" "encoding/base64" "os" "path/filepath" "testing" "github.com/carlosframework/platform/internal/release" ) func TestPlan14RootFloorHealthyAdoptRootRunner(t *testing.T) { scratch := generationRootRunnerDir(t) t.Setenv("TMPDIR", scratch) for _, healthy := range []bool{false, true} { t.Run(map[bool]string{false: "failed health", true: "healthy"}[healthy], func(t *testing.T) { f, _ := applyHappyVersion(t, "v2.0.0") f.answer["units:"+filepath.Join(f.a.stagingDir(), "carlos")] += " data-instances.mount" for path := f.a.optDir; path != scratch; path = filepath.Dir(path) { if err := os.Chmod(path, 0755); err != nil { t.Fatal(err) } } t.Setenv("CARLOS_RELEASE_PUBKEY", base64.StdEncoding.EncodeToString(f.pub)) id := rootFloorIdentity{"deploy", "box", "en"} f.a.rootMoves = func() (rootFloorIdentity, bool, error) { return id, true, nil } raw, etag, err := f.store.Get(t.Context(), release.SystemPointerKey("router", "stable")) if err != nil { t.Fatal(err) } var p release.SystemPointer if err := release.VerifyRootEnvelope(f.pub, raw, &p); err != nil { t.Fatal(err) } p.Current.ReleaseTag = "v2.0.0" p.Current.MovesProtocol = 1 wire, err := release.SignRootEnvelope(release.RootSigner(f.priv), p) if err != nil { t.Fatal(err) } if _, err := f.store.Put(t.Context(), release.SystemPointerKey("router", "stable"), wire, etag); err != nil { t.Fatal(err) } rootFloorSignRunnerBytes(t, f.a.optDir, systemBinaryPath(f.a.optDir), "v1.0.0", []byte("#!/bin/sh\necho carlos v1.0.0\n"), f.priv) f.answer["version:"+systemPrevPath(f.a.optDir)] = "carlos v1.0.0\n" if !healthy { f.probeErr = os.ErrInvalid } err = f.a.Run(context.Background(), false) floors, e := openRootMovesFloors(f.a.optDir, id, false) if e != nil { t.Fatal(e) } defer floors.close() _, accepted := floors.record(rootFloorAccepted) if healthy { if err != nil || accepted != nil { t.Fatal("healthy apply did not accept", err, accepted) } h, e := floors.healthy() if e != nil || h.Version != "v2.0.0" { t.Fatal("wrong accepted build", h, e) } } else if accepted == nil { t.Fatal("failed health accepted a floor") } }) } } func TestPlan14RootFloorUnknownCrashRecoveryRootRunner(t *testing.T) { scratch := generationRootRunnerDir(t) t.Setenv("TMPDIR", scratch) f, _ := applyHappyVersion(t, "v2.0.0") for path := f.a.optDir; path != scratch; path = filepath.Dir(path) { if err := os.Chmod(path, 0755); err != nil { t.Fatal(err) } } f.brokenBox("v2.0.0", "v1.0.0", "v1.0.0") t.Setenv("CARLOS_RELEASE_PUBKEY", base64.StdEncoding.EncodeToString(f.pub)) id := rootFloorIdentity{"deploy", "box", "en"} f.a.rootMoves = func() (rootFloorIdentity, bool, error) { return id, true, nil } rootFloorSignRunnerBytes(t, f.a.optDir, systemBinaryPath(f.a.optDir), "v2.0.0", []byte("#!/bin/sh\nexit 99\n"), f.priv) rootFloorSignRunnerBytes(t, f.a.optDir, systemPrevPath(f.a.optDir), "v1.0.0", []byte("#!/bin/sh\necho carlos v1.0.0\n"), f.priv) delete(f.answer, "version:"+systemBinaryPath(f.a.optDir)) f.a.openStore = func(context.Context) (release.Store, ed25519.PublicKey, error) { t.Fatal("offline recovery opened provider") return nil, nil, os.ErrPermission } restored, err := f.a.recoverIfNeeded(t.Context()) if err != nil || !restored { t.Fatal("unknown-history restore failed", restored, err) } b, err := readRootFloorBinary(f.a.optDir, systemBinaryPath(f.a.optDir)) if err != nil || b.Version != "v1.0.0" { t.Fatal("prev not restored", b, err) } if f.result() == nil || f.result().Outcome != systemOutcomeRolledBack { t.Fatal("recovery did not finish", f.result()) } if !f.called("systemctl restart --no-block " + systemEdgeUnit) { t.Fatal("recovery did not schedule edge", f.calls) } }