use core::fmt;
use super::Error;
use crate::shared::printable;
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Connect(err) => write!(f, "cannot reach the shepherd: {err}"),
Self::Request(err) => write!(f, "the shepherd refused a request: {err}"),
Self::Refused {
daemon_version,
message,
} => write!(
f,
"the shepherd refused this dog's handshake: {message}. It is running {}, and \
the two no longer speak the same protocol. Upgrade whichever of them is \
behind; the shepherd starts this dog again on its own once they agree",
match daemon_version {
Some(version) => format!("shep {version}"),
None => "a version it did not name".to_owned(),
}
),
Self::Protocol(what) => write!(f, "unexpected answer from the shepherd: {what}"),
Self::Config(what) => write!(f, "bad deploy configuration: {what}"),
Self::Smit(err) => write!(f, "could not publish a smit: {err}"),
Self::Held { sheep, sha } => write!(
f,
"{sheep} is held at {sha}: the last attempt at that commit did not land, so it \
is left alone until the branch moves rather than rebuilt and rolled back every \
interval. Push a fix, or run `shep deploy {sheep}` to try the same commit again"
),
Self::NotCutOver { sheep, tree } => write!(
f,
"{sheep} has a deploy tree but was never cut over to it, so there is nothing to \
deploy: its record names no released sha, and nothing has ever been served from \
that tree. Deploying now would reload {sheep} at its own checkout and report \
success for a release nothing ran. Finish the cutover instead - remove {} and \
run `shep-deploy setup {sheep}`.",
tree.display()
),
Self::Io { path, source } => {
write!(f, "{}: {source}", printable(path.display()))
}
Self::Git {
command,
status,
stderr,
} => match status {
Some(code) => write!(
f,
"`{command}` exited with status {code}: {}",
printable(stderr)
),
None => write!(
f,
"`{command}` was killed by a signal: {}",
printable(stderr)
),
},
Self::Raced {
sheep,
started,
found,
} => write!(
f,
"another deploy of {sheep} moved current from {started} to {found} while this \
one was building - refusing to swap on top of it, because that release may be \
newer than this one"
),
Self::RollbackFailed { why, source } => write!(
f,
"rolling back after {why} failed: {source} - the rollback can stop at any of its \
three steps, so check `current`, deploy.toml and what the shepherd is actually \
running against each other before retrying"
),
Self::Split {
sheep,
on,
running,
why,
source,
} => write!(
f,
"{sheep}: {why}, so it was rolled back to {on} - but no reload onto it could be \
confirmed ({source}). current and deploy.toml both name {on}; one or more \
instances may still be running {running}. The reload that blocked this one has \
to finish first: a `stopping` row in `shep flock` means it is still draining, \
but the earlier part of a swap shows no row of its own, so wait for the pids in \
`shep describe {sheep}` to hold still. Then `shep reload {sheep}` finishes the \
rollback."
),
Self::RolledBack { to, source } => {
write!(f, "rolled back to {to} after: {source}")
}
Self::Unverified { sheep, sha, why } => write!(
f,
"{sheep}: {why}, and there is no earlier release to roll back to - neither \
current nor deploy.toml names one that is still on disk - so it is still \
pointed at {sha}"
),
Self::CutOver {
sheep,
why,
removed,
repaired,
tree,
source,
} => {
write!(f, "{sheep}'s first cutover did not come up. {why}")?;
if let Some(source) = source {
write!(f, " ({source})")?;
}
if *removed {
write!(
f,
" Nothing this cutover started is left registered, and the original is \
still running on the release it was already serving."
)?;
} else {
write!(
f,
" An instance this cutover started could NOT be removed, so {sheep} may \
now be running more instances than it was: `shep describe {sheep}` \
lists them with their ids, and `shep delete <id>` removes one. Compare \
the count against what {sheep} is configured to run."
)?;
}
if !repaired {
write!(
f,
" Before that, one thing is NOT back as it was: the shepherd recorded \
the new release against {sheep} when it accepted the start, and that \
record could not be put back. It is correct in the running process and \
wrong on disk, so a daemon restart followed by `shep muster` would bring \
{sheep} back on the release that was just rejected. Re-register it from \
its own Flockfile to correct the record before restarting the shepherd."
)?;
}
write!(
f,
" {sheep} is NOT a deploy target: its record names no deployed release and \
nothing has ever been served from its tree. Do NOT run `shep deploy \
{sheep}` against it - that would reload the sheep at its own checkout and \
report success for a release nothing served. Fix what this message names, \
remove {}, and run `shep-deploy setup {sheep}` again.",
tree.display()
)?;
Ok(())
}
Self::Stranded { sheep, sha, ids } => {
let removes: Vec<String> =
ids.iter().map(|id| format!("`shep delete {id}`")).collect();
write!(
f,
"{sheep} was cut over to {sha} and the new release is serving, but {} of the \
instances it replaced could not be removed and are still registered. They \
are still running the code from {sheep}'s own checkout, and they will not \
go away on their own: every deploy from now on reloads EVERY instance of \
the name and respawns each from its own spec, so these come back on the \
pre-adoption config each time. Remove them: {}.",
ids.len(),
removes.join(", ")
)
}
Self::AlreadyDeploying { sheep } => write!(
f,
"another deploy of {sheep} is already running; this one did nothing. \
The dog polls on its own, so a deploy started by hand while a tick \
is in flight meets this"
),
Self::BuildTimedOut { after } => write!(
f,
"the build did not finish within {after:?} and was abandoned; raise \
build_timeout under [deploy] in dogs.toml if it legitimately needs longer"
),
Self::Build { status } => match status {
Some(code) => write!(f, "the build exited with status {code}"),
None => write!(f, "the build was killed by a signal"),
},
Self::Panicked { sheep, what } => write!(
f,
"the deploy of {sheep} panicked partway: {what}. That is a bug in shep-deploy \
rather than in {sheep}. Compare `current`, deploy.toml and `shep describe \
{sheep}` against each other before deploying again, and please report the \
panic"
),
}
}
}