carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package release
2
3import "fmt"
4
5// HeldWakeInput is what the held wake check decides on (settings spec §4.3
6// rule 3, "The wake checks too"; ruling R-HELD-PURE): the registry columns
7// and root's records the agent reads for the wake, read by the caller so the
8// decision stays pure. The host's heldTarget and the update simulation's
9// wake both call DecideHeldWake with it.
10type HeldWakeInput struct {
11 Settings string // the route's update_settings column, as the registry holds it
12 Build BuildStateCopy // the registry copy, refreshed for the wake
13 Consumed *Consumed // root's file for the host; nil when it has none (or it does not read)
14 Refusal *Refusal // root's refusal record for the host; nil when none
15 Installed string // the build the live link names on this box; "" when none
16}
17
18// HeldWakeDecision is the held wake check's answer. The zero value is "not
19// held": today's wake.
20type HeldWakeDecision struct {
21 Target string // the build the instance must run
22 Held bool
23 // Unknown is rule 3's "build unknown": refused, root installs nothing.
24 Unknown bool
25 // Failed: root reports Target failed on this box (its decision, or a
26 // not-up refusal record).
27 Failed bool
28 // AskRoot: root has made no decision for the current settings, so the
29 // wake cannot know whether the instance is held. It holds: Target (the
30 // build state's build, "" when there is none) runs only if it is
31 // already installed; otherwise the wake asks root and waits for its
32 // decision. Never "not held" (Astra round 1 finding 4).
33 AskRoot bool
34 // Unread: the copy's latest refresh failed (ReadErr). Refused: a held
35 // wake is never decided from a stale copy (Astra round 1 finding 1).
36 Unread bool
37}
38
39// DecideHeldWake is the held wake check's decision, in root's rule order
40// (Target, §4.3) so root and the wake agree:
41//
42// - default settings: not held (today's wake), nothing else read;
43// - the copy's latest refresh failed: Unread, refused;
44// - a column that does not parse even leniently: held as unknown once
45// root has decided unknown for exactly this column (ruling
46// R-SETTINGS-UNREADABLE), otherwise AskRoot;
47// - a pin: held on it (rule 1);
48// - with the copy read, a pending request resolved to T, the build
49// state's resolution first (R-RESOLUTION-STATE-WINS): held on T (rule
50// 2; manual and major are ignored for it);
51// - otherwise rule 3's target as Target computes it: an unread copy is
52// unknown; else last_executed; else the installed build (Astra round 1
53// finding 5); else, with a replica, unknown; else a new instance, which
54// root gives the head, so not held. Each named build is failure-checked
55// against root's word (Failed);
56// - whether rule 3 holds at all needs root when the setting is a major
57// hold (the agent does not read tags). Root's consumed Reason is read
58// only when it was decided for the current column (Consumed.Settings);
59// with no such decision the wake holds and asks root (AskRoot), carrying
60// rule 3's target as the one build it may run if installed. Manual
61// holds by rule 3; a major hold holds when root's decision for this
62// column is major, unknown, or failed on rule 3's target (ruling
63// R-HELD-FAILED); any other decision for this column is not held.
64func DecideHeldWake(in HeldWakeInput) HeldWakeDecision {
65 c := in.Consumed
66 decided := c != nil && c.Settings != "" && c.Settings == SettingsDigest(in.Settings)
67 u, uerr := ParseUpdateSettingsLenient(in.Settings)
68 if uerr == nil && u.Default() {
69 return HeldWakeDecision{}
70 }
71 if in.Build.ReadErr != "" {
72 return HeldWakeDecision{Held: true, Unread: true}
73 }
74 if uerr != nil {
75 if decided && c.Reason == string(ReasonUnknown) {
76 return HeldWakeDecision{Held: true, Unknown: true}
77 }
78 return HeldWakeDecision{Held: true, AskRoot: true}
79 }
80 rootFailed := func(v string) bool {
81 return v != "" && ((c != nil && c.Reason == string(ReasonFailed) && c.Detail == v) ||
82 (in.Refusal != nil && in.Refusal.Kind == RefusalNotUp && in.Refusal.Version == v))
83 }
84 held := func(v string) HeldWakeDecision {
85 return HeldWakeDecision{Target: v, Held: true, Failed: rootFailed(v)}
86 }
87 if u.Pin != "" {
88 return held(u.Pin)
89 }
90 state := in.Build.State
91 if in.Build.Read() && u.InstallRequest > Watermark(c, state) {
92 if res := resolvedFor(c, state, u.InstallRequest); res != nil {
93 return held(res.Target)
94 }
95 }
96 var r3 HeldWakeDecision
97 switch {
98 case !in.Build.Read():
99 r3 = HeldWakeDecision{Held: true, Unknown: true}
100 case state != nil && state.LastExecuted != "":
101 r3 = held(state.LastExecuted)
102 case in.Installed != "":
103 r3 = held(in.Installed)
104 case in.Build.Replica:
105 r3 = HeldWakeDecision{Held: true, Unknown: true}
106 }
107 if !decided {
108 return HeldWakeDecision{Held: true, AskRoot: true, Target: r3.Target, Failed: r3.Failed}
109 }
110 if u.Updates == UpdatesManual {
111 return r3
112 }
113 if u.Major != nil {
114 switch c.Reason {
115 case string(ReasonMajor), string(ReasonUnknown):
116 return r3
117 case string(ReasonFailed):
118 if r3.Target != "" && c.Detail == r3.Target {
119 return r3
120 }
121 }
122 }
123 return HeldWakeDecision{}
124}
125
126// HeldExecStale is the pre-exec write's revalidation (Astra round 1 finding
127// 1): d is DecideHeldWake on the build state read under the lease. A held
128// target other than the build about to run means the wake decided on a
129// stale copy, and the record is refused, so nothing runs. The build the
130// state's last_executed names is never stale (the data last ran it: a
131// restart or recycle of it is not a move onto another build), and a target
132// root reports failed is left to the planner's recovery (ruling
133// R-HELD-FAILED), as an unknown target is to the wake's own refusal. nil
134// when the build may run.
135//
136// inPlaceOf is the build the zero-downtime planner chose build in place of,
137// for this exec only ("" when it chose nothing: ruling R-PLANNED-RECOVERY):
138// a planned fallback after B failed, the good build booted untried, or a
139// recovery boot onto the good build because the failure book blocks the
140// installed build. When the state read under the lease holds exactly that
141// build as the target, the planner's reason for leaving it is the reason the
142// held target is left (settings spec §6: the scheduler, swap, failure
143// records and fallback act on the settings' build), so build runs, before
144// root's consumed file names the target failed. A selection made in place of
145// any other build admits nothing: the good build is never exempt in general,
146// since it is often the stale build a wake decided on (Astra round 1
147// finding 1).
148func HeldExecStale(d HeldWakeDecision, st *BuildState, build, inPlaceOf string) error {
149 if !d.Held || d.Unknown || d.Unread || d.Failed || d.Target == "" || d.Target == build {
150 return nil
151 }
152 if st != nil && st.LastExecuted == build {
153 return nil
154 }
155 if inPlaceOf != "" && inPlaceOf == d.Target {
156 return nil
157 }
158 return fmt.Errorf("the build state names %s as this instance's build, not %s: the wake decided on a stale copy", d.Target, build)
159}
160