A library for writing host-specific, single-binary configuration management and deployment tools
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

355 lines
8.8 KiB

  1. use crate::async_utils::sleep;
  2. use crate::loggers::Logger;
  3. use crate::symbols::Symbol;
  4. use async_trait::async_trait;
  5. use std::error::Error;
  6. use std::fmt;
  7. use std::fmt::Debug;
  8. use std::time::Duration;
  9. #[async_trait(?Send)]
  10. pub trait SymbolRunner {
  11. async fn run_symbol<S: Symbol + Debug, L: Logger>(
  12. &self,
  13. symbol: &S,
  14. logger: &L,
  15. force: bool,
  16. ) -> Result<bool, Box<dyn Error>>;
  17. }
  18. #[derive(Debug)]
  19. pub enum SymbolRunError {
  20. Symbol(Box<dyn Error>),
  21. ExecuteDidNotReach(()),
  22. }
  23. impl Error for SymbolRunError {
  24. fn cause(&self) -> Option<&dyn Error> {
  25. match self {
  26. Self::Symbol(ref e) => Some(&**e),
  27. Self::ExecuteDidNotReach(_) => None,
  28. }
  29. }
  30. }
  31. impl fmt::Display for SymbolRunError {
  32. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  33. match self {
  34. Self::Symbol(ref e) => write!(f, "{}", e),
  35. Self::ExecuteDidNotReach(_) => write!(f, "Target not reached after executing symbol"),
  36. }
  37. }
  38. }
  39. #[derive(Clone, Debug, Default)]
  40. pub struct InitializingSymbolRunner;
  41. impl InitializingSymbolRunner {
  42. pub fn new() -> Self {
  43. Self
  44. }
  45. async fn exec_symbol<S: Symbol + Debug, L: Logger>(
  46. &self,
  47. symbol: &S,
  48. logger: &L,
  49. ) -> Result<(), Box<dyn Error>> {
  50. logger.info(format!("Executing {:?}", symbol));
  51. symbol.execute().await?;
  52. let target_reached = symbol.target_reached().await?;
  53. logger.trace(format!(
  54. "Symbol reports target_reached: {:?} (should be true)",
  55. target_reached
  56. ));
  57. if target_reached {
  58. Ok(())
  59. } else {
  60. Err(Box::new(SymbolRunError::ExecuteDidNotReach(())))
  61. }
  62. }
  63. }
  64. #[async_trait(?Send)]
  65. impl SymbolRunner for InitializingSymbolRunner {
  66. async fn run_symbol<S: Symbol + Debug, L: Logger>(
  67. &self,
  68. symbol: &S,
  69. logger: &L,
  70. force: bool,
  71. ) -> Result<bool, Box<dyn Error>> {
  72. let executed = if force {
  73. logger.debug("Forcing symbol execution");
  74. self.exec_symbol(symbol, logger).await?;
  75. true
  76. } else {
  77. let target_reached = symbol.target_reached().await?;
  78. if target_reached {
  79. logger.debug(format!("{:?} already reached", symbol));
  80. } else {
  81. logger.trace(format!(
  82. "Symbol reports target_reached: {:?}",
  83. target_reached
  84. ));
  85. self.exec_symbol(symbol, logger).await?;
  86. }
  87. !target_reached
  88. };
  89. Ok(executed)
  90. }
  91. }
  92. #[derive(Clone, Debug)]
  93. pub struct DelayingSymbolRunner<R>(R);
  94. impl<R> DelayingSymbolRunner<R> {
  95. pub fn new(symbol_runner: R) -> Self {
  96. Self(symbol_runner)
  97. }
  98. }
  99. #[async_trait(?Send)]
  100. impl<R> SymbolRunner for DelayingSymbolRunner<R>
  101. where
  102. R: SymbolRunner,
  103. {
  104. async fn run_symbol<S: Symbol + Debug, L: Logger>(
  105. &self,
  106. symbol: &S,
  107. logger: &L,
  108. force: bool,
  109. ) -> Result<bool, Box<dyn Error>> {
  110. sleep(Duration::from_millis(
  111. (((std::time::SystemTime::now()
  112. .duration_since(std::time::UNIX_EPOCH)
  113. .unwrap()
  114. .subsec_micros()
  115. % 20) as f32
  116. / 2.0)
  117. .exp()
  118. / 8.0) as u64,
  119. ))
  120. .await;
  121. self.0.run_symbol(symbol, logger, force).await
  122. }
  123. }
  124. #[derive(Clone, Debug, Default)]
  125. pub struct DrySymbolRunner;
  126. impl DrySymbolRunner {
  127. pub fn new() -> Self {
  128. Self
  129. }
  130. }
  131. #[async_trait(?Send)]
  132. impl SymbolRunner for DrySymbolRunner {
  133. async fn run_symbol<S: Symbol + Debug, L: Logger>(
  134. &self,
  135. symbol: &S,
  136. logger: &L,
  137. force: bool,
  138. ) -> Result<bool, Box<dyn Error>> {
  139. let would_execute = if force {
  140. logger.info(format!("Would force-execute {:?}", symbol));
  141. true
  142. } else {
  143. let target_reached = symbol.target_reached().await?;
  144. logger.debug(format!(
  145. "Symbol reports target_reached: {:?}",
  146. target_reached
  147. ));
  148. if !target_reached {
  149. logger.info(format!("Would execute {:?}", symbol));
  150. }
  151. !target_reached
  152. };
  153. Ok(would_execute)
  154. }
  155. }
  156. #[derive(Clone, Debug)]
  157. pub struct ReportingSymbolRunner<R>(R);
  158. impl<R> ReportingSymbolRunner<R> {
  159. pub fn new(symbol_runner: R) -> Self {
  160. Self(symbol_runner)
  161. }
  162. }
  163. #[async_trait(?Send)]
  164. impl<R> SymbolRunner for ReportingSymbolRunner<R>
  165. where
  166. R: SymbolRunner,
  167. {
  168. async fn run_symbol<S: Symbol + Debug, L: Logger>(
  169. &self,
  170. symbol: &S,
  171. logger: &L,
  172. force: bool,
  173. ) -> Result<bool, Box<dyn Error>> {
  174. logger.debug(format!("Running symbol {:?}", symbol));
  175. let res = self.0.run_symbol(symbol, logger, force).await;
  176. if let Err(ref e) = res {
  177. logger.info(format!("Failed on {:?} with {}, aborting.", symbol, e))
  178. } else {
  179. logger.debug(format!("Successfully finished {:?}", symbol))
  180. }
  181. res
  182. }
  183. }
  184. #[cfg(test)]
  185. mod test {
  186. use super::{DrySymbolRunner, InitializingSymbolRunner, ReportingSymbolRunner, SymbolRunner};
  187. use crate::async_utils::sleep;
  188. use crate::async_utils::{run, try_join};
  189. use crate::loggers::StoringLogger;
  190. use crate::symbols::Symbol;
  191. use async_trait::async_trait;
  192. use std::cell::RefCell;
  193. use std::error::Error;
  194. use std::fmt;
  195. use std::fmt::Debug;
  196. use std::time::Duration;
  197. #[derive(Debug, PartialEq, Clone)]
  198. enum DummySymbolError {
  199. Error(()),
  200. }
  201. impl Error for DummySymbolError {}
  202. impl fmt::Display for DummySymbolError {
  203. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  204. write!(f, "Dummy symbol error")
  205. }
  206. }
  207. #[derive(Debug)]
  208. struct DummySymbol<T, E> {
  209. _target_reached: RefCell<T>,
  210. _execute: RefCell<E>,
  211. }
  212. #[async_trait(?Send)]
  213. impl<
  214. E: Iterator<Item = Result<(), Box<dyn Error>>>,
  215. T: Iterator<Item = Result<bool, Box<dyn Error>>>,
  216. > Symbol for DummySymbol<T, E>
  217. {
  218. async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
  219. self._target_reached.borrow_mut().next().unwrap()
  220. }
  221. async fn execute(&self) -> Result<(), Box<dyn Error>> {
  222. self._execute.borrow_mut().next().unwrap()
  223. }
  224. }
  225. impl<
  226. E: Iterator<Item = Result<(), Box<dyn Error>>>,
  227. T: Iterator<Item = Result<bool, Box<dyn Error>>>,
  228. > DummySymbol<T, E>
  229. {
  230. fn new<
  231. IE: IntoIterator<IntoIter = E, Item = Result<(), Box<dyn Error>>>,
  232. IT: IntoIterator<IntoIter = T, Item = Result<bool, Box<dyn Error>>>,
  233. >(
  234. target_reached: IT,
  235. execute: IE,
  236. ) -> Self {
  237. Self {
  238. _target_reached: RefCell::new(target_reached.into_iter()),
  239. _execute: RefCell::new(execute.into_iter()),
  240. }
  241. }
  242. }
  243. fn run_symbol<S: Symbol + Debug>(s: S) -> Result<bool, Box<dyn Error>> {
  244. run(InitializingSymbolRunner::new().run_symbol(&s, &StoringLogger::new(), false))
  245. }
  246. #[test]
  247. fn nothing_needed_to_be_done() {
  248. let result = run_symbol(DummySymbol::new(vec![Ok(true)], vec![Ok(())]));
  249. assert!(result.is_ok());
  250. }
  251. #[test]
  252. fn everything_is_ok() {
  253. let result = run_symbol(DummySymbol::new(vec![Ok(false), Ok(true)], vec![Ok(())]));
  254. assert!(result.is_ok());
  255. }
  256. #[test]
  257. fn executing_did_not_change_state() {
  258. let result = run_symbol(DummySymbol::new(vec![Ok(false), Ok(false)], vec![Ok(())]));
  259. assert_eq!(
  260. result.unwrap_err().to_string(),
  261. "Target not reached after executing symbol"
  262. );
  263. }
  264. #[test]
  265. fn executing_did_not_work() {
  266. let result = run_symbol(DummySymbol::new(
  267. vec![Ok(false)],
  268. vec![Err(Box::new(DummySymbolError::Error(())) as Box<dyn Error>)],
  269. ));
  270. assert_eq!(result.unwrap_err().to_string(), "Dummy symbol error");
  271. }
  272. #[derive(Debug)]
  273. struct SleeperSymbol;
  274. #[async_trait(?Send)]
  275. impl Symbol for SleeperSymbol {
  276. async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
  277. sleep(Duration::from_millis(0)).await;
  278. Ok(true)
  279. }
  280. async fn execute(&self) -> Result<(), Box<dyn Error>> {
  281. unimplemented!();
  282. }
  283. }
  284. #[test]
  285. fn actually_support_parallel_execution() {
  286. run(async {
  287. let s1 = SleeperSymbol;
  288. let s2 = DummySymbol::new(vec![Ok(false), Ok(true)], vec![Ok(())]);
  289. let l1 = StoringLogger::new();
  290. let l2 = StoringLogger::new();
  291. let runner1 = InitializingSymbolRunner::new();
  292. let result = try_join!(
  293. runner1.run_symbol(&s1, &l1, false),
  294. runner1.run_symbol(&s2, &l2, false),
  295. )
  296. .unwrap();
  297. assert_eq!(result, (false, true));
  298. let s2 = DummySymbol::new(vec![Ok(false), Ok(true)], vec![Ok(())]);
  299. let l1 = StoringLogger::new();
  300. let l2 = StoringLogger::new();
  301. let runner2 = DrySymbolRunner::new();
  302. let result = try_join!(
  303. runner2.run_symbol(&s1, &l1, false),
  304. runner2.run_symbol(&s2, &l2, false),
  305. )
  306. .unwrap();
  307. assert_eq!(result, (false, true));
  308. let s2 = DummySymbol::new(vec![Ok(false), Ok(true)], vec![Ok(())]);
  309. let l1 = StoringLogger::new();
  310. let l2 = StoringLogger::new();
  311. let runner3 = ReportingSymbolRunner::new(runner1);
  312. let result = try_join!(
  313. runner3.run_symbol(&s1, &l1, false),
  314. runner3.run_symbol(&s2, &l2, false),
  315. )
  316. .unwrap();
  317. assert_eq!(result, (false, true));
  318. });
  319. }
  320. }