use crate::command_runner::{SetuidCommandRunner, StdCommandRunner}; use crate::resources::{ AcmeAccountKey, AcmeChallengesDir, AcmeChallengesNginxSnippet, AcmeRootCert, AcmeUser, Cert, CertChain, Cron, Csr, DefaultServer, Dir, File, GitCheckout, Key, KeyAndCertBundle, LoadedDirectory, MariaDbDatabase, MariaDbUser, NpmInstall, Owner, PhpFpmPool, PostgresqlDatabase, Resource, ServeCustom, ServePhp, ServeRedir, ServeService, ServeStatic, StoredDirectory, SystemdSocketService, User, UserForDomain, WordpressPlugin, WordpressTranslation, }; use crate::static_files::LETS_ENCRYPT_R3; use crate::storage::SimpleStorage; use crate::storage::Storage; use crate::symbols::acme::Cert as CertSymbol; use crate::symbols::concat::Concat as ConcatSymbol; use crate::symbols::cron::Cron as CronSymbol; use crate::symbols::dir::Dir as DirSymbol; use crate::symbols::file::File as FileSymbol; use crate::symbols::git::Checkout as GitCheckoutSymbol; use crate::symbols::mariadb::{ Database as MariaDbDatabaseSymbol, Dump as MariaDbDumpSymbol, User as MariaDbUserSymbol, }; use crate::symbols::npm::Install as NpmInstallSymbol; use crate::symbols::owner::Owner as OwnerSymbol; use crate::symbols::postgresql::PostgreSQLDatabase as PostgreSQLDatabaseSymbol; use crate::symbols::saved_directory::{SavedDirectory as SavedDirectorySymbol, StorageDirection}; use crate::symbols::systemd::{ ReloadService as ReloadServiceSymbol, UserService as UserServiceSymbol, UserSession as SystemdUserSessionSymbol, }; use crate::symbols::tls::Csr as CsrSymbol; use crate::symbols::tls::Key as KeySymbol; use crate::symbols::user::User as UserSymbol; use crate::symbols::wordpress::{ Plugin as WordpressPluginSymbol, Translation as WordpressTranslationSymbol, }; use crate::templates::nginx; use crate::templates::php::{ fpm_pool_config as php_fpm_pool_config, FpmPoolConfig as PhpFpmPoolConfig, }; use crate::templates::systemd::{ nodejs_service as systemd_nodejs_service, socket_service as systemd_socket_service, }; use crate::to_artifact::ToArtifact; use std::fmt::Display; use std::path::Path; use std::rc::Rc; pub trait ImplementationBuilder { type Prerequisites: ToArtifact; fn prerequisites(resource: &R) -> Self::Prerequisites; type Implementation; fn create( resource: &R, target: &R::Artifact, inputs: ::Artifact, ) -> Self::Implementation where R: Resource; } #[derive(Debug)] pub struct DefaultBuilder; impl ImplementationBuilder> for DefaultBuilder { type Prerequisites = (); fn prerequisites(_resource: &Key) -> Self::Prerequisites {} type Implementation = KeySymbol>; fn create( _resource: &Key, target: & as Resource>::Artifact, (): ::Artifact, ) -> Self::Implementation { KeySymbol::new(StdCommandRunner, target.clone_rc()) } } impl ImplementationBuilder> for DefaultBuilder { type Prerequisites = Key; fn prerequisites(resource: &Csr) -> Self::Prerequisites { Key(resource.0.clone()) } type Implementation = CsrSymbol, Rc>; fn create( resource: &Csr, target: & as Resource>::Artifact, key: ::Artifact, ) -> Self::Implementation { CsrSymbol::new( StdCommandRunner, resource.0.clone(), key.clone_rc(), target.clone_rc(), ) } } impl ImplementationBuilder> for DefaultBuilder { type Prerequisites = ( Csr, AcmeRootCert, AcmeAccountKey, AcmeChallengesDir, AcmeUser, DefaultServer, ); fn prerequisites(resource: &Cert) -> Self::Prerequisites { ( Csr(resource.0.clone()), AcmeRootCert, AcmeAccountKey, AcmeChallengesDir, AcmeUser, DefaultServer, ) } type Implementation = CertSymbol>, SetuidCommandRunner>, D, Rc>; fn create( resource: &Cert, target: & as Resource>::Artifact, (csr, root_cert, account_key, challenges_dir, (user_name, _), _): ::Artifact, ) -> Self::Implementation { CertSymbol::new( resource.0.clone(), SetuidCommandRunner::new(user_name.0), root_cert.clone_rc(), account_key.clone_rc(), challenges_dir.clone_rc(), csr.clone_rc(), target.clone_rc(), ) } } impl ImplementationBuilder> for DefaultBuilder { type Prerequisites = (Cert, AcmeRootCert); fn prerequisites(resource: &CertChain) -> Self::Prerequisites { (Cert(resource.0.clone()), AcmeRootCert) } type Implementation = ConcatSymbol<[Rc; 2], Rc, Rc>; fn create( _resource: &CertChain, target: & as Resource>::Artifact, (cert, root_cert): ::Artifact, ) -> Self::Implementation { ConcatSymbol::new([cert.clone_rc(), root_cert.clone_rc()], target.clone_rc()) } } impl ImplementationBuilder> for DefaultBuilder { type Prerequisites = (CertChain, Key); fn prerequisites(resource: &KeyAndCertBundle) -> Self::Prerequisites { (CertChain(resource.0.clone()), Key(resource.0.clone())) } type Implementation = ConcatSymbol<[Rc; 2], Rc, Rc>; fn create( _resource: &KeyAndCertBundle, target: & as Resource>::Artifact, (cert_chain, key): ::Artifact, ) -> Self::Implementation { ConcatSymbol::new([key.clone_rc(), cert_chain.clone_rc()], target.clone_rc()) } } impl + Clone> ImplementationBuilder> for DefaultBuilder { type Prerequisites = (); fn prerequisites(_resource: &File

) -> Self::Prerequisites {} type Implementation = FileSymbol>; fn create( resource: &File

, _target: & as Resource>::Artifact, (): ::Artifact, ) -> Self::Implementation { FileSymbol::new(resource.0.clone(), resource.1.clone()) } } impl<'a, P: AsRef + Clone> ImplementationBuilder> for DefaultBuilder { type Prerequisites = (); fn prerequisites(_resource: &GitCheckout<'a, P>) -> Self::Prerequisites {} type Implementation = GitCheckoutSymbol; fn create( resource: &GitCheckout<'a, P>, _target: & as Resource>::Artifact, (): ::Artifact, ) -> Self::Implementation { GitCheckoutSymbol::new(resource.0.clone(), resource.1, resource.2, StdCommandRunner) } } impl ImplementationBuilder for DefaultBuilder { type Prerequisites = AcmeChallengesNginxSnippet; fn prerequisites(_resource: &DefaultServer) -> Self::Prerequisites { AcmeChallengesNginxSnippet } type Implementation = ( FileSymbol, Box>, ReloadServiceSymbol, ); fn create( _resource: &DefaultServer, target: &::Artifact, challenges_snippet_path: ::Artifact, ) -> Self::Implementation { ( FileSymbol::new( target.clone_rc(), nginx::default_server(challenges_snippet_path).into(), ), ReloadServiceSymbol::new(StdCommandRunner, "nginx"), ) } } impl + Clone + Display> ImplementationBuilder> for DefaultBuilder { type Prerequisites = (CertChain, Key, AcmeChallengesNginxSnippet); fn prerequisites(resource: &ServeCustom) -> Self::Prerequisites { ( CertChain(resource.0.clone()), Key(resource.0.clone()), AcmeChallengesNginxSnippet, ) } type Implementation = ( FileSymbol, Box>, ReloadServiceSymbol, ); fn create( resource: &ServeCustom, target: & as Resource>::Artifact, (cert, key, challenges_snippet_path): ::Artifact, ) -> Self::Implementation { ( FileSymbol::new( target.clone_rc(), nginx::server_config(&resource.0, cert, key, &resource.1, challenges_snippet_path).into(), ), ReloadServiceSymbol::new(StdCommandRunner, "nginx"), ) } } impl, C: Clone + Into> ImplementationBuilder> for DefaultBuilder { type Prerequisites = ( PhpFpmPool, CertChain, Key, AcmeChallengesNginxSnippet, ); fn prerequisites(resource: &ServePhp) -> Self::Prerequisites { ( PhpFpmPool(resource.0.clone(), resource.4.clone().into()), CertChain(resource.0.clone()), Key(resource.0.clone()), AcmeChallengesNginxSnippet, ) } type Implementation = ( FileSymbol, Box>, ReloadServiceSymbol, ); fn create( resource: &ServePhp, target: & as Resource>::Artifact, (pool, cert, key, challenges_snippet_path): ::Artifact, ) -> Self::Implementation { ( FileSymbol::new( target.clone_rc(), nginx::server_config( &resource.0, cert, key, nginx::php_snippet(resource.2, pool.0, &resource.1) + &resource.3, challenges_snippet_path, ) .into(), ), ReloadServiceSymbol::new(StdCommandRunner, "nginx"), ) } } impl> ImplementationBuilder> for DefaultBuilder { type Prerequisites = ( SystemdSocketService, CertChain, Key, AcmeChallengesNginxSnippet, ); fn prerequisites(resource: &ServeService) -> Self::Prerequisites { ( SystemdSocketService( resource.0.clone(), resource.1, resource.2.clone(), resource.4.clone(), resource.5, ), CertChain(resource.0.clone()), Key(resource.0.clone()), AcmeChallengesNginxSnippet, ) } type Implementation = ( FileSymbol, Box>, ReloadServiceSymbol, ); fn create( resource: &ServeService, target: & as Resource>::Artifact, (socket, cert, key, challenges_snippet_path): ::Artifact, ) -> Self::Implementation { ( FileSymbol::new( target.clone_rc(), nginx::server_config( &resource.0, cert, key, nginx::proxy_snippet(&socket.0, &resource.3), challenges_snippet_path, ) .into(), ), ReloadServiceSymbol::new(StdCommandRunner, "nginx"), ) } } impl + Clone + Display> ImplementationBuilder> for DefaultBuilder { type Prerequisites = (CertChain, Key, AcmeChallengesNginxSnippet); fn prerequisites(resource: &ServeRedir) -> Self::Prerequisites { ( CertChain(resource.0.clone()), Key(resource.0.clone()), AcmeChallengesNginxSnippet, ) } type Implementation = ( FileSymbol, Box>, ReloadServiceSymbol, ); fn create( resource: &ServeRedir, target: & as Resource>::Artifact, (cert, key, challenges_snippet_path): ::Artifact, ) -> Self::Implementation { ( FileSymbol::new( target.clone_rc(), nginx::server_config( &resource.0, cert, key, nginx::redir_snippet(resource.1.as_ref()), challenges_snippet_path, ) .into(), ), ReloadServiceSymbol::new(StdCommandRunner, "nginx"), ) } } impl + Clone + Display, P: AsRef> ImplementationBuilder> for DefaultBuilder { type Prerequisites = (CertChain, Key, AcmeChallengesNginxSnippet); fn prerequisites(resource: &ServeStatic) -> Self::Prerequisites { ( CertChain(resource.0.clone()), Key(resource.0.clone()), AcmeChallengesNginxSnippet, ) } type Implementation = ( FileSymbol, Box>, ReloadServiceSymbol, ); fn create( resource: &ServeStatic, target: & as Resource>::Artifact, (cert, key, challenges_snippet_path): ::Artifact, ) -> Self::Implementation { ( FileSymbol::new( target.clone_rc(), nginx::server_config( &resource.0, cert, key, nginx::static_snippet(resource.1.as_ref()), challenges_snippet_path, ) .into(), ), ReloadServiceSymbol::new(StdCommandRunner, "nginx"), ) } } impl ImplementationBuilder> for DefaultBuilder { type Prerequisites = (); fn prerequisites(_resource: &PhpFpmPool) -> Self::Prerequisites {} type Implementation = ( FileSymbol, Box>, ReloadServiceSymbol>, ); fn create( resource: &PhpFpmPool, (socket_path, conf_path, user_name, service_name): & as Resource>::Artifact, (): ::Artifact, ) -> Self::Implementation { ( FileSymbol::new( conf_path.clone_rc(), php_fpm_pool_config(&user_name.0, socket_path, &resource.1).into(), ), ReloadServiceSymbol::new(StdCommandRunner, service_name.0.clone()), ) } } impl> ImplementationBuilder> for DefaultBuilder { type Prerequisites = (); fn prerequisites(_resource: &SystemdSocketService) -> Self::Prerequisites {} type Implementation = ( // First three could be parallel FileSymbol, Box>, SystemdUserSessionSymbol<'static, Rc, StdCommandRunner>, OwnerSymbol, Rc>, UserServiceSymbol<'static, Rc, Rc>, ); fn create( resource: &SystemdSocketService, (socket_path, conf_path, user_name): & as Resource>::Artifact, (): ::Artifact, ) -> Self::Implementation { ( FileSymbol::new( conf_path.clone_rc(), if resource.4 { systemd_nodejs_service(&resource.2, socket_path, &resource.3) } else { systemd_socket_service( socket_path, resource.2.as_ref().to_str().unwrap(), &resource.3, "", ) } .into(), ), SystemdUserSessionSymbol::new(user_name.0.clone(), &StdCommandRunner), OwnerSymbol::new( conf_path.as_ref().parent().unwrap().into(), user_name.0.clone(), StdCommandRunner, ), UserServiceSymbol::new(socket_path.clone_rc(), user_name.0.clone(), resource.1), ) } } impl ImplementationBuilder> for DefaultBuilder { type Prerequisites = (); fn prerequisites(_resource: &Dir

) -> Self::Prerequisites {} type Implementation = DirSymbol

; fn create( resource: &Dir

, _target: & as Resource>::Artifact, (): ::Artifact, ) -> Self::Implementation { DirSymbol::new(resource.0.clone()) } } impl> ImplementationBuilder> for DefaultBuilder { type Prerequisites = (); fn prerequisites(_resource: &NpmInstall

) -> Self::Prerequisites {} type Implementation = NpmInstallSymbol<'static, P, StdCommandRunner>; fn create( resource: &NpmInstall

, _target: & as Resource>::Artifact, (): ::Artifact, ) -> Self::Implementation { NpmInstallSymbol::new(resource.0.clone(), &StdCommandRunner) } } impl> ImplementationBuilder> for DefaultBuilder { type Prerequisites = (); fn prerequisites(_resource: &StoredDirectory

) -> Self::Prerequisites {} type Implementation = SavedDirectorySymbol; fn create( resource: &StoredDirectory

, target: & as Resource>::Artifact, (): ::Artifact, ) -> Self::Implementation { SavedDirectorySymbol::new( resource.1.clone(), SimpleStorage::new(target.clone_rc()), StorageDirection::Store, StdCommandRunner, ) } } impl> ImplementationBuilder> for DefaultBuilder { type Prerequisites = (); fn prerequisites(_resource: &LoadedDirectory

) -> Self::Prerequisites {} type Implementation = SavedDirectorySymbol; fn create( resource: &LoadedDirectory

, target: & as Resource>::Artifact, (): ::Artifact, ) -> Self::Implementation { SavedDirectorySymbol::new( resource.1.clone(), SimpleStorage::new(target.clone_rc()), StorageDirection::Load, StdCommandRunner, ) } } impl ImplementationBuilder> for DefaultBuilder { type Prerequisites = (); fn prerequisites(_resource: &UserForDomain) -> Self::Prerequisites {} type Implementation = UserSymbol, Rc, StdCommandRunner>; fn create( _resource: &UserForDomain, (user_name, home_path): & as Resource>::Artifact, (): ::Artifact, ) -> Self::Implementation { UserSymbol::new(user_name.0.clone(), home_path.into(), StdCommandRunner) } } impl ImplementationBuilder for DefaultBuilder { type Prerequisites = (); fn prerequisites(_resource: &User) -> Self::Prerequisites {} type Implementation = UserSymbol, Rc, StdCommandRunner>; fn create( resource: &User, home_path: &::Artifact, (): ::Artifact, ) -> Self::Implementation { UserSymbol::new(resource.0.clone(), home_path.into(), StdCommandRunner) } } impl + Clone> ImplementationBuilder> for DefaultBuilder { type Prerequisites = (); fn prerequisites(_resource: &Owner

) -> Self::Prerequisites {} type Implementation = OwnerSymbol>; fn create( resource: &Owner

, (): & as Resource>::Artifact, (): ::Artifact, ) -> Self::Implementation { OwnerSymbol::new(resource.1.clone(), resource.0.clone(), StdCommandRunner) } } impl ImplementationBuilder for DefaultBuilder { type Prerequisites = (); fn prerequisites(_resource: &AcmeUser) -> Self::Prerequisites {} type Implementation = UserSymbol, Rc, StdCommandRunner>; fn create( _resource: &AcmeUser, (user_name, home_path): &::Artifact, (): ::Artifact, ) -> Self::Implementation { UserSymbol::new(user_name.0.clone(), home_path.into(), StdCommandRunner) } } impl ImplementationBuilder for DefaultBuilder { type Prerequisites = AcmeUser; fn prerequisites(_resource: &AcmeChallengesDir) -> Self::Prerequisites { AcmeUser } type Implementation = ( DirSymbol>, OwnerSymbol, Rc>, ); fn create( _resource: &AcmeChallengesDir, target: &::Artifact, (user_name, _): ::Artifact, ) -> Self::Implementation { ( DirSymbol::new(target.clone_rc()), OwnerSymbol::new(target.clone_rc(), user_name.0, StdCommandRunner), ) } } impl ImplementationBuilder for DefaultBuilder { type Prerequisites = AcmeChallengesDir; fn prerequisites(_resource: &AcmeChallengesNginxSnippet) -> Self::Prerequisites { AcmeChallengesDir } type Implementation = FileSymbol, Box>; fn create( _resource: &AcmeChallengesNginxSnippet, target: &::Artifact, challenges_dir: ::Artifact, ) -> Self::Implementation { FileSymbol::new( target.clone_rc(), nginx::acme_challenges_snippet(challenges_dir).into(), ) } } impl ImplementationBuilder for DefaultBuilder { type Prerequisites = AcmeUser; fn prerequisites(_resource: &AcmeAccountKey) -> Self::Prerequisites { AcmeUser } type Implementation = ( KeySymbol>, OwnerSymbol, Rc>, ); fn create( _resource: &AcmeAccountKey, target: &::Artifact, (user_name, _): ::Artifact, ) -> Self::Implementation { ( KeySymbol::new(StdCommandRunner, target.clone_rc()), OwnerSymbol::new(target.clone_rc(), user_name.0, StdCommandRunner), ) } } impl ImplementationBuilder for DefaultBuilder { type Prerequisites = (); fn prerequisites(_resource: &AcmeRootCert) -> Self::Prerequisites {} type Implementation = FileSymbol, &'static str>; fn create( _resource: &AcmeRootCert, target: &::Artifact, (): ::Artifact, ) -> Self::Implementation { FileSymbol::new(target.clone_rc(), LETS_ENCRYPT_R3) } } impl ImplementationBuilder> for DefaultBuilder { type Prerequisites = (); fn prerequisites(_resource: &MariaDbUser) -> Self::Prerequisites {} type Implementation = MariaDbUserSymbol<'static, Rc, StdCommandRunner>; fn create( _resource: &MariaDbUser, user_name: & as Resource>::Artifact, _: ::Artifact, ) -> Self::Implementation { MariaDbUserSymbol::new(user_name.0.clone(), &StdCommandRunner) } } impl ImplementationBuilder> for DefaultBuilder { type Prerequisites = MariaDbUser; fn prerequisites(resource: &MariaDbDatabase) -> Self::Prerequisites { MariaDbUser(resource.0.clone()) } type Implementation = ( MariaDbDatabaseSymbol<'static, Rc, SimpleStorage, StdCommandRunner>, MariaDbDumpSymbol<'static, Rc, StdCommandRunner, SimpleStorage>, ); fn create( _resource: &MariaDbDatabase, (db_name, _, data_path): & as Resource>::Artifact, _: ::Artifact, ) -> Self::Implementation { let db_dump = SimpleStorage::new(data_path.clone_rc()); ( MariaDbDatabaseSymbol::new(db_name.0.clone(), db_dump.clone(), &StdCommandRunner), MariaDbDumpSymbol::new(db_name.0.clone(), db_dump, &StdCommandRunner), ) } } impl ImplementationBuilder> for DefaultBuilder { type Prerequisites = (); fn prerequisites(_: &PostgresqlDatabase) -> Self::Prerequisites {} type Implementation = (PostgreSQLDatabaseSymbol<'static, Rc, Rc, StdCommandRunner>,); fn create( _resource: &PostgresqlDatabase, (db_name, data_path): & as Resource>::Artifact, _: ::Artifact, ) -> Self::Implementation { let db_dump = SimpleStorage::new(data_path.clone_rc()); (PostgreSQLDatabaseSymbol::new( db_name.0.clone(), db_dump.read_filename().unwrap().to_str().unwrap().into(), &StdCommandRunner, ),) } } impl> ImplementationBuilder> for DefaultBuilder { type Prerequisites = Dir>; fn prerequisites(resource: &WordpressPlugin

) -> Self::Prerequisites { Dir::new(resource.0.as_ref().join("wp-content/plugins")) } type Implementation = WordpressPluginSymbol<'static, P, &'static str, StdCommandRunner>; fn create( resource: &WordpressPlugin

, (): & as Resource>::Artifact, _: ::Artifact, ) -> Self::Implementation { WordpressPluginSymbol::new(resource.0.clone(), resource.1, &StdCommandRunner) } } impl> ImplementationBuilder> for DefaultBuilder { type Prerequisites = Dir>; fn prerequisites(resource: &WordpressTranslation

) -> Self::Prerequisites { Dir::new(resource.0.as_ref().join("wp-content/languages")) } type Implementation = WordpressTranslationSymbol<'static, &'static str, Box, StdCommandRunner>; fn create( resource: &WordpressTranslation

, (): & as Resource>::Artifact, _: ::Artifact, ) -> Self::Implementation { WordpressTranslationSymbol::new( (*resource.0.as_ref().join("wp-content/languages")).into(), resource.1, resource.2, &StdCommandRunner, ) } } impl ImplementationBuilder> for DefaultBuilder { type Prerequisites = UserForDomain; fn prerequisites(resource: &Cron) -> Self::Prerequisites { UserForDomain(resource.0.clone()) } type Implementation = CronSymbol<'static, Box<[u8]>, Rc, StdCommandRunner>; fn create( resource: &Cron, (): & as Resource>::Artifact, user_name: ::Artifact, ) -> Self::Implementation { CronSymbol::new((user_name.0).0, &resource.1, &StdCommandRunner) } }