//go:build linux && rootrunner package host import ( "encoding/json" "os" "path/filepath" "testing" "time" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/hostguard" "github.com/carlosframework/platform/internal/provision" "github.com/carlosframework/platform/internal/release" "github.com/carlosframework/platform/internal/rollout" "github.com/carlosframework/platform/internal/rootparticipation" "github.com/carlosframework/platform/internal/rootstate" ) func TestPlan14RootFailureProductionOpener(t *testing.T) { if os.Geteuid() != 0 { t.Fatal("root required") } for _, path := range []string{"/var/lib/carlos-claims", "/var/lib/carlos-release", rootRetryProjection} { if err := os.Mkdir(path, 0755); err != nil { t.Fatal("existing fixture", path, err) } path := path t.Cleanup(func() { os.RemoveAll(path) }) } state, err := rootstate.Open(rootparticipation.Namespace, true, false) if err != nil { t.Fatal(err) } c := rootparticipation.Census{Schema: 1, Box: "box", Enrollment: "en", Migration: 1, Revision: 1, LaunchRevision: 1, Complete: true} raw, _ := json.Marshal(c) if err = state.Write("census.json", raw); err != nil { t.Fatal(err) } state.Close() failures, err := rootstate.Open(rollout.RootFailuresDir, true, false) if err != nil { t.Fatal(err) } defer failures.Close() book, err := rollout.InitializeRootFailures(failures, rootUpdatesMigration(c), nil) if err != nil { t.Fatal(err) } rec := provision.Record{Schema: 1, Host: "h.example.com", Account: "bab", App: "km", Channel: "canary/x", Pin: "v1", CreatedAt: time.Now().UTC()} if _, err = book.Record(rollout.FailureRecord{Account: rec.Account, App: rec.App, Host: rec.Host, Build: "v1", HeldDown: []string{rec.Host}}); err != nil { t.Fatal(err) } inc, _ := hostclaim.Incarnation(rec.Account, rec.App, rec.Host, rec.CreatedAt) fences, err := rootstate.Open("/var/lib/carlos-claims/authority/fences", true, false) if err != nil { t.Fatal(err) } defer fences.Close() in := hostguard.RootInstallation{Host: rec.Host, Account: rec.Account, App: rec.App, Record: provision.RecordKey(rec.Account, rec.App, rec.Host), Incarnation: inc, Stamp: 1, Box: c.Box, Enrollment: c.Enrollment, Migration: c.Migration, InventoryRevision: 1} if err = hostguard.NewRootAuthority(fences).RetainMigrationDeny(t.Context(), in, 1); err != nil { t.Fatal(err) } store := release.NewDirStore(t.TempDir()) plan14PutRecord(t, store, rec) claim := hostclaim.Claim{V: 2, Host: rec.Host, Gen: 1, Holder: &hostclaim.Holder{Family: "instance", Key: in.Record, Account: rec.Account, App: rec.App, Incarnation: inc, CreatedAt: rec.CreatedAt, Location: "instances/" + rec.Host, Stamp: 1, Session: 1, WriterBox: c.Box}, Op: hostclaim.Op{Kind: hostclaim.KindServing, Exec: c.Box}} if err = claim.Validate(); err != nil { t.Fatal(err) } key, _ := hostclaim.Key(rec.Host) raw, _ = json.Marshal(claim) if _, err = store.Put(t.Context(), key, raw, ""); err != nil { t.Fatal(err) } seed, public, _ := release.GenerateKey() priv, _ := release.ParseSigningKey(seed, "test") pub, _ := release.ParsePublicKey(public, "test") m := release.Manifest{Schema: 1, App: rec.App, Version: "v1", Kind: "binary", Artifacts: map[string]release.Artifact{runtimePlatform(): {}}} if err = release.Ship(t.Context(), store, rec.Account, m, map[string][]byte{runtimePlatform(): []byte("v1")}); err != nil { t.Fatal(err) } if err = release.Promote(t.Context(), store, priv, rec.Account, rec.App, rec.Channel, "v1"); err != nil { t.Fatal(err) } data, opt := t.TempDir(), t.TempDir() if err = os.Mkdir(filepath.Join(data, rollout.FailureFile), 0700); err != nil { t.Fatal(err) } rt := provision.RouteFor(rec, data, "", false, nil) rt.Account, rt.App, rt.Channel, rt.GoodVersion = "bad", "foreign", "forged", "forged" reg := openReg(t, rt) if err = release.Pass(t.Context(), reg, store, pub, opt, nil, updatesPassOption(data, true)); err != nil { t.Fatal(err) } if v, _ := release.LiveVersion(opt, rec.Host); v != "v1" { t.Fatal("sticky or legacy state held production adoption", v) } opened, err := productionRootUpdates(data)(t.Context(), store, pub) if err != nil || opened == nil { t.Fatal(err) } resolved, err := opened.Resolve(t.Context(), rec.Host) if err != nil || resolved.Route.Account != rec.Account || resolved.Route.App != rec.App || resolved.Failed("v1") { t.Fatal("canonical activation policy", resolved, err) } opened.Close() wire, err := release.SignHostRetry(release.RootSigner(priv), rec.Account, rec.App, rec.Host, "v1", book.ID(), book.PublishedEpoch(rec.Account, rec.App, "v1", rec.Host), time.Now()) if err != nil { t.Fatal(err) } retries := release.ScopedRestartGeneration{Schema: 1, Requests: []release.RestartRequest{{ID: "retry", RequestedAt: time.Now().UTC().Format(time.RFC3339), Scope: release.RestartScope{Kind: release.RestartScopeRetry, Value: rec.Host, Signed: wire}}}} raw, _ = json.Marshal(retries) if _, err = store.Put(t.Context(), release.HostRetryKey(rec.Account, rec.App), raw, ""); err != nil { t.Fatal(err) } opened, err = productionRootUpdates(data)(t.Context(), store, pub) if err != nil { t.Fatal(err) } if _, err = opened.Resolve(t.Context(), rec.Host); err != nil { t.Fatal(err) } opened.Close() book, err = rollout.OpenRootFailures(failures, rootUpdatesMigration(c)) if err != nil || book.HeldDown(rec.Account, rec.App, "v1", rec.Host) { t.Fatal("production retry not settled", err) } _, etag, _ := store.Get(t.Context(), key) claim.Op.Exec = "foreign" raw, _ = json.Marshal(claim) if _, err = store.Put(t.Context(), key, raw, etag); err != nil { t.Fatal(err) } opened, err = productionRootUpdates(data)(t.Context(), store, pub) if err != nil { t.Fatal(err) } defer opened.Close() if _, err = opened.Resolve(t.Context(), rec.Host); err == nil { t.Fatal("foreign controller authority accepted") } }