carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package hostguard
2
3import (
4 "context"
5 "github.com/carlosframework/platform/internal/hostclaim"
6)
7
8func (g Guard) BeginColdHandover(ctx context.Context, prior, barrier FenceTarget, check func(*hostclaim.Claim) error) error {
9 return g.beginColdHandover(ctx, prior, barrier, nil, check)
10}
11
12func (g Guard) BeginServingHandover(ctx context.Context, prior, barrier FenceTarget, installation RootInstallation, check func(*hostclaim.Claim) error) error {
13 if g.Root == nil || prior.Op != "" || prior.Phase != "" || prior.ObligationID != "" || barrier.Op != "" || barrier.Phase != "" || barrier.ObligationID != "" || installation.Empty || installation.Host != prior.Host || installation.Incarnation != prior.Incarnation || installation.Stamp != prior.Stamp || installation.Box != prior.Box || installation.Enrollment != prior.Enrollment || installation.InventoryRevision == 0 {
14 return ErrInvalidInput
15 }
16 return g.beginColdHandover(ctx, prior, barrier, &installation, check)
17}
18
19func (g Guard) beginColdHandover(ctx context.Context, prior, barrier FenceTarget, installation *RootInstallation, check func(*hostclaim.Claim) error) error {
20 return g.locked(ctx, prior.Host, func(d *recordDir) error {
21 if ((installation == nil || prior.Session != 0) && prior.validate(prior.Host) != nil) || barrier.validate(prior.Host) != nil || prior.Host != barrier.Host || prior.Incarnation != barrier.Incarnation || barrier.Stamp <= prior.Stamp || barrier.Session <= prior.Session || barrier.Stamp != barrier.Session || check == nil {
22 return ErrInvalidInput
23 }
24 claim, err := g.fresh(ctx, prior.Host)
25 if err != nil {
26 return err
27 }
28 if err = check(claim); err != nil {
29 return err
30 }
31 if !barrier.matchesSession(claim, Captured{Incarnation: barrier.Incarnation, Stamp: barrier.Stamp}) {
32 return ErrFenceTarget
33 }
34 r, err := d.read(prior.Host)
35 if err != nil {
36 return err
37 }
38 if installation != nil && (r.installation != *installation || r.quarantined || r.pendingInstallation != nil) {
39 return ErrRecordIncarnation
40 }
41 found := false
42 for _, receipt := range r.FenceJournal {
43 if receipt.Target == prior {
44 found = true
45 }
46 }
47 if !found && (installation == nil || prior.Session != 0) {
48 return ErrNoFenceReceipt
49 }
50 if installation != nil && prior.Session == 0 && (prior.FenceID != "" || (r.ActiveFence != nil && *r.ActiveFence != barrier)) {
51 return ErrFenceTarget
52 }
53 if r.ActiveFence != nil && *r.ActiveFence == barrier {
54 return d.sync(prior.Host)
55 }
56 if r.Incarnation != prior.Incarnation || (r.ActiveStamp != 0 && r.ActiveStamp != prior.Stamp) {
57 return ErrStampMismatch
58 }
59 if r.ActiveFence != nil && *r.ActiveFence != prior {
60 return ErrFenceActive
61 }
62 copied := barrier
63 r.ActiveFence = &copied
64 if r.root {
65 r.barrier = &copied
66 }
67 return d.write(r)
68 })
69}
70func (g Guard) CompleteColdHandover(ctx context.Context, barrier FenceTarget, alreadyComplete bool, check func(*hostclaim.Claim) error, fn func() error) error {
71 return g.locked(ctx, barrier.Host, func(d *recordDir) error {
72 claim, err := g.fresh(ctx, barrier.Host)
73 if err != nil {
74 return err
75 }
76 if check == nil || fn == nil {
77 return ErrInvalidInput
78 }
79 if err = check(claim); err != nil {
80 return err
81 }
82 r, err := d.read(barrier.Host)
83 if err != nil {
84 return err
85 }
86 if alreadyComplete && r.Incarnation == barrier.Incarnation && r.ActiveStamp == barrier.Stamp && r.ActiveFence == nil {
87 if g.Root == nil {
88 return d.sync(barrier.Host)
89 }
90 if err := g.refreshRootInventory(ctx, &r); err != nil {
91 return err
92 }
93 return d.write(r)
94 }
95 if r.ActiveFence == nil || *r.ActiveFence != barrier || r.Incarnation != barrier.Incarnation {
96 return ErrFenceTarget
97 }
98 if err = fn(); err != nil {
99 return err
100 }
101 if r.ActiveStamp != barrier.Stamp {
102 r.retire()
103 r.ActiveStamp = barrier.Stamp
104 }
105 r.ActiveFence = nil
106 r.barrier = nil
107 if err := g.refreshRootInventory(ctx, &r); err != nil {
108 return err
109 }
110 return d.write(r)
111 })
112}
113
114func (g Guard) CompletedColdPredecessor(ctx context.Context, host string, captured Captured, session uint64, op, phase string) (FenceReceipt, error) {
115 var found FenceReceipt
116 err := g.fenceLocked(ctx, host, captured, func(d *recordDir) error {
117 r, err := d.read(host)
118 if err != nil {
119 return err
120 }
121 if r.root && r.ActiveFence != nil {
122 t := *r.ActiveFence
123 if t.Host == host && t.Incarnation == captured.Incarnation && t.Stamp == captured.Stamp && t.Session == session && t.Op == op && t.Phase == phase {
124 for _, receipt := range r.FenceJournal {
125 if receipt.Target == t {
126 found = receipt
127 return nil
128 }
129 }
130 return ErrNoFenceReceipt
131 }
132 }
133 for _, receipt := range r.FenceJournal {
134 t := receipt.Target
135 if t.Host == host && t.Incarnation == captured.Incarnation && t.Stamp == captured.Stamp && t.Session == session && t.Op == op && t.Phase == phase {
136 if found.Target.FenceID != "" && found.Target != t {
137 return ErrFenceTarget
138 }
139 found = receipt
140 }
141 }
142 if found.Target.FenceID == "" {
143 return ErrNoFenceReceipt
144 }
145 return nil
146 })
147 return found, err
148}
149
150func (g Guard) AbandonColdHandover(ctx context.Context, barrier FenceTarget) error {
151 if g.Root == nil {
152 return ErrUnsupported
153 }
154 return g.readLocked(ctx, barrier.Host, func(d *recordDir) error {
155 c, err := g.fresh(ctx, barrier.Host)
156 if err != nil {
157 return err
158 }
159 if c.Holder.Incarnation == barrier.Incarnation && (c.Holder.Stamp < barrier.Stamp || c.Holder.Session <= barrier.Session) {
160 return ErrFenced
161 }
162 r, err := d.read(barrier.Host)
163 if err != nil {
164 return err
165 }
166 if r.barrier == nil {
167 return nil
168 }
169 if *r.barrier != barrier {
170 return ErrFenceTarget
171 }
172 r.rootRetired = append(r.rootRetired, Captured{Incarnation: barrier.Incarnation, Stamp: barrier.Stamp})
173 r.barrier = nil
174 if r.ActiveFence != nil && *r.ActiveFence == barrier {
175 r.ActiveFence = nil
176 }
177 return d.write(r)
178 })
179}
180
181// SettleObsoleteColdHandover settles an interrupted cold handover from prior
182// whose journaled successor root found obsolete at startup: stamped under a
183// box binding other than the one in root's startup roster read (roster
184// binding B3). It is fenced: it runs under the host lock every handover step
185// takes, and only for a prior whose completed fence receipt this record
186// holds. It needs no claim session advance, unlike AbandonColdHandover: the
187// trigger is the binding change, decided by root from its own roster read.
188//
189// It undoes BeginColdHandover: the barrier is cleared and the deny slot it
190// took goes back to the prior's completed fence, the state the prior's stop
191// left. It records the successor's stamp as retired so it is never
192// installed or reused, and then runs record, which persists the settlement
193// in the handover's own journal. The prior keeps its historical cleanup
194// authority: its stamp stays the active one, its completed fence and
195// receipt stay, so its cleanup stop and retirement still complete. Settling
196// again, with the barrier already cleared, only re-runs record.
197func (g Guard) SettleObsoleteColdHandover(ctx context.Context, prior, barrier FenceTarget, record func() error) error {
198 if g.Root == nil {
199 return ErrUnsupported
200 }
201 if record == nil || prior.validate(prior.Host) != nil || barrier.validate(prior.Host) != nil || prior.Host != barrier.Host || prior.Incarnation != barrier.Incarnation || barrier.Stamp <= prior.Stamp || barrier.Session <= prior.Session || barrier.Stamp != barrier.Session {
202 return ErrInvalidInput
203 }
204 return g.locked(ctx, prior.Host, func(d *recordDir) error {
205 r, err := d.read(prior.Host)
206 if err != nil {
207 return err
208 }
209 successor := Captured{Incarnation: barrier.Incarnation, Stamp: barrier.Stamp}
210 retired := false
211 for _, old := range r.rootRetired {
212 if old == successor {
213 retired = true
214 }
215 }
216 // Already settled: only the journal's record may be missing. Whatever
217 // happened to the host since (a new successor, a new stop) stands.
218 if retired && r.barrier == nil && (r.ActiveFence == nil || *r.ActiveFence != barrier) {
219 return record()
220 }
221 found := false
222 for _, receipt := range r.FenceJournal {
223 if receipt.Target == prior {
224 found = true
225 }
226 }
227 if !found {
228 return ErrNoFenceReceipt
229 }
230 if r.Incarnation != prior.Incarnation || r.ActiveStamp != prior.Stamp {
231 return ErrStampMismatch
232 }
233 if r.barrier != nil && *r.barrier != barrier || r.ActiveFence != nil && *r.ActiveFence != barrier && *r.ActiveFence != prior {
234 return ErrFenceTarget
235 }
236 if !retired {
237 r.rootRetired = append(r.rootRetired, successor)
238 }
239 r.barrier = nil
240 if r.ActiveFence != nil && *r.ActiveFence == barrier {
241 restored := prior
242 r.ActiveFence = &restored
243 }
244 if err := d.write(r); err != nil {
245 return err
246 }
247 return record()
248 })
249}
250