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
//! The supervised dog deploying on its own tick.
use fs;
use Duration;
use crate*;
use crate*;
/// How long the poll test gives the supervised dog to notice a branch that
/// moved between two of its own ticks.
///
/// This number is an assertion rather than a safety margin, and it is
/// bracketed on both sides. Below it: the test configures
/// `interval = "1s"`, and the deploy it triggers is a local fetch, a
/// worktree, a swap and a reload, which the rest of this tier does in about
/// two seconds. Above it: `crate::config`'s `DEFAULT_INTERVAL` is thirty
/// seconds, so a dog that never found its own `[deploy]` section cannot
/// get inside this however healthy it is. That gap is the only thing making
/// the section's interval observable at all - see
/// [`the_supervised_dog_deploys_a_moved_branch_without_being_asked`].
const POLL_WINDOW: Duration = from_secs;
/// The default `crate::config` falls back to when a dog cannot name itself,
/// restated because a test binary cannot see a binary crate's internals.
///
/// The assertion below is the whole point. `POLL_WINDOW` sits between a
/// measured two seconds and this thirty, and that bracket is what makes the
/// configured interval observable. Lower this toward twenty and the test stops
/// distinguishing a dog that read its section from one that did not, then
/// starts flaking, in that order. Neither failure names its cause, so the
/// relationship is asserted at compile time rather than described.
const DEFAULT_INTERVAL_SECS: u64 = 30;
const _: = assert!;
/// fails if the supervised mode does not deploy on its own, or does it on a
/// configuration it never read.
///
/// `watch = "auto"` means nothing until the binary, given NO ARGV AT ALL as
/// the dog contract requires, connects, works out its own adopted name from
/// its own pid in a real flock listing, reads the `[dog.<name>]` section
/// that name selects, finds its targets on real disk and deploys one. Every
/// one of those is stubbed in the unit tests, and the pid lookup in
/// particular can only be wrong against a real daemon.
///
/// # The order of the last three steps is the whole test
///
/// An earlier version of this adopted the dog AFTER moving the branch, and
/// it covered none of the self-identification it claimed to. `adopted_name`
/// returning `None` unconditionally passed it in 1.25 seconds, because
/// `crate::config` answers a nameless dog with the documented defaults, the
/// loop ticks once before it ever sleeps, and by then the branch had already
/// moved. Every observable was identical whether the section was read,
/// misread, or never found - and the same blind spot swallowed
/// `Request::DogConfig` and `DogConfig::parse` along with it.
///
/// So the branch moves LAST, strictly between two ticks, and the deploy is
/// required inside [`POLL_WINDOW`]. That makes the interval the thing being
/// measured: at the configured second the dog gets there with time to spare,
/// and at the thirty-second default it cannot get there at all. A dog that
/// never found its section now fails this rather than passing it.
///
/// Waiting for the tick before the branch moves is what makes that
/// bracketing sound, and an up-to-date tick says nothing an operator or a
/// test could read - `report` is silent on `UpToDate` by design. What it
/// does leave is `FETCH_HEAD`: `deploy::go` fetches before it compares
/// anything, so the file reappearing is a tick that ran to completion and
/// found nothing to do.
///
/// `dogs.toml` is written before the adopt, not after, because the dog reads
/// its section once at startup rather than per tick. `dogs.toml`, not
/// `shep.toml`: shep 0.1.32 moved every dog's section there, under its bare
/// name, and migrates a `[dog.<name>]` still in `shep.toml` only at boot.
/// This test writes after the shepherd is up, so a section written to the
/// old file would never be read, and the dog would run at its default
/// interval and fail the window below. That is what happened the day shep
/// 0.2.0 shipped.
///
/// # Why the dog's own stdout is asserted on as well
///
/// Not belt and braces. `shep adopt` vets a candidate by spawning it with no
/// argv and the real `$SHEP_HOME` before it records anything, so for about
/// fifty milliseconds a second copy of this binary is running the same poll
/// loop against the same targets. That copy is killed with its stdio on
/// `/dev/null`, so a line in the dog's log can only have come from the
/// instance the shepherd supervises. The running release alone would not
/// tell the two apart, and neither would a mutation of the loop, which the
/// vetting spawn runs too.