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.

482 lines
13 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
2 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. use crate::artifacts::{
  2. DatabaseName as DatabaseNameArtifact, Path as PathArtifact, ServiceName as ServiceNameArtifact,
  3. UserName as UserNameArtifact,
  4. };
  5. use crate::resources::{
  6. AcmeAccountKey, AcmeChallengesDir, AcmeChallengesNginxSnippet, AcmeRootCert, AcmeUser, Cert,
  7. CertChain, Cron, Csr, DefaultServer, Dir, File, GitCheckout, Key, KeyAndCertBundle,
  8. LoadedDirectory, MariaDbDatabase, MariaDbUser, NpmInstall, Owner, PhpFpmPool, PostgresqlDatabase,
  9. Resource, ServeCustom, ServePhp, ServeRedir, ServeService, ServeStatic, StoredDirectory,
  10. SystemdSocketService, User, UserForDomain, WordpressPlugin, WordpressTranslation,
  11. };
  12. use crate::to_artifact::ToArtifact;
  13. use std::fmt::Display;
  14. use std::marker::PhantomData;
  15. use std::path::{Path, PathBuf};
  16. pub trait Policy {
  17. #[must_use]
  18. fn acme_user() -> &'static str {
  19. "acme"
  20. }
  21. #[must_use]
  22. fn user_home(user_name: &str) -> PathBuf {
  23. Path::new("/home").join(user_name)
  24. }
  25. #[must_use]
  26. fn user_name_for_domain(domain_name: &'_ str) -> String {
  27. domain_name.split('.').rev().fold(String::new(), |result, part| if result.is_empty() { result } else { result + "_" } + part)
  28. }
  29. #[must_use]
  30. fn php_version() -> &'static str {
  31. "7.0"
  32. }
  33. #[must_use]
  34. fn path_for_data(name: impl Display) -> PathBuf {
  35. Path::new("/root/data").join(format!("_{}", name))
  36. }
  37. }
  38. #[derive(Debug)]
  39. pub struct DefaultPolicy;
  40. impl Policy for DefaultPolicy {}
  41. pub trait ResourceLocator<R> {
  42. type Prerequisites: ToArtifact;
  43. fn locate(resource: &R) -> (R::Artifact, Self::Prerequisites)
  44. where
  45. R: Resource;
  46. }
  47. #[derive(Debug)]
  48. pub struct DefaultLocator<P = DefaultPolicy> {
  49. phantom: PhantomData<P>,
  50. }
  51. impl<P, D: AsRef<str>> ResourceLocator<Key<D>> for DefaultLocator<P> {
  52. type Prerequisites = Dir<PathBuf>;
  53. fn locate(resource: &Key<D>) -> (<Key<D> as Resource>::Artifact, Self::Prerequisites) {
  54. (
  55. PathArtifact::from(format!("/etc/ssl/private/{}.key", resource.0.as_ref())),
  56. Dir("/etc/ssl/private".into()),
  57. )
  58. }
  59. }
  60. impl<P, D: AsRef<str>> ResourceLocator<Csr<D>> for DefaultLocator<P> {
  61. type Prerequisites = Dir<PathBuf>;
  62. fn locate(resource: &Csr<D>) -> (<Csr<D> as Resource>::Artifact, Self::Prerequisites) {
  63. (
  64. PathArtifact::from(format!("/etc/ssl/local_certs/{}.csr", resource.0.as_ref())),
  65. Dir("/etc/ssl/local_certs".into()),
  66. )
  67. }
  68. }
  69. impl<P, D: AsRef<str>> ResourceLocator<Cert<D>> for DefaultLocator<P> {
  70. type Prerequisites = Dir<PathBuf>;
  71. fn locate(resource: &Cert<D>) -> (<Cert<D> as Resource>::Artifact, Self::Prerequisites) {
  72. (
  73. PathArtifact::from(format!("/etc/ssl/local_certs/{}.crt", resource.0.as_ref())),
  74. Dir("/etc/ssl/local_certs".into()),
  75. )
  76. }
  77. }
  78. impl<P, D: AsRef<str>> ResourceLocator<CertChain<D>> for DefaultLocator<P> {
  79. type Prerequisites = Dir<PathBuf>;
  80. fn locate(
  81. resource: &CertChain<D>,
  82. ) -> (<CertChain<D> as Resource>::Artifact, Self::Prerequisites) {
  83. (
  84. PathArtifact::from(format!(
  85. "/etc/ssl/local_certs/{}.chained.crt",
  86. resource.0.as_ref()
  87. )),
  88. Dir("/etc/ssl/local_certs".into()),
  89. )
  90. }
  91. }
  92. impl<P, D: AsRef<str>> ResourceLocator<KeyAndCertBundle<D>> for DefaultLocator<P> {
  93. type Prerequisites = Dir<PathBuf>;
  94. fn locate(
  95. resource: &KeyAndCertBundle<D>,
  96. ) -> (
  97. <KeyAndCertBundle<D> as Resource>::Artifact,
  98. Self::Prerequisites,
  99. ) {
  100. (
  101. PathArtifact::from(format!(
  102. "/etc/ssl/private/{}.with_key.crt",
  103. resource.0.as_ref()
  104. )),
  105. Dir("/etc/ssl/private".into()),
  106. )
  107. }
  108. }
  109. impl<'a, POLICY, P: AsRef<Path>> ResourceLocator<File<P>> for DefaultLocator<POLICY> {
  110. type Prerequisites = Dir<PathBuf>;
  111. fn locate(resource: &File<P>) -> (<File<P> as Resource>::Artifact, Self::Prerequisites) {
  112. ((), Dir(resource.0.as_ref().parent().unwrap().into()))
  113. }
  114. }
  115. impl<'a, POLICY, P: AsRef<Path>> ResourceLocator<GitCheckout<'a, P>> for DefaultLocator<POLICY> {
  116. type Prerequisites = Dir<PathBuf>;
  117. fn locate(
  118. resource: &GitCheckout<'a, P>,
  119. ) -> (
  120. <GitCheckout<'a, P> as Resource>::Artifact,
  121. Self::Prerequisites,
  122. ) {
  123. ((), Dir(resource.0.as_ref().parent().unwrap().into()))
  124. }
  125. }
  126. impl<POLICY, P: Clone + AsRef<Path>> ResourceLocator<Dir<P>> for DefaultLocator<POLICY> {
  127. type Prerequisites = Option<Dir<PathBuf>>;
  128. fn locate(resource: &Dir<P>) -> (<Dir<P> as Resource>::Artifact, Self::Prerequisites) {
  129. ((), resource.0.as_ref().parent().map(|p| Dir(p.into())))
  130. }
  131. }
  132. impl<POLICY, P> ResourceLocator<NpmInstall<P>> for DefaultLocator<POLICY> {
  133. type Prerequisites = ();
  134. fn locate(
  135. _resource: &NpmInstall<P>,
  136. ) -> (<NpmInstall<P> as Resource>::Artifact, Self::Prerequisites) {
  137. ((), ())
  138. }
  139. }
  140. impl<POLICY: Policy, P: AsRef<Path>> ResourceLocator<StoredDirectory<P>>
  141. for DefaultLocator<POLICY>
  142. {
  143. type Prerequisites = ();
  144. fn locate(
  145. resource: &StoredDirectory<P>,
  146. ) -> (
  147. <StoredDirectory<P> as Resource>::Artifact,
  148. Self::Prerequisites,
  149. ) {
  150. (PathArtifact::from(POLICY::path_for_data(resource.0)), ())
  151. }
  152. }
  153. impl<POLICY: Policy, P: AsRef<Path>> ResourceLocator<LoadedDirectory<P>>
  154. for DefaultLocator<POLICY>
  155. {
  156. type Prerequisites = Dir<PathBuf>;
  157. fn locate(
  158. resource: &LoadedDirectory<P>,
  159. ) -> (
  160. <LoadedDirectory<P> as Resource>::Artifact,
  161. Self::Prerequisites,
  162. ) {
  163. (
  164. PathArtifact::from(POLICY::path_for_data(resource.0)),
  165. Dir(resource.1.as_ref().parent().unwrap().into()),
  166. )
  167. }
  168. }
  169. impl<P: Policy> ResourceLocator<AcmeAccountKey> for DefaultLocator<P> {
  170. type Prerequisites = Dir<PathBuf>;
  171. fn locate(
  172. _resource: &AcmeAccountKey,
  173. ) -> (<AcmeAccountKey as Resource>::Artifact, Self::Prerequisites) {
  174. let acme_user = P::acme_user();
  175. let home = P::user_home(acme_user);
  176. (PathArtifact::from(home.join("account.key")), Dir(home))
  177. }
  178. }
  179. impl<P: Policy> ResourceLocator<AcmeUser> for DefaultLocator<P> {
  180. type Prerequisites = ();
  181. fn locate(_resource: &AcmeUser) -> (<AcmeUser as Resource>::Artifact, Self::Prerequisites) {
  182. let acme_user = P::acme_user();
  183. (UserNameArtifact(acme_user.into()), ())
  184. }
  185. }
  186. impl<P: Policy> ResourceLocator<AcmeChallengesDir> for DefaultLocator<P> {
  187. type Prerequisites = Dir<PathBuf>;
  188. fn locate(
  189. _resource: &AcmeChallengesDir,
  190. ) -> (
  191. <AcmeChallengesDir as Resource>::Artifact,
  192. Self::Prerequisites,
  193. ) {
  194. let acme_user = P::acme_user();
  195. let home = P::user_home(acme_user);
  196. (PathArtifact::from(home.join("challenges")), Dir(home))
  197. }
  198. }
  199. impl<P: Policy> ResourceLocator<AcmeChallengesNginxSnippet> for DefaultLocator<P> {
  200. type Prerequisites = ();
  201. fn locate(
  202. _resource: &AcmeChallengesNginxSnippet,
  203. ) -> (
  204. <AcmeChallengesNginxSnippet as Resource>::Artifact,
  205. Self::Prerequisites,
  206. ) {
  207. (
  208. PathArtifact::from("/etc/nginx/snippets/acme-challenge.conf"),
  209. (),
  210. )
  211. }
  212. }
  213. impl<P: Policy> ResourceLocator<AcmeRootCert> for DefaultLocator<P> {
  214. type Prerequisites = Dir<PathBuf>;
  215. fn locate(
  216. _resource: &AcmeRootCert,
  217. ) -> (<AcmeRootCert as Resource>::Artifact, Self::Prerequisites) {
  218. let acme_user = P::acme_user();
  219. let home = P::user_home(acme_user);
  220. (
  221. PathArtifact::from(home.join("lets_encrypt_r3.pem")),
  222. Dir(home),
  223. )
  224. }
  225. }
  226. impl<P: Policy, D: AsRef<str>> ResourceLocator<UserForDomain<D>> for DefaultLocator<P> {
  227. type Prerequisites = ();
  228. fn locate(
  229. resource: &UserForDomain<D>,
  230. ) -> (
  231. <UserForDomain<D> as Resource>::Artifact,
  232. Self::Prerequisites,
  233. ) {
  234. let user_name = P::user_name_for_domain(resource.0.as_ref());
  235. let home = P::user_home(&user_name);
  236. ((UserNameArtifact(user_name), PathArtifact::from(home)), ())
  237. }
  238. }
  239. impl<P> ResourceLocator<User> for DefaultLocator<P> {
  240. type Prerequisites = ();
  241. fn locate(_resource: &User) -> (<User as Resource>::Artifact, Self::Prerequisites) {
  242. ((), ())
  243. }
  244. }
  245. impl<P, POLICY> ResourceLocator<Owner<P>> for DefaultLocator<POLICY> {
  246. type Prerequisites = User;
  247. fn locate(resource: &Owner<P>) -> (<Owner<P> as Resource>::Artifact, Self::Prerequisites) {
  248. ((), User(resource.0.clone()))
  249. }
  250. }
  251. impl<P> ResourceLocator<DefaultServer> for DefaultLocator<P> {
  252. type Prerequisites = ();
  253. fn locate(
  254. _resource: &DefaultServer,
  255. ) -> (<DefaultServer as Resource>::Artifact, Self::Prerequisites) {
  256. (PathArtifact::from("/etc/nginx/sites-enabled/default"), ())
  257. }
  258. }
  259. impl<D: AsRef<Path>, POLICY> ResourceLocator<ServeCustom<D>> for DefaultLocator<POLICY> {
  260. type Prerequisites = ();
  261. fn locate(
  262. resource: &ServeCustom<D>,
  263. ) -> (<ServeCustom<D> as Resource>::Artifact, Self::Prerequisites) {
  264. (
  265. PathArtifact::from(Path::new("/etc/nginx/sites-enabled/").join(&resource.0)),
  266. (),
  267. )
  268. }
  269. }
  270. impl<D: AsRef<Path>, P, C, POLICY> ResourceLocator<ServePhp<D, P, C>> for DefaultLocator<POLICY> {
  271. type Prerequisites = ();
  272. fn locate(
  273. resource: &ServePhp<D, P, C>,
  274. ) -> (
  275. <ServePhp<D, P, C> as Resource>::Artifact,
  276. Self::Prerequisites,
  277. ) {
  278. (
  279. PathArtifact::from(Path::new("/etc/nginx/sites-enabled/").join(&resource.0)),
  280. (),
  281. )
  282. }
  283. }
  284. impl<D: AsRef<Path>, P, POLICY> ResourceLocator<ServeService<D, P>> for DefaultLocator<POLICY> {
  285. type Prerequisites = ();
  286. fn locate(
  287. resource: &ServeService<D, P>,
  288. ) -> (
  289. <ServeService<D, P> as Resource>::Artifact,
  290. Self::Prerequisites,
  291. ) {
  292. (
  293. PathArtifact::from(Path::new("/etc/nginx/sites-enabled/").join(&resource.0)),
  294. (),
  295. )
  296. }
  297. }
  298. impl<D: AsRef<Path>, POLICY> ResourceLocator<ServeRedir<D>> for DefaultLocator<POLICY> {
  299. type Prerequisites = ();
  300. fn locate(
  301. resource: &ServeRedir<D>,
  302. ) -> (<ServeRedir<D> as Resource>::Artifact, Self::Prerequisites) {
  303. (
  304. PathArtifact::from(Path::new("/etc/nginx/sites-enabled/").join(&resource.0)),
  305. (),
  306. )
  307. }
  308. }
  309. impl<D: AsRef<Path>, P, POLICY> ResourceLocator<ServeStatic<D, P>> for DefaultLocator<POLICY> {
  310. type Prerequisites = ();
  311. fn locate(
  312. resource: &ServeStatic<D, P>,
  313. ) -> (
  314. <ServeStatic<D, P> as Resource>::Artifact,
  315. Self::Prerequisites,
  316. ) {
  317. (
  318. PathArtifact::from(Path::new("/etc/nginx/sites-enabled/").join(&resource.0)),
  319. (),
  320. )
  321. }
  322. }
  323. impl<D: Clone + AsRef<str>, P: Policy> ResourceLocator<PhpFpmPool<D>> for DefaultLocator<P> {
  324. type Prerequisites = ();
  325. fn locate(
  326. resource: &PhpFpmPool<D>,
  327. ) -> (<PhpFpmPool<D> as Resource>::Artifact, Self::Prerequisites) {
  328. let ((user, _), ()) = Self::locate(&UserForDomain(&resource.0));
  329. let php_version = P::php_version();
  330. (
  331. (
  332. PathArtifact::from(format!("/run/php/{}.sock", user.0)),
  333. PathArtifact::from(format!(
  334. "/etc/php/{}/fpm/pool.d/{}.conf",
  335. php_version, user.0
  336. )),
  337. user,
  338. ServiceNameArtifact(format!("php{}-fpm", php_version)),
  339. ),
  340. (),
  341. )
  342. }
  343. }
  344. impl<D: Clone + AsRef<str>, P, POLICY: Policy> ResourceLocator<SystemdSocketService<D, P>>
  345. for DefaultLocator<POLICY>
  346. {
  347. type Prerequisites = Dir<PathBuf>;
  348. fn locate(
  349. resource: &SystemdSocketService<D, P>,
  350. ) -> (
  351. <SystemdSocketService<D, P> as Resource>::Artifact,
  352. Self::Prerequisites,
  353. ) {
  354. let ((user_name, home_path), ()) = Self::locate(&UserForDomain(&resource.0));
  355. let config = home_path.as_ref().join(".config");
  356. let service_dir_path = config.join("systemd/user");
  357. (
  358. (
  359. PathArtifact::from(format!("/var/tmp/{}-{}.socket", user_name.0, resource.1)),
  360. PathArtifact::from(service_dir_path.join(format!("{}.service", resource.1))),
  361. user_name,
  362. ),
  363. Dir(service_dir_path),
  364. )
  365. }
  366. }
  367. impl<D: AsRef<str>, P: Policy> ResourceLocator<MariaDbDatabase<D>> for DefaultLocator<P> {
  368. type Prerequisites = ();
  369. fn locate(
  370. resource: &MariaDbDatabase<D>,
  371. ) -> (
  372. <MariaDbDatabase<D> as Resource>::Artifact,
  373. Self::Prerequisites,
  374. ) {
  375. let ((user_name, _), ()) = Self::locate(&UserForDomain(&resource.0));
  376. (
  377. (
  378. DatabaseNameArtifact(user_name.0.clone()),
  379. user_name.clone(),
  380. PathArtifact::from(P::path_for_data(format!("{}.sql", user_name.0))),
  381. ),
  382. (),
  383. )
  384. }
  385. }
  386. impl<D: AsRef<str>, P: Policy> ResourceLocator<MariaDbUser<D>> for DefaultLocator<P> {
  387. type Prerequisites = ();
  388. fn locate(
  389. resource: &MariaDbUser<D>,
  390. ) -> (<MariaDbUser<D> as Resource>::Artifact, Self::Prerequisites) {
  391. let ((user_name, _), ()) = Self::locate(&UserForDomain(&resource.0));
  392. ((user_name), ())
  393. }
  394. }
  395. impl<D: AsRef<str>, P: Policy> ResourceLocator<PostgresqlDatabase<D>> for DefaultLocator<P> {
  396. type Prerequisites = ();
  397. fn locate(
  398. resource: &PostgresqlDatabase<D>,
  399. ) -> (
  400. <PostgresqlDatabase<D> as Resource>::Artifact,
  401. Self::Prerequisites,
  402. ) {
  403. let ((user_name, _), ()) = Self::locate(&UserForDomain(&resource.0));
  404. (
  405. (
  406. DatabaseNameArtifact(user_name.0.clone()),
  407. PathArtifact::from(P::path_for_data(format!("{}.sql", user_name.0))),
  408. ),
  409. (),
  410. )
  411. }
  412. }
  413. impl<P, POLICY> ResourceLocator<WordpressPlugin<P>> for DefaultLocator<POLICY> {
  414. type Prerequisites = ();
  415. fn locate(
  416. _resource: &WordpressPlugin<P>,
  417. ) -> (
  418. <WordpressPlugin<P> as Resource>::Artifact,
  419. Self::Prerequisites,
  420. ) {
  421. ((), ())
  422. }
  423. }
  424. impl<P, POLICY> ResourceLocator<WordpressTranslation<P>> for DefaultLocator<POLICY> {
  425. type Prerequisites = ();
  426. fn locate(
  427. _resource: &WordpressTranslation<P>,
  428. ) -> (
  429. <WordpressTranslation<P> as Resource>::Artifact,
  430. Self::Prerequisites,
  431. ) {
  432. ((), ())
  433. }
  434. }
  435. impl<D, P> ResourceLocator<Cron<D>> for DefaultLocator<P> {
  436. type Prerequisites = ();
  437. fn locate(_resource: &Cron<D>) -> (<Cron<D> as Resource>::Artifact, Self::Prerequisites) {
  438. ((), ())
  439. }
  440. }