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.

452 lines
13 KiB

  1. mod realizer;
  2. mod symbol_runner;
  3. mod util;
  4. pub use symbol_runner::{
  5. DelayingSymbolRunner, DrySymbolRunner, InitializingSymbolRunner, ReportingSymbolRunner,
  6. SymbolRunner,
  7. };
  8. mod cache;
  9. mod runnable;
  10. use crate::loggers::Logger;
  11. use crate::resources::{DefaultArtifacts, DefaultResources, FromArtifact, FromResource};
  12. use crate::symbols::Symbol;
  13. use crate::{DefaultBuilder, DefaultLocator};
  14. use crate::{ImplementationBuilder, ResourceLocator};
  15. use async_trait::async_trait;
  16. use cache::Cache;
  17. use realizer::Realizer;
  18. use runnable::Runnable;
  19. use slog::o;
  20. use std::error::Error;
  21. use std::fmt::Debug;
  22. use std::hash::Hash;
  23. use std::rc::Rc;
  24. use util::{Add, AddGeneric, AddResult, AddableResource, Recorder};
  25. // Necessary for the recursive type
  26. #[derive(Debug)]
  27. pub struct ActualSetup<SR, L, B, Rs, As>(Cache<Realizer<SR, L, B, Self>, Rs, As>);
  28. #[async_trait(?Send)]
  29. impl<T: AddableResource + Debug, SR, L, B, Rs, As> Add<T> for ActualSetup<SR, L, B, Rs, As>
  30. where
  31. Cache<Realizer<SR, L, B, Self>, Rs, As>: Add<T>,
  32. {
  33. async fn add(&self, logger: &Rc<slog::Logger>, r: Rc<T>, force_run: bool) -> AddResult<T> {
  34. self.0.add(logger, r, force_run).await
  35. }
  36. }
  37. // This is for self-referential T
  38. // FIXME: Wait for specialization
  39. #[async_trait(?Send)]
  40. impl<
  41. SR: 'static + SymbolRunner,
  42. T: AddableResource,
  43. Rs: 'static + Hash + Eq + FromResource<T>,
  44. As: 'static + FromArtifact<T> + Clone,
  45. L: 'static + ResourceLocator<T, Prerequisites = Option<T>>,
  46. B: 'static + ImplementationBuilder<T>,
  47. > AddGeneric<Option<T>> for ActualSetup<SR, L, B, Rs, As>
  48. where
  49. <B as ImplementationBuilder<T>>::Implementation: Runnable + Debug,
  50. Self: AddGeneric<B::Prerequisites>,
  51. T::Artifact: Clone,
  52. // These bounds cannot be replaced by
  53. // `Realizer<SR, L, B>: Add<T, Self>`
  54. // because the prerequisites are Option<T>, too, and thus this would
  55. // require AddGeneric<Option<T>> to already be implemented
  56. {
  57. async fn add_generic(
  58. &self,
  59. logger: &Rc<slog::Logger>,
  60. r: Option<T>,
  61. force_run: bool,
  62. ) -> AddResult<Option<T>> {
  63. Ok(match r {
  64. Some(r) => {
  65. let (result, did_run) = self.add(logger, Rc::new(r), force_run).await?;
  66. (Some(result), did_run)
  67. }
  68. None => (None, false),
  69. })
  70. }
  71. }
  72. #[derive(Debug)]
  73. pub struct Setup<
  74. SR,
  75. LOG,
  76. L = DefaultLocator,
  77. B = DefaultBuilder,
  78. Rs = DefaultResources<'static, &'static str>,
  79. As = DefaultArtifacts<'static, &'static str>,
  80. >(LOG, Rc<SR>, Rc<ActualSetup<SR, L, B, Rs, As>>);
  81. impl<SR, LOG> Setup<SR, LOG> {
  82. pub fn new(symbol_runner: SR, logger: LOG) -> Self {
  83. Self::new_with(symbol_runner, logger)
  84. }
  85. }
  86. impl<L, B, As, SR, LOG, Rs: Hash + Eq> Setup<SR, LOG, L, B, Rs, As> {
  87. pub fn new_with(symbol_runner: SR, logger: LOG) -> Self {
  88. let runner = Rc::new(symbol_runner);
  89. let inner = Rc::new_cyclic(|inner| {
  90. ActualSetup(Cache::new(Realizer::new(Rc::clone(&runner), inner.clone())))
  91. });
  92. Self(logger, runner, inner)
  93. }
  94. }
  95. impl<
  96. SR: 'static,
  97. LOG: 'static + Logger,
  98. L: 'static,
  99. B: 'static,
  100. Rs: 'static + Eq + Hash,
  101. As: 'static,
  102. > Setup<SR, LOG, L, B, Rs, As>
  103. {
  104. pub async fn add_force<R: AddableResource>(&self, resource: R, force_run: bool) -> AddResult<R>
  105. where
  106. Rs: FromResource<R>,
  107. As: FromArtifact<R> + Clone,
  108. ActualSetup<SR, L, B, Rs, As>: Add<R>,
  109. {
  110. let recorder = Recorder::default();
  111. let result = {
  112. let log = Rc::new(slog::Logger::root(recorder.clone(), o!()));
  113. self.2.add(&log, Rc::new(resource), force_run).await
  114. };
  115. self.log_result(recorder, result.as_ref().map(|(_, did_run)| *did_run));
  116. result
  117. }
  118. pub async fn add<R: AddableResource>(&self, resource: R) -> AddResult<R>
  119. where
  120. Rs: FromResource<R>,
  121. As: FromArtifact<R> + Clone,
  122. R::Artifact: Clone,
  123. ActualSetup<SR, L, B, Rs, As>: Add<R>,
  124. {
  125. self.add_force(resource, false).await
  126. }
  127. pub async fn run_symbol<S: Symbol + Debug>(
  128. &self,
  129. symbol: S,
  130. force: bool,
  131. ) -> Result<bool, Box<dyn Error>>
  132. where
  133. SR: SymbolRunner,
  134. {
  135. let recorder = Recorder::default();
  136. let result = {
  137. let log = Rc::new(slog::Logger::root(
  138. recorder.clone(),
  139. o!("symbol" => format!("{symbol:?}")),
  140. ));
  141. self.1.run_symbol(&symbol, &log, force).await
  142. };
  143. self.log_result(recorder, result.as_ref().copied());
  144. result
  145. }
  146. fn log_result(&self, recorder: Recorder, result: Result<bool, &Box<dyn Error>>) {
  147. let log = match result {
  148. Ok(false) => String::new(),
  149. Ok(true) => recorder.into_string(slog::Level::Info),
  150. Err(e) => recorder.into_string(slog::Level::Trace),
  151. };
  152. if log.is_empty() {
  153. self.0.write(3, ".");
  154. } else {
  155. self.0.writeln(3, &log);
  156. }
  157. }
  158. }
  159. #[cfg(test)]
  160. mod test {
  161. use super::symbol_runner::TestSymbolRunner;
  162. use crate::async_utils::run;
  163. use crate::loggers::{Entry, StoringLogger};
  164. use crate::resources::{FromArtifact, FromResource, Resource};
  165. use crate::symbols::Symbol;
  166. use crate::to_artifact::ToArtifact;
  167. use crate::{ImplementationBuilder, ResourceLocator, Setup};
  168. use async_trait::async_trait;
  169. use regex::Regex;
  170. use std::cell::RefCell;
  171. use std::error::Error;
  172. use std::fmt::Debug;
  173. use std::rc::{Rc, Weak};
  174. #[derive(Debug, PartialEq, Eq, Hash)]
  175. struct TestResource<T>(&'static str, T);
  176. impl<T> Resource for TestResource<T> {
  177. type Artifact = ();
  178. }
  179. #[derive(Debug, Hash, PartialEq, Eq)]
  180. enum Resources {
  181. A(Rc<TestResource<()>>),
  182. B(Rc<TestResource<&'static str>>),
  183. C(Rc<TestResource<(&'static str, &'static str)>>),
  184. D(Rc<TestResource<((&'static str, &'static str), &'static str)>>),
  185. }
  186. impl FromResource<TestResource<()>> for Resources {
  187. fn from_resource(inner: &Rc<TestResource<()>>) -> (Self, Weak<TestResource<()>>) {
  188. (Self::A(Rc::clone(&inner)), Rc::downgrade(&inner))
  189. }
  190. }
  191. impl FromResource<TestResource<&'static str>> for Resources {
  192. fn from_resource(
  193. inner: &Rc<TestResource<&'static str>>,
  194. ) -> (Self, Weak<TestResource<&'static str>>) {
  195. (Self::B(Rc::clone(&inner)), Rc::downgrade(&inner))
  196. }
  197. }
  198. impl FromResource<TestResource<(&'static str, &'static str)>> for Resources {
  199. fn from_resource(
  200. inner: &Rc<TestResource<(&'static str, &'static str)>>,
  201. ) -> (Self, Weak<TestResource<(&'static str, &'static str)>>) {
  202. (Self::C(Rc::clone(&inner)), Rc::downgrade(&inner))
  203. }
  204. }
  205. impl FromResource<TestResource<((&'static str, &'static str), &'static str)>> for Resources {
  206. fn from_resource(
  207. inner: &Rc<TestResource<((&'static str, &'static str), &'static str)>>,
  208. ) -> (
  209. Self,
  210. Weak<TestResource<((&'static str, &'static str), &'static str)>>,
  211. ) {
  212. (Self::D(Rc::clone(&inner)), Rc::downgrade(&inner))
  213. }
  214. }
  215. #[derive(Clone)]
  216. struct Artifacts;
  217. impl<V> FromArtifact<TestResource<V>> for Artifacts {
  218. fn from_artifact(_from: ()) -> Self {
  219. Self
  220. }
  221. #[allow(clippy::unused_unit)]
  222. fn into_artifact(self) -> () {
  223. #[allow(clippy::unused_unit)]
  224. ()
  225. }
  226. }
  227. struct TestResourceLocator;
  228. impl<T> ResourceLocator<TestResource<T>> for TestResourceLocator {
  229. type Prerequisites = ();
  230. fn locate(_resource: &TestResource<T>) -> (<TestResource<T> as ToArtifact>::Artifact, ()) {
  231. ((), ())
  232. }
  233. }
  234. #[derive(Debug)]
  235. struct TestSymbol {
  236. result: Result<bool, Box<str>>,
  237. }
  238. impl TestSymbol {
  239. fn new(def: &str) -> Self {
  240. let first_char = def.chars().next().unwrap();
  241. Self {
  242. result: if first_char == '!' {
  243. Err(def.into())
  244. } else {
  245. Ok(first_char.is_uppercase())
  246. },
  247. }
  248. }
  249. }
  250. #[async_trait(?Send)]
  251. impl Symbol for TestSymbol {
  252. async fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
  253. self.result.clone().map_err(|s| s.to_string().into())
  254. }
  255. async fn execute(&self) -> Result<(), Box<dyn Error>> {
  256. Ok(())
  257. }
  258. }
  259. struct TestImplementationBuilder;
  260. impl ImplementationBuilder<TestResource<((&'static str, &'static str), &'static str)>>
  261. for TestImplementationBuilder
  262. {
  263. type Implementation = TestSymbol;
  264. type Prerequisites = (TestResource<(&'static str, &'static str)>, TestResource<()>);
  265. fn prerequisites(
  266. resource: &TestResource<((&'static str, &'static str), &'static str)>,
  267. ) -> Self::Prerequisites {
  268. (
  269. TestResource("complex_resource", (resource.1).0), // FIXME: Only one of these can exist
  270. TestResource((resource.1).1, ()),
  271. )
  272. }
  273. fn create(
  274. resource: &TestResource<((&'static str, &'static str), &'static str)>,
  275. (): &(),
  276. _inputs: <Self::Prerequisites as ToArtifact>::Artifact,
  277. ) -> Self::Implementation {
  278. TestSymbol::new(resource.0)
  279. }
  280. }
  281. impl ImplementationBuilder<TestResource<(&'static str, &'static str)>>
  282. for TestImplementationBuilder
  283. {
  284. type Implementation = TestSymbol;
  285. type Prerequisites = (TestResource<()>, TestResource<()>);
  286. fn prerequisites(resource: &TestResource<(&'static str, &'static str)>) -> Self::Prerequisites {
  287. (
  288. TestResource((resource.1).0, ()),
  289. TestResource((resource.1).1, ()),
  290. )
  291. }
  292. fn create(
  293. resource: &TestResource<(&'static str, &'static str)>,
  294. (): &(),
  295. _inputs: <Self::Prerequisites as ToArtifact>::Artifact,
  296. ) -> Self::Implementation {
  297. TestSymbol::new(resource.0)
  298. }
  299. }
  300. impl ImplementationBuilder<TestResource<&'static str>> for TestImplementationBuilder {
  301. type Implementation = TestSymbol;
  302. type Prerequisites = TestResource<()>;
  303. fn prerequisites(resource: &TestResource<&'static str>) -> Self::Prerequisites {
  304. TestResource(resource.1, ())
  305. }
  306. fn create(
  307. resource: &TestResource<&'static str>,
  308. (): &(),
  309. _inputs: <Self::Prerequisites as ToArtifact>::Artifact,
  310. ) -> Self::Implementation {
  311. TestSymbol::new(resource.0)
  312. }
  313. }
  314. impl ImplementationBuilder<TestResource<()>> for TestImplementationBuilder {
  315. type Implementation = TestSymbol;
  316. type Prerequisites = ();
  317. fn prerequisites(_resource: &TestResource<()>) -> Self::Prerequisites {}
  318. fn create(resource: &TestResource<()>, (): &(), (): ()) -> Self::Implementation {
  319. TestSymbol::new(resource.0)
  320. }
  321. }
  322. #[allow(clippy::type_complexity)]
  323. fn get_setup() -> (
  324. Rc<RefCell<usize>>,
  325. Setup<
  326. TestSymbolRunner,
  327. StoringLogger,
  328. TestResourceLocator,
  329. TestImplementationBuilder,
  330. Resources,
  331. Artifacts,
  332. >,
  333. StoringLogger,
  334. ) {
  335. let (count, runner) = TestSymbolRunner::new();
  336. let logger = StoringLogger::new();
  337. (count, Setup::new_with(runner, logger.clone()), logger)
  338. }
  339. #[test]
  340. fn correctly_uses_force() {
  341. run(async {
  342. let (count, setup, _) = get_setup();
  343. setup.add(TestResource("A", "b")).await.unwrap();
  344. assert_eq!(*count.borrow(), 2);
  345. setup.add(TestResource("A", "b")).await.unwrap();
  346. assert_eq!(*count.borrow(), 2);
  347. let (count, setup, _) = get_setup();
  348. setup.add(TestResource("A", "B")).await.unwrap();
  349. assert_eq!(*count.borrow(), 0);
  350. });
  351. }
  352. #[test]
  353. fn failing_dependencies_deadlock() {
  354. run(async {
  355. let (count, setup, _) = get_setup();
  356. assert_eq!(
  357. setup
  358. .add(TestResource("a", (("b", "!x"), "!x")))
  359. .await
  360. .unwrap_err()
  361. .to_string(),
  362. "!x"
  363. );
  364. assert_eq!(*count.borrow(), 1);
  365. });
  366. }
  367. #[test]
  368. fn run_reached_symbol() {
  369. run(async {
  370. let (count, setup, log) = get_setup();
  371. let did_run = setup
  372. .run_symbol(TestSymbol { result: Ok(true) }, false)
  373. .await
  374. .unwrap();
  375. drop(setup);
  376. assert!(!did_run);
  377. assert_eq!(*count.borrow(), 0);
  378. assert_eq!(log.release(), vec![Entry(3, ".".into())]);
  379. });
  380. }
  381. #[test]
  382. fn run_not_reached_symbol() {
  383. run(async {
  384. let (count, setup, log) = get_setup();
  385. let did_run = setup
  386. .run_symbol(TestSymbol { result: Ok(false) }, false)
  387. .await
  388. .unwrap();
  389. drop(setup);
  390. assert!(did_run);
  391. assert_eq!(*count.borrow(), 1);
  392. let log = log.release();
  393. assert_eq!(log.len(), 1);
  394. assert_eq!(log[0].0, 3);
  395. let re = Regex::new(r"^symbol: TestSymbol \{ result: Ok\(false\) \}\n \w+ \d{1,2} \d{2}:\d{2}:\d{2}.\d{3} INFO run\n$").unwrap();
  396. assert!(re.is_match(&log[0].1));
  397. });
  398. }
  399. use super::{ActualSetup, AddGeneric, Cache, Realizer};
  400. #[test]
  401. fn empty_tuple_add_generic() {
  402. let setup = Rc::new_cyclic(|inner| {
  403. ActualSetup(Cache::<Realizer<(), (), (), _>, (), ()>::new(
  404. Realizer::new(Rc::new(()), inner.clone()),
  405. ))
  406. });
  407. run(async {
  408. assert!(setup
  409. .add_generic(
  410. &Rc::new(slog::Logger::root(slog::Discard, slog::o!())),
  411. (),
  412. false
  413. )
  414. .await
  415. .is_ok());
  416. })
  417. }
  418. }