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
2
3package host
4
5import (
6 "context"
7 "encoding/json"
8 "errors"
9 "net"
10 "os"
11 "path/filepath"
12 "testing"
13
14 "github.com/carlosframework/platform/internal/hostbox"
15 "github.com/carlosframework/platform/internal/hostclaim"
16 execlease "github.com/carlosframework/platform/internal/hostexec"
17 "github.com/carlosframework/platform/internal/hostguard"
18 "github.com/carlosframework/platform/internal/rootparticipation"
19)
20
21func productionExecutionFixture(t *testing.T, kind string) (*recoveryFixture, ClaimRootHTTP, *launchParticipationDisk, *claimExecutionRuntime, *productionClaimLocal) {
22 t.Helper()
23 f, root, _ := plan14InstallRegistration(t, kind, "checking")
24 if err := os.Mkdir(filepath.Join(root.Roots["data"], ".hostexec"), 0700); err != nil {
25 t.Fatal(err)
26 }
27 root.Reconcile.Census.SharedPaths["data"] = append(root.Reconcile.Census.SharedPaths["data"], ".hostexec")
28 disk := &launchParticipationDisk{&rollbackDisk{dir: t.TempDir()}}
29 raw, _ := json.Marshal(rootparticipation.Census{Schema: 1, Box: "box-1", Enrollment: "en", Migration: 1, Revision: 1, LaunchRevision: 1, Complete: true})
30 if err := disk.Write("census.json", raw); err != nil {
31 t.Fatal(err)
32 }
33 boot, err := execlease.BootID()
34 if err != nil {
35 t.Fatal(err)
36 }
37 start, err := claimLaunchProcessStart("/proc", os.Getpid())
38 if err != nil {
39 t.Fatal(err)
40 }
41 root.Stop.Authenticate = func(context.Context, net.Conn) (StopPeer, error) {
42 return StopPeer{UID: 1001, PID: os.Getpid(), ProcessStart: start, BootID: boot, ControllerID: "agent"}, nil
43 }
44 root.Stop.Coordinator.Kernel = stopKernelFunc(func(context.Context, hostguard.FenceTarget, StopInventory, DrainAcknowledgement) (StopKernelEvidence, error) {
45 return StopKernelEvidence{}, ErrClaimStopInventory
46 })
47 runtime, err := newClaimExecutionRuntime(root, disk)
48 if err != nil {
49 t.Fatal(err)
50 }
51 root.Execution, root.executionRuntime = runtime.broker, runtime
52 t.Cleanup(func() { _ = runtime.Close(context.Background()) })
53 socket := serveClaimRoot(t, root)
54 p := &productionClaimLocal{Resources: ClaimRuntimeResources{Mode: "agent", DataDir: root.Roots["data"]}, Store: f.claims, lifetime: t.Context(),
55 assembly: &ClaimAssembly{Claims: f.claims, Guard: root.Stop.Coordinator.Guard, Wake: &ClaimWakeAssembly{}, Launches: root.Launches, Reconcile: &root.Reconcile},
56 executionCall: rootExecutionClient(socket), slots: make(map[string]*claimParticipation)}
57 t.Cleanup(func() { _ = p.Close(context.Background()) })
58 return f, root, disk, runtime, p
59}
60
61func TestPlan14ExecutionProductionClientSelection(t *testing.T) {
62 f, root, disk, runtime, local := productionExecutionFixture(t, hostclaim.KindLanding)
63 actor, _ := root.InventoryActor(t.Context())
64 o := f.read()
65 if err := local.prepareWith(t.Context(), actor, o); err != nil {
66 t.Fatal(err)
67 }
68 if _, ok := local.slots[o.Host].Controller.(*execlease.RootClient); !ok {
69 t.Fatal("agent selected a second lease owner")
70 }
71 raw, err := disk.Read(o.Host + ".json")
72 if err != nil {
73 t.Fatal(err)
74 }
75 var record execlease.ExecutionRecord
76 if err := rootparticipation.Decode(raw, &record); err != nil {
77 t.Fatal(err)
78 }
79 if record.Status != "active" || !record.Authority.ParticipationOnly || record.LeaseETag == "" {
80 t.Fatalf("root did not acquire participation: %+v", record)
81 }
82 selection := hostbox.Selection{Box: actor.Box, Region: actor.Region, Captured: hostguard.Captured{Incarnation: o.Claim.Holder.Incarnation, Stamp: o.Claim.Holder.Stamp}}
83 if err := local.Check(t.Context(), actor, o.Claim, selection); err != nil {
84 t.Fatal(err)
85 }
86 if err := local.prepareWith(t.Context(), actor, o); err != nil {
87 t.Fatal(err)
88 }
89 if _, err := execlease.ReadRoot(t.Context(), disk, execlease.SystemClock{}, record.Request, false); err == nil {
90 t.Fatal("preinstallation participation granted root effects")
91 }
92 if err := runtime.Close(t.Context()); err != nil {
93 t.Fatal(err)
94 }
95 if err := local.prepareWith(t.Context(), actor, o); err == nil {
96 t.Fatal("closed broker admission remained active")
97 }
98 if err := rootparticipation.Decode(mustExecutionRead(t, disk, o.Host), &record); err != nil || record.Status != "lost" {
99 t.Fatalf("shutdown discarded obligation: %+v %v", record, err)
100 }
101}
102
103func mustExecutionRead(t *testing.T, disk *launchParticipationDisk, host string) []byte {
104 t.Helper()
105 raw, err := disk.Read(host + ".json")
106 if err != nil {
107 t.Fatal(err)
108 }
109 return raw
110}
111
112func TestPlan14ExecutionProductionServingBeforeInstallation(t *testing.T) {
113 f, root, disk, _, local := productionExecutionFixture(t, hostclaim.KindServing)
114 actor, _ := root.InventoryActor(t.Context())
115 o := f.read()
116 selection := hostbox.Selection{Box: actor.Box, Region: actor.Region, Captured: hostguard.Captured{Incarnation: o.Claim.Holder.Incarnation, Stamp: o.Claim.Holder.Stamp}}
117 if err := local.Check(t.Context(), actor, o.Claim, selection); err == nil {
118 t.Fatal("serving had no root observation")
119 }
120 if err := local.prepareWith(t.Context(), actor, o); err != nil {
121 t.Fatal(err)
122 }
123 if err := local.Check(t.Context(), actor, o.Claim, selection); err != nil {
124 t.Fatal(err)
125 }
126 if _, err := disk.Read(o.Host + ".json"); !errors.Is(err, os.ErrNotExist) {
127 t.Fatal("serving acquired an operation lease", err)
128 }
129 if err := disk.Write("update.json", []byte(`{}`)); err != nil {
130 t.Fatal(err)
131 }
132 if err := local.prepareWith(t.Context(), actor, o); err == nil {
133 t.Fatal("update marker did not revoke preparation")
134 }
135 if err := local.Check(t.Context(), actor, o.Claim, selection); err == nil {
136 t.Fatal("failed preparation retained serving admission")
137 }
138}
139
140func TestPlan14ExecutionProductionSettlement(t *testing.T) {
141 f, root, disk, _, local := productionExecutionFixture(t, hostclaim.KindLanding)
142 actor, _ := root.InventoryActor(t.Context())
143 if err := local.prepareWith(t.Context(), actor, f.read()); err != nil {
144 t.Fatal(err)
145 }
146 c := local.slots[f.read().Host].Controller
147 if err := c.Close(t.Context()); err == nil {
148 t.Fatal("CC9 cleared participation while still a participant")
149 }
150 var r execlease.ExecutionRecord
151 if err := rootparticipation.Decode(mustExecutionRead(t, disk, f.read().Host), &r); err != nil || r.Status != "lost" {
152 t.Fatalf("CC9 failure lost denial: %+v %v", r, err)
153 }
154 _, err := hostclaim.Update(t.Context(), f.claims, f.read(), func(c *hostclaim.Claim) error {
155 c.Op = hostclaim.Op{Kind: hostclaim.KindServing, Exec: actor.Box}
156 return nil
157 })
158 if err != nil {
159 t.Fatal(err)
160 }
161 if err := local.prepareWith(t.Context(), actor, f.read()); err != nil {
162 t.Fatal(err)
163 }
164 if err := rootparticipation.Decode(mustExecutionRead(t, disk, f.read().Host), &r); err != nil || r.Status != "settled" {
165 t.Fatalf("CC9 failed to settle: %+v %v", r, err)
166 }
167}
168