#[non_exhaustive]pub enum Request {
Show 27 variants
Ping,
ListFlock,
Describe {
selector: SelectorSpec,
},
Start {
apps: Vec<AppConfig>,
},
Add {
apps: Vec<AppConfig>,
},
ConfigDrift {
apps: Vec<AppConfig>,
},
ApplyConfig {
apps: Vec<DeclaredApp>,
reset: ResetDepth,
},
Stop {
selector: SelectorSpec,
},
Restart {
selector: SelectorSpec,
},
Reload {
selector: SelectorSpec,
},
Delete {
selector: SelectorSpec,
},
Scale {
name: String,
count: u32,
},
SetSmit {
sheep: String,
smit: Option<Smit>,
},
Reopen {
selector: SelectorSpec,
},
Flush {
selector: SelectorSpec,
},
Trigger {
selector: SelectorSpec,
action: String,
params: Option<String>,
},
Signal {
selector: SelectorSpec,
signal: String,
},
SendLine {
selector: SelectorSpec,
line: String,
},
SaveRoll,
Muster,
DogConfig {
name: String,
},
EnableDog {
name: String,
source: DogSource,
},
DisableDog {
name: String,
},
DogStaleness,
HandoverFitness,
KillDaemon,
Subscribe {
topics: Vec<String>,
},
}Expand description
One RPC request
Variants (Non-exhaustive)§
This enum is marked as non-exhaustive
Ping
Liveness check
ListFlock
Full flock listing
Describe
Detailed info for matching sheep
Fields
selector: SelectorSpecWhich sheep
Start
Register + start apps
Fields
apps: Vec<AppConfig>App configs — the daemon MUST re-normalize (peer input is
untrusted); failures return RpcErrorCode::InvalidConfig
Add
Register apps as flock members without starting any of them
Everything Self::Start does to the flock’s membership and none of
what it does to processes: each app lands Stopped, holds no pid, and
nothing is spawned. shep add is the verb.
It exists because a Flockfile is a template. One shipping
env = { DB_PASSWORD = "" } would otherwise have to be STARTED before
it could be configured, and a process spawned against an empty
database URL crashes, spends its restart budget, and has to be stopped
before the operator can get anywhere near it.
Idempotent by name, like the muster restore that shares its supervisor
path: an app the flock already has is answered as it stands, running
or not, and nothing about it changes. Config is a separate request:
Self::ApplyConfig is what merges a template into an app the flock
already has, and shep add sends both.
Answers Response::Added.
Fields
apps: Vec<AppConfig>App configs, carried exactly as Self::Start carries them. The
daemon MUST re-normalize (peer input is untrusted); failures
return RpcErrorCode::InvalidConfig
ConfigDrift
Ask which of apps name a sheep the flock already has under a
different config
Read-only: nothing is registered, started, or changed. Self::Start
on an already-registered name adds instances rather than reconciling
config, which is what shep stock relies on; this is how a caller
finds out that an edit it just read from a Flockfile is one Start
will not apply, instead of the edit vanishing without a word.
Answers Response::Drifted with one SheepDrift per app that is
both registered and different. An app the flock does not have is
absent from the answer, not reported as unchanged: Start will
register it, so there is nothing to warn about.
Fields
apps: Vec<AppConfig>The configs to compare against, exactly as Self::Start would
carry them. The daemon MUST re-normalize (peer input is
untrusted, and an unnormalized config would report every default
it has not spelled out as a difference); failures return
RpcErrorCode::InvalidConfig.
ApplyConfig
Merge each declared app into the sheep of the same name, applying what can be applied and parking the rest for that sheep’s next spawn
The acting half of Self::ConfigDrift, which only reports. Nothing
is registered, nothing is pruned and nothing running is killed: an app
the flock does not have is refused by name rather than started, and a
field the running child was spawned from waits for a shep reload
instead of taking one.
Additive by default, which is what reset exists to widen. A
Flockfile arrives from the app’s own repository, so a load appends
what nobody has established and leaves everything an operator set
since alone.
Answers Response::Applied with one SheepApplied per entry in
apps, in the order given, whether or not the app was found and
whether or not anything changed. One app that cannot be applied does
not cost the rest of the file its load; its refusal rides in
SheepApplied::refused.
Fields
apps: Vec<DeclaredApp>The apps to merge in, each carrying the keys its document
literally wrote. The daemon MUST re-normalize the merge result
(peer input is untrusted) and refuses the whole request with
RpcErrorCode::InvalidConfig when two entries share a name:
the second would be merged against a store the first has not
written yet, so its record would be the one that survives.
reset: ResetDepthHow much of what the operator has set since a template last
loaded this request may overwrite. Default
ResetDepth::None, which overwrites nothing.
ResetDepth::Settings was renamed to ResetDepth::Policy (and
File/Env were added) in protocol 3, and the reason that
version exists: unlike an added variant, a rename changes the
wire spelling of an operation that was already shipping, so a
daemon built against protocol 2 cannot decode a client sending
the new name.
Stop
Stop matching sheep (stay registered)
Fields
selector: SelectorSpecWhich sheep
Restart
Restart matching sheep
Fields
selector: SelectorSpecWhich sheep
Reload
Replace each matching sheep with a fresh instance of the same app, one instance of an app at a time, so the app has a window in which it can stay reachable across the swap
Fields
selector: SelectorSpecWhich sheep. No default anywhere in the stack — a reload replaces
running processes, so the operator names the target, exactly as
stop/restart/delete do (see shep reload).
Delete
Stop + deregister matching sheep
Fields
selector: SelectorSpecWhich sheep
Scale
Set how many instances one app runs (see shep stock).
§Why a name and not a selector
Every other verb here takes a SelectorSpec, and this one
deliberately does not. instances is a per-app number and instance
slots are allocated against the same-name group
(shep_daemon::assemble::instance_slots), so a selector matching two
apps would have to mean either “four of each” or “four in total”, and
neither reading is more obviously right than the other. A name has one
meaning.
§Why absolute and not a delta
There is no +N/-N form and there will not be one. An absolute count
is idempotent — run it twice, get the same flock — where two operators
sending +2 against the same app get a number neither of them asked
for. This project’s own trace notes also record a crash on pm2’s
relative-remove path, and those notes exist so shep does not reproduce
what they record.
Fields
name: StringThe app’s name, exactly as its config spells it. Not a selector: no
all, no regex, no fold:.
count: u32How many instances the app has when this returns. 0 is refused
with RpcErrorCode::InvalidConfig — normalize rejects
instances == 0 for every other path into the daemon, and shep delete is the verb for removing an app.
SetSmit
Attach a short marker to sheep for shep flock to paint, or clear
it with None.
By NAME rather than a selector, for Self::Scale’s reason (see its
own doc above): a smit belongs to a sheep, not to one of its
instances, and every instance of that name shows it — including one
spawned after the smit was painted.
Held in memory and scoped to the connection that sent it. When that connection closes, for any reason, the smits it painted go with it. A publisher therefore republishes rather than publishing on change.
shep does not parse it and has no opinion about what it means.
Reopen
Reopen every matched sheep’s log files, for an external rotator that
has renamed them (create-mode rotation)
Fields
selector: SelectorSpecWhich sheep
Flush
Empty every matched sheep’s log files: flush what is still pending, then truncate the recorded paths
Fields
selector: SelectorSpecWhich sheep. No default anywhere in the stack — this destroys
log data, so the operator names the target (see shep flush).
Trigger
Send a named action to every matched sheep over its shepherd channel
and report what each app says back (see shep trigger).
Fields
selector: SelectorSpecWhich sheep. No default anywhere in the stack, matching
stop/restart/reload/delete/flush: an operator names the
target rather than trigger an action against the whole flock by
accident.
Signal
Deliver one signal to every matched sheep’s OWN process — never its
process group (see shep signal).
Fields
selector: SelectorSpecWhich sheep. No default anywhere in the stack, matching every other verb that reaches a running process: an operator names the target rather than signal the whole flock by accident.
signal: StringThe signal’s name, as
OperatorSignal spells it — the
SIG prefix and the case are both optional.
A String rather than the enum, for the reason
AppConfig::kill_signal
is one: the wire stays plain text a person can read in a capture,
and the daemon re-validates regardless, because peer input is
untrusted. A name outside the grammar answers
RpcErrorCode::InvalidConfig.
SendLine
Write one line to every matched sheep’s stdin (see shep whisper).
Fields
selector: SelectorSpecWhich sheep. No default, matching every other verb that reaches a running process.
line: StringThe line, WITHOUT its terminator — the shepherd appends exactly one
\n when it writes. Carrying the terminator here would leave “did
the caller include one” as a question every hop has to re-answer,
and a caller that included two would send an empty line the app
never asked for.
A line containing an embedded newline is refused
(RpcErrorCode::InvalidConfig): it would deliver two commands
where the operator typed one.
SaveRoll
Write the muster roll now, bypassing the snapshot writer’s debounce
Muster
Assemble the flock from the muster roll on disk: start every app the roll recorded running, leaving every app the flock already has exactly as it stands
DogConfig
Ask for one dog’s [dog.<name>] section, as the dog itself parses it
EnableDog
Start one dog now, marking it as coming from source
DisableDog
Stop and deregister one dog
Answers Response::Deleted, the same reply Delete gives: disabling
deregisters exactly as Delete does, so this is the same fact and not
a coincidence of shape. A variant of its own (DogDisabled, say) would
carry nothing Deleted does not.
DogStaleness
Ask which dogs this daemon has given up on, and which it is still
waiting to hear from (shep daemon reload, the handover design’s
G13).
Read-only, and about THIS daemon’s own handshakes. A dog’s recorded crate version describes the process that was running when it connected, so it says nothing about a dog that has since been replaced; the only thing that knows whether a dog can talk to this daemon is whether this daemon accepted its handshake. That is what this answers, which is why the reading is worth taking AFTER a reload rather than before one.
Answers Response::DogStaleness.
§Why this variant does not move PROTOCOL_VERSION
The same argument Self::HandoverFitness makes above, and the
same gate enforces it: its only caller is shep daemon reload, which
asks it of the successor it has just proven is running this binary’s
own version. An older daemon is never sent it.
HandoverFitness
Ask whether this daemon could hand its flock to a successor in place,
rather than stopping it and starting it again (shep daemon reload).
Read-only, and nothing here triggers a handover. The trigger is a signal and always was: a socket request cannot be the trigger, because the case that most needs a reload is the one where the daemon refuses the client at the handshake. What travels over the socket is the DECISION, for a reason a signal cannot serve (spec H3a) – a signal carries no reply, so a daemon that took one, refused, and fell back to its own graceful stop would leave the client polling for a successor nobody started, with the flock down and staying down.
Answers Response::HandoverFitness. Every refusal is a feature the
running daemon cannot yet carry, not an error: the caller falls back
to a stop-and-start, which is correct behaviour rather than a degraded
one, and prints the reason to the operator who asked for the reload.
§Why this variant does not move PROTOCOL_VERSION
An older daemon cannot deserialize a variant it has never seen, which
is normally what a bump is for. It is never sent to one. daemon reload is an exempt verb, so it connects to a mismatched daemon
deliberately, learns the daemon’s crate version from the handshake,
and takes the stop arm for anything predating the handover without
ever asking. shep-cli’s commands::daemon holds that gate and a test
of its own pins it.
KillDaemon
Graceful daemon shutdown
Subscribe
Subscribe this connection to bus topics (glob patterns)