use crate::error::Error;
use crate::region::{MICROVM_REGIONS, Region};
#[derive(Clone, Debug, PartialEq)]
pub struct Check {
pub name: &'static str,
pub ok: bool,
pub fatal: bool,
pub ran: bool,
pub detail: String,
pub remedy: String,
}
impl Check {
pub fn pass(name: &'static str, detail: impl Into<String>) -> Self {
Self {
name,
ok: true,
fatal: true,
ran: true,
detail: detail.into(),
remedy: String::new(),
}
}
pub fn fail(name: &'static str, detail: impl Into<String>, remedy: impl Into<String>) -> Self {
Self {
name,
ok: false,
fatal: true,
ran: true,
detail: detail.into(),
remedy: remedy.into(),
}
}
pub fn not_run(name: &'static str, reason: impl Into<String>) -> Self {
Self {
ran: false,
..Self::fail(
name,
format!("not run: {}", reason.into()),
"fix the check above",
)
}
}
#[must_use]
pub fn advisory(mut self) -> Self {
self.fatal = false;
self
}
}
pub fn healthy(checks: &[Check]) -> bool {
checks
.iter()
.filter(|check| check.fatal)
.all(|check| check.ok)
}
#[derive(Clone, Debug)]
pub struct PreflightReport {
pub region: Option<Region>,
pub checks: Vec<Check>,
}
impl PreflightReport {
pub fn ok(&self) -> bool {
healthy(&self.checks)
}
pub fn check(&self, name: &str) -> Option<&Check> {
self.checks.iter().find(|check| check.name == name)
}
}
fn known_regions() -> String {
MICROVM_REGIONS
.iter()
.map(Region::as_str)
.collect::<Vec<_>>()
.join(", ")
}
pub fn region_check(resolved: &Result<Region, Error>) -> Check {
match resolved {
Ok(region) if region.is_supported() => {
Check::pass("region", format!("{region} is a known MicroVMs region"))
}
Ok(region) => Check::fail(
"region",
format!("{region} is not in this client's list of MicroVMs regions"),
format!(
"known: {}; the service check below says whether it runs MicroVMs",
known_regions()
),
)
.advisory(),
Err(error) => Check::fail(
"region",
error.to_string(),
format!("known: {}", known_regions()),
),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn only_a_fatal_failure_is_unhealthy() {
let advisory = Check::fail("region", "unlisted", "see below").advisory();
assert!(healthy(&[Check::pass("a", "fine"), advisory.clone()]));
assert!(!healthy(&[
advisory,
Check::not_run("service", "no region")
]));
assert!(!Check::not_run("service", "no region").ran);
}
#[test]
fn the_region_line_passes_a_listed_region_and_advises_on_an_unlisted_one() {
assert!(region_check(&Ok(Region::UsEast1)).ok);
let unlisted = region_check(&Ok(Region::unlisted("xx-new-1")));
assert!(!unlisted.ok && !unlisted.fatal, "{unlisted:?}");
let refused = region_check(&Err(Error::invalid_arg("no region")));
assert!(!refused.ok && refused.fatal, "{refused:?}");
assert!(refused.remedy.contains("us-east-1"), "{refused:?}");
}
}