use super::*;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Link {
Live,
Retrying {
attempt: u32,
},
Lost {
at_local: String,
why: String,
},
}
#[derive(Debug, Clone)]
pub struct Row {
pub info: ProcessInfo,
pub anchor: Instant,
}
impl Row {
#[must_use]
pub(crate) fn reported(&self) -> Reported {
if self.info.disabled == Some(true) && self.info.status == ProcStatus::Stopped {
return Reported::Disabled;
}
if self.info.dog.is_none() {
return Reported::Live(self.info.status);
}
Reported::of(self.info.status, self.info.handshook)
}
}
#[derive(Debug, Clone)]
pub struct GroupTotals {
pub count: usize,
pub restarts: u32,
pub cpu: Option<f32>,
pub memory: Option<u64>,
pub uptime_ms: Option<u64>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Sent {
Lambs {
id: u32,
},
Action {
verb: ActionVerb,
target: RowKey,
name: String,
},
Dog {
name: String,
enable: bool,
source: DogSource,
ticket: u64,
},
SheepConfig {
name: String,
},
ApplyField {
name: String,
ticket: u64,
key: String,
value: FieldValue,
authority: WriteAuthority,
},
DogSection {
name: String,
},
SetDogSection {
name: String,
ticket: u64,
toml: DogSectionToml,
authority: WriteAuthority,
},
SetSheepDogTable {
name: String,
dog: String,
ticket: u64,
table: Option<DogTable>,
authority: WriteAuthority,
},
SetEnv {
name: String,
ticket: u64,
key: String,
value: Option<EnvValue>,
authority: WriteAuthority,
},
}
impl Sent {
#[must_use]
pub fn request(&self) -> Request {
match self {
Self::Lambs { id } => Request::Describe {
selector: SelectorSpec::Id(*id),
},
Self::Action { verb, target, .. } => {
let selector = match target {
RowKey::Sheep(id) => SelectorSpec::Id(*id),
RowKey::Group(name) => SelectorSpec::Name(name.clone()),
RowKey::Fold(name) => SelectorSpec::Fold(name.clone()),
RowKey::Section(_) => unreachable!("a header is never an action target"),
};
match verb {
ActionVerb::Stop => Request::Stop { selector },
ActionVerb::Restart => Request::Restart { selector },
ActionVerb::Reload => Request::Reload { selector },
}
}
Self::Dog {
name,
enable: true,
source,
..
} => Request::EnableDog {
name: name.clone(),
source: source.clone(),
},
Self::Dog {
name,
enable: false,
..
} => Request::DisableDog { name: name.clone() },
Self::SheepConfig { name } => Request::SheepConfig { name: name.clone() },
Self::ApplyField {
name, key, value, ..
} => Request::SetSheepField {
name: name.clone(),
key: key.clone(),
value: value.as_value().clone(),
},
Self::DogSection { name } => Request::DogConfig { name: name.clone() },
Self::SetDogSection { name, toml, .. } => Request::SetDogConfig {
name: name.clone(),
toml: toml.clone(),
},
Self::SetSheepDogTable {
name, dog, table, ..
} => Request::SetSheepDogSettings {
name: name.clone(),
dog: dog.clone(),
table: table.clone(),
},
Self::SetEnv {
name, key, value, ..
} => Request::SetSheepEnv {
name: name.clone(),
key: key.clone(),
value: value.clone(),
},
}
}
pub(super) fn ticket(&self) -> Option<u64> {
match self {
Self::ApplyField { ticket, .. }
| Self::SetDogSection { ticket, .. }
| Self::SetSheepDogTable { ticket, .. }
| Self::SetEnv { ticket, .. } => Some(*ticket),
Self::Lambs { .. }
| Self::Action { .. }
| Self::Dog { .. }
| Self::SheepConfig { .. }
| Self::DogSection { .. } => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DogEdit {
pub name: String,
pub enable: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DogProbe {
pub name: String,
pub adopted_path: Option<PathBuf>,
pub schema: serde_json::Value,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum RowKey {
Group(String),
Sheep(u32),
Section(&'static str),
Fold(String),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Grouping {
#[default]
Flat,
ByFold,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum LambWalk {
Walked(Vec<Lamb>),
NotWalked,
Failed,
}
#[derive(Debug, Clone, Copy)]
pub struct LogSize {
pub(super) id: u32,
pub(super) total_bytes: Option<u64>,
}
#[derive(Debug, Clone)]
pub struct LambReading {
pub(super) id: u32,
pub(super) at: Instant,
pub(super) walk: LambWalk,
}
pub(super) const UNRECOGNISED_REPLY: &str =
"the shepherd answered something this lookout does not understand";
pub(super) fn failed_reply(result: Result<Response, RequestError>) -> String {
match result {
Ok(_) => UNRECOGNISED_REPLY.to_owned(),
Err(RequestError::Rpc(err)) => err.message,
Err(other) => other.to_string(),
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Notice {
pub(super) text: String,
pub(super) grave: bool,
}
impl Notice {
#[must_use]
pub(super) fn grave(text: impl Into<String>) -> Self {
Self {
text: text.into(),
grave: true,
}
}
#[must_use]
pub(super) fn info(text: impl Into<String>) -> Self {
Self {
text: text.into(),
grave: false,
}
}
#[must_use]
pub fn is_grave(&self) -> bool {
self.grave
}
}
impl fmt::Display for Notice {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.text)
}
}
#[cfg(test)]
mod tests {
use super::*;
use shep_core::protocol::{RpcError, RpcErrorCode};
#[test]
fn notice_grave_and_info_set_the_flag_and_keep_the_text() {
let grave = Notice::grave("refused: the control gate is closed");
assert!(grave.is_grave());
assert_eq!(grave.text, "refused: the control gate is closed");
assert_eq!(grave.to_string(), "refused: the control gate is closed");
let info = Notice::info("restart scheduled for api");
assert!(!info.is_grave());
assert_eq!(info.text, "restart scheduled for api");
assert_eq!(info.to_string(), "restart scheduled for api");
}
#[test]
fn failed_reply_names_each_of_its_three_cases_apart() {
let unrecognised = failed_reply(Ok(Response::Pong));
let daemon = failed_reply(Err(RequestError::Rpc(RpcError {
code: RpcErrorCode::NotFound,
message: "no sheep named web".into(),
daemon_version: None,
})));
let transport = failed_reply(Err(RequestError::Timeout {
after: Duration::from_secs(3),
}));
assert_eq!(unrecognised, UNRECOGNISED_REPLY);
assert_eq!(daemon, "no sheep named web");
assert_eq!(transport, "no reply within 3s");
}
}