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.

318 lines
8.0 KiB

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