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 "github.com/carlosframework/platform/internal/rootstate"
8 "net"
9 "path/filepath"
10 "sort"
11 "time"
12
13 "github.com/carlosframework/platform/internal/hostbox"
14 "github.com/carlosframework/platform/internal/hostclaim"
15 "github.com/carlosframework/platform/internal/hostguard"
16 "github.com/carlosframework/platform/internal/provision"
17)
18
19// servingParkRoot composes local root authority with the filesystem ledger.
20// Requests supply identities only; installation paths and receipts are read
21// under root's host lock. Serving activation still waits for recovery witnesses.
22type servingParkRoot struct {
23 root ClaimRootHTTP
24 ledger *servingParkLedger
25 empty servingEmptyAuthority
26}
27
28func newServingParkAuthority(h ClaimRootHTTP, state rootstate.Store, empty servingEmptyAuthority) (servingParkAuthority, error) {
29 if state == nil || h.Journals == nil || h.Journals.Launches == nil || h.Journals.Inventory == nil || h.Journals.Inventory.Remote() || h.Stop.Coordinator.Guard.Root == nil || h.Stop.Authenticate == nil || h.RequireNewWork == nil || h.InventoryActor == nil || empty == nil || !filepath.IsAbs(h.Roots["data"]) || h.Roots["data"] != h.Stop.Coordinator.Guard.DataDir {
30 return nil, hostbox.ErrUnsupported
31 }
32 ledger := &servingParkLedger{state: state, fs: newServingParkFS(h.Roots["data"], state), journal: h.Journals.Launches, now: time.Now}
33 return &servingParkRoot{root: h, ledger: ledger, empty: empty}, nil
34}
35
36func (p *servingParkRoot) withAuthority(ctx context.Context, conn net.Conn, current servingArtifactIdentity, fn func(hostclaim.Observed, hostguard.FenceView, hostbox.Actor, servingArtifactDirectory, uint64) error) error {
37 if conn == nil || fn == nil {
38 return hostbox.ErrUnsupported
39 }
40 return p.withRootInstallation(ctx, current, func() error {
41 // Waiting for root locks must not preserve a stale peer authentication.
42 peer, err := p.root.Stop.Authenticate(ctx, conn)
43 if err != nil {
44 return err
45 }
46 if peer.UID == 0 || peer.PID <= 0 || peer.ProcessStart == "" || peer.BootID == "" || peer.ControllerID == "" {
47 return ErrClaimStopIdentity
48 }
49 return nil
50 }, fn)
51}
52
53// withRootInstallation is also used by root startup to replay private intents.
54// It grants no new admission, enrollment, stop receipt or claim tag.
55func (p *servingParkRoot) withRootInstallation(ctx context.Context, current servingArtifactIdentity, check func() error, fn func(hostclaim.Observed, hostguard.FenceView, hostbox.Actor, servingArtifactDirectory, uint64) error) error {
56 if ctx == nil || check == nil || p == nil || p.ledger == nil || p.root.Stop.Authenticate == nil || p.root.RequireNewWork == nil || p.root.InventoryActor == nil || p.root.Journals == nil || p.root.Journals.Inventory == nil || p.root.Journals.Inventory.Remote() || p.root.Stop.Coordinator.Guard.Root == nil || fn == nil {
57 return hostbox.ErrUnsupported
58 }
59 data := p.root.Roots["data"]
60 if !filepath.IsAbs(data) || data != p.root.Stop.Coordinator.Guard.DataDir {
61 return hostbox.ErrChanged
62 }
63 return p.ledger.withNamespace(ctx, func() error {
64 return p.root.Stop.Coordinator.Guard.WithServingRetirement(ctx, current.Host, hostguard.Captured{Incarnation: current.Incarnation, Stamp: current.Stamp}, func(o hostclaim.Observed, view hostguard.FenceView) error {
65 if err := check(); err != nil {
66 return err
67 }
68 if err := p.root.RequireNewWork(ctx); err != nil {
69 return err
70 }
71 actor, err := p.root.InventoryActor(ctx)
72 if err != nil {
73 return err
74 }
75 if !actor.Eligible || actor.Quarantined || actor.Enrollment == "" || actor.Revision == 0 || actor.Binding == "" {
76 return hostbox.ErrChanged
77 }
78 actual, err := servingWriterIdentity(o, actor.Box, actor.Region)
79 if err != nil || actual != current || o.ETag == "" {
80 return hostbox.ErrChanged
81 }
82 in, revision, err := p.root.Journals.Inventory.Snapshot(ctx, current.Host, view.Captured)
83 if err != nil {
84 return err
85 }
86 dir, err := servingParkStateDirectory(data, o, view.Installation, in, revision, actor)
87 if err != nil {
88 return err
89 }
90 db, err := filepath.Rel(data, provision.NestedDBPath(data, current.Host))
91 if err != nil {
92 return err
93 }
94 tags, err := servingInventoryPair(o.Claim.Inventory[actor.Box], filepath.ToSlash(db))
95 if err != nil || tags > current.Stamp {
96 return hostbox.ErrChanged
97 }
98 if err = ctx.Err(); err != nil {
99 return err
100 }
101 fresh, err := p.root.Stop.Coordinator.Guard.Claims.Read(ctx, current.Host)
102 if err != nil {
103 return err
104 }
105 if fresh.ETag != o.ETag || fresh.Absent {
106 return hostbox.ErrChanged
107 }
108 return fn(o, view, actor, dir, tags)
109 })
110 })
111}
112
113func servingRootStops(ctx context.Context, view hostguard.FenceView, prior servingArtifactIdentity, actor hostbox.Actor) ([]hostguard.FenceReceipt, error) {
114 if prior.Host != view.Host || prior.Incarnation != view.Installation.Incarnation || prior.Box != actor.Box || prior.Region != actor.Region || prior.Stamp == 0 || prior.Session != prior.Stamp {
115 return nil, hostbox.ErrChanged
116 }
117 known := view.Captured == (hostguard.Captured{Incarnation: prior.Incarnation, Stamp: prior.Stamp})
118 for _, retired := range view.Retired {
119 known = known || retired == (hostguard.Captured{Incarnation: prior.Incarnation, Stamp: prior.Stamp})
120 }
121 if !known {
122 return nil, hostguard.ErrNoFenceReceipt
123 }
124 var out []hostguard.FenceReceipt
125 seen := make(map[string]bool)
126 for _, receipt := range view.Receipts {
127 t := receipt.Target
128 if t.Host != prior.Host || t.Incarnation != prior.Incarnation || t.Stamp != prior.Stamp || t.Box != prior.Box || t.Op != "" || t.Phase != "" || t.ObligationID != "" {
129 continue
130 }
131 if t.Session != prior.Session || t.Region != prior.Region || t.Enrollment != actor.Enrollment || !servingPriorBinding(t.DirectoryRevision, actor) || t.FenceID == "" || seen[t.FenceID] || verifyRetirementReceipt(ctx, receipt) != nil {
132 return nil, ErrClaimStopReceipt
133 }
134 seen[t.FenceID] = true
135 out = append(out, receipt)
136 }
137 if len(out) == 0 {
138 return nil, hostguard.ErrNoFenceReceipt
139 }
140 sort.Slice(out, func(i, j int) bool { return out[i].Target.FenceID < out[j].Target.FenceID })
141 return out, nil
142}
143
144func (p *servingParkRoot) parkServingDatabase(ctx context.Context, conn net.Conn, current, requestedPrior servingArtifactIdentity) (servingParkAuthorization, error) {
145 var out servingParkAuthorization
146 resumeLegacy := false
147 err := p.withAuthority(ctx, conn, current, func(o hostclaim.Observed, view hostguard.FenceView, actor hostbox.Actor, dir servingArtifactDirectory, tags uint64) error {
148 var a servingParkAuthorization
149 var saved *servingParkRecord
150 var continuation []hostguard.FenceReceipt
151 var next servingParkAuthorization
152 slot, err := p.ledger.slot(servingParkAuthorization{Successor: current})
153 if err != nil {
154 return err
155 }
156 id := slot.Pending
157 if id == "" && tags == current.Stamp {
158 id = slot.Last
159 }
160 if id != "" {
161 r, err := p.ledger.read(id)
162 if err != nil {
163 return err
164 }
165 a, saved = r.Authorization, &r
166 if a.Legacy {
167 if requestedPrior != (servingArtifactIdentity{}) {
168 prior, e := servingPredecessor(o.Claim, current, tags, requestedPrior.Generation)
169 if e != nil || prior != requestedPrior {
170 return hostbox.ErrChanged
171 }
172 }
173 resumeLegacy = true
174 return nil
175 }
176 if a.Directory != dir {
177 return hostbox.ErrChanged
178 }
179 active := r.activeAuthorization()
180 if err = validateServingRootHistory(ctx, o, view, actor, r); err != nil {
181 return err
182 }
183 if requestedPrior != (servingArtifactIdentity{}) {
184 stamp := tags
185 if tags == current.Stamp {
186 // Retagging has consumed the old pair. An original request
187 // still selects the predecessor saved before that change.
188 stamp = active.TagsFrom
189 }
190 prior, e := servingPredecessor(o.Claim, current, stamp, requestedPrior.Generation)
191 if e != nil || prior != requestedPrior {
192 return hostbox.ErrChanged
193 }
194 }
195 if tags != active.TagsFrom && tags != active.Successor.Stamp && tags != current.Stamp {
196 return hostbox.ErrChanged
197 }
198 if active.Successor != current {
199 if r.Complete || active.Successor.Stamp >= current.Stamp {
200 return hostbox.ErrChanged
201 }
202 prior, e := servingPredecessor(o.Claim, current, active.Successor.Stamp, active.Successor.Generation)
203 if e != nil || prior != active.Successor {
204 return hostbox.ErrChanged
205 }
206 continuation, err = servingRootStops(ctx, view, prior, actor)
207 if err != nil {
208 return err
209 }
210 next = active
211 next.Successor, next.TagsFrom = current, tags
212 }
213 } else {
214 if requestedPrior == (servingArtifactIdentity{}) {
215 return hostbox.ErrChanged
216 }
217 prior, err := servingPredecessor(o.Claim, current, tags, requestedPrior.Generation)
218 if err != nil || prior != requestedPrior {
219 return hostbox.ErrChanged
220 }
221 a = servingParkAuthorization{TagsFrom: tags, Predecessor: prior, OriginalSuccessor: current, Successor: current, Directory: dir}
222 a.ID = servingRetirementID(prior, current, false)
223 }
224 prior, err := servingPredecessor(o.Claim, current, a.Predecessor.Stamp, a.Predecessor.Generation)
225 if err != nil || prior != a.Predecessor {
226 return hostbox.ErrChanged
227 }
228 receipts, err := servingRootStops(ctx, view, prior, actor)
229 if err != nil {
230 return err
231 }
232 if saved != nil && !sameServingRootStops(saved.Receipts, receipts) {
233 return ErrClaimStopReceipt
234 }
235 if err = p.prepareServingNamespace(ctx, a, receipts, nil); err != nil {
236 return err
237 }
238 r, err := p.ledger.parkLocked(ctx, a, receipts)
239 if err != nil {
240 return err
241 }
242 if err = p.ledger.fs.CheckParked(&r); err != nil {
243 return err
244 }
245 if next.ID != "" {
246 r, err = p.ledger.continueLocked(ctx, a.ID, next, continuation)
247 if err != nil {
248 return err
249 }
250 }
251 out = r.activeAuthorization()
252 return nil
253 })
254 if err == nil && resumeLegacy {
255 return p.parkLegacyServingDatabase(ctx, conn, current, servingLegacyPredecessor{})
256 }
257 return out, err
258}
259
260func (p *servingParkRoot) completeServingDatabasePark(ctx context.Context, conn net.Conn, id string, current servingArtifactIdentity) error {
261 return p.withAuthority(ctx, conn, current, func(o hostclaim.Observed, view hostguard.FenceView, actor hostbox.Actor, dir servingArtifactDirectory, tags uint64) error {
262 r, err := p.ledger.read(id)
263 if err != nil {
264 return err
265 }
266 a := r.activeAuthorization()
267 if err = validateServingRootHistory(ctx, o, view, actor, r); err != nil {
268 return err
269 }
270 if a.Successor != current || a.Directory != dir || tags != current.Stamp {
271 return hostbox.ErrChanged
272 }
273 if a.Legacy {
274 if err = p.validateLegacyRootEvidence(ctx, o, view, actor, r.Authorization, r.LegacyEvidence, r.Receipts); err != nil {
275 return err
276 }
277 if err = p.ledger.legacyUnlaunched(ctx, current, actor.Enrollment); err != nil {
278 return err
279 }
280 return p.ledger.completeLocked(ctx, id, current)
281 }
282 prior, err := servingPredecessor(o.Claim, current, a.Predecessor.Stamp, a.Predecessor.Generation)
283 if err != nil || prior != a.Predecessor {
284 return hostbox.ErrChanged
285 }
286 receipts, err := servingRootStops(ctx, view, prior, actor)
287 if err != nil {
288 return err
289 }
290 if !sameServingRootStops(r.Receipts, receipts) {
291 return ErrClaimStopReceipt
292 }
293 return p.ledger.completeLocked(ctx, id, current)
294 })
295}
296
297// Every recorded successor must still be a retained local admission; each
298// transition carries the root stop of the successor it superseded.
299func validateServingRootHistory(ctx context.Context, o hostclaim.Observed, view hostguard.FenceView, actor hostbox.Actor, r servingParkRecord) error {
300 current, err := servingWriterIdentity(o, actor.Box, actor.Region)
301 if err != nil {
302 return err
303 }
304 previous := r.Authorization.Successor
305 for _, continuation := range r.Continuations {
306 prior, err := servingPredecessor(o.Claim, current, previous.Stamp, previous.Generation)
307 if err != nil || prior != previous {
308 return hostbox.ErrChanged
309 }
310 receipts, err := servingRootStops(ctx, view, previous, actor)
311 if err != nil {
312 return err
313 }
314 if !sameServingRootStops(receipts, continuation.Receipts) {
315 return ErrClaimStopReceipt
316 }
317 previous = continuation.Authorization.Successor
318 }
319 if previous == current {
320 return nil
321 }
322 prior, err := servingPredecessor(o.Claim, current, previous.Stamp, previous.Generation)
323 if err != nil || prior != previous {
324 return hostbox.ErrChanged
325 }
326 return nil
327}
328