microvms_domain/
preflight.rs1use crate::error::Error;
12use crate::region::{MICROVM_REGIONS, Region};
13
14#[derive(Clone, Debug, PartialEq)]
16pub struct Check {
17 pub name: &'static str,
18 pub ok: bool,
19 pub fatal: bool,
21 pub ran: bool,
23 pub detail: String,
24 pub remedy: String,
26}
27
28impl Check {
29 pub fn pass(name: &'static str, detail: impl Into<String>) -> Self {
30 Self {
31 name,
32 ok: true,
33 fatal: true,
34 ran: true,
35 detail: detail.into(),
36 remedy: String::new(),
37 }
38 }
39
40 pub fn fail(name: &'static str, detail: impl Into<String>, remedy: impl Into<String>) -> Self {
41 Self {
42 name,
43 ok: false,
44 fatal: true,
45 ran: true,
46 detail: detail.into(),
47 remedy: remedy.into(),
48 }
49 }
50
51 pub fn not_run(name: &'static str, reason: impl Into<String>) -> Self {
53 Self {
54 ran: false,
55 ..Self::fail(
56 name,
57 format!("not run: {}", reason.into()),
58 "fix the check above",
59 )
60 }
61 }
62
63 #[must_use]
65 pub fn advisory(mut self) -> Self {
66 self.fatal = false;
67 self
68 }
69}
70
71pub fn healthy(checks: &[Check]) -> bool {
73 checks
74 .iter()
75 .filter(|check| check.fatal)
76 .all(|check| check.ok)
77}
78
79#[derive(Clone, Debug)]
81pub struct PreflightReport {
82 pub region: Option<Region>,
84 pub checks: Vec<Check>,
86}
87
88impl PreflightReport {
89 pub fn ok(&self) -> bool {
91 healthy(&self.checks)
92 }
93
94 pub fn check(&self, name: &str) -> Option<&Check> {
96 self.checks.iter().find(|check| check.name == name)
97 }
98}
99
100fn known_regions() -> String {
101 MICROVM_REGIONS
102 .iter()
103 .map(Region::as_str)
104 .collect::<Vec<_>>()
105 .join(", ")
106}
107
108pub fn region_check(resolved: &Result<Region, Error>) -> Check {
112 match resolved {
113 Ok(region) if region.is_supported() => {
114 Check::pass("region", format!("{region} is a known MicroVMs region"))
115 }
116 Ok(region) => Check::fail(
117 "region",
118 format!("{region} is not in this client's list of MicroVMs regions"),
119 format!(
120 "known: {}; the service check below says whether it runs MicroVMs",
121 known_regions()
122 ),
123 )
124 .advisory(),
125 Err(error) => Check::fail(
126 "region",
127 error.to_string(),
128 format!("known: {}", known_regions()),
129 ),
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136
137 #[test]
138 fn only_a_fatal_failure_is_unhealthy() {
139 let advisory = Check::fail("region", "unlisted", "see below").advisory();
140 assert!(healthy(&[Check::pass("a", "fine"), advisory.clone()]));
141 assert!(!healthy(&[
142 advisory,
143 Check::not_run("service", "no region")
144 ]));
145 assert!(!Check::not_run("service", "no region").ran);
146 }
147
148 #[test]
149 fn the_region_line_passes_a_listed_region_and_advises_on_an_unlisted_one() {
150 assert!(region_check(&Ok(Region::UsEast1)).ok);
151 let unlisted = region_check(&Ok(Region::unlisted("xx-new-1")));
152 assert!(!unlisted.ok && !unlisted.fatal, "{unlisted:?}");
153 let refused = region_check(&Err(Error::invalid_arg("no region")));
154 assert!(!refused.ok && refused.fatal, "{refused:?}");
155 assert!(refused.remedy.contains("us-east-1"), "{refused:?}");
156 }
157}