control.rs (5431B)
1 #[cfg(not(feature = "testing"))] 2 use std::io::{Write, stdin, stdout}; 3 4 use async_process::driver; 5 use log::{debug, error, info, warn}; 6 7 #[cfg(not(feature = "testing"))] 8 use kanit_common::error::Context; 9 use kanit_common::error::{Error, Result}; 10 use kanit_diagnostics::tap as kanit_tap; 11 use kanit_diagnostics::timing as kanit_timing; 12 use kanit_executor::{join_all, spawn}; 13 use kanit_unit::{RcUnit, UnitName}; 14 15 use crate::loader; 16 use crate::loader::Loader; 17 18 #[cfg(not(feature = "testing"))] 19 fn critical_unit_fail(err: Error) -> Result<()> { 20 loop { 21 error!("a critical unit failed to start, continue? [y/n]: "); 22 23 let _ = stdout().flush(); 24 25 let mut input = String::new(); 26 27 match stdin().read_line(&mut input) { 28 Ok(_) => match input.chars().next() { 29 Some('y') => return Ok(()), 30 Some('n') => return Err(err), 31 _ => {} 32 }, 33 Err(e) => return Err(e).context("failed to read stdin"), 34 } 35 } 36 } 37 38 #[cfg(feature = "testing")] 39 fn critical_unit_fail(err: Error) -> Result<()> { 40 kanit_tap::bail(Some(err.to_string())); 41 42 Err(err) 43 } 44 45 async fn start_unit(tuple: (usize, RcUnit)) -> Result<Option<UnitName>> { 46 let (j, unit) = tuple; 47 48 let mut unit_b = unit.borrow_mut(); 49 50 debug!("loading unit {}", unit_b.name()); 51 52 let id = kanit_timing::push_scope(format!("unit:{}", unit_b.name())); 53 54 if !unit_b.prepare().await? { 55 warn!("failed preparations for {}", unit_b.name()); 56 kanit_tap::not_ok(j + 1, Some("failed preparations")); 57 return Ok(None); 58 } 59 60 if let Err(e) = unit_b.start().await { 61 if e.is_recoverable() { 62 warn!("{e}"); 63 kanit_tap::not_ok(j + 1, Some(e)); 64 } else { 65 error!("{e}"); 66 critical_unit_fail(e)?; 67 } 68 69 kanit_timing::pop_scope(id); 70 71 return Ok(None); 72 } 73 74 kanit_timing::pop_scope(id); 75 76 kanit_tap::ok(j + 1, Some(unit_b.name())); 77 78 debug!("finished loading unit {}", unit_b.name()); 79 80 Ok(Some(unit_b.name().clone())) 81 } 82 83 async fn start_level(i: usize, name: Box<str>) -> Result<()> { 84 let mut loader = Loader::obtain()?.borrow_mut(); 85 let level = loader.grouping.get(&name).cloned().unwrap_or_else(Vec::new); 86 87 info!("starting level {name}"); 88 89 let scope_str = format!("level:{name}"); 90 91 kanit_tap::enter_subtest(Some(&scope_str)); 92 93 let id = kanit_timing::push_scope(&scope_str); 94 95 kanit_tap::plan(level.len()); 96 97 for (j, group) in level.into_iter().enumerate() { 98 let group_str = format!("group:{j}"); 99 100 kanit_tap::enter_subtest(Some(&group_str)); 101 kanit_tap::plan(group.len()); 102 103 let handles = join_all(group.into_iter().enumerate().map(start_unit)).await; 104 105 for handle in handles { 106 if let Some(n) = handle? { 107 loader.mark_started(n); 108 } 109 } 110 111 kanit_tap::exit_subtest(); 112 kanit_tap::ok(j + 1, Some(&group_str)); 113 } 114 115 kanit_timing::pop_scope(id); 116 117 kanit_tap::exit_subtest(); 118 kanit_tap::ok(i + 1, Some(&scope_str)); 119 120 Ok(()) 121 } 122 123 pub async fn start() -> Result<()> { 124 kanit_timing::register(); 125 126 loader::init_loader()?; 127 128 // include teardown as well 129 // sysboot, boot, default * 2 130 kanit_tap::plan(6); 131 132 let driver_task = spawn(driver()); 133 134 start_level(0, Box::from("sysboot")).await?; 135 start_level(1, Box::from("boot")).await?; 136 start_level(2, Box::from("default")).await?; 137 138 driver_task.cancel().await; 139 140 { 141 let loader = Loader::obtain()?; 142 143 let _ = loader.borrow().save(); 144 } 145 146 Ok(()) 147 } 148 149 async fn stop_unit(tuple: (usize, RcUnit)) -> Result<()> { 150 let (j, unit) = tuple; 151 152 let mut unit_b = unit.borrow_mut(); 153 154 debug!("unloading unit {}", unit_b.name()); 155 156 match unit_b.stop().await { 157 Ok(_) => kanit_tap::ok(j + 1, Some(unit_b.name())), 158 Err(e) => { 159 kanit_tap::not_ok(j + 1, Some(unit_b.name())); 160 161 if e.is_recoverable() { 162 warn!("{e}") 163 } else { 164 error!("{e}") // won't stop still 165 } 166 } 167 } 168 169 debug!("finished unloading unit {}", unit_b.name()); 170 171 Ok(()) 172 } 173 174 async fn teardown_level(total: usize, i: usize, name: Box<str>) -> Result<()> { 175 let loader = Loader::obtain()?.borrow(); 176 let level = loader.grouping.get(&name).cloned().unwrap_or_else(Vec::new); 177 178 info!("stopping level {name}"); 179 180 let scope_str = format!("level:{name}-stop"); 181 kanit_tap::enter_subtest(Some(&scope_str)); 182 183 kanit_tap::plan(level.len()); 184 185 for (j, group) in level.into_iter().enumerate() { 186 let group_str = format!("group:{j}"); 187 188 kanit_tap::enter_subtest(Some(&group_str)); 189 kanit_tap::plan(group.len()); 190 191 let handles = join_all(group.into_iter().enumerate().map(stop_unit)).await; 192 193 for handle in handles { 194 handle?; 195 } 196 197 kanit_tap::exit_subtest(); 198 kanit_tap::ok(j + 1, Some(&group_str)); 199 } 200 201 kanit_tap::exit_subtest(); 202 kanit_tap::ok(total + (total - i), Some(&scope_str)); 203 204 Ok(()) 205 } 206 207 pub async fn teardown() -> Result<()> { 208 let driver_task = spawn(driver()); 209 210 teardown_level(3, 2, Box::from("default")).await?; 211 teardown_level(3, 1, Box::from("boot")).await?; 212 teardown_level(3, 0, Box::from("sysboot")).await?; 213 214 driver_task.cancel().await; 215 216 Ok(()) 217 }