1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
//! forjar#380: drift for `type: task` — execute the assertion, don't hash it.
//!
//! # The defect
//!
//! paiml/infra's policy is that a guard IS a forjar resource: `completion_check`
//! is the assertion, `command` reports the violation. Drift saw none of them.
//! Files were compared by content hash, images by manifest digest, and every
//! other type by re-running `state_query_script` and comparing its digest to
//! the one the lock recorded — which skips any resource whose lock entry has no
//! observed state at all. `executor::refresh_seed::converged_entry` writes
//! precisely that entry (`observed: None`, empty details) for every resource an
//! `apply --refresh` found already satisfied, i.e. for every CI checkout and
//! every reimaged box. Measured on 1.21.1, one task guard, marker deleted after
//! the apply:
//!
//! ```text
//! forjar apply --refresh -> 0 converged, 1 unchanged
//! rm <the file the check asserts>
//! forjar drift -m box -> "No drift detected." (exit 0)
//! ```
//!
//! # Why this is not the hash path with a wider filter
//!
//! For a resource whose observable is an ASSERTION, the digest comparison asks
//! the wrong question. `hash("task=pending") != hash("task=completed")` detects
//! a CHANGE against a recorded baseline; with no baseline recorded there is
//! nothing to compare and the hash path reports clean. But an assertion needs
//! no baseline — a `completion_check` that fails right now is drift whether or
//! not anything was ever written down about it. So this detector runs the check
//! and reads its EXIT CODE, exactly as `apply` and `--refresh` do.
//!
//! # Side effects — the assumption this makes load-bearing
//!
//! A `completion_check` is supposed to be a pure predicate and NOTHING enforces
//! that. Running it from drift makes that assumption load-bearing on a command
//! operators cron. It is not a new class of exposure — `apply`, `plan` and
//! `--refresh` already execute the same script through the same transport, so
//! any check with side effects has been firing on every apply — but the
//! FREQUENCY is new, and a check that mutates will now mutate hourly. If that
//! matters for a given host, `--no-task-checks` turns it off for the run, and
//! the census reports how many resources that silenced.
use ;
use should_ignore_drift;
use ;
use crate;
/// Per-invocation bounds on how much work a drift run may do on the target.
/// Does the task detector own this resource's drift verdict?
///
/// Service-mode tasks are excluded: their `check_script` asserts a PID file
/// rather than the declared `completion_check`, so they keep the state-query
/// path and its digest, which is what their lock entries were written against.
pub
/// Run the `completion_check` of every converged task, over the same transport
/// `apply` uses (`transport::exec_script` dispatches pepita > container > local
/// > ssh), under the same 60s bound as every other drift query.
pub
/// Why this task would not be checked, or `None` to check it.
///
/// THE LOCK'S STATUS IS NOT CONSULTED HERE, AND THAT IS THE POINT (forjar#487).
///
/// This used to return `SkipReason::NotConverged` for anything the lock did not
/// record as `Converged` or `Drifted`. `src/tripwire/drift/mod.rs` still does,
/// for a reason written down there and still correct: a failed apply's recorded
/// HASH is not a baseline anything can be compared against, so the state-query,
/// file and image paths have nothing to measure and must say so.
///
/// A `completion_check` is not a baseline. It is an ASSERTION: it asks the host
/// a question and reads the answer, and it needs nothing from the lock to be
/// answerable. A failed apply does not make the question unanswerable — it
/// makes it urgent. That is the whole shape of the guard resource forjar itself
/// makes necessary: `command` refuses loudly and names the human step, and the
/// check is the only thing that can say whether the step has happened.
///
/// Measured before the fix, on a guard whose check passed by hand:
///
/// ```text
/// inspected 0 of 1 resource(s) in scope: none
/// skipped 1: not converged in the lock 1
/// No drift detected.
/// ```
///
/// The operator hit it on two machines at once — `skipped 10: in the lock, not
/// in the config 7, not converged in the lock 3`, where those three were these
/// guards. Drift was blind to precisely the resources the lock believed were
/// broken. The earlier note here said `Drifted` is re-checked because it means
/// "needs work", not "stop looking" (forjar#310); `Failed` means the same thing
/// and more, and now no status stops the check.
///
/// What remains below is unchanged and still skips: an explicit
/// `lifecycle.ignore_drift`, and `--no-task-checks`. Both are the operator
/// declining the measurement, and both stay NAMED in the census, because an
/// unmeasured check and a passed check must never print the same thing.
/// Execute one task's completion check on the target. Non-zero exit is drift.
pub
/// The verdict marker the check printed (`task=pending`), or its stderr.
///
/// Bounded: a `completion_check` is arbitrary shell and may print a megabyte.
/// A task finding.
///
/// `expected_hash`/`actual_hash` carry words rather than digests here, because
/// an assertion has no hash: the expected state is "the check passes" and the
/// observed state is "it does not". Writing a plausible-looking digest into
/// those fields would be a value forjar had not measured.