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 "strconv"
6 "strings"
7
8 "github.com/carlosframework/platform/internal/hostclaim"
9)
10
11// writerStopPrefix marks the obligation component of an operation stop that
12// names the operation's own admitted writer session, not a chore (claim Plan
13// 9 D4). A landing target stops its probe before a restarted probe or a cold
14// wake is admitted; no chore exists for that, and none is invented.
15const writerStopPrefix = "writer-session-"
16
17// WriterStopObligation is the obligation component with which an operation's
18// executor stops writer session s under that operation. It is deterministic,
19// so a repeated request for the same session meets the same fence and
20// receipt, and every later session gets its own.
21func WriterStopObligation(session uint64) string {
22 return writerStopPrefix + strconv.FormatUint(session, 10)
23}
24
25func writerStop(obligation string) bool {
26 return strings.HasPrefix(obligation, writerStopPrefix)
27}
28
29// LandingWriter returns the admission of the claim's current writer session
30// when that session is box's own landing writer: the op is a landing at a
31// phase of the landing's session row (probing, probed, committed; claim Plan
32// 9 D1), box executes it and is holder.writer_box, and the session was
33// admitted exactly once, cold, on box, into the held generation. A landing's
34// first probe, whose predecessor is the source's session on another box, is
35// never box's writer here.
36func LandingWriter(c *hostclaim.Claim, box string) (hostclaim.AdmissionRecord, bool) {
37 return operationWriter(c, box, hostclaim.KindLanding)
38}
39
40func operationWriter(c *hostclaim.Claim, box, kind string) (hostclaim.AdmissionRecord, bool) {
41 if c == nil || c.Holder == nil || box == "" || c.Holder.Session == 0 {
42 return hostclaim.AdmissionRecord{}, false
43 }
44 h := c.Holder
45 if c.Op.Kind != kind || c.Op.ID == "" || c.Op.Exec != box || h.WriterBox != box ||
46 !hostclaim.SessionPhase(kind, c.Op.Phase) {
47 return hostclaim.AdmissionRecord{}, false
48 }
49 var found hostclaim.AdmissionRecord
50 matched := 0
51 for _, a := range h.Admissions {
52 if a.Session == h.Session {
53 if a.Box != box || a.Mode != "cold" || a.Generation != h.Generation {
54 return hostclaim.AdmissionRecord{}, false
55 }
56 found = a
57 matched++
58 }
59 }
60 return found, matched == 1
61}
62
63// LandingWriterStop is the obligation component with which box may ask root
64// to stop the claim's current writer session as the landing's own writer:
65// LandingWriter holds, the session was admitted in region, and no chore of
66// the claim carries the same id. Any other claim has no writer stop.
67func LandingWriterStop(c *hostclaim.Claim, box, region string) (string, bool) {
68 return operationWriterStop(c, box, region, hostclaim.KindLanding)
69}
70
71// RelocationWriterStop authorizes only this box's admitted relocation writer.
72func RelocationWriterStop(c *hostclaim.Claim, box, region string) (string, bool) {
73 return operationWriterStop(c, box, region, hostclaim.KindRelocating)
74}
75
76func operationWriterStop(c *hostclaim.Claim, box, region, kind string) (string, bool) {
77 a, ok := operationWriter(c, box, kind)
78 if !ok || region == "" || a.Region != region {
79 return "", false
80 }
81 id := WriterStopObligation(c.Holder.Session)
82 for _, o := range c.Obligations {
83 if o.ID == id {
84 return "", false
85 }
86 }
87 return id, true
88}
89
90// CompletedWriterStops returns every completed writer-session stop journalled
91// for exactly the captured installation, under op, on box. Fence receipts are
92// append-only, so a receipt returned here stays effective.
93func (g Guard) CompletedWriterStops(ctx context.Context, host string, captured Captured, op, box string) ([]FenceReceipt, error) {
94 if ctx == nil || captured.Incarnation == "" || captured.Stamp == 0 || op == "" || box == "" {
95 return nil, ErrInvalidInput
96 }
97 var out []FenceReceipt
98 err := g.fenceLocked(ctx, host, captured, func(d *recordDir) error {
99 r, err := d.read(host)
100 if err != nil {
101 return err
102 }
103 for _, receipt := range r.FenceJournal {
104 t := receipt.Target
105 if t.Host == host && t.Incarnation == captured.Incarnation && t.Stamp == captured.Stamp && t.Op == op && t.Box == box && t.ObligationID == WriterStopObligation(t.Session) {
106 out = append(out, receipt)
107 }
108 }
109 return nil
110 })
111 return out, err
112}
113
114// CompletedServingStops selects completed stops for one serving installation.
115// Operation receipts cannot stand in for a serving stop.
116func (g Guard) CompletedServingStops(ctx context.Context, host string, captured Captured, session uint64, box string) ([]FenceReceipt, error) {
117 if ctx == nil || host == "" || captured.Incarnation == "" || captured.Stamp == 0 || session == 0 || box == "" {
118 return nil, ErrInvalidInput
119 }
120 var out []FenceReceipt
121 err := g.fenceLocked(ctx, host, captured, func(d *recordDir) error {
122 r, err := d.read(host)
123 if err != nil {
124 return err
125 }
126 for _, receipt := range r.FenceJournal {
127 t := receipt.Target
128 if t.Host != host || t.Incarnation != captured.Incarnation || t.Stamp != captured.Stamp || t.Box != box || t.Op != "" || t.Phase != "" || t.ObligationID != "" {
129 continue
130 }
131 if t.Session != session {
132 return ErrNoFenceReceipt
133 }
134 out = append(out, receipt)
135 }
136 if len(out) == 0 {
137 return ErrNoFenceReceipt
138 }
139 return nil
140 })
141 if err != nil {
142 return nil, err
143 }
144 return out, nil
145}
146
147func RecoveryWriter(c *hostclaim.Claim, box string) (hostclaim.AdmissionRecord, bool) {
148 if c == nil || (c.Op.Kind != hostclaim.KindRestoring && c.Op.Kind != hostclaim.KindReverting) || c.Op.Intent["source"] != box {
149 return hostclaim.AdmissionRecord{}, false
150 }
151 return operationWriter(c, box, c.Op.Kind)
152}
153func RecoveryWriterStop(c *hostclaim.Claim, box, region string) (string, bool) {
154 if c != nil && c.Holder != nil && region != "" && box != "" && (c.Op.Kind == hostclaim.KindRestoring || c.Op.Kind == hostclaim.KindReverting) && c.Op.ID != "" && c.Op.Phase == hostclaim.PhaseProbing && c.Op.Exec == box && c.Op.Intent["source"] == box {
155 h := c.Holder
156 stamp, err := strconv.ParseUint(c.Op.Journal[hostclaim.JournalSourceStamp], 10, 64)
157 if err == nil && stamp > 0 && h.Stamp == stamp && h.Session == 0 && h.Generation == "" && h.WriterBox == "" && len(h.Admissions) == 0 && c.Op.Journal[hostclaim.JournalCutSession] == "0" && c.Op.Journal[hostclaim.JournalSrcGen] == "" {
158 id := WriterStopObligation(0)
159 for _, ob := range c.Obligations {
160 if ob.ID == id {
161 return "", false
162 }
163 }
164 return id, true
165 }
166 }
167 if _, ok := RecoveryWriter(c, box); !ok {
168 return "", false
169 }
170 return operationWriterStop(c, box, region, c.Op.Kind)
171}
172