61 changed files with 2743 additions and 3101 deletions
			
			
		- 
					1Cargo.toml
 - 
					18src/artifacts/mod.rs
 - 
					5src/build.rs
 - 
					767src/builder.rs
 - 
					125src/factory.rs
 - 
					16src/lib.rs
 - 
					443src/locator.rs
 - 
					36src/repository.rs
 - 
					289src/resources/mod.rs
 - 
					200src/schema.rs
 - 
					345src/setup.rs
 - 
					1src/static_files.rs
 - 
					23src/storage.rs
 - 
					85src/symbols/acme/account_key.rs
 - 
					101src/symbols/acme/cert.rs
 - 
					117src/symbols/acme/chain.rs
 - 
					113src/symbols/acme/mod.rs
 - 
					36src/symbols/concat.rs
 - 
					31src/symbols/cron.rs
 - 
					48src/symbols/dir.rs
 - 
					436src/symbols/factory.rs
 - 
					39src/symbols/file.rs
 - 
					103src/symbols/git/checkout.rs
 - 
					6src/symbols/git/mod.rs
 - 
					196src/symbols/hook.rs
 - 
					213src/symbols/list.rs
 - 
					38src/symbols/mariadb/database.rs
 - 
					75src/symbols/mariadb/database_dump.rs
 - 
					56src/symbols/mariadb/dump.rs
 - 
					8src/symbols/mariadb/mod.rs
 - 
					37src/symbols/mariadb/user.rs
 - 
					70src/symbols/mod.rs
 - 
					1src/symbols/nginx/mod.rs
 - 
					246src/symbols/nginx/server.rs
 - 
					31src/symbols/noop.rs
 - 
					27src/symbols/npm.rs
 - 
					64src/symbols/owner.rs
 - 
					16src/symbols/postgresql/database.rs
 - 
					87src/symbols/saved_directory.rs
 - 
					10src/symbols/systemd/mod.rs
 - 
					42src/symbols/systemd/reload.rs
 - 
					165src/symbols/systemd/user_service.rs
 - 
					62src/symbols/systemd/user_session.rs
 - 
					96src/symbols/tls/csr.rs
 - 
					76src/symbols/tls/key.rs
 - 
					6src/symbols/tls/mod.rs
 - 
					119src/symbols/tls/self_signed_cert.rs
 - 
					187src/symbols/user.rs
 - 
					7src/symbols/wordpress/mod.rs
 - 
					39src/symbols/wordpress/plugin.rs
 - 
					48src/symbols/wordpress/translation.rs
 - 
					3src/templates/mod.rs
 - 
					13src/templates/nginx/mod.rs
 - 
					202src/templates/nginx/server.rs
 - 
					24src/templates/php.rs
 - 
					45src/templates/systemd.rs
 - 
					25src/to_artifact.rs
 - 
					27static_files/lets_encrypt_x3_cross_signed.pem
 - 
					11tests/file.rs
 - 
					72tests/setup.rs
 - 
					16tests/storage.rs
 
@ -0,0 +1,18 @@ | 
				
			|||
use std::path::{Path as ActualPath, PathBuf};
 | 
				
			|||
 | 
				
			|||
#[derive(Clone, Debug)]
 | 
				
			|||
pub struct Path(pub PathBuf);
 | 
				
			|||
impl AsRef<ActualPath> for Path {
 | 
				
			|||
  fn as_ref(&self) -> &ActualPath {
 | 
				
			|||
    &self.0
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[derive(Clone, Debug)]
 | 
				
			|||
pub struct UserName(pub String);
 | 
				
			|||
 | 
				
			|||
#[derive(Clone, Debug)]
 | 
				
			|||
pub struct ServiceName(pub String);
 | 
				
			|||
 | 
				
			|||
#[derive(Clone, Debug)]
 | 
				
			|||
pub struct DatabaseName(pub String);
 | 
				
			|||
@ -0,0 +1,767 @@ | 
				
			|||
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, Resource,
 | 
				
			|||
  ServeCustom, ServePhp, ServeRedir, ServeService, ServeStatic, StoredDirectory,
 | 
				
			|||
  SystemdSocketService, User, UserForDomain, WordpressPlugin, WordpressTranslation,
 | 
				
			|||
};
 | 
				
			|||
use crate::static_files::LETS_ENCRYPT_X3_CROSS_SIGNED;
 | 
				
			|||
use crate::storage::SimpleStorage;
 | 
				
			|||
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::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;
 | 
				
			|||
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, PathBuf};
 | 
				
			|||
 | 
				
			|||
pub trait SymbolBuilder<R> {
 | 
				
			|||
  type Prerequisites: ToArtifact;
 | 
				
			|||
  fn prerequisites(resource: &R) -> Self::Prerequisites;
 | 
				
			|||
 | 
				
			|||
  type Symbol;
 | 
				
			|||
  fn create(
 | 
				
			|||
    resource: &R,
 | 
				
			|||
    target: &R::Artifact,
 | 
				
			|||
    inputs: <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				
			|||
  ) -> Self::Symbol
 | 
				
			|||
  where
 | 
				
			|||
    R: Resource;
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
pub struct DefaultBuilder;
 | 
				
			|||
 | 
				
			|||
impl<D> SymbolBuilder<Key<D>> for DefaultBuilder {
 | 
				
			|||
  type Prerequisites = ();
 | 
				
			|||
  fn prerequisites(_resource: &Key<D>) -> Self::Prerequisites {}
 | 
				
			|||
 | 
				
			|||
  type Symbol = KeySymbol<StdCommandRunner, PathBuf>;
 | 
				
			|||
  fn create(
 | 
				
			|||
    _resource: &Key<D>,
 | 
				
			|||
    target: &<Key<D> as Resource>::Artifact,
 | 
				
			|||
    (): <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				
			|||
  ) -> Self::Symbol {
 | 
				
			|||
    KeySymbol::new(StdCommandRunner, target.0.clone())
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<D: Clone> SymbolBuilder<Csr<D>> for DefaultBuilder {
 | 
				
			|||
  type Prerequisites = Key<D>;
 | 
				
			|||
  fn prerequisites(resource: &Csr<D>) -> Self::Prerequisites {
 | 
				
			|||
    Key(resource.0.clone())
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  type Symbol = CsrSymbol<StdCommandRunner, D, PathBuf, PathBuf>;
 | 
				
			|||
  fn create(
 | 
				
			|||
    resource: &Csr<D>,
 | 
				
			|||
    target: &<Csr<D> as Resource>::Artifact,
 | 
				
			|||
    key: <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				
			|||
  ) -> Self::Symbol {
 | 
				
			|||
    CsrSymbol::new(
 | 
				
			|||
      StdCommandRunner,
 | 
				
			|||
      resource.0.clone(),
 | 
				
			|||
      key.0,
 | 
				
			|||
      target.0.clone(),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<D: Clone> SymbolBuilder<Cert<D>> for DefaultBuilder {
 | 
				
			|||
  type Prerequisites = (
 | 
				
			|||
    Csr<D>,
 | 
				
			|||
    AcmeRootCert,
 | 
				
			|||
    AcmeAccountKey,
 | 
				
			|||
    AcmeChallengesDir,
 | 
				
			|||
    AcmeUser,
 | 
				
			|||
    DefaultServer,
 | 
				
			|||
  );
 | 
				
			|||
  fn prerequisites(resource: &Cert<D>) -> Self::Prerequisites {
 | 
				
			|||
    (
 | 
				
			|||
      Csr(resource.0.clone()),
 | 
				
			|||
      AcmeRootCert,
 | 
				
			|||
      AcmeAccountKey,
 | 
				
			|||
      AcmeChallengesDir,
 | 
				
			|||
      AcmeUser,
 | 
				
			|||
      DefaultServer,
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  type Symbol = CertSymbol<
 | 
				
			|||
    SetuidCommandRunner<'static, String, StdCommandRunner>,
 | 
				
			|||
    SetuidCommandRunner<'static, String, StdCommandRunner>,
 | 
				
			|||
    D,
 | 
				
			|||
    PathBuf,
 | 
				
			|||
  >;
 | 
				
			|||
  fn create(
 | 
				
			|||
    resource: &Cert<D>,
 | 
				
			|||
    target: &<Cert<D> as Resource>::Artifact,
 | 
				
			|||
    (csr, root_cert, account_key, challenges_dir, user_name, _): <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				
			|||
  ) -> Self::Symbol {
 | 
				
			|||
    CertSymbol::new(
 | 
				
			|||
      resource.0.clone(),
 | 
				
			|||
      SetuidCommandRunner::new(user_name.0, &StdCommandRunner),
 | 
				
			|||
      root_cert.0,
 | 
				
			|||
      account_key.0,
 | 
				
			|||
      challenges_dir.0,
 | 
				
			|||
      csr.0,
 | 
				
			|||
      target.0.clone(),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<D: Clone> SymbolBuilder<CertChain<D>> for DefaultBuilder {
 | 
				
			|||
  type Prerequisites = (Cert<D>, AcmeRootCert);
 | 
				
			|||
  fn prerequisites(resource: &CertChain<D>) -> Self::Prerequisites {
 | 
				
			|||
    (Cert(resource.0.clone()), AcmeRootCert)
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  type Symbol = ConcatSymbol<[PathBuf; 2], PathBuf, PathBuf>;
 | 
				
			|||
  fn create(
 | 
				
			|||
    _resource: &CertChain<D>,
 | 
				
			|||
    target: &<CertChain<D> as Resource>::Artifact,
 | 
				
			|||
    (cert, root_cert): <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				
			|||
  ) -> Self::Symbol {
 | 
				
			|||
    ConcatSymbol::new([cert.0, root_cert.0], target.0.clone())
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<D: Clone> SymbolBuilder<KeyAndCertBundle<D>> for DefaultBuilder {
 | 
				
			|||
  type Prerequisites = (CertChain<D>, Key<D>);
 | 
				
			|||
  fn prerequisites(resource: &KeyAndCertBundle<D>) -> Self::Prerequisites {
 | 
				
			|||
    (CertChain(resource.0.clone()), Key(resource.0.clone()))
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  type Symbol = ConcatSymbol<[PathBuf; 2], PathBuf, PathBuf>;
 | 
				
			|||
  fn create(
 | 
				
			|||
    _resource: &KeyAndCertBundle<D>,
 | 
				
			|||
    target: &<KeyAndCertBundle<D> as Resource>::Artifact,
 | 
				
			|||
    (cert_chain, key): <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				
			|||
  ) -> Self::Symbol {
 | 
				
			|||
    ConcatSymbol::new([cert_chain.0, key.0], target.0.clone())
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<P: AsRef<Path> + Clone> SymbolBuilder<File<P>> for DefaultBuilder {
 | 
				
			|||
  type Prerequisites = ();
 | 
				
			|||
  fn prerequisites(_resource: &File<P>) -> Self::Prerequisites {}
 | 
				
			|||
 | 
				
			|||
  type Symbol = FileSymbol<P, String>;
 | 
				
			|||
  fn create(
 | 
				
			|||
    resource: &File<P>,
 | 
				
			|||
    _target: &<File<P> as Resource>::Artifact,
 | 
				
			|||
    (): <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				
			|||
  ) -> Self::Symbol {
 | 
				
			|||
    FileSymbol::new(resource.0.clone(), resource.1.clone())
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<'a, P: AsRef<Path> + Clone> SymbolBuilder<GitCheckout<'a, P>> for DefaultBuilder {
 | 
				
			|||
  type Prerequisites = ();
 | 
				
			|||
  fn prerequisites(_resource: &GitCheckout<'a, P>) -> Self::Prerequisites {}
 | 
				
			|||
 | 
				
			|||
  type Symbol = GitCheckoutSymbol<StdCommandRunner, StdCommandRunner, P, &'a str, &'a str>;
 | 
				
			|||
  fn create(
 | 
				
			|||
    resource: &GitCheckout<'a, P>,
 | 
				
			|||
    _target: &<GitCheckout<'a, P> as Resource>::Artifact,
 | 
				
			|||
    (): <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				
			|||
  ) -> Self::Symbol {
 | 
				
			|||
    GitCheckoutSymbol::new(resource.0.clone(), resource.1, resource.2, StdCommandRunner)
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl SymbolBuilder<DefaultServer> for DefaultBuilder {
 | 
				
			|||
  type Prerequisites = AcmeChallengesNginxSnippet;
 | 
				
			|||
  fn prerequisites(_resource: &DefaultServer) -> Self::Prerequisites {
 | 
				
			|||
    AcmeChallengesNginxSnippet
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  type Symbol = (
 | 
				
			|||
    FileSymbol<PathBuf, String>,
 | 
				
			|||
    ReloadServiceSymbol<StdCommandRunner, StdCommandRunner, &'static str>,
 | 
				
			|||
  );
 | 
				
			|||
  fn create(
 | 
				
			|||
    _resource: &DefaultServer,
 | 
				
			|||
    target: &<DefaultServer as Resource>::Artifact,
 | 
				
			|||
    challenges_snippet_path: <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				
			|||
  ) -> Self::Symbol {
 | 
				
			|||
    (
 | 
				
			|||
      FileSymbol::new(
 | 
				
			|||
        target.0.clone(),
 | 
				
			|||
        nginx::default_server(challenges_snippet_path),
 | 
				
			|||
      ),
 | 
				
			|||
      ReloadServiceSymbol::new(StdCommandRunner, "nginx"),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<D: AsRef<str> + Clone + Display> SymbolBuilder<ServeCustom<D>> for DefaultBuilder {
 | 
				
			|||
  type Prerequisites = (CertChain<D>, Key<D>, AcmeChallengesNginxSnippet);
 | 
				
			|||
  fn prerequisites(resource: &ServeCustom<D>) -> Self::Prerequisites {
 | 
				
			|||
    (
 | 
				
			|||
      CertChain(resource.0.clone()),
 | 
				
			|||
      Key(resource.0.clone()),
 | 
				
			|||
      AcmeChallengesNginxSnippet,
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  type Symbol = (
 | 
				
			|||
    FileSymbol<PathBuf, String>,
 | 
				
			|||
    ReloadServiceSymbol<StdCommandRunner, StdCommandRunner, &'static str>,
 | 
				
			|||
  );
 | 
				
			|||
  fn create(
 | 
				
			|||
    resource: &ServeCustom<D>,
 | 
				
			|||
    target: &<ServeCustom<D> as Resource>::Artifact,
 | 
				
			|||
    (cert, key, challenges_snippet_path): <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				
			|||
  ) -> Self::Symbol {
 | 
				
			|||
    (
 | 
				
			|||
      FileSymbol::new(
 | 
				
			|||
        target.0.clone(),
 | 
				
			|||
        nginx::server_config(
 | 
				
			|||
          &resource.0,
 | 
				
			|||
          cert.0,
 | 
				
			|||
          key.0,
 | 
				
			|||
          &resource.1,
 | 
				
			|||
          challenges_snippet_path,
 | 
				
			|||
        ),
 | 
				
			|||
      ),
 | 
				
			|||
      ReloadServiceSymbol::new(StdCommandRunner, "nginx"),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<D: Clone + Display, P: AsRef<Path>> SymbolBuilder<ServePhp<D, P>> for DefaultBuilder {
 | 
				
			|||
  type Prerequisites = (
 | 
				
			|||
    PhpFpmPool<D>,
 | 
				
			|||
    CertChain<D>,
 | 
				
			|||
    Key<D>,
 | 
				
			|||
    AcmeChallengesNginxSnippet,
 | 
				
			|||
  );
 | 
				
			|||
  fn prerequisites(resource: &ServePhp<D, P>) -> Self::Prerequisites {
 | 
				
			|||
    (
 | 
				
			|||
      PhpFpmPool(resource.0.clone(), 10),
 | 
				
			|||
      CertChain(resource.0.clone()),
 | 
				
			|||
      Key(resource.0.clone()),
 | 
				
			|||
      AcmeChallengesNginxSnippet,
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  type Symbol = (
 | 
				
			|||
    FileSymbol<PathBuf, String>,
 | 
				
			|||
    ReloadServiceSymbol<StdCommandRunner, StdCommandRunner, &'static str>,
 | 
				
			|||
  );
 | 
				
			|||
  fn create(
 | 
				
			|||
    resource: &ServePhp<D, P>,
 | 
				
			|||
    target: &<ServePhp<D, P> as Resource>::Artifact,
 | 
				
			|||
    (pool, cert, key, challenges_snippet_path): <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				
			|||
  ) -> Self::Symbol {
 | 
				
			|||
    (
 | 
				
			|||
      FileSymbol::new(
 | 
				
			|||
        target.0.clone(),
 | 
				
			|||
        nginx::server_config(
 | 
				
			|||
          &resource.0,
 | 
				
			|||
          cert.0,
 | 
				
			|||
          key.0,
 | 
				
			|||
          nginx::php_snippet(resource.2, &pool.0, &resource.1) + &resource.3,
 | 
				
			|||
          challenges_snippet_path,
 | 
				
			|||
        ),
 | 
				
			|||
      ),
 | 
				
			|||
      ReloadServiceSymbol::new(StdCommandRunner, "nginx"),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<D: Clone + Display, P: Clone + AsRef<Path>> SymbolBuilder<ServeService<D, P>>
 | 
				
			|||
  for DefaultBuilder
 | 
				
			|||
{
 | 
				
			|||
  type Prerequisites = (
 | 
				
			|||
    SystemdSocketService<D, P>,
 | 
				
			|||
    CertChain<D>,
 | 
				
			|||
    Key<D>,
 | 
				
			|||
    AcmeChallengesNginxSnippet,
 | 
				
			|||
  );
 | 
				
			|||
  fn prerequisites(resource: &ServeService<D, P>) -> 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 Symbol = (
 | 
				
			|||
    FileSymbol<PathBuf, String>,
 | 
				
			|||
    ReloadServiceSymbol<StdCommandRunner, StdCommandRunner, &'static str>,
 | 
				
			|||
  );
 | 
				
			|||
  fn create(
 | 
				
			|||
    resource: &ServeService<D, P>,
 | 
				
			|||
    target: &<ServeService<D, P> as Resource>::Artifact,
 | 
				
			|||
    (socket, cert, key, challenges_snippet_path): <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				
			|||
  ) -> Self::Symbol {
 | 
				
			|||
    (
 | 
				
			|||
      FileSymbol::new(
 | 
				
			|||
        target.0.clone(),
 | 
				
			|||
        nginx::server_config(
 | 
				
			|||
          &resource.0,
 | 
				
			|||
          cert.0,
 | 
				
			|||
          key.0,
 | 
				
			|||
          nginx::proxy_snippet(&socket.0, &resource.3),
 | 
				
			|||
          challenges_snippet_path,
 | 
				
			|||
        ),
 | 
				
			|||
      ),
 | 
				
			|||
      ReloadServiceSymbol::new(StdCommandRunner, "nginx"),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<D: AsRef<str> + Clone + Display> SymbolBuilder<ServeRedir<D>> for DefaultBuilder {
 | 
				
			|||
  type Prerequisites = (CertChain<D>, Key<D>, AcmeChallengesNginxSnippet);
 | 
				
			|||
  fn prerequisites(resource: &ServeRedir<D>) -> Self::Prerequisites {
 | 
				
			|||
    (
 | 
				
			|||
      CertChain(resource.0.clone()),
 | 
				
			|||
      Key(resource.0.clone()),
 | 
				
			|||
      AcmeChallengesNginxSnippet,
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  type Symbol = (
 | 
				
			|||
    FileSymbol<PathBuf, String>,
 | 
				
			|||
    ReloadServiceSymbol<StdCommandRunner, StdCommandRunner, &'static str>,
 | 
				
			|||
  );
 | 
				
			|||
  fn create(
 | 
				
			|||
    resource: &ServeRedir<D>,
 | 
				
			|||
    target: &<ServeRedir<D> as Resource>::Artifact,
 | 
				
			|||
    (cert, key, challenges_snippet_path): <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				
			|||
  ) -> Self::Symbol {
 | 
				
			|||
    (
 | 
				
			|||
      FileSymbol::new(
 | 
				
			|||
        target.0.clone(),
 | 
				
			|||
        nginx::server_config(
 | 
				
			|||
          &resource.0,
 | 
				
			|||
          cert.0,
 | 
				
			|||
          key.0,
 | 
				
			|||
          nginx::redir_snippet(resource.1.as_ref()),
 | 
				
			|||
          challenges_snippet_path,
 | 
				
			|||
        ),
 | 
				
			|||
      ),
 | 
				
			|||
      ReloadServiceSymbol::new(StdCommandRunner, "nginx"),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<D: AsRef<str> + Clone + Display, P: AsRef<Path>> SymbolBuilder<ServeStatic<D, P>>
 | 
				
			|||
  for DefaultBuilder
 | 
				
			|||
{
 | 
				
			|||
  type Prerequisites = (CertChain<D>, Key<D>, AcmeChallengesNginxSnippet);
 | 
				
			|||
  fn prerequisites(resource: &ServeStatic<D, P>) -> Self::Prerequisites {
 | 
				
			|||
    (
 | 
				
			|||
      CertChain(resource.0.clone()),
 | 
				
			|||
      Key(resource.0.clone()),
 | 
				
			|||
      AcmeChallengesNginxSnippet,
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  type Symbol = (
 | 
				
			|||
    FileSymbol<PathBuf, String>,
 | 
				
			|||
    ReloadServiceSymbol<StdCommandRunner, StdCommandRunner, &'static str>,
 | 
				
			|||
  );
 | 
				
			|||
  fn create(
 | 
				
			|||
    resource: &ServeStatic<D, P>,
 | 
				
			|||
    target: &<ServeStatic<D, P> as Resource>::Artifact,
 | 
				
			|||
    (cert, key, challenges_snippet_path): <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				
			|||
  ) -> Self::Symbol {
 | 
				
			|||
    (
 | 
				
			|||
      FileSymbol::new(
 | 
				
			|||
        target.0.clone(),
 | 
				
			|||
        nginx::server_config(
 | 
				
			|||
          &resource.0,
 | 
				
			|||
          cert.0,
 | 
				
			|||
          key.0,
 | 
				
			|||
          nginx::static_snippet(resource.1.as_ref()),
 | 
				
			|||
          challenges_snippet_path,
 | 
				
			|||
        ),
 | 
				
			|||
      ),
 | 
				
			|||
      ReloadServiceSymbol::new(StdCommandRunner, "nginx"),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<D: Clone> SymbolBuilder<PhpFpmPool<D>> for DefaultBuilder {
 | 
				
			|||
  type Prerequisites = ();
 | 
				
			|||
  fn prerequisites(_resource: &PhpFpmPool<D>) -> Self::Prerequisites {}
 | 
				
			|||
 | 
				
			|||
  type Symbol = (
 | 
				
			|||
    FileSymbol<PathBuf, String>,
 | 
				
			|||
    ReloadServiceSymbol<StdCommandRunner, StdCommandRunner, String>,
 | 
				
			|||
  );
 | 
				
			|||
  fn create(
 | 
				
			|||
    resource: &PhpFpmPool<D>,
 | 
				
			|||
    (socket_path, conf_path, user_name, service_name): &<PhpFpmPool<D> as Resource>::Artifact,
 | 
				
			|||
    (): <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				
			|||
  ) -> Self::Symbol {
 | 
				
			|||
    (
 | 
				
			|||
      FileSymbol::new(
 | 
				
			|||
        conf_path.0.clone(),
 | 
				
			|||
        php_fpm_pool_config(&user_name.0, &socket_path.0, resource.1),
 | 
				
			|||
      ),
 | 
				
			|||
      ReloadServiceSymbol::new(StdCommandRunner, service_name.0.clone()),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<D, P: AsRef<Path>> SymbolBuilder<SystemdSocketService<D, P>> for DefaultBuilder {
 | 
				
			|||
  type Prerequisites = ();
 | 
				
			|||
  fn prerequisites(_resource: &SystemdSocketService<D, P>) -> Self::Prerequisites {}
 | 
				
			|||
 | 
				
			|||
  type Symbol = (
 | 
				
			|||
    FileSymbol<PathBuf, String>,
 | 
				
			|||
    SystemdUserSessionSymbol<'static, String, StdCommandRunner>,
 | 
				
			|||
    UserServiceSymbol<'static, PathBuf, String, StdCommandRunner>,
 | 
				
			|||
  );
 | 
				
			|||
  fn create(
 | 
				
			|||
    resource: &SystemdSocketService<D, P>,
 | 
				
			|||
    (socket_path, conf_path, user_name): &<SystemdSocketService<D, P> as Resource>::Artifact,
 | 
				
			|||
    (): <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				
			|||
  ) -> Self::Symbol {
 | 
				
			|||
    (
 | 
				
			|||
      FileSymbol::new(
 | 
				
			|||
        conf_path.0.clone(),
 | 
				
			|||
        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,
 | 
				
			|||
            "",
 | 
				
			|||
          )
 | 
				
			|||
        },
 | 
				
			|||
      ),
 | 
				
			|||
      SystemdUserSessionSymbol::new(user_name.0.clone(), &StdCommandRunner),
 | 
				
			|||
      UserServiceSymbol::new(
 | 
				
			|||
        socket_path.0.clone(),
 | 
				
			|||
        user_name.0.clone(),
 | 
				
			|||
        resource.1,
 | 
				
			|||
        &StdCommandRunner,
 | 
				
			|||
      ),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<P: Clone> SymbolBuilder<Dir<P>> for DefaultBuilder {
 | 
				
			|||
  type Prerequisites = ();
 | 
				
			|||
  fn prerequisites(_resource: &Dir<P>) -> Self::Prerequisites {}
 | 
				
			|||
 | 
				
			|||
  type Symbol = DirSymbol<P>;
 | 
				
			|||
  fn create(
 | 
				
			|||
    resource: &Dir<P>,
 | 
				
			|||
    _target: &<Dir<P> as Resource>::Artifact,
 | 
				
			|||
    (): <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				
			|||
  ) -> Self::Symbol {
 | 
				
			|||
    DirSymbol::new(resource.0.clone())
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<P: Clone + AsRef<Path>> SymbolBuilder<NpmInstall<P>> for DefaultBuilder {
 | 
				
			|||
  type Prerequisites = ();
 | 
				
			|||
  fn prerequisites(_resource: &NpmInstall<P>) -> Self::Prerequisites {}
 | 
				
			|||
 | 
				
			|||
  type Symbol = NpmInstallSymbol<'static, P, StdCommandRunner>;
 | 
				
			|||
  fn create(
 | 
				
			|||
    resource: &NpmInstall<P>,
 | 
				
			|||
    _target: &<NpmInstall<P> as Resource>::Artifact,
 | 
				
			|||
    (): <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				
			|||
  ) -> Self::Symbol {
 | 
				
			|||
    NpmInstallSymbol::new(resource.0.clone(), &StdCommandRunner)
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<P: Clone + AsRef<Path>> SymbolBuilder<StoredDirectory<P>> for DefaultBuilder {
 | 
				
			|||
  type Prerequisites = ();
 | 
				
			|||
  fn prerequisites(_resource: &StoredDirectory<P>) -> Self::Prerequisites {}
 | 
				
			|||
 | 
				
			|||
  type Symbol = SavedDirectorySymbol<StdCommandRunner, StdCommandRunner, P, SimpleStorage>;
 | 
				
			|||
  fn create(
 | 
				
			|||
    resource: &StoredDirectory<P>,
 | 
				
			|||
    target: &<StoredDirectory<P> as Resource>::Artifact,
 | 
				
			|||
    (): <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				
			|||
  ) -> Self::Symbol {
 | 
				
			|||
    SavedDirectorySymbol::new(
 | 
				
			|||
      resource.1.clone(),
 | 
				
			|||
      SimpleStorage::new(target.0.clone()),
 | 
				
			|||
      StorageDirection::Store,
 | 
				
			|||
      StdCommandRunner,
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<P: Clone + AsRef<Path>> SymbolBuilder<LoadedDirectory<P>> for DefaultBuilder {
 | 
				
			|||
  type Prerequisites = ();
 | 
				
			|||
  fn prerequisites(_resource: &LoadedDirectory<P>) -> Self::Prerequisites {}
 | 
				
			|||
 | 
				
			|||
  type Symbol = SavedDirectorySymbol<StdCommandRunner, StdCommandRunner, P, SimpleStorage>;
 | 
				
			|||
  fn create(
 | 
				
			|||
    resource: &LoadedDirectory<P>,
 | 
				
			|||
    target: &<LoadedDirectory<P> as Resource>::Artifact,
 | 
				
			|||
    (): <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				
			|||
  ) -> Self::Symbol {
 | 
				
			|||
    SavedDirectorySymbol::new(
 | 
				
			|||
      resource.1.clone(),
 | 
				
			|||
      SimpleStorage::new(target.0.clone()),
 | 
				
			|||
      StorageDirection::Load,
 | 
				
			|||
      StdCommandRunner,
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<D: Clone> SymbolBuilder<UserForDomain<D>> for DefaultBuilder {
 | 
				
			|||
  type Prerequisites = ();
 | 
				
			|||
  fn prerequisites(_resource: &UserForDomain<D>) -> Self::Prerequisites {}
 | 
				
			|||
 | 
				
			|||
  type Symbol = UserSymbol<String, StdCommandRunner>;
 | 
				
			|||
  fn create(
 | 
				
			|||
    _resource: &UserForDomain<D>,
 | 
				
			|||
    (user_name, _home_path): &<UserForDomain<D> as Resource>::Artifact,
 | 
				
			|||
    (): <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				
			|||
  ) -> Self::Symbol {
 | 
				
			|||
    UserSymbol::new(user_name.0.clone(), StdCommandRunner)
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl SymbolBuilder<User> for DefaultBuilder {
 | 
				
			|||
  type Prerequisites = ();
 | 
				
			|||
  fn prerequisites(_resource: &User) -> Self::Prerequisites {}
 | 
				
			|||
 | 
				
			|||
  type Symbol = UserSymbol<String, StdCommandRunner>;
 | 
				
			|||
  fn create(
 | 
				
			|||
    resource: &User,
 | 
				
			|||
    (): &<User as Resource>::Artifact,
 | 
				
			|||
    (): <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				
			|||
  ) -> Self::Symbol {
 | 
				
			|||
    UserSymbol::new(resource.0.clone(), StdCommandRunner)
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<P: AsRef<Path> + Clone> SymbolBuilder<Owner<P>> for DefaultBuilder {
 | 
				
			|||
  type Prerequisites = ();
 | 
				
			|||
  fn prerequisites(_resource: &Owner<P>) -> Self::Prerequisites {}
 | 
				
			|||
 | 
				
			|||
  type Symbol = OwnerSymbol<StdCommandRunner, StdCommandRunner, P, String>;
 | 
				
			|||
  fn create(
 | 
				
			|||
    resource: &Owner<P>,
 | 
				
			|||
    (): &<Owner<P> as Resource>::Artifact,
 | 
				
			|||
    (): <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				
			|||
  ) -> Self::Symbol {
 | 
				
			|||
    OwnerSymbol::new(resource.1.clone(), resource.0.clone(), StdCommandRunner)
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl SymbolBuilder<AcmeUser> for DefaultBuilder {
 | 
				
			|||
  type Prerequisites = ();
 | 
				
			|||
  fn prerequisites(_resource: &AcmeUser) -> Self::Prerequisites {}
 | 
				
			|||
 | 
				
			|||
  type Symbol = UserSymbol<String, StdCommandRunner>;
 | 
				
			|||
  fn create(
 | 
				
			|||
    _resource: &AcmeUser,
 | 
				
			|||
    user_name: &<AcmeUser as Resource>::Artifact,
 | 
				
			|||
    (): <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				
			|||
  ) -> Self::Symbol {
 | 
				
			|||
    UserSymbol::new(user_name.0.clone(), StdCommandRunner)
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl SymbolBuilder<AcmeChallengesDir> for DefaultBuilder {
 | 
				
			|||
  type Prerequisites = AcmeUser;
 | 
				
			|||
  fn prerequisites(_resource: &AcmeChallengesDir) -> Self::Prerequisites {
 | 
				
			|||
    AcmeUser
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  type Symbol = (
 | 
				
			|||
    DirSymbol<PathBuf>,
 | 
				
			|||
    OwnerSymbol<StdCommandRunner, StdCommandRunner, PathBuf, String>,
 | 
				
			|||
  );
 | 
				
			|||
  fn create(
 | 
				
			|||
    _resource: &AcmeChallengesDir,
 | 
				
			|||
    target: &<AcmeChallengesDir as Resource>::Artifact,
 | 
				
			|||
    user_name: <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				
			|||
  ) -> Self::Symbol {
 | 
				
			|||
    (
 | 
				
			|||
      DirSymbol::new(target.0.clone()),
 | 
				
			|||
      OwnerSymbol::new(target.0.clone(), user_name.0, StdCommandRunner),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl SymbolBuilder<AcmeChallengesNginxSnippet> for DefaultBuilder {
 | 
				
			|||
  type Prerequisites = AcmeChallengesDir;
 | 
				
			|||
  fn prerequisites(_resource: &AcmeChallengesNginxSnippet) -> Self::Prerequisites {
 | 
				
			|||
    AcmeChallengesDir
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  type Symbol = FileSymbol<PathBuf, String>;
 | 
				
			|||
  fn create(
 | 
				
			|||
    _resource: &AcmeChallengesNginxSnippet,
 | 
				
			|||
    target: &<AcmeChallengesNginxSnippet as Resource>::Artifact,
 | 
				
			|||
    challenges_dir: <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				
			|||
  ) -> Self::Symbol {
 | 
				
			|||
    FileSymbol::new(
 | 
				
			|||
      target.0.clone(),
 | 
				
			|||
      nginx::acme_challenges_snippet(challenges_dir),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl SymbolBuilder<AcmeAccountKey> for DefaultBuilder {
 | 
				
			|||
  type Prerequisites = AcmeUser;
 | 
				
			|||
  fn prerequisites(_resource: &AcmeAccountKey) -> Self::Prerequisites {
 | 
				
			|||
    AcmeUser
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  type Symbol = (
 | 
				
			|||
    KeySymbol<StdCommandRunner, PathBuf>,
 | 
				
			|||
    OwnerSymbol<StdCommandRunner, StdCommandRunner, PathBuf, String>,
 | 
				
			|||
  );
 | 
				
			|||
  fn create(
 | 
				
			|||
    _resource: &AcmeAccountKey,
 | 
				
			|||
    target: &<AcmeAccountKey as Resource>::Artifact,
 | 
				
			|||
    user_name: <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				
			|||
  ) -> Self::Symbol {
 | 
				
			|||
    (
 | 
				
			|||
      KeySymbol::new(StdCommandRunner, target.0.clone()),
 | 
				
			|||
      OwnerSymbol::new(target.0.clone(), user_name.0, StdCommandRunner),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl SymbolBuilder<AcmeRootCert> for DefaultBuilder {
 | 
				
			|||
  type Prerequisites = ();
 | 
				
			|||
  fn prerequisites(_resource: &AcmeRootCert) -> Self::Prerequisites {}
 | 
				
			|||
 | 
				
			|||
  type Symbol = FileSymbol<PathBuf, &'static str>;
 | 
				
			|||
  fn create(
 | 
				
			|||
    _resource: &AcmeRootCert,
 | 
				
			|||
    target: &<AcmeRootCert as Resource>::Artifact,
 | 
				
			|||
    (): <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				
			|||
  ) -> Self::Symbol {
 | 
				
			|||
    FileSymbol::new(target.0.clone(), LETS_ENCRYPT_X3_CROSS_SIGNED)
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<D> SymbolBuilder<MariaDbUser<D>> for DefaultBuilder {
 | 
				
			|||
  type Prerequisites = ();
 | 
				
			|||
  fn prerequisites(_resource: &MariaDbUser<D>) -> Self::Prerequisites {}
 | 
				
			|||
 | 
				
			|||
  type Symbol = MariaDbUserSymbol<'static, String, StdCommandRunner>;
 | 
				
			|||
  fn create(
 | 
				
			|||
    _resource: &MariaDbUser<D>,
 | 
				
			|||
    user_name: &<MariaDbUser<D> as Resource>::Artifact,
 | 
				
			|||
    _: <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				
			|||
  ) -> Self::Symbol {
 | 
				
			|||
    MariaDbUserSymbol::new(user_name.0.clone(), &StdCommandRunner)
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<D: Clone> SymbolBuilder<MariaDbDatabase<D>> for DefaultBuilder {
 | 
				
			|||
  type Prerequisites = MariaDbUser<D>;
 | 
				
			|||
  fn prerequisites(resource: &MariaDbDatabase<D>) -> Self::Prerequisites {
 | 
				
			|||
    MariaDbUser(resource.0.clone())
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  type Symbol = (
 | 
				
			|||
    MariaDbDatabaseSymbol<'static, String, SimpleStorage, StdCommandRunner>,
 | 
				
			|||
    MariaDbDumpSymbol<'static, String, StdCommandRunner, SimpleStorage>,
 | 
				
			|||
  );
 | 
				
			|||
  fn create(
 | 
				
			|||
    _resource: &MariaDbDatabase<D>,
 | 
				
			|||
    (db_name, _, data_path): &<MariaDbDatabase<D> as Resource>::Artifact,
 | 
				
			|||
    _: <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				
			|||
  ) -> Self::Symbol {
 | 
				
			|||
    let db_dump = SimpleStorage::new(data_path.0.clone());
 | 
				
			|||
    (
 | 
				
			|||
      MariaDbDatabaseSymbol::new(db_name.0.clone(), db_dump.clone(), &StdCommandRunner),
 | 
				
			|||
      MariaDbDumpSymbol::new(db_name.0.clone(), db_dump, &StdCommandRunner),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<P: Clone + AsRef<Path>> SymbolBuilder<WordpressPlugin<P>> for DefaultBuilder {
 | 
				
			|||
  type Prerequisites = Dir<PathBuf>;
 | 
				
			|||
  fn prerequisites(resource: &WordpressPlugin<P>) -> Self::Prerequisites {
 | 
				
			|||
    Dir(resource.0.as_ref().join("wp-content/plugins"))
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  type Symbol = WordpressPluginSymbol<'static, P, &'static str, StdCommandRunner>;
 | 
				
			|||
  fn create(
 | 
				
			|||
    resource: &WordpressPlugin<P>,
 | 
				
			|||
    (): &<WordpressPlugin<P> as Resource>::Artifact,
 | 
				
			|||
    _: <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				
			|||
  ) -> Self::Symbol {
 | 
				
			|||
    WordpressPluginSymbol::new(resource.0.clone(), resource.1, &StdCommandRunner)
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<P: Clone + AsRef<Path>> SymbolBuilder<WordpressTranslation<P>> for DefaultBuilder {
 | 
				
			|||
  type Prerequisites = Dir<PathBuf>;
 | 
				
			|||
  fn prerequisites(resource: &WordpressTranslation<P>) -> Self::Prerequisites {
 | 
				
			|||
    Dir(resource.0.as_ref().join("wp-content/languages"))
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  type Symbol = WordpressTranslationSymbol<'static, &'static str, PathBuf, StdCommandRunner>;
 | 
				
			|||
  fn create(
 | 
				
			|||
    resource: &WordpressTranslation<P>,
 | 
				
			|||
    (): &<WordpressTranslation<P> as Resource>::Artifact,
 | 
				
			|||
    _: <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				
			|||
  ) -> Self::Symbol {
 | 
				
			|||
    WordpressTranslationSymbol::new(
 | 
				
			|||
      resource.0.as_ref().join("wp-content/languages"),
 | 
				
			|||
      resource.1,
 | 
				
			|||
      resource.2,
 | 
				
			|||
      &StdCommandRunner,
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<D: Clone> SymbolBuilder<Cron<D>> for DefaultBuilder {
 | 
				
			|||
  type Prerequisites = UserForDomain<D>;
 | 
				
			|||
  fn prerequisites(resource: &Cron<D>) -> Self::Prerequisites {
 | 
				
			|||
    UserForDomain(resource.0.clone())
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  type Symbol = CronSymbol<'static, String, String, StdCommandRunner>;
 | 
				
			|||
  fn create(
 | 
				
			|||
    resource: &Cron<D>,
 | 
				
			|||
    (): &<Cron<D> as Resource>::Artifact,
 | 
				
			|||
    user_name: <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				
			|||
  ) -> Self::Symbol {
 | 
				
			|||
    CronSymbol::new((user_name.0).0, resource.1.clone(), &StdCommandRunner)
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
@ -1,125 +0,0 @@ | 
				
			|||
use regex::Regex;
 | 
				
			|||
 | 
				
			|||
use crate::command_runner::CommandRunner;
 | 
				
			|||
use crate::loggers::StdErrLogger;
 | 
				
			|||
use crate::repository::SymbolRepository;
 | 
				
			|||
use crate::resources::Resource;
 | 
				
			|||
use crate::schema::{
 | 
				
			|||
  NonRepeatingSymbolRunner, ReportingSymbolRunner, RequirementsResolvingSymbolRunner,
 | 
				
			|||
};
 | 
				
			|||
use crate::symbols::dir::Dir;
 | 
				
			|||
use crate::symbols::list::List;
 | 
				
			|||
use crate::symbols::owner::Owner;
 | 
				
			|||
use crate::symbols::systemd::user_session::SystemdUserSession;
 | 
				
			|||
use crate::symbols::tls::{TlsCsr, TlsKey};
 | 
				
			|||
use crate::symbols::user::SystemUserAdder;
 | 
				
			|||
use crate::symbols::user::{User, UserAdder};
 | 
				
			|||
use crate::symbols::{Symbol, SymbolRunner};
 | 
				
			|||
 | 
				
			|||
#[derive(Default)]
 | 
				
			|||
pub struct Factory {}
 | 
				
			|||
 | 
				
			|||
impl Factory {
 | 
				
			|||
  pub fn new() -> Self {
 | 
				
			|||
    Self::default()
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn get_repo<'a, CR: CommandRunner>(
 | 
				
			|||
    &self,
 | 
				
			|||
    command_runner: &'a CR,
 | 
				
			|||
  ) -> DefaultSymbolRepository<'a, SystemUserAdder<'a, CR>, CR> {
 | 
				
			|||
    DefaultSymbolRepository::new(command_runner)
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn get_symbol_runner<'a, RUNNER: SymbolRunner, REPO: SymbolRepository<'a>>(
 | 
				
			|||
    &self,
 | 
				
			|||
    symbol_runner: &'a RUNNER,
 | 
				
			|||
    repo: &'a REPO,
 | 
				
			|||
  ) -> Box<dyn 'a + SymbolRunner> {
 | 
				
			|||
    let runner1 = ReportingSymbolRunner::new(symbol_runner, StdErrLogger);
 | 
				
			|||
    let runner2 = NonRepeatingSymbolRunner::new(runner1);
 | 
				
			|||
    Box::new(RequirementsResolvingSymbolRunner::new(runner2, repo))
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
pub struct DefaultSymbolRepository<'a, A: 'a + UserAdder, C: CommandRunner> {
 | 
				
			|||
  user_adder: A,
 | 
				
			|||
  command_runner: &'a C,
 | 
				
			|||
 | 
				
			|||
  home: Regex,
 | 
				
			|||
  home_config: Regex,
 | 
				
			|||
  csr: Regex,
 | 
				
			|||
  private_key: Regex,
 | 
				
			|||
  systemd_linger: Regex,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<'a, C: 'a + CommandRunner> DefaultSymbolRepository<'a, SystemUserAdder<'a, C>, C> {
 | 
				
			|||
  pub fn new(command_runner: &'a C) -> Self {
 | 
				
			|||
    Self {
 | 
				
			|||
      command_runner,
 | 
				
			|||
      user_adder: SystemUserAdder::new(command_runner),
 | 
				
			|||
      home: Regex::new("^/home/([^/]+)$").unwrap(),
 | 
				
			|||
      home_config: Regex::new("^/home/([^/]+)/.config(?:/|$)").unwrap(),
 | 
				
			|||
      csr: Regex::new("^/etc/ssl/local_certs/([^/]+).csr$").unwrap(),
 | 
				
			|||
      private_key: Regex::new("^/etc/ssl/private/([^/]+).key$").unwrap(),
 | 
				
			|||
      systemd_linger: Regex::new("^/var/lib/systemd/linger/([^/]+)$").unwrap(),
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<'a, C: CommandRunner> SymbolRepository<'a>
 | 
				
			|||
  for DefaultSymbolRepository<'a, SystemUserAdder<'a, C>, C>
 | 
				
			|||
{
 | 
				
			|||
  fn get_symbol(&'a self, resource: &Resource) -> Option<Box<dyn Symbol + 'a>> {
 | 
				
			|||
    match resource.get_type() {
 | 
				
			|||
      "user" => Some(Box::new(User::new(
 | 
				
			|||
        resource.get_value().to_string().into(),
 | 
				
			|||
        self.command_runner,
 | 
				
			|||
        &self.user_adder,
 | 
				
			|||
      ))),
 | 
				
			|||
      "dir" => {
 | 
				
			|||
        let value = resource.get_value();
 | 
				
			|||
        Some(if let Some(matches) = self.home_config.captures(value) {
 | 
				
			|||
          Box::new(List::new(vec![
 | 
				
			|||
            Box::new(Dir::new(value.to_string())),
 | 
				
			|||
            Box::new(Owner::new(
 | 
				
			|||
              value.to_string(),
 | 
				
			|||
              matches[1].to_string(),
 | 
				
			|||
              self.command_runner,
 | 
				
			|||
            )),
 | 
				
			|||
          ]))
 | 
				
			|||
        } else if let Some(matches) = self.home.captures(value) {
 | 
				
			|||
          Box::new(User::new(
 | 
				
			|||
            matches[1].to_string().into(),
 | 
				
			|||
            self.command_runner,
 | 
				
			|||
            &self.user_adder,
 | 
				
			|||
          ))
 | 
				
			|||
        } else {
 | 
				
			|||
          Box::new(Dir::new(value.to_string()))
 | 
				
			|||
        })
 | 
				
			|||
      }
 | 
				
			|||
      "file" => {
 | 
				
			|||
        let value = resource.get_value();
 | 
				
			|||
        if let Some(matches) = self.csr.captures(value) {
 | 
				
			|||
          Some(Box::new(TlsCsr::new(
 | 
				
			|||
            matches[1].to_string().into(),
 | 
				
			|||
            self.command_runner,
 | 
				
			|||
          )))
 | 
				
			|||
        } else if let Some(matches) = self.private_key.captures(value) {
 | 
				
			|||
          Some(Box::new(TlsKey::new(
 | 
				
			|||
            matches[1].to_string().into(),
 | 
				
			|||
            self.command_runner,
 | 
				
			|||
          )))
 | 
				
			|||
        } else if let Some(matches) = self.systemd_linger.captures(value) {
 | 
				
			|||
          Some(Box::new(SystemdUserSession::new(
 | 
				
			|||
            matches[1].to_string(),
 | 
				
			|||
            self.command_runner,
 | 
				
			|||
          )))
 | 
				
			|||
        } else {
 | 
				
			|||
          None
 | 
				
			|||
        }
 | 
				
			|||
      }
 | 
				
			|||
      _ => None,
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
@ -0,0 +1,443 @@ | 
				
			|||
use crate::artifacts::{
 | 
				
			|||
  DatabaseName as DatabaseNameArtifact, Path as PathArtifact, ServiceName as ServiceNameArtifact,
 | 
				
			|||
  UserName as UserNameArtifact,
 | 
				
			|||
};
 | 
				
			|||
use crate::resources::{
 | 
				
			|||
  AcmeAccountKey, AcmeChallengesDir, AcmeChallengesNginxSnippet, AcmeRootCert, AcmeUser, Cert,
 | 
				
			|||
  CertChain, Cron, Csr, DefaultServer, Dir, File, GitCheckout, Key, KeyAndCertBundle,
 | 
				
			|||
  LoadedDirectory, MariaDbDatabase, MariaDbUser, NpmInstall, Owner, PhpFpmPool, Resource,
 | 
				
			|||
  ServeCustom, ServePhp, ServeRedir, ServeService, ServeStatic, StoredDirectory,
 | 
				
			|||
  SystemdSocketService, User, UserForDomain, WordpressPlugin, WordpressTranslation,
 | 
				
			|||
};
 | 
				
			|||
use crate::to_artifact::ToArtifact;
 | 
				
			|||
use std::fmt::Display;
 | 
				
			|||
use std::marker::PhantomData;
 | 
				
			|||
use std::path::{Path, PathBuf};
 | 
				
			|||
 | 
				
			|||
pub trait Policy {
 | 
				
			|||
  fn acme_user() -> &'static str {
 | 
				
			|||
    "acme"
 | 
				
			|||
  }
 | 
				
			|||
  fn user_home(user_name: &str) -> PathBuf {
 | 
				
			|||
    format!("/home/{}", user_name).into()
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn user_name_for_domain(domain_name: &'_ str) -> String {
 | 
				
			|||
    domain_name.split('.').rev().fold(String::new(), |result, part| if result.is_empty() { result } else { result + "_" } + part)
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn php_version() -> &'static str {
 | 
				
			|||
    "7.0"
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn path_for_data(name: impl Display) -> PathBuf {
 | 
				
			|||
    ("/root/data".as_ref() as &Path).join(format!("_{}", name))
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
pub struct DefaultPolicy;
 | 
				
			|||
 | 
				
			|||
impl Policy for DefaultPolicy {}
 | 
				
			|||
 | 
				
			|||
pub trait ResourceLocator<R> {
 | 
				
			|||
  type Prerequisites: ToArtifact;
 | 
				
			|||
  fn locate(resource: &R) -> (R::Artifact, Self::Prerequisites)
 | 
				
			|||
  where
 | 
				
			|||
    R: Resource;
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
pub struct DefaultLocator<P = DefaultPolicy> {
 | 
				
			|||
  phantom: PhantomData<P>,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<P, D: AsRef<str>> ResourceLocator<Key<D>> for DefaultLocator<P> {
 | 
				
			|||
  type Prerequisites = Dir<PathBuf>;
 | 
				
			|||
  fn locate(resource: &Key<D>) -> (<Key<D> as Resource>::Artifact, Self::Prerequisites) {
 | 
				
			|||
    (
 | 
				
			|||
      PathArtifact(format!("/etc/ssl/private/{}.key", resource.0.as_ref()).into()),
 | 
				
			|||
      Dir("/etc/ssl/private".into()),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<P, D: AsRef<str>> ResourceLocator<Csr<D>> for DefaultLocator<P> {
 | 
				
			|||
  type Prerequisites = Dir<PathBuf>;
 | 
				
			|||
  fn locate(resource: &Csr<D>) -> (<Csr<D> as Resource>::Artifact, Self::Prerequisites) {
 | 
				
			|||
    (
 | 
				
			|||
      PathArtifact(format!("/etc/ssl/local_certs/{}.csr", resource.0.as_ref()).into()),
 | 
				
			|||
      Dir("/etc/ssl/local_certs".into()),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<P, D: AsRef<str>> ResourceLocator<Cert<D>> for DefaultLocator<P> {
 | 
				
			|||
  type Prerequisites = Dir<PathBuf>;
 | 
				
			|||
  fn locate(resource: &Cert<D>) -> (<Cert<D> as Resource>::Artifact, Self::Prerequisites) {
 | 
				
			|||
    (
 | 
				
			|||
      PathArtifact(format!("/etc/ssl/local_certs/{}.crt", resource.0.as_ref()).into()),
 | 
				
			|||
      Dir("/etc/ssl/local_certs".into()),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<P, D: AsRef<str>> ResourceLocator<CertChain<D>> for DefaultLocator<P> {
 | 
				
			|||
  type Prerequisites = Dir<PathBuf>;
 | 
				
			|||
  fn locate(
 | 
				
			|||
    resource: &CertChain<D>,
 | 
				
			|||
  ) -> (<CertChain<D> as Resource>::Artifact, Self::Prerequisites) {
 | 
				
			|||
    (
 | 
				
			|||
      PathArtifact(format!("/etc/ssl/local_certs/{}.chained.crt", resource.0.as_ref()).into()),
 | 
				
			|||
      Dir("/etc/ssl/local_certs".into()),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<P, D: AsRef<str>> ResourceLocator<KeyAndCertBundle<D>> for DefaultLocator<P> {
 | 
				
			|||
  type Prerequisites = Dir<PathBuf>;
 | 
				
			|||
  fn locate(
 | 
				
			|||
    resource: &KeyAndCertBundle<D>,
 | 
				
			|||
  ) -> (
 | 
				
			|||
    <KeyAndCertBundle<D> as Resource>::Artifact,
 | 
				
			|||
    Self::Prerequisites,
 | 
				
			|||
  ) {
 | 
				
			|||
    (
 | 
				
			|||
      PathArtifact(format!("/etc/ssl/private/{}.with_key.crt", resource.0.as_ref()).into()),
 | 
				
			|||
      Dir("/etc/ssl/private".into()),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<'a, POLICY, P: AsRef<Path>> ResourceLocator<File<P>> for DefaultLocator<POLICY> {
 | 
				
			|||
  type Prerequisites = Dir<PathBuf>;
 | 
				
			|||
  fn locate(resource: &File<P>) -> (<File<P> as Resource>::Artifact, Self::Prerequisites) {
 | 
				
			|||
    ((), Dir(resource.0.as_ref().parent().unwrap().into()))
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<'a, POLICY, P: AsRef<Path>> ResourceLocator<GitCheckout<'a, P>> for DefaultLocator<POLICY> {
 | 
				
			|||
  type Prerequisites = Dir<PathBuf>;
 | 
				
			|||
  fn locate(
 | 
				
			|||
    resource: &GitCheckout<'a, P>,
 | 
				
			|||
  ) -> (
 | 
				
			|||
    <GitCheckout<'a, P> as Resource>::Artifact,
 | 
				
			|||
    Self::Prerequisites,
 | 
				
			|||
  ) {
 | 
				
			|||
    ((), Dir(resource.0.as_ref().parent().unwrap().into()))
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<POLICY, P: Clone + AsRef<Path>> ResourceLocator<Dir<P>> for DefaultLocator<POLICY> {
 | 
				
			|||
  type Prerequisites = Option<Dir<PathBuf>>;
 | 
				
			|||
  fn locate(resource: &Dir<P>) -> (<Dir<P> as Resource>::Artifact, Self::Prerequisites) {
 | 
				
			|||
    ((), resource.0.as_ref().parent().map(|p| Dir(p.into())))
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<POLICY, P> ResourceLocator<NpmInstall<P>> for DefaultLocator<POLICY> {
 | 
				
			|||
  type Prerequisites = ();
 | 
				
			|||
  fn locate(
 | 
				
			|||
    _resource: &NpmInstall<P>,
 | 
				
			|||
  ) -> (<NpmInstall<P> as Resource>::Artifact, Self::Prerequisites) {
 | 
				
			|||
    ((), ())
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<POLICY: Policy, P: AsRef<Path>> ResourceLocator<StoredDirectory<P>>
 | 
				
			|||
  for DefaultLocator<POLICY>
 | 
				
			|||
{
 | 
				
			|||
  type Prerequisites = ();
 | 
				
			|||
  fn locate(
 | 
				
			|||
    resource: &StoredDirectory<P>,
 | 
				
			|||
  ) -> (
 | 
				
			|||
    <StoredDirectory<P> as Resource>::Artifact,
 | 
				
			|||
    Self::Prerequisites,
 | 
				
			|||
  ) {
 | 
				
			|||
    (PathArtifact(POLICY::path_for_data(resource.0)), ())
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<POLICY: Policy, P: AsRef<Path>> ResourceLocator<LoadedDirectory<P>>
 | 
				
			|||
  for DefaultLocator<POLICY>
 | 
				
			|||
{
 | 
				
			|||
  type Prerequisites = Dir<PathBuf>;
 | 
				
			|||
  fn locate(
 | 
				
			|||
    resource: &LoadedDirectory<P>,
 | 
				
			|||
  ) -> (
 | 
				
			|||
    <LoadedDirectory<P> as Resource>::Artifact,
 | 
				
			|||
    Self::Prerequisites,
 | 
				
			|||
  ) {
 | 
				
			|||
    (
 | 
				
			|||
      PathArtifact(POLICY::path_for_data(resource.0)),
 | 
				
			|||
      Dir(resource.1.as_ref().parent().unwrap().into()),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<P: Policy> ResourceLocator<AcmeAccountKey> for DefaultLocator<P> {
 | 
				
			|||
  type Prerequisites = Dir<PathBuf>;
 | 
				
			|||
  fn locate(
 | 
				
			|||
    _resource: &AcmeAccountKey,
 | 
				
			|||
  ) -> (<AcmeAccountKey as Resource>::Artifact, Self::Prerequisites) {
 | 
				
			|||
    let acme_user = P::acme_user();
 | 
				
			|||
    let home = P::user_home(acme_user);
 | 
				
			|||
    (PathArtifact(home.join("account.key")), Dir(home))
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<P: Policy> ResourceLocator<AcmeUser> for DefaultLocator<P> {
 | 
				
			|||
  type Prerequisites = ();
 | 
				
			|||
  fn locate(_resource: &AcmeUser) -> (<AcmeUser as Resource>::Artifact, Self::Prerequisites) {
 | 
				
			|||
    let acme_user = P::acme_user();
 | 
				
			|||
    (UserNameArtifact(acme_user.into()), ())
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<P: Policy> ResourceLocator<AcmeChallengesDir> for DefaultLocator<P> {
 | 
				
			|||
  type Prerequisites = Dir<PathBuf>;
 | 
				
			|||
  fn locate(
 | 
				
			|||
    _resource: &AcmeChallengesDir,
 | 
				
			|||
  ) -> (
 | 
				
			|||
    <AcmeChallengesDir as Resource>::Artifact,
 | 
				
			|||
    Self::Prerequisites,
 | 
				
			|||
  ) {
 | 
				
			|||
    let acme_user = P::acme_user();
 | 
				
			|||
    let home = P::user_home(acme_user);
 | 
				
			|||
    (PathArtifact(home.join("challenges")), Dir(home))
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<P: Policy> ResourceLocator<AcmeChallengesNginxSnippet> for DefaultLocator<P> {
 | 
				
			|||
  type Prerequisites = ();
 | 
				
			|||
  fn locate(
 | 
				
			|||
    _resource: &AcmeChallengesNginxSnippet,
 | 
				
			|||
  ) -> (
 | 
				
			|||
    <AcmeChallengesNginxSnippet as Resource>::Artifact,
 | 
				
			|||
    Self::Prerequisites,
 | 
				
			|||
  ) {
 | 
				
			|||
    (
 | 
				
			|||
      PathArtifact("/etc/nginx/snippets/acme-challenge.conf".into()),
 | 
				
			|||
      (),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<P: Policy> ResourceLocator<AcmeRootCert> for DefaultLocator<P> {
 | 
				
			|||
  type Prerequisites = Dir<PathBuf>;
 | 
				
			|||
  fn locate(
 | 
				
			|||
    _resource: &AcmeRootCert,
 | 
				
			|||
  ) -> (<AcmeRootCert as Resource>::Artifact, Self::Prerequisites) {
 | 
				
			|||
    let acme_user = P::acme_user();
 | 
				
			|||
    let home = P::user_home(acme_user);
 | 
				
			|||
    (
 | 
				
			|||
      PathArtifact(home.join("lets_encrypt_x3_cross_signed.pem")),
 | 
				
			|||
      Dir(home),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<P: Policy, D: AsRef<str>> ResourceLocator<UserForDomain<D>> for DefaultLocator<P> {
 | 
				
			|||
  type Prerequisites = ();
 | 
				
			|||
  fn locate(
 | 
				
			|||
    resource: &UserForDomain<D>,
 | 
				
			|||
  ) -> (
 | 
				
			|||
    <UserForDomain<D> as Resource>::Artifact,
 | 
				
			|||
    Self::Prerequisites,
 | 
				
			|||
  ) {
 | 
				
			|||
    let user_name = P::user_name_for_domain(resource.0.as_ref());
 | 
				
			|||
    let home = P::user_home(&user_name);
 | 
				
			|||
    ((UserNameArtifact(user_name), PathArtifact(home)), ())
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<P> ResourceLocator<User> for DefaultLocator<P> {
 | 
				
			|||
  type Prerequisites = ();
 | 
				
			|||
  fn locate(_resource: &User) -> (<User as Resource>::Artifact, Self::Prerequisites) {
 | 
				
			|||
    ((), ())
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<P, POLICY> ResourceLocator<Owner<P>> for DefaultLocator<POLICY> {
 | 
				
			|||
  type Prerequisites = User;
 | 
				
			|||
  fn locate(resource: &Owner<P>) -> (<Owner<P> as Resource>::Artifact, Self::Prerequisites) {
 | 
				
			|||
    ((), User(resource.0.clone()))
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<P> ResourceLocator<DefaultServer> for DefaultLocator<P> {
 | 
				
			|||
  type Prerequisites = ();
 | 
				
			|||
  fn locate(
 | 
				
			|||
    _resource: &DefaultServer,
 | 
				
			|||
  ) -> (<DefaultServer as Resource>::Artifact, Self::Prerequisites) {
 | 
				
			|||
    (PathArtifact("/etc/nginx/sites-enabled/default".into()), ())
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<D: AsRef<Path>, POLICY> ResourceLocator<ServeCustom<D>> for DefaultLocator<POLICY> {
 | 
				
			|||
  type Prerequisites = ();
 | 
				
			|||
  fn locate(
 | 
				
			|||
    resource: &ServeCustom<D>,
 | 
				
			|||
  ) -> (<ServeCustom<D> as Resource>::Artifact, Self::Prerequisites) {
 | 
				
			|||
    (
 | 
				
			|||
      PathArtifact(("/etc/nginx/sites-enabled/".as_ref() as &Path).join(&resource.0)),
 | 
				
			|||
      (),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<D: AsRef<Path>, P, POLICY> ResourceLocator<ServePhp<D, P>> for DefaultLocator<POLICY> {
 | 
				
			|||
  type Prerequisites = ();
 | 
				
			|||
  fn locate(
 | 
				
			|||
    resource: &ServePhp<D, P>,
 | 
				
			|||
  ) -> (<ServePhp<D, P> as Resource>::Artifact, Self::Prerequisites) {
 | 
				
			|||
    (
 | 
				
			|||
      PathArtifact(("/etc/nginx/sites-enabled/".as_ref() as &Path).join(&resource.0)),
 | 
				
			|||
      (),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<D: AsRef<Path>, P, POLICY> ResourceLocator<ServeService<D, P>> for DefaultLocator<POLICY> {
 | 
				
			|||
  type Prerequisites = ();
 | 
				
			|||
  fn locate(
 | 
				
			|||
    resource: &ServeService<D, P>,
 | 
				
			|||
  ) -> (
 | 
				
			|||
    <ServeService<D, P> as Resource>::Artifact,
 | 
				
			|||
    Self::Prerequisites,
 | 
				
			|||
  ) {
 | 
				
			|||
    (
 | 
				
			|||
      PathArtifact(("/etc/nginx/sites-enabled/".as_ref() as &Path).join(&resource.0)),
 | 
				
			|||
      (),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<D: AsRef<Path>, POLICY> ResourceLocator<ServeRedir<D>> for DefaultLocator<POLICY> {
 | 
				
			|||
  type Prerequisites = ();
 | 
				
			|||
  fn locate(
 | 
				
			|||
    resource: &ServeRedir<D>,
 | 
				
			|||
  ) -> (<ServeRedir<D> as Resource>::Artifact, Self::Prerequisites) {
 | 
				
			|||
    (
 | 
				
			|||
      PathArtifact(("/etc/nginx/sites-enabled/".as_ref() as &Path).join(&resource.0)),
 | 
				
			|||
      (),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<D: AsRef<Path>, P, POLICY> ResourceLocator<ServeStatic<D, P>> for DefaultLocator<POLICY> {
 | 
				
			|||
  type Prerequisites = ();
 | 
				
			|||
  fn locate(
 | 
				
			|||
    resource: &ServeStatic<D, P>,
 | 
				
			|||
  ) -> (
 | 
				
			|||
    <ServeStatic<D, P> as Resource>::Artifact,
 | 
				
			|||
    Self::Prerequisites,
 | 
				
			|||
  ) {
 | 
				
			|||
    (
 | 
				
			|||
      PathArtifact(("/etc/nginx/sites-enabled/".as_ref() as &Path).join(&resource.0)),
 | 
				
			|||
      (),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<D: Clone + AsRef<str>, P: Policy> ResourceLocator<PhpFpmPool<D>> for DefaultLocator<P> {
 | 
				
			|||
  type Prerequisites = ();
 | 
				
			|||
  fn locate(
 | 
				
			|||
    resource: &PhpFpmPool<D>,
 | 
				
			|||
  ) -> (<PhpFpmPool<D> as Resource>::Artifact, Self::Prerequisites) {
 | 
				
			|||
    let ((user, _), ()) = Self::locate(&UserForDomain(&resource.0));
 | 
				
			|||
    let php_version = P::php_version();
 | 
				
			|||
    (
 | 
				
			|||
      (
 | 
				
			|||
        PathArtifact(format!("/run/php/{}.sock", user.0).into()),
 | 
				
			|||
        PathArtifact(format!("/etc/php/{}/fpm/pool.d/{}.conf", php_version, user.0).into()),
 | 
				
			|||
        user,
 | 
				
			|||
        ServiceNameArtifact(format!("php{}-fpm", php_version)),
 | 
				
			|||
      ),
 | 
				
			|||
      (),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<D: Clone + AsRef<str>, P, POLICY: Policy> ResourceLocator<SystemdSocketService<D, P>>
 | 
				
			|||
  for DefaultLocator<POLICY>
 | 
				
			|||
{
 | 
				
			|||
  type Prerequisites = (Dir<PathBuf>, Owner<PathBuf>);
 | 
				
			|||
  fn locate(
 | 
				
			|||
    resource: &SystemdSocketService<D, P>,
 | 
				
			|||
  ) -> (
 | 
				
			|||
    <SystemdSocketService<D, P> as Resource>::Artifact,
 | 
				
			|||
    Self::Prerequisites,
 | 
				
			|||
  ) {
 | 
				
			|||
    let ((user_name, home_path), ()) = Self::locate(&UserForDomain(&resource.0));
 | 
				
			|||
    let config = home_path.0.join(".config");
 | 
				
			|||
    let service_dir_path = config.join("systemd/user");
 | 
				
			|||
    (
 | 
				
			|||
      (
 | 
				
			|||
        PathArtifact(format!("/var/tmp/{}-{}.socket", user_name.0, resource.1).into()),
 | 
				
			|||
        PathArtifact(service_dir_path.join(format!("{}.service", resource.1))),
 | 
				
			|||
        user_name.clone(),
 | 
				
			|||
      ),
 | 
				
			|||
      (Dir(service_dir_path), Owner(user_name.0, config)),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<D: AsRef<str>, P: Policy> ResourceLocator<MariaDbDatabase<D>> for DefaultLocator<P> {
 | 
				
			|||
  type Prerequisites = ();
 | 
				
			|||
  fn locate(
 | 
				
			|||
    resource: &MariaDbDatabase<D>,
 | 
				
			|||
  ) -> (
 | 
				
			|||
    <MariaDbDatabase<D> as Resource>::Artifact,
 | 
				
			|||
    Self::Prerequisites,
 | 
				
			|||
  ) {
 | 
				
			|||
    let ((user_name, _), ()) = Self::locate(&UserForDomain(&resource.0));
 | 
				
			|||
    (
 | 
				
			|||
      (
 | 
				
			|||
        DatabaseNameArtifact(user_name.0.clone()),
 | 
				
			|||
        user_name.clone(),
 | 
				
			|||
        PathArtifact(P::path_for_data(format!("{}.sql", user_name.0))),
 | 
				
			|||
      ),
 | 
				
			|||
      (),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<D: AsRef<str>, P: Policy> ResourceLocator<MariaDbUser<D>> for DefaultLocator<P> {
 | 
				
			|||
  type Prerequisites = ();
 | 
				
			|||
  fn locate(
 | 
				
			|||
    resource: &MariaDbUser<D>,
 | 
				
			|||
  ) -> (<MariaDbUser<D> as Resource>::Artifact, Self::Prerequisites) {
 | 
				
			|||
    let ((user_name, _), ()) = Self::locate(&UserForDomain(&resource.0));
 | 
				
			|||
    ((user_name), ())
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<P, POLICY> ResourceLocator<WordpressPlugin<P>> for DefaultLocator<POLICY> {
 | 
				
			|||
  type Prerequisites = ();
 | 
				
			|||
  fn locate(
 | 
				
			|||
    _resource: &WordpressPlugin<P>,
 | 
				
			|||
  ) -> (
 | 
				
			|||
    <WordpressPlugin<P> as Resource>::Artifact,
 | 
				
			|||
    Self::Prerequisites,
 | 
				
			|||
  ) {
 | 
				
			|||
    ((), ())
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<P, POLICY> ResourceLocator<WordpressTranslation<P>> for DefaultLocator<POLICY> {
 | 
				
			|||
  type Prerequisites = ();
 | 
				
			|||
  fn locate(
 | 
				
			|||
    _resource: &WordpressTranslation<P>,
 | 
				
			|||
  ) -> (
 | 
				
			|||
    <WordpressTranslation<P> as Resource>::Artifact,
 | 
				
			|||
    Self::Prerequisites,
 | 
				
			|||
  ) {
 | 
				
			|||
    ((), ())
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<D, P> ResourceLocator<Cron<D>> for DefaultLocator<P> {
 | 
				
			|||
  type Prerequisites = ();
 | 
				
			|||
  fn locate(_resource: &Cron<D>) -> (<Cron<D> as Resource>::Artifact, Self::Prerequisites) {
 | 
				
			|||
    ((), ())
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
@ -1,36 +0,0 @@ | 
				
			|||
use std::collections::HashMap;
 | 
				
			|||
 | 
				
			|||
use crate::resources::Resource;
 | 
				
			|||
use crate::symbols::Symbol;
 | 
				
			|||
 | 
				
			|||
pub trait SymbolRepository<'a> {
 | 
				
			|||
  fn get_symbol(&'a self, resource: &Resource) -> Option<Box<dyn Symbol + 'a>>;
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<'a, C> SymbolRepository<'a> for C
 | 
				
			|||
where
 | 
				
			|||
  C: Fn(&Resource) -> Option<Box<dyn Symbol + 'a>>,
 | 
				
			|||
{
 | 
				
			|||
  fn get_symbol(&'a self, resource: &Resource) -> Option<Box<dyn Symbol + 'a>> {
 | 
				
			|||
    self(resource)
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
pub struct DispatchingSymbolRepository<'a> {
 | 
				
			|||
  repositories: HashMap<&'a str, Box<dyn SymbolRepository<'a> + 'a>>,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<'a> DispatchingSymbolRepository<'a> {
 | 
				
			|||
  pub fn new(repositories: HashMap<&'a str, Box<dyn SymbolRepository<'a> + 'a>>) -> Self {
 | 
				
			|||
    DispatchingSymbolRepository { repositories }
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<'a> SymbolRepository<'a> for DispatchingSymbolRepository<'a> {
 | 
				
			|||
  fn get_symbol(&'a self, resource: &Resource) -> Option<Box<dyn Symbol + 'a>> {
 | 
				
			|||
    self
 | 
				
			|||
      .repositories
 | 
				
			|||
      .get(resource.get_type())
 | 
				
			|||
      .and_then(|repo| repo.get_symbol(resource))
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
@ -1,15 +1,282 @@ | 
				
			|||
#[derive(PartialEq, Eq, Hash, Clone)]
 | 
				
			|||
pub struct Resource(pub String, pub String);
 | 
				
			|||
use crate::artifacts::{
 | 
				
			|||
  DatabaseName as DatabaseNameArtifact, Path as PathArtifact, ServiceName as ServiceNameArtifact,
 | 
				
			|||
  UserName as UserNameArtifact,
 | 
				
			|||
};
 | 
				
			|||
use std::hash::Hash;
 | 
				
			|||
use std::path::PathBuf;
 | 
				
			|||
 | 
				
			|||
impl<'a> Resource {
 | 
				
			|||
  pub fn new<A: Into<String>, B: Into<String>>(rtype: A, value: B) -> Self {
 | 
				
			|||
    Self(rtype.into(), value.into())
 | 
				
			|||
  }
 | 
				
			|||
pub trait Resource {
 | 
				
			|||
  type Artifact;
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
  pub fn get_type(&self) -> &str {
 | 
				
			|||
    &self.0
 | 
				
			|||
  }
 | 
				
			|||
  pub fn get_value(&self) -> &str {
 | 
				
			|||
    &self.1
 | 
				
			|||
#[derive(Debug, Hash, PartialEq, Eq)]
 | 
				
			|||
pub struct Key<D>(pub D);
 | 
				
			|||
impl<D> Resource for Key<D> {
 | 
				
			|||
  type Artifact = PathArtifact;
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[derive(Debug, Hash, PartialEq, Eq)]
 | 
				
			|||
pub struct Csr<D>(pub D);
 | 
				
			|||
impl<D> Resource for Csr<D> {
 | 
				
			|||
  type Artifact = PathArtifact;
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[derive(Debug, Hash, PartialEq, Eq)]
 | 
				
			|||
pub struct Cert<D>(pub D);
 | 
				
			|||
impl<D> Resource for Cert<D> {
 | 
				
			|||
  type Artifact = PathArtifact;
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[derive(Debug, Hash, PartialEq, Eq)]
 | 
				
			|||
pub struct CertChain<D>(pub D);
 | 
				
			|||
impl<D> Resource for CertChain<D> {
 | 
				
			|||
  type Artifact = PathArtifact;
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[derive(Debug, Hash, PartialEq, Eq)]
 | 
				
			|||
pub struct KeyAndCertBundle<D>(pub D);
 | 
				
			|||
impl<D> Resource for KeyAndCertBundle<D> {
 | 
				
			|||
  type Artifact = PathArtifact;
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[derive(Debug, Hash, PartialEq, Eq)]
 | 
				
			|||
pub struct File<P>(pub P, pub String);
 | 
				
			|||
impl<'a, P> Resource for File<P> {
 | 
				
			|||
  type Artifact = ();
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[derive(Debug, Hash, PartialEq, Eq)]
 | 
				
			|||
pub struct GitCheckout<'a, P>(pub P, pub &'a str, pub &'a str);
 | 
				
			|||
impl<'a, P> Resource for GitCheckout<'a, P> {
 | 
				
			|||
  type Artifact = ();
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[derive(Debug, Hash, PartialEq, Eq)]
 | 
				
			|||
pub struct Dir<P>(pub P);
 | 
				
			|||
impl<P> Resource for Dir<P> {
 | 
				
			|||
  type Artifact = ();
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[derive(Debug, Hash, PartialEq, Eq)]
 | 
				
			|||
pub struct UserForDomain<D>(pub D);
 | 
				
			|||
impl<D> Resource for UserForDomain<D> {
 | 
				
			|||
  type Artifact = (UserNameArtifact, PathArtifact);
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[derive(Debug, Hash, PartialEq, Eq)]
 | 
				
			|||
pub struct AcmeAccountKey;
 | 
				
			|||
impl Resource for AcmeAccountKey {
 | 
				
			|||
  type Artifact = PathArtifact;
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[derive(Debug, Hash, PartialEq, Eq)]
 | 
				
			|||
pub struct AcmeUser;
 | 
				
			|||
impl Resource for AcmeUser {
 | 
				
			|||
  type Artifact = UserNameArtifact;
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[derive(Debug, Hash, PartialEq, Eq)]
 | 
				
			|||
pub struct AcmeRootCert;
 | 
				
			|||
impl Resource for AcmeRootCert {
 | 
				
			|||
  type Artifact = PathArtifact;
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[derive(Debug, Hash, PartialEq, Eq)]
 | 
				
			|||
pub struct AcmeChallengesDir;
 | 
				
			|||
impl Resource for AcmeChallengesDir {
 | 
				
			|||
  type Artifact = PathArtifact;
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[derive(Debug, Hash, PartialEq, Eq)]
 | 
				
			|||
pub struct AcmeChallengesNginxSnippet;
 | 
				
			|||
impl Resource for AcmeChallengesNginxSnippet {
 | 
				
			|||
  type Artifact = PathArtifact;
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[derive(Debug, Hash, PartialEq, Eq)]
 | 
				
			|||
pub struct StoredDirectory<P>(pub &'static str, pub P);
 | 
				
			|||
impl<P> Resource for StoredDirectory<P> {
 | 
				
			|||
  type Artifact = PathArtifact;
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[derive(Debug, Hash, PartialEq, Eq)]
 | 
				
			|||
pub struct LoadedDirectory<P>(pub &'static str, pub P);
 | 
				
			|||
impl<P> Resource for LoadedDirectory<P> {
 | 
				
			|||
  type Artifact = PathArtifact;
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
pub fn get_saved_directory<P: Clone>(
 | 
				
			|||
  storage_name: &'static str,
 | 
				
			|||
  target: P,
 | 
				
			|||
) -> (StoredDirectory<P>, LoadedDirectory<P>) {
 | 
				
			|||
  (
 | 
				
			|||
    StoredDirectory(storage_name, target.clone()),
 | 
				
			|||
    LoadedDirectory(storage_name, target),
 | 
				
			|||
  )
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[derive(Debug, Hash, PartialEq, Eq)]
 | 
				
			|||
pub struct User(pub String);
 | 
				
			|||
impl Resource for User {
 | 
				
			|||
  type Artifact = ();
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[derive(Debug, Hash, PartialEq, Eq)]
 | 
				
			|||
pub struct SystemdSocketService<D, P>(pub D, pub &'static str, pub P, pub P, pub bool);
 | 
				
			|||
impl<D, P> Resource for SystemdSocketService<D, P> {
 | 
				
			|||
  type Artifact = (PathArtifact, PathArtifact, UserNameArtifact);
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[derive(Debug, Hash, PartialEq, Eq)]
 | 
				
			|||
pub struct NpmInstall<P>(pub P);
 | 
				
			|||
impl<P> Resource for NpmInstall<P> {
 | 
				
			|||
  type Artifact = ();
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[derive(Debug, Hash, PartialEq, Eq)]
 | 
				
			|||
pub struct Owner<P>(pub String, pub P);
 | 
				
			|||
impl<P> Resource for Owner<P> {
 | 
				
			|||
  type Artifact = ();
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[derive(Debug, Hash, PartialEq, Eq)]
 | 
				
			|||
pub struct ServeCustom<D>(pub D, pub String);
 | 
				
			|||
impl<D> Resource for ServeCustom<D> {
 | 
				
			|||
  type Artifact = PathArtifact;
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[derive(Debug, Hash, PartialEq, Eq)]
 | 
				
			|||
pub struct ServePhp<D, P>(pub D, pub P, pub &'static str, pub String, pub usize);
 | 
				
			|||
impl<D, P> Resource for ServePhp<D, P> {
 | 
				
			|||
  type Artifact = PathArtifact;
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
// Domain, service name, exec, static, dir
 | 
				
			|||
#[derive(Debug, Hash, PartialEq, Eq)]
 | 
				
			|||
pub struct ServeService<D, P>(pub D, pub &'static str, pub P, pub P, pub P, pub bool);
 | 
				
			|||
impl<D, P> Resource for ServeService<D, P> {
 | 
				
			|||
  type Artifact = PathArtifact;
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[derive(Debug, Hash, PartialEq, Eq)]
 | 
				
			|||
pub struct ServeRedir<D>(pub D, pub D);
 | 
				
			|||
impl<D> Resource for ServeRedir<D> {
 | 
				
			|||
  type Artifact = PathArtifact;
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[derive(Debug, Hash, PartialEq, Eq)]
 | 
				
			|||
pub struct ServeStatic<D, P>(pub D, pub P);
 | 
				
			|||
impl<D, P> Resource for ServeStatic<D, P> {
 | 
				
			|||
  type Artifact = PathArtifact;
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[derive(Debug, Hash, PartialEq, Eq)]
 | 
				
			|||
pub struct DefaultServer;
 | 
				
			|||
impl Resource for DefaultServer {
 | 
				
			|||
  type Artifact = PathArtifact;
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[derive(Debug, Hash, PartialEq, Eq)]
 | 
				
			|||
pub struct PhpFpmPool<D>(pub D, pub usize);
 | 
				
			|||
impl<D> Resource for PhpFpmPool<D> {
 | 
				
			|||
  type Artifact = (
 | 
				
			|||
    PathArtifact,
 | 
				
			|||
    PathArtifact,
 | 
				
			|||
    UserNameArtifact,
 | 
				
			|||
    ServiceNameArtifact,
 | 
				
			|||
  );
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
// A single domain could possibly need multiple databases
 | 
				
			|||
#[derive(Debug, Hash, PartialEq, Eq)]
 | 
				
			|||
pub struct MariaDbDatabase<D>(pub D);
 | 
				
			|||
impl<D> Resource for MariaDbDatabase<D> {
 | 
				
			|||
  type Artifact = (DatabaseNameArtifact, UserNameArtifact, PathArtifact);
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[derive(Debug, Hash, PartialEq, Eq)]
 | 
				
			|||
pub struct MariaDbUser<D>(pub D);
 | 
				
			|||
impl<D> Resource for MariaDbUser<D> {
 | 
				
			|||
  type Artifact = UserNameArtifact;
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[derive(Debug, Hash, PartialEq, Eq)]
 | 
				
			|||
pub struct WordpressPlugin<P>(pub P, pub &'static str);
 | 
				
			|||
impl<P> Resource for WordpressPlugin<P> {
 | 
				
			|||
  type Artifact = ();
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[derive(Debug, Hash, PartialEq, Eq)]
 | 
				
			|||
pub struct WordpressTranslation<P>(pub P, pub &'static str, pub &'static str);
 | 
				
			|||
impl<P> Resource for WordpressTranslation<P> {
 | 
				
			|||
  type Artifact = ();
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[derive(Debug, Hash, PartialEq, Eq)]
 | 
				
			|||
pub struct Cron<D>(pub D, pub String);
 | 
				
			|||
impl<D> Resource for Cron<D> {
 | 
				
			|||
  type Artifact = ();
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
pub trait BorrowResource<R> {
 | 
				
			|||
  fn borrow_resource(&self) -> Option<&R>;
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
macro_rules! default_resources {
 | 
				
			|||
  ( $($name:ident: $type:ty,)* ) => {
 | 
				
			|||
    #[derive(Debug, PartialEq, Eq, Hash)]
 | 
				
			|||
    pub enum DefaultResources<'a, D> {
 | 
				
			|||
      $( $name($type) ),*
 | 
				
			|||
    }
 | 
				
			|||
 | 
				
			|||
    $(impl<'a, D> From<$type> for DefaultResources<'a, D> {
 | 
				
			|||
      fn from(from: $type) -> Self {
 | 
				
			|||
        Self::$name(from)
 | 
				
			|||
      }
 | 
				
			|||
    })*
 | 
				
			|||
 | 
				
			|||
    $(impl<'a, D> BorrowResource<$type> for DefaultResources<'a, D> {
 | 
				
			|||
      fn borrow_resource(&self) -> Option<&$type> {
 | 
				
			|||
        match self {
 | 
				
			|||
          Self::$name(v) => Some(v),
 | 
				
			|||
          _ => None
 | 
				
			|||
        }
 | 
				
			|||
      }
 | 
				
			|||
    })*
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
default_resources!(
 | 
				
			|||
  AcmeAccountKey: AcmeAccountKey,
 | 
				
			|||
  AcmeChallengesDir: AcmeChallengesDir,
 | 
				
			|||
  AcmeChallengesNginxSnippet: AcmeChallengesNginxSnippet,
 | 
				
			|||
  AcmeRootCert: AcmeRootCert,
 | 
				
			|||
  AcmeUser: AcmeUser,
 | 
				
			|||
  Cert: Cert<D>,
 | 
				
			|||
  CertChain: CertChain<D>,
 | 
				
			|||
  Cron: Cron<D>,
 | 
				
			|||
  Csr: Csr<D>,
 | 
				
			|||
  DefaultServer: DefaultServer,
 | 
				
			|||
  Dir: Dir<PathBuf>,
 | 
				
			|||
  File: File<PathBuf>,
 | 
				
			|||
  GitCheckout: GitCheckout<'a, PathBuf>,
 | 
				
			|||
  Key: Key<D>,
 | 
				
			|||
  KeyAndCertBundle: KeyAndCertBundle<D>,
 | 
				
			|||
  LoadedDirectory: LoadedDirectory<PathBuf>,
 | 
				
			|||
  MariaDbDatabase: MariaDbDatabase<D>,
 | 
				
			|||
  MariaDbUser: MariaDbUser<D>,
 | 
				
			|||
  SystemdSocketService: SystemdSocketService<D, PathBuf>,
 | 
				
			|||
  NpmInstall: NpmInstall<PathBuf>,
 | 
				
			|||
  Owner: Owner<PathBuf>,
 | 
				
			|||
  PhpFpmPool: PhpFpmPool<D>,
 | 
				
			|||
  ServeCustom: ServeCustom<D>,
 | 
				
			|||
  ServeService: ServeService<D, PathBuf>,
 | 
				
			|||
  ServePhp: ServePhp<D, PathBuf>,
 | 
				
			|||
  ServeRedir: ServeRedir<D>,
 | 
				
			|||
  ServeStatic: ServeStatic<D, PathBuf>,
 | 
				
			|||
  StoredDirectory: StoredDirectory<PathBuf>,
 | 
				
			|||
  User: User,
 | 
				
			|||
  UserForDomain: UserForDomain<D>,
 | 
				
			|||
  WordpressPlugin: WordpressPlugin<PathBuf>,
 | 
				
			|||
  WordpressTranslation: WordpressTranslation<PathBuf>,
 | 
				
			|||
);
 | 
				
			|||
@ -0,0 +1,345 @@ | 
				
			|||
use crate::resources::{BorrowResource, DefaultResources, Resource};
 | 
				
			|||
use crate::schema::SymbolRunner;
 | 
				
			|||
use crate::symbols::Symbol;
 | 
				
			|||
use crate::to_artifact::ToArtifact;
 | 
				
			|||
use crate::{DefaultBuilder, DefaultLocator, ResourceLocator, SymbolBuilder};
 | 
				
			|||
use std::collections::HashSet;
 | 
				
			|||
use std::error::Error;
 | 
				
			|||
use std::fmt::Debug;
 | 
				
			|||
use std::hash::Hash;
 | 
				
			|||
use std::marker::PhantomData;
 | 
				
			|||
 | 
				
			|||
pub trait CanHandle<X: ToArtifact> {
 | 
				
			|||
  fn handle(&mut self, x: X) -> Result<(X::Artifact, bool), Box<dyn Error>>;
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
macro_rules! can_handle {
 | 
				
			|||
  ( $($name:ident)* ) => (
 | 
				
			|||
    #[allow(non_snake_case)]
 | 
				
			|||
    impl<_SR: SymbolRunner, _L, _R: Hash + Eq, _B, $($name: Resource,)*>
 | 
				
			|||
      CanHandle<($($name,)*)>
 | 
				
			|||
      for Setup<_SR, _L, _R, _B>
 | 
				
			|||
      where
 | 
				
			|||
      $(
 | 
				
			|||
        _B: SymbolBuilder<$name>,
 | 
				
			|||
        <_B as SymbolBuilder<$name>>::Symbol: Runnable + Debug,
 | 
				
			|||
        _L: ResourceLocator<$name>,
 | 
				
			|||
        _R: From<$name> + BorrowResource<$name>,
 | 
				
			|||
        Self: CanHandle<<_B as SymbolBuilder<$name>>::Prerequisites> +
 | 
				
			|||
          CanHandle<<_L as ResourceLocator<$name>>::Prerequisites>
 | 
				
			|||
      ),*
 | 
				
			|||
    {
 | 
				
			|||
      fn handle(&mut self, ($($name,)*): ($($name,)*)) -> Result<(($($name::Artifact,)*), bool), Box<dyn Error>>
 | 
				
			|||
      {
 | 
				
			|||
        $(let $name = self.add($name)?;)*
 | 
				
			|||
        Ok((($($name.0,)*), false $(|| $name.1)*))
 | 
				
			|||
      }
 | 
				
			|||
    }
 | 
				
			|||
  );
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
for_each_tuple!(can_handle);
 | 
				
			|||
 | 
				
			|||
// This is for self-referential T
 | 
				
			|||
// FIXME: Wait for specialization
 | 
				
			|||
impl<_SR: SymbolRunner, _L, _R: Hash + Eq, _B, T: Resource> CanHandle<Option<T>>
 | 
				
			|||
  for Setup<_SR, _L, _R, _B>
 | 
				
			|||
where
 | 
				
			|||
  _B: SymbolBuilder<T>,
 | 
				
			|||
  <_B as SymbolBuilder<T>>::Symbol: Runnable + Debug,
 | 
				
			|||
  _L: ResourceLocator<T, Prerequisites = Option<T>>,
 | 
				
			|||
  _R: From<T> + BorrowResource<T>,
 | 
				
			|||
  Self: CanHandle<<_B as SymbolBuilder<T>>::Prerequisites>,
 | 
				
			|||
{
 | 
				
			|||
  fn handle(
 | 
				
			|||
    &mut self,
 | 
				
			|||
    r: Option<T>,
 | 
				
			|||
  ) -> Result<(<Option<T> as ToArtifact>::Artifact, bool), Box<dyn Error>> {
 | 
				
			|||
    Ok(match r {
 | 
				
			|||
      Some(r) => {
 | 
				
			|||
        let (result, did_run) = self.add(r)?;
 | 
				
			|||
        (Some(result), did_run)
 | 
				
			|||
      }
 | 
				
			|||
      None => (None, false),
 | 
				
			|||
    })
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<_SR: SymbolRunner, _L, _R: Hash + Eq, _B, T: Resource> CanHandle<T> for Setup<_SR, _L, _R, _B>
 | 
				
			|||
where
 | 
				
			|||
  _B: SymbolBuilder<T>,
 | 
				
			|||
  <_B as SymbolBuilder<T>>::Symbol: Runnable + Debug,
 | 
				
			|||
  _L: ResourceLocator<T>,
 | 
				
			|||
  _R: From<T> + Debug + BorrowResource<T>,
 | 
				
			|||
  Self: CanHandle<<_B as SymbolBuilder<T>>::Prerequisites>
 | 
				
			|||
    + CanHandle<<_L as ResourceLocator<T>>::Prerequisites>,
 | 
				
			|||
{
 | 
				
			|||
  fn handle(&mut self, r: T) -> Result<(<T as ToArtifact>::Artifact, bool), Box<dyn Error>> {
 | 
				
			|||
    self.add(r)
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
pub struct Setup<
 | 
				
			|||
  SR,
 | 
				
			|||
  L = DefaultLocator,
 | 
				
			|||
  R = DefaultResources<'static, &'static str>,
 | 
				
			|||
  B = DefaultBuilder,
 | 
				
			|||
> {
 | 
				
			|||
  symbol_runner: SR,
 | 
				
			|||
  resources: HashSet<R>,
 | 
				
			|||
  phantom: PhantomData<(L, B)>,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
// https://github.com/rust-lang/rust/issues/27336
 | 
				
			|||
impl<SR> Setup<SR> {
 | 
				
			|||
  pub fn new(symbol_runner: SR) -> Self {
 | 
				
			|||
    Self {
 | 
				
			|||
      symbol_runner,
 | 
				
			|||
      resources: Default::default(),
 | 
				
			|||
      phantom: Default::default(),
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<SR, L, R: Hash + Eq, B> Setup<SR, L, R, B> {
 | 
				
			|||
  pub fn new_with(symbol_runner: SR) -> Self {
 | 
				
			|||
    Self {
 | 
				
			|||
      symbol_runner,
 | 
				
			|||
      resources: Default::default(),
 | 
				
			|||
      phantom: Default::default(),
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
pub trait Runnable {
 | 
				
			|||
  fn run<R: SymbolRunner>(&self, runner: &R, force: bool) -> Result<bool, Box<dyn Error>>;
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<S: Symbol + Debug> Runnable for S {
 | 
				
			|||
  fn run<R: SymbolRunner>(&self, runner: &R, force: bool) -> Result<bool, Box<dyn Error>> {
 | 
				
			|||
    runner.run_symbol(self, force)
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
macro_rules! runnable_for_tuple {
 | 
				
			|||
  ( $($name:ident)* ) => (
 | 
				
			|||
    #[allow(non_snake_case)]
 | 
				
			|||
    impl<$($name: Symbol + Debug,)*> Runnable for ($($name,)*) {
 | 
				
			|||
      #[allow(unused)]
 | 
				
			|||
      fn run<_R: SymbolRunner>(&self, runner: &_R, force: bool) -> Result<bool, Box<dyn Error>> {
 | 
				
			|||
        let ($($name,)*) = self;
 | 
				
			|||
        Ok($(runner.run_symbol($name, force)? || )* false)
 | 
				
			|||
      }
 | 
				
			|||
    }
 | 
				
			|||
  );
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
for_each_tuple!(runnable_for_tuple);
 | 
				
			|||
 | 
				
			|||
impl<SR: SymbolRunner, L, Rs: Hash + Eq, B> Setup<SR, L, Rs, B> {
 | 
				
			|||
  pub fn add<R: Resource>(&mut self, resource: R) -> Result<(R::Artifact, bool), Box<dyn Error>>
 | 
				
			|||
  where
 | 
				
			|||
    B: SymbolBuilder<R>,
 | 
				
			|||
    <B as SymbolBuilder<R>>::Symbol: Runnable + Debug,
 | 
				
			|||
    L: ResourceLocator<R>,
 | 
				
			|||
    Rs: From<R> + BorrowResource<R>,
 | 
				
			|||
    Self: CanHandle<B::Prerequisites> + CanHandle<<L as ResourceLocator<R>>::Prerequisites>,
 | 
				
			|||
  {
 | 
				
			|||
    self.add_force(resource, false)
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn add_force<R: Resource>(
 | 
				
			|||
    &mut self,
 | 
				
			|||
    resource: R,
 | 
				
			|||
    force_run: bool,
 | 
				
			|||
  ) -> Result<(R::Artifact, bool), Box<dyn Error>>
 | 
				
			|||
  where
 | 
				
			|||
    B: SymbolBuilder<R>,
 | 
				
			|||
    <B as SymbolBuilder<R>>::Symbol: Runnable + Debug,
 | 
				
			|||
    L: ResourceLocator<R>,
 | 
				
			|||
    Rs: From<R> + BorrowResource<R>,
 | 
				
			|||
    Self: CanHandle<B::Prerequisites> + CanHandle<<L as ResourceLocator<R>>::Prerequisites>,
 | 
				
			|||
  {
 | 
				
			|||
    let (target, target_prereqs) = L::locate(&resource);
 | 
				
			|||
    let storable_resource = Rs::from(resource);
 | 
				
			|||
    let did_run = if self.resources.get(&storable_resource).is_some() {
 | 
				
			|||
      assert!(
 | 
				
			|||
        !force_run,
 | 
				
			|||
        "Forcing to run an already-added resource is a logical error"
 | 
				
			|||
      );
 | 
				
			|||
      false
 | 
				
			|||
    } else {
 | 
				
			|||
      let (_, target_prereqs_did_run) = self.handle(target_prereqs)?;
 | 
				
			|||
      let (symbol, prereqs_did_run) =
 | 
				
			|||
        self.get_symbol(storable_resource.borrow_resource().unwrap(), &target)?;
 | 
				
			|||
      self.resources.insert(storable_resource);
 | 
				
			|||
      self.run_symbol(
 | 
				
			|||
        symbol,
 | 
				
			|||
        force_run || target_prereqs_did_run || prereqs_did_run,
 | 
				
			|||
      )?
 | 
				
			|||
    };
 | 
				
			|||
    Ok((target, did_run))
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn get_symbol<R: Resource>(
 | 
				
			|||
    &mut self,
 | 
				
			|||
    resource: &R,
 | 
				
			|||
    target: &R::Artifact,
 | 
				
			|||
  ) -> Result<(<B as SymbolBuilder<R>>::Symbol, bool), Box<dyn Error>>
 | 
				
			|||
  where
 | 
				
			|||
    B: SymbolBuilder<R>,
 | 
				
			|||
    Self: CanHandle<B::Prerequisites>,
 | 
				
			|||
  {
 | 
				
			|||
    let (prereqs, prereqs_did_run) = self.handle(B::prerequisites(resource))?;
 | 
				
			|||
    Ok((B::create(resource, target, prereqs), prereqs_did_run))
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn run_symbol<S: Runnable>(&self, symbol: S, force: bool) -> Result<bool, Box<dyn Error>> {
 | 
				
			|||
    symbol.run(&self.symbol_runner, force)
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[cfg(test)]
 | 
				
			|||
mod test {
 | 
				
			|||
  use crate::resources::{BorrowResource, Resource};
 | 
				
			|||
  use crate::schema::SymbolRunner;
 | 
				
			|||
  use crate::symbols::Symbol;
 | 
				
			|||
  use crate::to_artifact::ToArtifact;
 | 
				
			|||
  use crate::{ResourceLocator, Setup, SymbolBuilder};
 | 
				
			|||
  use std::cell::RefCell;
 | 
				
			|||
  use std::error::Error;
 | 
				
			|||
  use std::fmt::Debug;
 | 
				
			|||
  use std::rc::Rc;
 | 
				
			|||
 | 
				
			|||
  struct TestSymbolRunner {
 | 
				
			|||
    count: Rc<RefCell<usize>>,
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  impl SymbolRunner for TestSymbolRunner {
 | 
				
			|||
    fn run_symbol<S: Symbol + Debug>(
 | 
				
			|||
      &self,
 | 
				
			|||
      symbol: &S,
 | 
				
			|||
      force: bool,
 | 
				
			|||
    ) -> Result<bool, Box<dyn Error>> {
 | 
				
			|||
      let run = force || !symbol.target_reached()?;
 | 
				
			|||
      if run {
 | 
				
			|||
        *self.count.borrow_mut() += 1;
 | 
				
			|||
      }
 | 
				
			|||
      Ok(run)
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  #[derive(Debug, PartialEq, Eq, Hash)]
 | 
				
			|||
  struct TestResource<T>(&'static str, T);
 | 
				
			|||
  impl<T> Resource for TestResource<T> {
 | 
				
			|||
    type Artifact = ();
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  #[derive(Debug, Hash, PartialEq, Eq)]
 | 
				
			|||
  enum Resources {
 | 
				
			|||
    A(TestResource<&'static str>),
 | 
				
			|||
    B(TestResource<()>),
 | 
				
			|||
  }
 | 
				
			|||
  impl From<TestResource<&'static str>> for Resources {
 | 
				
			|||
    fn from(from: TestResource<&'static str>) -> Self {
 | 
				
			|||
      Self::A(from)
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
  impl From<TestResource<()>> for Resources {
 | 
				
			|||
    fn from(from: TestResource<()>) -> Self {
 | 
				
			|||
      Self::B(from)
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  impl BorrowResource<TestResource<&'static str>> for Resources {
 | 
				
			|||
    fn borrow_resource(&self) -> Option<&TestResource<&'static str>> {
 | 
				
			|||
      match self {
 | 
				
			|||
        Self::A(a) => Some(a),
 | 
				
			|||
        _ => None,
 | 
				
			|||
      }
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
  impl BorrowResource<TestResource<()>> for Resources {
 | 
				
			|||
    fn borrow_resource(&self) -> Option<&TestResource<()>> {
 | 
				
			|||
      match self {
 | 
				
			|||
        Self::B(b) => Some(b),
 | 
				
			|||
        _ => None,
 | 
				
			|||
      }
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  struct TestResourceLocator;
 | 
				
			|||
  impl<T> ResourceLocator<TestResource<T>> for TestResourceLocator {
 | 
				
			|||
    type Prerequisites = ();
 | 
				
			|||
    fn locate(_resource: &TestResource<T>) -> (<TestResource<T> as ToArtifact>::Artifact, ()) {
 | 
				
			|||
      ((), ())
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  struct TestSymbolBuilder;
 | 
				
			|||
  impl SymbolBuilder<TestResource<&'static str>> for TestSymbolBuilder {
 | 
				
			|||
    type Symbol = TestSymbol;
 | 
				
			|||
    type Prerequisites = TestResource<()>;
 | 
				
			|||
 | 
				
			|||
    fn prerequisites(resource: &TestResource<&'static str>) -> Self::Prerequisites {
 | 
				
			|||
      TestResource(resource.1, ())
 | 
				
			|||
    }
 | 
				
			|||
    fn create(
 | 
				
			|||
      resource: &TestResource<&'static str>,
 | 
				
			|||
      (): &(),
 | 
				
			|||
      _inputs: <Self::Prerequisites as ToArtifact>::Artifact,
 | 
				
			|||
    ) -> Self::Symbol {
 | 
				
			|||
      TestSymbol {
 | 
				
			|||
        reached: resource.0.chars().next().unwrap().is_uppercase(),
 | 
				
			|||
      }
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
  impl SymbolBuilder<TestResource<()>> for TestSymbolBuilder {
 | 
				
			|||
    type Symbol = TestSymbol;
 | 
				
			|||
    type Prerequisites = ();
 | 
				
			|||
 | 
				
			|||
    fn prerequisites(_resource: &TestResource<()>) -> Self::Prerequisites {}
 | 
				
			|||
    fn create(resource: &TestResource<()>, (): &(), (): ()) -> Self::Symbol {
 | 
				
			|||
      TestSymbol {
 | 
				
			|||
        reached: resource.0.chars().next().unwrap().is_uppercase(),
 | 
				
			|||
      }
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  #[derive(Debug)]
 | 
				
			|||
  struct TestSymbol {
 | 
				
			|||
    reached: bool,
 | 
				
			|||
  }
 | 
				
			|||
  impl Symbol for TestSymbol {
 | 
				
			|||
    fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
 | 
				
			|||
      Ok(self.reached)
 | 
				
			|||
    }
 | 
				
			|||
    fn execute(&self) -> Result<(), Box<dyn Error>> {
 | 
				
			|||
      Ok(())
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn get_setup() -> (
 | 
				
			|||
    Rc<RefCell<usize>>,
 | 
				
			|||
    Setup<TestSymbolRunner, TestResourceLocator, Resources, TestSymbolBuilder>,
 | 
				
			|||
  ) {
 | 
				
			|||
    let count = Rc::new(RefCell::new(0));
 | 
				
			|||
    let runner = TestSymbolRunner {
 | 
				
			|||
      count: Rc::clone(&count),
 | 
				
			|||
    };
 | 
				
			|||
    (count, Setup::new_with(runner))
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  #[test]
 | 
				
			|||
  fn correctly_uses_force() {
 | 
				
			|||
    let (count, mut setup) = get_setup();
 | 
				
			|||
    setup.add(TestResource("A", "b")).unwrap();
 | 
				
			|||
    assert_eq!(*count.borrow(), 2);
 | 
				
			|||
    setup.add(TestResource("A", "b")).unwrap();
 | 
				
			|||
    assert_eq!(*count.borrow(), 2);
 | 
				
			|||
 | 
				
			|||
    let (count, mut setup) = get_setup();
 | 
				
			|||
    setup.add(TestResource("A", "B")).unwrap();
 | 
				
			|||
    assert_eq!(*count.borrow(), 0);
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
@ -0,0 +1 @@ | 
				
			|||
include!(concat!(env!("OUT_DIR"), "/static_files.rs"));
 | 
				
			|||
@ -1,85 +0,0 @@ | 
				
			|||
use std::error::Error;
 | 
				
			|||
use std::fmt;
 | 
				
			|||
use std::path::Path;
 | 
				
			|||
 | 
				
			|||
use crate::command_runner::CommandRunner;
 | 
				
			|||
use crate::resources::Resource;
 | 
				
			|||
use crate::symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
 | 
				
			|||
 | 
				
			|||
pub struct AcmeAccountKey<'a, P: AsRef<Path>, C: CommandRunner> {
 | 
				
			|||
  path: P,
 | 
				
			|||
  command_runner: &'a C,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<'a, P: AsRef<Path>, C: CommandRunner> AcmeAccountKey<'a, P, C> {
 | 
				
			|||
  pub fn new(path: P, command_runner: &'a C) -> Self {
 | 
				
			|||
    AcmeAccountKey {
 | 
				
			|||
      path,
 | 
				
			|||
      command_runner,
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn get_bytes(&self) -> u32 {
 | 
				
			|||
    4096
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<P: AsRef<Path>, C: CommandRunner> fmt::Display for AcmeAccountKey<'_, P, C> {
 | 
				
			|||
  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
				
			|||
    write!(f, "AcmeAccountKey {}", self.path.as_ref().display())
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<P: AsRef<Path>, C: CommandRunner> Symbol for AcmeAccountKey<'_, P, C> {
 | 
				
			|||
  fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
 | 
				
			|||
    if !self.path.as_ref().exists() {
 | 
				
			|||
      return Ok(false);
 | 
				
			|||
    }
 | 
				
			|||
    let stdout = self.command_runner.get_output(
 | 
				
			|||
      "openssl",
 | 
				
			|||
      args![
 | 
				
			|||
        "rsa",
 | 
				
			|||
        "-in",
 | 
				
			|||
        self.path.as_ref(),
 | 
				
			|||
        "-noout",
 | 
				
			|||
        "-check",
 | 
				
			|||
        "-text",
 | 
				
			|||
      ],
 | 
				
			|||
    )?;
 | 
				
			|||
    Ok(stdout.ends_with("RSA key ok\n".as_bytes()))
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn execute(&self) -> Result<(), Box<dyn Error>> {
 | 
				
			|||
    self.command_runner.run_successfully(
 | 
				
			|||
      "openssl",
 | 
				
			|||
      args![
 | 
				
			|||
        "genrsa",
 | 
				
			|||
        "-out",
 | 
				
			|||
        self.path.as_ref(),
 | 
				
			|||
        self.get_bytes().to_string(),
 | 
				
			|||
      ],
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn get_prerequisites(&self) -> Vec<Resource> {
 | 
				
			|||
    if let Some(parent) = self.path.as_ref().parent() {
 | 
				
			|||
      vec![Resource::new("dir", parent.to_str().unwrap())]
 | 
				
			|||
    } else {
 | 
				
			|||
      vec![]
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
 | 
				
			|||
    Box::new(SymbolAction::new(runner, self))
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn into_action<'b>(self: Box<Self>, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b>
 | 
				
			|||
  where
 | 
				
			|||
    Self: 'b,
 | 
				
			|||
  {
 | 
				
			|||
    Box::new(OwnedSymbolAction::new(runner, *self))
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[cfg(test)]
 | 
				
			|||
mod test {}
 | 
				
			|||
@ -1,117 +0,0 @@ | 
				
			|||
use std::error::Error;
 | 
				
			|||
use std::fmt;
 | 
				
			|||
use std::fs::File as FsFile;
 | 
				
			|||
use std::io::Write;
 | 
				
			|||
use std::path::{Path, PathBuf};
 | 
				
			|||
 | 
				
			|||
use crate::command_runner::CommandRunner;
 | 
				
			|||
use crate::resources::Resource;
 | 
				
			|||
use crate::symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
 | 
				
			|||
 | 
				
			|||
pub struct AcmeCertChain<'a, D: AsRef<str>, R: AsRef<Path>, C: CommandRunner> {
 | 
				
			|||
  domain: D,
 | 
				
			|||
  command_runner: &'a C,
 | 
				
			|||
  root_cert: R,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<'a, D: AsRef<str>, R: AsRef<Path>, C: CommandRunner> AcmeCertChain<'a, D, R, C> {
 | 
				
			|||
  pub fn new(domain: D, command_runner: &'a C, root_cert: R) -> Self {
 | 
				
			|||
    AcmeCertChain {
 | 
				
			|||
      domain,
 | 
				
			|||
      command_runner,
 | 
				
			|||
      root_cert,
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn get_single_cert_path(&self) -> PathBuf {
 | 
				
			|||
    format!("/etc/ssl/local_certs/{}.crt", self.domain.as_ref()).into()
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn get_cert_chain_path(&self) -> PathBuf {
 | 
				
			|||
    format!("/etc/ssl/local_certs/{}.chained.crt", self.domain.as_ref()).into()
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<D: AsRef<str>, R: AsRef<Path>, C: CommandRunner> fmt::Display for AcmeCertChain<'_, D, R, C> {
 | 
				
			|||
  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
				
			|||
    write!(f, "AcmeCertChain {}", self.domain.as_ref())
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
const DAYS_IN_SECONDS: u32 = 24 * 60 * 60;
 | 
				
			|||
 | 
				
			|||
impl<D: AsRef<str>, R: AsRef<Path>, C: CommandRunner> Symbol for AcmeCertChain<'_, D, R, C> {
 | 
				
			|||
  fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
 | 
				
			|||
    if !self.get_cert_chain_path().exists() {
 | 
				
			|||
      return Ok(false);
 | 
				
			|||
    }
 | 
				
			|||
 | 
				
			|||
    let stdout = self.command_runner.get_output(
 | 
				
			|||
      "openssl",
 | 
				
			|||
      args![
 | 
				
			|||
        "x509",
 | 
				
			|||
        "-in",
 | 
				
			|||
        self.get_cert_chain_path(),
 | 
				
			|||
        "-noout",
 | 
				
			|||
        "-subject",
 | 
				
			|||
        "-checkend",
 | 
				
			|||
        (30 * DAYS_IN_SECONDS).to_string(),
 | 
				
			|||
      ],
 | 
				
			|||
    )?;
 | 
				
			|||
    if stdout
 | 
				
			|||
      != format!(
 | 
				
			|||
        "subject=CN = {}\nCertificate will not expire\n",
 | 
				
			|||
        self.domain.as_ref()
 | 
				
			|||
      )
 | 
				
			|||
      .as_bytes()
 | 
				
			|||
    {
 | 
				
			|||
      return Ok(false);
 | 
				
			|||
    }
 | 
				
			|||
    // FIXME: From my understanding, the -untrusted *.pem parameter shouldn't be necessary, but is necessary with openssl 1.1.0f-3
 | 
				
			|||
    Ok(
 | 
				
			|||
      self
 | 
				
			|||
        .command_runner
 | 
				
			|||
        .run_successfully(
 | 
				
			|||
          "openssl",
 | 
				
			|||
          args![
 | 
				
			|||
            "verify",
 | 
				
			|||
            "-untrusted",
 | 
				
			|||
            self.root_cert.as_ref(),
 | 
				
			|||
            self.get_cert_chain_path(),
 | 
				
			|||
          ],
 | 
				
			|||
        )
 | 
				
			|||
        .is_ok(),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn execute(&self) -> Result<(), Box<dyn Error>> {
 | 
				
			|||
    let output = self.command_runner.get_output(
 | 
				
			|||
      "cat",
 | 
				
			|||
      args![self.get_single_cert_path(), self.root_cert.as_ref(),],
 | 
				
			|||
    )?;
 | 
				
			|||
    let mut file = FsFile::create(self.get_cert_chain_path())?;
 | 
				
			|||
    file.write_all(&output)?;
 | 
				
			|||
    Ok(())
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn get_prerequisites(&self) -> Vec<Resource> {
 | 
				
			|||
    vec![Resource::new(
 | 
				
			|||
      "file",
 | 
				
			|||
      self.get_single_cert_path().to_str().unwrap(),
 | 
				
			|||
    )]
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
 | 
				
			|||
    Box::new(SymbolAction::new(runner, self))
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn into_action<'b>(self: Box<Self>, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b>
 | 
				
			|||
  where
 | 
				
			|||
    Self: 'b,
 | 
				
			|||
  {
 | 
				
			|||
    Box::new(OwnedSymbolAction::new(runner, *self))
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[cfg(test)]
 | 
				
			|||
mod test {}
 | 
				
			|||
@ -1,113 +1,2 @@ | 
				
			|||
use std::borrow::Cow;
 | 
				
			|||
use std::path::{Path, PathBuf};
 | 
				
			|||
 | 
				
			|||
use crate::command_runner::CommandRunner;
 | 
				
			|||
use crate::command_runner::SetuidCommandRunner;
 | 
				
			|||
use crate::symbols::concat::Concat;
 | 
				
			|||
use crate::symbols::dir::Dir;
 | 
				
			|||
use crate::symbols::file::File;
 | 
				
			|||
use crate::symbols::list::List;
 | 
				
			|||
use crate::symbols::owner::Owner;
 | 
				
			|||
use crate::symbols::Symbol;
 | 
				
			|||
 | 
				
			|||
mod account_key;
 | 
				
			|||
mod cert;
 | 
				
			|||
mod chain;
 | 
				
			|||
 | 
				
			|||
pub use self::account_key::AcmeAccountKey;
 | 
				
			|||
pub use self::cert::AcmeCert;
 | 
				
			|||
pub use self::chain::AcmeCertChain;
 | 
				
			|||
 | 
				
			|||
const ROOT_CERT_FILE_NAME: &str = "lets_encrypt_x3_cross_signed.pem";
 | 
				
			|||
const ACCOUNT_KEY_FILE_NAME: &str = "account.key";
 | 
				
			|||
 | 
				
			|||
pub struct Factory<'a, U: AsRef<str>, H: AsRef<Path>, C: AsRef<str>, R: CommandRunner> {
 | 
				
			|||
  user_name: U,
 | 
				
			|||
  home_dir: H,
 | 
				
			|||
  cert: C,
 | 
				
			|||
  command_runner: &'a R,
 | 
				
			|||
  acme_command_runner: SetuidCommandRunner<'a, U, R>,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<'a, U: Clone + AsRef<str>, H: AsRef<Path>, C: AsRef<str>, R: CommandRunner>
 | 
				
			|||
  Factory<'a, U, H, C, R>
 | 
				
			|||
{
 | 
				
			|||
  pub fn new(user_name: U, home_dir: H, cert: C, command_runner: &'a R) -> Self {
 | 
				
			|||
    let acme_command_runner = SetuidCommandRunner::new(user_name.clone(), command_runner);
 | 
				
			|||
    Self {
 | 
				
			|||
      user_name,
 | 
				
			|||
      home_dir,
 | 
				
			|||
      cert,
 | 
				
			|||
      command_runner,
 | 
				
			|||
      acme_command_runner,
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
  pub fn get_challenges_dir(&'a self) -> Cow<'_, Path> {
 | 
				
			|||
    [self.home_dir.as_ref(), "challenges".as_ref()]
 | 
				
			|||
      .iter()
 | 
				
			|||
      .collect::<PathBuf>()
 | 
				
			|||
      .into()
 | 
				
			|||
  }
 | 
				
			|||
  pub fn get_init(&'a self) -> impl Symbol + 'a {
 | 
				
			|||
    let root_cert_path: PathBuf = [self.home_dir.as_ref(), ROOT_CERT_FILE_NAME.as_ref()]
 | 
				
			|||
      .iter()
 | 
				
			|||
      .collect();
 | 
				
			|||
    let account_key_file: PathBuf = [self.home_dir.as_ref(), ACCOUNT_KEY_FILE_NAME.as_ref()]
 | 
				
			|||
      .iter()
 | 
				
			|||
      .collect();
 | 
				
			|||
    List::from((
 | 
				
			|||
      AcmeAccountKey::new(account_key_file.clone(), self.command_runner),
 | 
				
			|||
      Owner::new(
 | 
				
			|||
        account_key_file,
 | 
				
			|||
        self.user_name.clone(),
 | 
				
			|||
        self.command_runner,
 | 
				
			|||
      ),
 | 
				
			|||
      Dir::new(self.get_challenges_dir()),
 | 
				
			|||
      Owner::new(
 | 
				
			|||
        self.get_challenges_dir(),
 | 
				
			|||
        self.user_name.clone(),
 | 
				
			|||
        self.command_runner,
 | 
				
			|||
      ),
 | 
				
			|||
      Dir::new("/etc/ssl/local_certs"),
 | 
				
			|||
      Owner::new(
 | 
				
			|||
        "/etc/ssl/local_certs",
 | 
				
			|||
        self.user_name.clone(),
 | 
				
			|||
        self.command_runner,
 | 
				
			|||
      ),
 | 
				
			|||
      File::new(root_cert_path, self.cert.as_ref()),
 | 
				
			|||
    ))
 | 
				
			|||
  }
 | 
				
			|||
  pub fn get_cert<HOST: 'a + Clone + AsRef<str>>(&'a self, host: HOST) -> impl Symbol + 'a {
 | 
				
			|||
    let root_cert_path: PathBuf = [self.home_dir.as_ref(), ROOT_CERT_FILE_NAME.as_ref()]
 | 
				
			|||
      .iter()
 | 
				
			|||
      .collect();
 | 
				
			|||
    let account_key_path: PathBuf = [self.home_dir.as_ref(), ACCOUNT_KEY_FILE_NAME.as_ref()]
 | 
				
			|||
      .iter()
 | 
				
			|||
      .collect();
 | 
				
			|||
    List::from((
 | 
				
			|||
      AcmeCert::new(
 | 
				
			|||
        host.clone(),
 | 
				
			|||
        &self.acme_command_runner,
 | 
				
			|||
        root_cert_path.clone(),
 | 
				
			|||
        account_key_path,
 | 
				
			|||
        self.get_challenges_dir(),
 | 
				
			|||
      ),
 | 
				
			|||
      AcmeCertChain::new(host, &self.acme_command_runner, root_cert_path),
 | 
				
			|||
    ))
 | 
				
			|||
  }
 | 
				
			|||
  pub fn get_key_and_cert_bundle<HOST: 'a + Clone + AsRef<str>>(
 | 
				
			|||
    &'a self,
 | 
				
			|||
    host: HOST,
 | 
				
			|||
  ) -> impl Symbol + 'a {
 | 
				
			|||
    List::from((
 | 
				
			|||
      self.get_cert(host.clone()),
 | 
				
			|||
      Concat::new(
 | 
				
			|||
        [
 | 
				
			|||
          format!("/etc/ssl/private/{}.key", host.as_ref()),
 | 
				
			|||
          format!("/etc/ssl/local_certs/{}.chained.crt", host.as_ref()),
 | 
				
			|||
        ],
 | 
				
			|||
        format!("/etc/ssl/private/{}.with_key.crt", host.as_ref()),
 | 
				
			|||
      ),
 | 
				
			|||
    ))
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
pub use self::cert::Cert;
 | 
				
			|||
@ -1,436 +0,0 @@ | 
				
			|||
use std::borrow::Cow;
 | 
				
			|||
use std::path::Path;
 | 
				
			|||
use std::path::PathBuf;
 | 
				
			|||
 | 
				
			|||
use crate::command_runner::CommandRunner;
 | 
				
			|||
use crate::storage::{SimpleStorage, Storage};
 | 
				
			|||
use crate::symbols::acme::Factory as AcmeFactory;
 | 
				
			|||
use crate::symbols::cron::Cron;
 | 
				
			|||
use crate::symbols::file::File;
 | 
				
			|||
use crate::symbols::git::checkout::GitCheckout;
 | 
				
			|||
use crate::symbols::hook::Hook;
 | 
				
			|||
use crate::symbols::list::List;
 | 
				
			|||
use crate::symbols::mariadb::{DatabaseDump, MariaDBDatabase, MariaDBUser};
 | 
				
			|||
use crate::symbols::nginx::server::{server_config, NginxServer};
 | 
				
			|||
use crate::symbols::owner::Owner;
 | 
				
			|||
use crate::symbols::stored_directory::{StorageDirection, StoredDirectory};
 | 
				
			|||
use crate::symbols::systemd::reload::ReloadService;
 | 
				
			|||
use crate::symbols::systemd::user_service::UserService;
 | 
				
			|||
use crate::symbols::tls::SelfSignedTlsCert;
 | 
				
			|||
use crate::symbols::Symbol;
 | 
				
			|||
 | 
				
			|||
pub trait Policy {
 | 
				
			|||
  fn user_name_for_host(&self, host_name: &'static str) -> String {
 | 
				
			|||
    host_name.split('.').rev().fold(String::new(), |result, part| if result.is_empty() { result } else { result + "_" } + part)
 | 
				
			|||
  }
 | 
				
			|||
  fn home_for_user(&self, user_name: &str) -> PathBuf {
 | 
				
			|||
    format!("/home/{}", user_name).into()
 | 
				
			|||
  }
 | 
				
			|||
  fn socket_path(&self, user_name: &str, service_name: &str) -> PathBuf {
 | 
				
			|||
    format!("/var/tmp/{}-{}.socket", user_name, service_name).into()
 | 
				
			|||
  }
 | 
				
			|||
  fn acme_user(&self) -> Cow<'_, str> {
 | 
				
			|||
    "acme".into()
 | 
				
			|||
  }
 | 
				
			|||
  fn php_version(&self) -> &'static str {
 | 
				
			|||
    "7.0"
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
pub struct DefaultPolicy;
 | 
				
			|||
 | 
				
			|||
impl Policy for DefaultPolicy {}
 | 
				
			|||
 | 
				
			|||
pub struct SymbolFactory<'a, C: CommandRunner, P: Policy> {
 | 
				
			|||
  command_runner: &'a C,
 | 
				
			|||
  acme_factory: AcmeFactory<'a, Cow<'a, str>, PathBuf, &'a str, C>,
 | 
				
			|||
  policy: &'a P,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<'b, C: 'b + CommandRunner, P: 'b + Policy> SymbolFactory<'b, C, P> {
 | 
				
			|||
  pub fn new(command_runner: &'b C, policy: &'b P, cert: &'b str) -> Self {
 | 
				
			|||
    let acme_user = policy.acme_user();
 | 
				
			|||
    let acme_home = policy.home_for_user(&acme_user);
 | 
				
			|||
    let acme_factory = AcmeFactory::new(acme_user, acme_home, cert, command_runner);
 | 
				
			|||
    SymbolFactory {
 | 
				
			|||
      command_runner,
 | 
				
			|||
      acme_factory,
 | 
				
			|||
      policy,
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn get_cert<'a, H: 'a + AsRef<str> + Clone>(&'a self, host: H) -> impl Symbol + 'a {
 | 
				
			|||
    self.acme_factory.get_cert(host)
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn get_key_and_cert_bundle<'a, H: 'a + AsRef<str> + Clone>(
 | 
				
			|||
    &'a self,
 | 
				
			|||
    host: H,
 | 
				
			|||
  ) -> impl Symbol + 'a {
 | 
				
			|||
    self.acme_factory.get_key_and_cert_bundle(host)
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn get_nginx_acme_server<'a, S: 'a + Symbol>(
 | 
				
			|||
    &'a self,
 | 
				
			|||
    host: &'a str,
 | 
				
			|||
    nginx_server_symbol: S,
 | 
				
			|||
  ) -> impl Symbol + 'a {
 | 
				
			|||
    List::from((
 | 
				
			|||
      SelfSignedTlsCert::new(host, self.command_runner),
 | 
				
			|||
      Hook::new(
 | 
				
			|||
        nginx_server_symbol,
 | 
				
			|||
        ReloadService::new("nginx", self.command_runner),
 | 
				
			|||
      ),
 | 
				
			|||
      Hook::new(
 | 
				
			|||
        self.get_cert(host),
 | 
				
			|||
        ReloadService::new("nginx", self.command_runner),
 | 
				
			|||
      ),
 | 
				
			|||
    ))
 | 
				
			|||
  }
 | 
				
			|||
  pub fn get_nginx_acme_challenge_config<'a>(&'a self) -> impl Symbol + 'a {
 | 
				
			|||
    File::new(
 | 
				
			|||
      "/etc/nginx/snippets/acme-challenge.conf",
 | 
				
			|||
      format!(
 | 
				
			|||
        "location ^~ /.well-known/acme-challenge/ {{
 | 
				
			|||
  alias {}/;
 | 
				
			|||
  try_files $uri =404;
 | 
				
			|||
}}",
 | 
				
			|||
        self.acme_factory.get_challenges_dir().to_str().unwrap()
 | 
				
			|||
      ),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn get_php_fpm_pool_socket_path<'a>(&'a self, user_name: &str) -> PathBuf {
 | 
				
			|||
    format!("/run/php/{}.sock", user_name).into()
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn get_php_fpm_pool<'a>(&'a self, user_name: &str, max_children: usize) -> impl Symbol + 'a {
 | 
				
			|||
    let socket = self.get_php_fpm_pool_socket_path(user_name);
 | 
				
			|||
    let php_version = self.policy.php_version();
 | 
				
			|||
    Hook::new(
 | 
				
			|||
      File::new(
 | 
				
			|||
        format!("/etc/php/{}/fpm/pool.d/{}.conf", php_version, user_name),
 | 
				
			|||
        format!(
 | 
				
			|||
          "[{0}]
 | 
				
			|||
 | 
				
			|||
user = {0}
 | 
				
			|||
group = www-data
 | 
				
			|||
listen = {1}
 | 
				
			|||
listen.owner = www-data
 | 
				
			|||
pm = ondemand
 | 
				
			|||
pm.max_children = {2}
 | 
				
			|||
catch_workers_output = yes
 | 
				
			|||
env[PATH] = /usr/local/bin:/usr/bin:/bin
 | 
				
			|||
",
 | 
				
			|||
          user_name,
 | 
				
			|||
          socket.to_str().unwrap(),
 | 
				
			|||
          max_children
 | 
				
			|||
        ),
 | 
				
			|||
      ),
 | 
				
			|||
      ReloadService::new(format!("php{}-fpm", php_version), self.command_runner),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn serve_php<'a, ROOT: AsRef<Path>>(
 | 
				
			|||
    &'a self,
 | 
				
			|||
    host_name: &'static str,
 | 
				
			|||
    root_dir: ROOT,
 | 
				
			|||
    max_children: usize,
 | 
				
			|||
    additional_config: &'a str,
 | 
				
			|||
  ) -> impl Symbol + 'a {
 | 
				
			|||
    let user_name = self.policy.user_name_for_host(host_name);
 | 
				
			|||
    let socket = self.get_php_fpm_pool_socket_path(&user_name);
 | 
				
			|||
    List::from((
 | 
				
			|||
      self.get_php_fpm_pool(&user_name, max_children),
 | 
				
			|||
      self.get_nginx_acme_server(
 | 
				
			|||
        host_name,
 | 
				
			|||
        NginxServer::new_php(
 | 
				
			|||
          host_name,
 | 
				
			|||
          socket,
 | 
				
			|||
          root_dir,
 | 
				
			|||
          self.command_runner,
 | 
				
			|||
          additional_config,
 | 
				
			|||
        ),
 | 
				
			|||
      ),
 | 
				
			|||
    ))
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn serve_wordpress<'a, ROOT: 'a + AsRef<Path>>(
 | 
				
			|||
    &'a self,
 | 
				
			|||
    host_name: &'static str,
 | 
				
			|||
    root_dir: ROOT,
 | 
				
			|||
  ) -> impl Symbol + 'a {
 | 
				
			|||
    self.serve_php(
 | 
				
			|||
      host_name,
 | 
				
			|||
      root_dir,
 | 
				
			|||
      10,
 | 
				
			|||
      "
 | 
				
			|||
        location / {
 | 
				
			|||
          try_files         $uri $uri/ /index.php?$args;
 | 
				
			|||
        }
 | 
				
			|||
      ",
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn serve_dokuwiki<'a>(
 | 
				
			|||
    &'a self,
 | 
				
			|||
    host_name: &'static str,
 | 
				
			|||
    root_dir: &'static str,
 | 
				
			|||
  ) -> impl Symbol + 'a {
 | 
				
			|||
    let user_name = self.policy.user_name_for_host(host_name);
 | 
				
			|||
    let socket = self.get_php_fpm_pool_socket_path(&user_name);
 | 
				
			|||
    List::from((
 | 
				
			|||
      self.get_php_fpm_pool(&user_name, 10),
 | 
				
			|||
      self.get_nginx_acme_server(host_name,
 | 
				
			|||
        NginxServer::new(
 | 
				
			|||
          host_name,
 | 
				
			|||
          server_config(host_name, &format!("
 | 
				
			|||
      root                {};
 | 
				
			|||
      index               doku.php;
 | 
				
			|||
      location ~ [^/]\\.php(/|$) {{
 | 
				
			|||
        fastcgi_pass      unix:{};
 | 
				
			|||
        include           \"snippets/fastcgi-php.conf\";
 | 
				
			|||
      }}
 | 
				
			|||
 | 
				
			|||
      location ~ /(data/|conf/|bin/|inc/|install.php) {{ deny all; }}
 | 
				
			|||
 | 
				
			|||
      location / {{ try_files $uri $uri/ @dokuwiki; }}
 | 
				
			|||
 | 
				
			|||
      location @dokuwiki {{
 | 
				
			|||
          # rewrites \"doku.php/\" out of the URLs if you set the userewrite setting to .htaccess in dokuwiki config page
 | 
				
			|||
          rewrite ^/_media/(.*) /lib/exe/fetch.php?media=$1 last;
 | 
				
			|||
          rewrite ^/_detail/(.*) /lib/exe/detail.php?media=$1 last;
 | 
				
			|||
          rewrite ^/_export/([^/]+)/(.*) /doku.php?do=export_$1&id=$2 last;
 | 
				
			|||
          rewrite ^/(.*) /doku.php?id=$1&$args last;
 | 
				
			|||
      }}
 | 
				
			|||
  ",
 | 
				
			|||
        root_dir,
 | 
				
			|||
        socket.to_str().unwrap())),
 | 
				
			|||
        self.command_runner
 | 
				
			|||
      ))
 | 
				
			|||
    ))
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn serve_nextcloud<'a, ROOT: 'a + AsRef<Path>>(
 | 
				
			|||
    &'a self,
 | 
				
			|||
    host_name: &'static str,
 | 
				
			|||
    root_dir: ROOT,
 | 
				
			|||
  ) -> impl Symbol + 'a {
 | 
				
			|||
    self.serve_php(
 | 
				
			|||
      host_name,
 | 
				
			|||
      root_dir,
 | 
				
			|||
      25,
 | 
				
			|||
      "
 | 
				
			|||
              client_max_body_size 500M;
 | 
				
			|||
 | 
				
			|||
              # Disable gzip to avoid the removal of the ETag header
 | 
				
			|||
              gzip off;
 | 
				
			|||
 | 
				
			|||
              rewrite ^/caldav(.*)$ /remote.php/caldav$1 redirect;
 | 
				
			|||
              rewrite ^/carddav(.*)$ /remote.php/carddav$1 redirect;
 | 
				
			|||
              rewrite ^/webdav(.*)$ /remote.php/webdav$1 redirect;
 | 
				
			|||
 | 
				
			|||
              error_page 403 /core/templates/403.php;
 | 
				
			|||
              error_page 404 /core/templates/404.php;
 | 
				
			|||
 | 
				
			|||
              location = /robots.txt {
 | 
				
			|||
                allow all;
 | 
				
			|||
                log_not_found off;
 | 
				
			|||
                access_log off;
 | 
				
			|||
              }
 | 
				
			|||
 | 
				
			|||
              location ~ ^/(?:\\.htaccess|data|config|db_structure\\.xml|README) {
 | 
				
			|||
                deny all;
 | 
				
			|||
              }
 | 
				
			|||
 | 
				
			|||
              location / {
 | 
				
			|||
                # The following 2 rules are only needed with webfinger
 | 
				
			|||
                rewrite ^/.well-known/host-meta /public.php?service=host-meta last;
 | 
				
			|||
                rewrite ^/.well-known/host-meta.json /public.php?service=host-meta-json last;
 | 
				
			|||
 | 
				
			|||
                rewrite ^/.well-known/carddav /remote.php/carddav/ redirect;
 | 
				
			|||
                rewrite ^/.well-known/caldav /remote.php/caldav/ redirect;
 | 
				
			|||
 | 
				
			|||
                rewrite ^(/core/doc/[^\\/]+/)$ $1/index.html;
 | 
				
			|||
 | 
				
			|||
                try_files $uri $uri/ /index.php;
 | 
				
			|||
              }
 | 
				
			|||
 | 
				
			|||
              # Adding the cache control header for js and css files
 | 
				
			|||
              # Make sure it is BELOW the location ~ \\.php(?:$|/) { block
 | 
				
			|||
              location ~* \\.(?:css|js)$ {
 | 
				
			|||
                add_header Cache-Control \"public, max-age=7200\";
 | 
				
			|||
                # Optional: Don't log access to assets
 | 
				
			|||
                access_log off;
 | 
				
			|||
              }
 | 
				
			|||
 | 
				
			|||
              # Optional: Don't log access to other assets
 | 
				
			|||
              location ~* \\.(?:jpg|jpeg|gif|bmp|ico|png|swf)$ {
 | 
				
			|||
                access_log off;
 | 
				
			|||
              }
 | 
				
			|||
          ",
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn serve_redir<'a>(
 | 
				
			|||
    &'a self,
 | 
				
			|||
    host_name: &'static str,
 | 
				
			|||
    target: &'static str,
 | 
				
			|||
  ) -> impl Symbol + 'a {
 | 
				
			|||
    self.get_nginx_acme_server(
 | 
				
			|||
      host_name,
 | 
				
			|||
      NginxServer::new_redir(host_name, target, self.command_runner),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn serve_static<'a>(&'a self, host_name: &'static str, dir: &'a str) -> impl Symbol + 'a {
 | 
				
			|||
    self.get_nginx_acme_server(
 | 
				
			|||
      host_name,
 | 
				
			|||
      NginxServer::new_static(host_name, dir, self.command_runner),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn get_stored_directory<'a, T: Into<String>>(
 | 
				
			|||
    &'a self,
 | 
				
			|||
    storage_name: &'static str,
 | 
				
			|||
    target: T,
 | 
				
			|||
  ) -> (impl Symbol + 'a, impl Symbol + 'a) {
 | 
				
			|||
    let data = SimpleStorage::new("/root/data".to_string(), storage_name.to_string());
 | 
				
			|||
    let string_target = target.into();
 | 
				
			|||
    (
 | 
				
			|||
      StoredDirectory::new(
 | 
				
			|||
        string_target.clone(),
 | 
				
			|||
        data.clone(),
 | 
				
			|||
        StorageDirection::Save,
 | 
				
			|||
        self.command_runner,
 | 
				
			|||
      ),
 | 
				
			|||
      StoredDirectory::new(
 | 
				
			|||
        string_target,
 | 
				
			|||
        data,
 | 
				
			|||
        StorageDirection::Load,
 | 
				
			|||
        self.command_runner,
 | 
				
			|||
      ),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn get_mariadb_database<'a>(&'a self, name: &'static str) -> impl Symbol + 'a {
 | 
				
			|||
    let db_dump = SimpleStorage::new("/root/data".to_string(), format!("{}.sql", name));
 | 
				
			|||
    List::from((
 | 
				
			|||
      MariaDBDatabase::new(
 | 
				
			|||
        name,
 | 
				
			|||
        db_dump.read_filename().expect("Initial db dump missing"),
 | 
				
			|||
        self.command_runner,
 | 
				
			|||
      ),
 | 
				
			|||
      DatabaseDump::new(name, db_dump, self.command_runner),
 | 
				
			|||
    ))
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn get_mariadb_user<'a>(&'a self, user_name: &'static str) -> impl Symbol + 'a {
 | 
				
			|||
    MariaDBUser::new(user_name, self.command_runner)
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn get_git_checkout<'a, T: 'a + AsRef<Path>>(
 | 
				
			|||
    &'a self,
 | 
				
			|||
    target: T,
 | 
				
			|||
    source: &'a str,
 | 
				
			|||
    branch: &'a str,
 | 
				
			|||
  ) -> impl Symbol + 'a {
 | 
				
			|||
    GitCheckout::new(target, source, branch, self.command_runner)
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn get_owner<'a, U: 'a + AsRef<str>, F: 'a + AsRef<Path>>(
 | 
				
			|||
    &'a self,
 | 
				
			|||
    file: F,
 | 
				
			|||
    user: U,
 | 
				
			|||
  ) -> impl Symbol + 'a {
 | 
				
			|||
    Owner::new(file, user, self.command_runner)
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn get_file<'a, F: 'a + AsRef<str>, Q: 'a + AsRef<Path>>(
 | 
				
			|||
    &'a self,
 | 
				
			|||
    path: Q,
 | 
				
			|||
    content: F,
 | 
				
			|||
  ) -> impl Symbol + 'a {
 | 
				
			|||
    File::new(path, content)
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn get_cron<'a, T: 'a + AsRef<str>, U: 'a + AsRef<str>>(
 | 
				
			|||
    &'a self,
 | 
				
			|||
    user: U,
 | 
				
			|||
    content: T,
 | 
				
			|||
  ) -> impl Symbol + 'a {
 | 
				
			|||
    Cron::new(user, content, self.command_runner)
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn get_acme_user<'a>(&'a self) -> impl Symbol + 'a {
 | 
				
			|||
    self.acme_factory.get_init()
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn get_systemd_user_service<'a, U: 'a + AsRef<str> + Clone>(
 | 
				
			|||
    &'a self,
 | 
				
			|||
    user_name: U,
 | 
				
			|||
    service_name: &'a str,
 | 
				
			|||
    config: String,
 | 
				
			|||
  ) -> impl Symbol + 'a {
 | 
				
			|||
    let socket_path = self.policy.socket_path(user_name.as_ref(), service_name);
 | 
				
			|||
    let home = self.policy.home_for_user(user_name.as_ref());
 | 
				
			|||
    UserService::new(
 | 
				
			|||
      socket_path,
 | 
				
			|||
      &home,
 | 
				
			|||
      user_name,
 | 
				
			|||
      service_name,
 | 
				
			|||
      self.command_runner,
 | 
				
			|||
      config,
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn get_nodejs_systemd_user_service<'a, U: 'a + AsRef<str> + Clone>(
 | 
				
			|||
    &'a self,
 | 
				
			|||
    user_name: U,
 | 
				
			|||
    service_name: &'a str,
 | 
				
			|||
    path: &'a Path,
 | 
				
			|||
  ) -> impl Symbol + 'a {
 | 
				
			|||
    let socket_path = self.policy.socket_path(user_name.as_ref(), service_name);
 | 
				
			|||
    let home = self.policy.home_for_user(user_name.as_ref());
 | 
				
			|||
    UserService::new_nodejs(
 | 
				
			|||
      &home,
 | 
				
			|||
      user_name,
 | 
				
			|||
      service_name,
 | 
				
			|||
      path,
 | 
				
			|||
      self.command_runner,
 | 
				
			|||
      socket_path,
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn proxy_socket<'a, T: AsRef<Path>>(
 | 
				
			|||
    &'a self,
 | 
				
			|||
    host_name: &'static str,
 | 
				
			|||
    service_name: &'a str,
 | 
				
			|||
    root_dir: T,
 | 
				
			|||
  ) -> impl Symbol + 'a {
 | 
				
			|||
    let user_name = self.policy.user_name_for_host(host_name);
 | 
				
			|||
    let socket_path = self.policy.socket_path(&user_name, service_name);
 | 
				
			|||
    self.get_nginx_acme_server(
 | 
				
			|||
      host_name,
 | 
				
			|||
      NginxServer::new_proxy(host_name, &socket_path, root_dir, self.command_runner),
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn serve_nodejs<'a, 'c: 'a, S: 'a + Symbol, SP: 'a + AsRef<Path>>(
 | 
				
			|||
    &'c self,
 | 
				
			|||
    host: &'static str,
 | 
				
			|||
    service_name: &'a str,
 | 
				
			|||
    path: &'a Path,
 | 
				
			|||
    static_path: SP,
 | 
				
			|||
    nodejs_symbol: S,
 | 
				
			|||
  ) -> impl Symbol + 'a {
 | 
				
			|||
    let user_name = self.policy.user_name_for_host(host);
 | 
				
			|||
    List::from((
 | 
				
			|||
      Hook::new(
 | 
				
			|||
        nodejs_symbol,
 | 
				
			|||
        self.get_nodejs_systemd_user_service(user_name, service_name, path),
 | 
				
			|||
      ),
 | 
				
			|||
      self.proxy_socket(host, service_name, static_path),
 | 
				
			|||
    ))
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
@ -1,2 +1,4 @@ | 
				
			|||
pub mod checkout;
 | 
				
			|||
pub mod submodules;
 | 
				
			|||
mod checkout;
 | 
				
			|||
//pub mod submodules;
 | 
				
			|||
 | 
				
			|||
pub use checkout::Checkout;
 | 
				
			|||
@ -1,196 +0,0 @@ | 
				
			|||
use std::error::Error;
 | 
				
			|||
use std::fmt;
 | 
				
			|||
 | 
				
			|||
use crate::resources::Resource;
 | 
				
			|||
use crate::symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
 | 
				
			|||
 | 
				
			|||
pub struct Hook<A, B>
 | 
				
			|||
where
 | 
				
			|||
  A: Symbol,
 | 
				
			|||
  B: Symbol,
 | 
				
			|||
{
 | 
				
			|||
  a: A,
 | 
				
			|||
  b: B,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
// A and B are executed if either A or B are not reached
 | 
				
			|||
impl<A, B> Hook<A, B>
 | 
				
			|||
where
 | 
				
			|||
  A: Symbol,
 | 
				
			|||
  B: Symbol,
 | 
				
			|||
{
 | 
				
			|||
  pub fn new(a: A, b: B) -> Self {
 | 
				
			|||
    Self { a, b }
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<A, B> Symbol for Hook<A, B>
 | 
				
			|||
where
 | 
				
			|||
  A: Symbol,
 | 
				
			|||
  B: Symbol,
 | 
				
			|||
{
 | 
				
			|||
  fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
 | 
				
			|||
    self.a.target_reached().and_then(|reached| {
 | 
				
			|||
      if reached {
 | 
				
			|||
        self.b.target_reached()
 | 
				
			|||
      } else {
 | 
				
			|||
        Ok(reached)
 | 
				
			|||
      }
 | 
				
			|||
    })
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn execute(&self) -> Result<(), Box<dyn Error>> {
 | 
				
			|||
    self.a.execute()?;
 | 
				
			|||
    self.b.execute()
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn get_prerequisites(&self) -> Vec<Resource> {
 | 
				
			|||
    let mut r = vec![];
 | 
				
			|||
    r.extend(self.a.get_prerequisites().into_iter());
 | 
				
			|||
    r.extend(self.b.get_prerequisites().into_iter());
 | 
				
			|||
    r
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn provides(&self) -> Option<Vec<Resource>> {
 | 
				
			|||
    let mut r = vec![];
 | 
				
			|||
    if let Some(provides) = self.a.provides() {
 | 
				
			|||
      r.extend(provides.into_iter());
 | 
				
			|||
    }
 | 
				
			|||
    if let Some(provides) = self.b.provides() {
 | 
				
			|||
      r.extend(provides.into_iter());
 | 
				
			|||
    }
 | 
				
			|||
    if r.is_empty() {
 | 
				
			|||
      None
 | 
				
			|||
    } else {
 | 
				
			|||
      Some(r)
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn as_action<'a>(&'a self, runner: &'a dyn SymbolRunner) -> Box<dyn Action + 'a> {
 | 
				
			|||
    Box::new(SymbolAction::new(runner, self))
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn into_action<'a>(self: Box<Self>, runner: &'a dyn SymbolRunner) -> Box<dyn Action + 'a>
 | 
				
			|||
  where
 | 
				
			|||
    Self: 'a,
 | 
				
			|||
  {
 | 
				
			|||
    Box::new(OwnedSymbolAction::new(runner, *self))
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<A, B> fmt::Display for Hook<A, B>
 | 
				
			|||
where
 | 
				
			|||
  A: Symbol,
 | 
				
			|||
  B: Symbol,
 | 
				
			|||
{
 | 
				
			|||
  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
 | 
				
			|||
    write!(f, "Hook {} and then {}", self.a, self.b)
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[cfg(test)]
 | 
				
			|||
mod test {
 | 
				
			|||
  use std::error::Error;
 | 
				
			|||
  use std::fmt;
 | 
				
			|||
 | 
				
			|||
  use crate::symbols::hook::Hook;
 | 
				
			|||
  use crate::symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
 | 
				
			|||
 | 
				
			|||
  struct ErrSymbol(String);
 | 
				
			|||
  impl Symbol for ErrSymbol {
 | 
				
			|||
    fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
 | 
				
			|||
      Err(self.0.clone().into())
 | 
				
			|||
    }
 | 
				
			|||
    fn execute(&self) -> Result<(), Box<dyn Error>> {
 | 
				
			|||
      Err(self.0.clone().into())
 | 
				
			|||
    }
 | 
				
			|||
    fn as_action<'a>(&'a self, runner: &'a dyn SymbolRunner) -> Box<dyn Action + 'a> {
 | 
				
			|||
      Box::new(SymbolAction::new(runner, self))
 | 
				
			|||
    }
 | 
				
			|||
 | 
				
			|||
    fn into_action<'a>(self: Box<Self>, runner: &'a dyn SymbolRunner) -> Box<dyn Action + 'a>
 | 
				
			|||
    where
 | 
				
			|||
      Self: 'a,
 | 
				
			|||
    {
 | 
				
			|||
      Box::new(OwnedSymbolAction::new(runner, *self))
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
  impl fmt::Display for ErrSymbol {
 | 
				
			|||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
 | 
				
			|||
      write!(f, "")
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  struct OkSymbol(bool);
 | 
				
			|||
  impl Symbol for OkSymbol {
 | 
				
			|||
    fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
 | 
				
			|||
      Ok(self.0)
 | 
				
			|||
    }
 | 
				
			|||
    fn execute(&self) -> Result<(), Box<dyn Error>> {
 | 
				
			|||
      Ok(())
 | 
				
			|||
    }
 | 
				
			|||
    fn as_action<'a>(&'a self, runner: &'a dyn SymbolRunner) -> Box<dyn Action + 'a> {
 | 
				
			|||
      Box::new(SymbolAction::new(runner, self))
 | 
				
			|||
    }
 | 
				
			|||
 | 
				
			|||
    fn into_action<'a>(self: Box<Self>, runner: &'a dyn SymbolRunner) -> Box<dyn Action + 'a>
 | 
				
			|||
    where
 | 
				
			|||
      Self: 'a,
 | 
				
			|||
    {
 | 
				
			|||
      Box::new(OwnedSymbolAction::new(runner, *self))
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
  impl fmt::Display for OkSymbol {
 | 
				
			|||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
 | 
				
			|||
      write!(f, "")
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  #[test]
 | 
				
			|||
  fn first_target_reached_fails() {
 | 
				
			|||
    let res = Hook::new(ErrSymbol("first".into()), ErrSymbol("second".into())).target_reached();
 | 
				
			|||
    assert_eq!(res.unwrap_err().description(), "first");
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  #[test]
 | 
				
			|||
  fn first_target_not_reached() {
 | 
				
			|||
    let res = Hook::new(OkSymbol(false), ErrSymbol("second".into())).target_reached();
 | 
				
			|||
    assert_eq!(res.unwrap(), false);
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  #[test]
 | 
				
			|||
  fn second_target_reached_fails() {
 | 
				
			|||
    let res = Hook::new(OkSymbol(true), ErrSymbol("second".into())).target_reached();
 | 
				
			|||
    assert_eq!(res.unwrap_err().description(), "second");
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  #[test]
 | 
				
			|||
  fn second_target_not_reached() {
 | 
				
			|||
    let res = Hook::new(OkSymbol(true), OkSymbol(false)).target_reached();
 | 
				
			|||
    assert_eq!(res.unwrap(), false);
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  #[test]
 | 
				
			|||
  fn everything_reached() {
 | 
				
			|||
    let res = Hook::new(OkSymbol(true), OkSymbol(true)).target_reached();
 | 
				
			|||
    assert_eq!(res.unwrap(), true);
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  #[test]
 | 
				
			|||
  fn first_execute_fails() {
 | 
				
			|||
    let res = Hook::new(ErrSymbol("first".into()), ErrSymbol("second".into())).execute();
 | 
				
			|||
    assert_eq!(res.unwrap_err().description(), "first");
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  #[test]
 | 
				
			|||
  fn second_execute_fails() {
 | 
				
			|||
    let res = Hook::new(OkSymbol(true), ErrSymbol("second".into())).execute();
 | 
				
			|||
    assert_eq!(res.unwrap_err().description(), "second");
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  #[test]
 | 
				
			|||
  fn everything_executes() {
 | 
				
			|||
    let res = Hook::new(OkSymbol(true), OkSymbol(true)).execute();
 | 
				
			|||
    assert_eq!(res.unwrap(), ());
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
@ -1,213 +0,0 @@ | 
				
			|||
use std::error::Error;
 | 
				
			|||
use std::fmt;
 | 
				
			|||
 | 
				
			|||
use crate::resources::Resource;
 | 
				
			|||
use crate::symbols::{Action, Symbol, SymbolRunner};
 | 
				
			|||
 | 
				
			|||
pub struct List<'a> {
 | 
				
			|||
  symbols: Vec<Box<dyn Symbol + 'a>>,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<'a> List<'a> {
 | 
				
			|||
  pub fn new(symbols: Vec<Box<dyn Symbol + 'a>>) -> Self {
 | 
				
			|||
    List { symbols }
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl Symbol for List<'_> {
 | 
				
			|||
  fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
 | 
				
			|||
    for symbol in &self.symbols {
 | 
				
			|||
      if !symbol.target_reached()? {
 | 
				
			|||
        return Ok(false);
 | 
				
			|||
      }
 | 
				
			|||
    }
 | 
				
			|||
    Ok(true)
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn execute(&self) -> Result<(), Box<dyn Error>> {
 | 
				
			|||
    for symbol in &self.symbols {
 | 
				
			|||
      symbol.execute()?;
 | 
				
			|||
    }
 | 
				
			|||
    Ok(())
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn get_prerequisites(&self) -> Vec<Resource> {
 | 
				
			|||
    let mut r = vec![];
 | 
				
			|||
    for symbol in &self.symbols {
 | 
				
			|||
      for req in symbol.get_prerequisites() {
 | 
				
			|||
        if self.provides().map_or(true, |p| !p.contains(&req)) {
 | 
				
			|||
          r.push(req)
 | 
				
			|||
        }
 | 
				
			|||
      }
 | 
				
			|||
    }
 | 
				
			|||
    r
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn provides(&self) -> Option<Vec<Resource>> {
 | 
				
			|||
    let mut r = vec![];
 | 
				
			|||
    for symbol in &self.symbols {
 | 
				
			|||
      if let Some(provides) = symbol.provides() {
 | 
				
			|||
        r.extend(provides.into_iter());
 | 
				
			|||
      } else {
 | 
				
			|||
        return None;
 | 
				
			|||
      }
 | 
				
			|||
    }
 | 
				
			|||
    if r.is_empty() {
 | 
				
			|||
      None
 | 
				
			|||
    } else {
 | 
				
			|||
      Some(r)
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
 | 
				
			|||
    Box::new(SymbolListAction::new(runner, &self.symbols))
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn into_action<'b>(self: Box<Self>, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b>
 | 
				
			|||
  where
 | 
				
			|||
    Self: 'b,
 | 
				
			|||
  {
 | 
				
			|||
    Box::new(ListAction::new(
 | 
				
			|||
      self
 | 
				
			|||
        .symbols
 | 
				
			|||
        .into_iter()
 | 
				
			|||
        .map(|s| s.into_action(runner))
 | 
				
			|||
        .collect(),
 | 
				
			|||
    ))
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
macro_rules! from_tuple {
 | 
				
			|||
    ( $($name:ident)* ) => (
 | 
				
			|||
        #[allow(non_snake_case)]
 | 
				
			|||
        impl<'a, $($name: 'a + Symbol,)*> From<($($name,)*)> for List<'a> {
 | 
				
			|||
            fn from(($($name,)*): ($($name,)*)) -> Self {
 | 
				
			|||
                Self::new(vec![$(Box::new($name),)*])
 | 
				
			|||
            }
 | 
				
			|||
        }
 | 
				
			|||
    );
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
for_each_tuple!(from_tuple);
 | 
				
			|||
 | 
				
			|||
struct SymbolListAction<'a> {
 | 
				
			|||
  runner: &'a dyn SymbolRunner,
 | 
				
			|||
  symbols: &'a [Box<dyn Symbol + 'a>],
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<'a> SymbolListAction<'a> {
 | 
				
			|||
  fn new(runner: &'a dyn SymbolRunner, symbols: &'a [Box<dyn Symbol + 'a>]) -> Self {
 | 
				
			|||
    Self { runner, symbols }
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl Action for SymbolListAction<'_> {
 | 
				
			|||
  fn run(&self) -> Result<(), Box<dyn Error>> {
 | 
				
			|||
    for symbol in self.symbols {
 | 
				
			|||
      symbol.as_action(self.runner).run()?;
 | 
				
			|||
    }
 | 
				
			|||
    Ok(())
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
pub struct ListAction<'a> {
 | 
				
			|||
  actions: Vec<Box<dyn Action + 'a>>,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<'a> ListAction<'a> {
 | 
				
			|||
  pub fn new(actions: Vec<Box<dyn Action + 'a>>) -> Self {
 | 
				
			|||
    Self { actions }
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl Action for ListAction<'_> {
 | 
				
			|||
  fn run(&self) -> Result<(), Box<dyn Error>> {
 | 
				
			|||
    for action in &self.actions {
 | 
				
			|||
      action.run()?;
 | 
				
			|||
    }
 | 
				
			|||
    Ok(())
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl fmt::Display for List<'_> {
 | 
				
			|||
  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
 | 
				
			|||
    write!(f, "List [ ")?;
 | 
				
			|||
    for symbol in &self.symbols {
 | 
				
			|||
      write!(f, "{} ", symbol)?;
 | 
				
			|||
    }
 | 
				
			|||
    write!(f, "]")
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
/*
 | 
				
			|||
#[cfg(test)]
 | 
				
			|||
mod test {
 | 
				
			|||
  use std::error::Error;
 | 
				
			|||
  use std::fmt;
 | 
				
			|||
 | 
				
			|||
  use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
 | 
				
			|||
  use symbols::hook::List;
 | 
				
			|||
 | 
				
			|||
  struct ErrSymbol(String);
 | 
				
			|||
  impl Symbol for ErrSymbol {
 | 
				
			|||
    fn target_reached(&self) -> Result<bool, Box<Error>> { Err(self.0.clone().into()) }
 | 
				
			|||
    fn execute(&self) -> Result<(), Box<Error>> { Err(self.0.clone().into()) }
 | 
				
			|||
  }
 | 
				
			|||
  impl fmt::Display for ErrSymbol { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>{ write!(f, "") } }
 | 
				
			|||
 | 
				
			|||
  struct OkSymbol(bool);
 | 
				
			|||
  impl Symbol for OkSymbol {
 | 
				
			|||
    fn target_reached(&self) -> Result<bool, Box<Error>> { Ok(self.0) }
 | 
				
			|||
    fn execute(&self) -> Result<(), Box<Error>> { Ok(()) }
 | 
				
			|||
  }
 | 
				
			|||
  impl fmt::Display for OkSymbol { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>{ write!(f, "") } }
 | 
				
			|||
 | 
				
			|||
  #[test]
 | 
				
			|||
  fn first_target_reached_fails() {
 | 
				
			|||
    let res = List::new(ErrSymbol("first".into()), ErrSymbol("second".into())).target_reached();
 | 
				
			|||
    assert_eq!(res.unwrap_err().description(), "first");
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  #[test]
 | 
				
			|||
  fn first_target_not_reached() {
 | 
				
			|||
    let res = List::new(OkSymbol(false), ErrSymbol("second".into())).target_reached();
 | 
				
			|||
    assert_eq!(res.unwrap(), false);
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  #[test]
 | 
				
			|||
  fn second_target_reached_fails() {
 | 
				
			|||
    let res = List::new(OkSymbol(true), ErrSymbol("second".into())).target_reached();
 | 
				
			|||
    assert_eq!(res.unwrap_err().description(), "second");
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  #[test]
 | 
				
			|||
  fn second_target_not_reached() {
 | 
				
			|||
    let res = List::new(OkSymbol(true), OkSymbol(false)).target_reached();
 | 
				
			|||
    assert_eq!(res.unwrap(), false);
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  #[test]
 | 
				
			|||
  fn everything_reached() {
 | 
				
			|||
    let res = List::new(OkSymbol(true), OkSymbol(true)).target_reached();
 | 
				
			|||
    assert_eq!(res.unwrap(), true);
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  #[test]
 | 
				
			|||
  fn first_execute_fails() {
 | 
				
			|||
    let res = List::new(ErrSymbol("first".into()), ErrSymbol("second".into())).execute();
 | 
				
			|||
    assert_eq!(res.unwrap_err().description(), "first");
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  #[test]
 | 
				
			|||
  fn second_execute_fails() {
 | 
				
			|||
    let res = List::new(OkSymbol(true), ErrSymbol("second".into())).execute();
 | 
				
			|||
    assert_eq!(res.unwrap_err().description(), "second");
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  #[test]
 | 
				
			|||
  fn everything_executes() {
 | 
				
			|||
    let res = List::new(OkSymbol(true), OkSymbol(true)).execute();
 | 
				
			|||
    assert_eq!(res.unwrap(), ());
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
*/
 | 
				
			|||
@ -1,75 +0,0 @@ | 
				
			|||
use std::error::Error;
 | 
				
			|||
use std::fmt;
 | 
				
			|||
use std::str::FromStr;
 | 
				
			|||
 | 
				
			|||
use crate::command_runner::CommandRunner;
 | 
				
			|||
use crate::storage::Storage;
 | 
				
			|||
use crate::symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
 | 
				
			|||
 | 
				
			|||
pub struct DatabaseDump<'a, N: AsRef<str>, C: CommandRunner, S: Storage> {
 | 
				
			|||
  db_name: N,
 | 
				
			|||
  storage: S,
 | 
				
			|||
  command_runner: &'a C,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<'a, N: AsRef<str>, C: CommandRunner, S: Storage> DatabaseDump<'a, N, C, S> {
 | 
				
			|||
  pub fn new(db_name: N, storage: S, command_runner: &'a C) -> Self {
 | 
				
			|||
    DatabaseDump {
 | 
				
			|||
      db_name,
 | 
				
			|||
      storage,
 | 
				
			|||
      command_runner,
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn run_sql(&self, sql: &str) -> Result<String, Box<dyn Error>> {
 | 
				
			|||
    let b = self
 | 
				
			|||
      .command_runner
 | 
				
			|||
      .get_output("mariadb", args!["--skip-column-names", "-B", "-e", sql])?;
 | 
				
			|||
    Ok(String::from_utf8(b)?)
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<N: AsRef<str>, C: CommandRunner, S: Storage> fmt::Display for DatabaseDump<'_, N, C, S> {
 | 
				
			|||
  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
				
			|||
    write!(f, "Dump MariaDB Database {}", self.db_name.as_ref())
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<N: AsRef<str>, C: CommandRunner, S: Storage> Symbol for DatabaseDump<'_, N, C, S> {
 | 
				
			|||
  fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
 | 
				
			|||
    let dump_date = self.storage.recent_date()?;
 | 
				
			|||
    let modified_date = self.run_sql(&format!("select UNIX_TIMESTAMP(MAX(UPDATE_TIME)) from information_schema.tables WHERE table_schema = '{}'", self.db_name.as_ref()))?;
 | 
				
			|||
    if modified_date.trim_end() == "NULL" {
 | 
				
			|||
      return Ok(false);
 | 
				
			|||
    }
 | 
				
			|||
    Ok(u64::from_str(modified_date.trim_end())? <= dump_date)
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn execute(&self) -> Result<(), Box<dyn Error>> {
 | 
				
			|||
    self.command_runner.run_successfully(
 | 
				
			|||
      "sh",
 | 
				
			|||
      args![
 | 
				
			|||
        "-c",
 | 
				
			|||
        format!(
 | 
				
			|||
          "mysqldump '{}' > {}",
 | 
				
			|||
          self.db_name.as_ref(),
 | 
				
			|||
          self.storage.write_filename()
 | 
				
			|||
        ),
 | 
				
			|||
      ],
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
 | 
				
			|||
    Box::new(SymbolAction::new(runner, self))
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn into_action<'b>(self: Box<Self>, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b>
 | 
				
			|||
  where
 | 
				
			|||
    Self: 'b,
 | 
				
			|||
  {
 | 
				
			|||
    Box::new(OwnedSymbolAction::new(runner, *self))
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[cfg(test)]
 | 
				
			|||
mod test {}
 | 
				
			|||
@ -0,0 +1,56 @@ | 
				
			|||
use std::error::Error;
 | 
				
			|||
use std::str::FromStr;
 | 
				
			|||
 | 
				
			|||
use crate::command_runner::CommandRunner;
 | 
				
			|||
use crate::storage::Storage;
 | 
				
			|||
use crate::symbols::Symbol;
 | 
				
			|||
 | 
				
			|||
#[derive(Debug)]
 | 
				
			|||
pub struct Dump<'a, N, C, S> {
 | 
				
			|||
  db_name: N,
 | 
				
			|||
  storage: S,
 | 
				
			|||
  command_runner: &'a C,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<'a, N, C: CommandRunner, S> Dump<'a, N, C, S> {
 | 
				
			|||
  pub fn new(db_name: N, storage: S, command_runner: &'a C) -> Self {
 | 
				
			|||
    Self {
 | 
				
			|||
      db_name,
 | 
				
			|||
      storage,
 | 
				
			|||
      command_runner,
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn run_sql(&self, sql: &str) -> Result<String, Box<dyn Error>> {
 | 
				
			|||
    let b = self
 | 
				
			|||
      .command_runner
 | 
				
			|||
      .get_output("mariadb", args!["--skip-column-names", "-B", "-e", sql])?;
 | 
				
			|||
    Ok(String::from_utf8(b)?)
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<N: AsRef<str>, C: CommandRunner, S: Storage> Symbol for Dump<'_, N, C, S> {
 | 
				
			|||
  fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
 | 
				
			|||
    let dump_date = self.storage.recent_date()?;
 | 
				
			|||
    let _modified_date = self.run_sql(&format!("select UNIX_TIMESTAMP(MAX(UPDATE_TIME)) from information_schema.tables WHERE table_schema = '{}'", self.db_name.as_ref()))?;
 | 
				
			|||
    let modified_date = _modified_date.trim_end();
 | 
				
			|||
    Ok(modified_date != "NULL" && u64::from_str(modified_date)? <= dump_date)
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn execute(&self) -> Result<(), Box<dyn Error>> {
 | 
				
			|||
    self.command_runner.run_successfully(
 | 
				
			|||
      "sh",
 | 
				
			|||
      args![
 | 
				
			|||
        "-c",
 | 
				
			|||
        format!(
 | 
				
			|||
          "mysqldump '{}' > {}",
 | 
				
			|||
          self.db_name.as_ref(),
 | 
				
			|||
          self.storage.write_filename().to_str().unwrap()
 | 
				
			|||
        ),
 | 
				
			|||
      ],
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[cfg(test)]
 | 
				
			|||
mod test {}
 | 
				
			|||
@ -1,7 +1,7 @@ | 
				
			|||
mod database;
 | 
				
			|||
mod database_dump;
 | 
				
			|||
mod dump;
 | 
				
			|||
mod user;
 | 
				
			|||
 | 
				
			|||
pub use self::database::MariaDBDatabase;
 | 
				
			|||
pub use self::database_dump::DatabaseDump;
 | 
				
			|||
pub use self::user::MariaDBUser;
 | 
				
			|||
pub use self::database::Database;
 | 
				
			|||
pub use self::dump::Dump;
 | 
				
			|||
pub use self::user::User;
 | 
				
			|||
@ -1,88 +1,22 @@ | 
				
			|||
use crate::resources::Resource;
 | 
				
			|||
use std::error::Error;
 | 
				
			|||
use std::fmt::Display;
 | 
				
			|||
 | 
				
			|||
pub trait Action {
 | 
				
			|||
  fn run(&self) -> Result<(), Box<dyn Error>>;
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
pub trait SymbolRunner {
 | 
				
			|||
  fn run_symbol(&self, symbol: &dyn Symbol) -> Result<(), Box<dyn Error>>;
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<R: SymbolRunner + ?Sized> SymbolRunner for Box<R> {
 | 
				
			|||
  fn run_symbol(&self, symbol: &dyn Symbol) -> Result<(), Box<dyn Error>> {
 | 
				
			|||
    (**self).run_symbol(symbol)
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
// Symbol
 | 
				
			|||
pub trait Symbol: Display {
 | 
				
			|||
pub trait Symbol {
 | 
				
			|||
  fn target_reached(&self) -> Result<bool, Box<dyn Error>>;
 | 
				
			|||
  fn execute(&self) -> Result<(), Box<dyn Error>>;
 | 
				
			|||
  fn get_prerequisites(&self) -> Vec<Resource> {
 | 
				
			|||
    vec![]
 | 
				
			|||
  }
 | 
				
			|||
  fn provides(&self) -> Option<Vec<Resource>> {
 | 
				
			|||
    None
 | 
				
			|||
  }
 | 
				
			|||
  fn as_action<'a>(&'a self, runner: &'a dyn SymbolRunner) -> Box<dyn Action + 'a>;
 | 
				
			|||
  fn into_action<'a>(self: Box<Self>, runner: &'a dyn SymbolRunner) -> Box<dyn Action + 'a>
 | 
				
			|||
  where
 | 
				
			|||
    Self: 'a;
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
// SymbolAction
 | 
				
			|||
pub struct SymbolAction<'a, S: Symbol> {
 | 
				
			|||
  runner: &'a dyn SymbolRunner,
 | 
				
			|||
  symbol: &'a S,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<'a, S: Symbol> SymbolAction<'a, S> {
 | 
				
			|||
  pub fn new(runner: &'a dyn SymbolRunner, symbol: &'a S) -> Self {
 | 
				
			|||
    Self { runner, symbol }
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<S: Symbol> Action for SymbolAction<'_, S> {
 | 
				
			|||
  fn run(&self) -> Result<(), Box<dyn Error>> {
 | 
				
			|||
    self.runner.run_symbol(self.symbol)
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
pub struct OwnedSymbolAction<'a, S: Symbol + 'a> {
 | 
				
			|||
  runner: &'a dyn SymbolRunner,
 | 
				
			|||
  symbol: S,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<'a, S: Symbol + 'a> OwnedSymbolAction<'a, S> {
 | 
				
			|||
  pub fn new(runner: &'a dyn SymbolRunner, symbol: S) -> Self {
 | 
				
			|||
    Self { runner, symbol }
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<'a, S: Symbol + 'a> Action for OwnedSymbolAction<'a, S> {
 | 
				
			|||
  fn run(&self) -> Result<(), Box<dyn Error>> {
 | 
				
			|||
    self.runner.run_symbol(&self.symbol)
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
pub mod acme;
 | 
				
			|||
pub mod concat;
 | 
				
			|||
pub mod cron;
 | 
				
			|||
pub mod dir;
 | 
				
			|||
pub mod factory;
 | 
				
			|||
pub mod file;
 | 
				
			|||
pub mod git;
 | 
				
			|||
pub mod hook;
 | 
				
			|||
pub mod list;
 | 
				
			|||
pub mod mariadb;
 | 
				
			|||
pub mod nginx;
 | 
				
			|||
pub mod noop;
 | 
				
			|||
pub mod npm;
 | 
				
			|||
pub mod owner;
 | 
				
			|||
pub mod postgresql;
 | 
				
			|||
pub mod stored_directory;
 | 
				
			|||
pub mod saved_directory;
 | 
				
			|||
pub mod systemd;
 | 
				
			|||
pub mod tls;
 | 
				
			|||
pub mod user;
 | 
				
			|||
 | 
				
			|||
@ -1 +0,0 @@ | 
				
			|||
pub mod server;
 | 
				
			|||
@ -1,246 +0,0 @@ | 
				
			|||
use std::error::Error;
 | 
				
			|||
use std::fmt;
 | 
				
			|||
use std::io;
 | 
				
			|||
use std::path::{Path, PathBuf};
 | 
				
			|||
 | 
				
			|||
use crate::command_runner::CommandRunner;
 | 
				
			|||
use crate::resources::Resource;
 | 
				
			|||
use crate::symbols::file::File as FileSymbol;
 | 
				
			|||
use crate::symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
 | 
				
			|||
 | 
				
			|||
#[derive(Debug)]
 | 
				
			|||
pub enum NginxServerError<E: Error> {
 | 
				
			|||
  ExecError(E),
 | 
				
			|||
  GenericError,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl From<io::Error> for NginxServerError<io::Error> {
 | 
				
			|||
  fn from(err: io::Error) -> Self {
 | 
				
			|||
    Self::ExecError(err)
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<E: Error> Error for NginxServerError<E> {
 | 
				
			|||
  fn description(&self) -> &str {
 | 
				
			|||
    match self {
 | 
				
			|||
      Self::ExecError(ref e) => e.description(),
 | 
				
			|||
      Self::GenericError => "Generic error",
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
  fn cause(&self) -> Option<&dyn Error> {
 | 
				
			|||
    match self {
 | 
				
			|||
      Self::ExecError(ref e) => Some(e),
 | 
				
			|||
      _ => None,
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<E: Error> fmt::Display for NginxServerError<E> {
 | 
				
			|||
  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
 | 
				
			|||
    write!(f, "{}", self.description())
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
pub struct NginxServer<'a, C: CommandRunner, T: AsRef<str>, P: AsRef<Path>> {
 | 
				
			|||
  command_runner: &'a C,
 | 
				
			|||
  file: FileSymbol<T, P>,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
pub fn server_config(domain: &str, content: &str) -> String {
 | 
				
			|||
  format!(
 | 
				
			|||
    "server {{
 | 
				
			|||
    listen                  443 ssl http2;
 | 
				
			|||
    server_name             {0};
 | 
				
			|||
    include                 \"snippets/acme-challenge.conf\";
 | 
				
			|||
 | 
				
			|||
    ssl_certificate         /etc/ssl/local_certs/{0}.chained.crt;
 | 
				
			|||
    ssl_certificate_key     /etc/ssl/private/{0}.key;
 | 
				
			|||
    add_header              Strict-Transport-Security \"max-age=31536000\";
 | 
				
			|||
 | 
				
			|||
    {1}
 | 
				
			|||
}}
 | 
				
			|||
 | 
				
			|||
# Redirect all HTTP links to the matching HTTPS page
 | 
				
			|||
server {{
 | 
				
			|||
    listen                  80;
 | 
				
			|||
    server_name             {0};
 | 
				
			|||
    include                 \"snippets/acme-challenge.conf\";
 | 
				
			|||
 | 
				
			|||
    location / {{
 | 
				
			|||
      return 301 https://$host$request_uri;
 | 
				
			|||
    }}
 | 
				
			|||
}}
 | 
				
			|||
",
 | 
				
			|||
    domain, content
 | 
				
			|||
  )
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
pub fn php_server_config_snippet<SOCKET: AsRef<Path>, STATIC: AsRef<Path>>(
 | 
				
			|||
  socket_path: SOCKET,
 | 
				
			|||
  static_path: STATIC,
 | 
				
			|||
) -> String {
 | 
				
			|||
  format!(
 | 
				
			|||
    "
 | 
				
			|||
    root                {};
 | 
				
			|||
    index               index.html index.php;
 | 
				
			|||
    location ~ [^/]\\.php(/|$) {{
 | 
				
			|||
      fastcgi_pass      unix:{};
 | 
				
			|||
      include           \"snippets/fastcgi-php.conf\";
 | 
				
			|||
    }}",
 | 
				
			|||
    static_path.as_ref().to_str().unwrap(),
 | 
				
			|||
    socket_path.as_ref().to_str().unwrap()
 | 
				
			|||
  )
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
pub trait SocketSpec {
 | 
				
			|||
  fn to_nginx(&self) -> String;
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<T> SocketSpec for T
 | 
				
			|||
where
 | 
				
			|||
  T: AsRef<Path>,
 | 
				
			|||
{
 | 
				
			|||
  fn to_nginx(&self) -> String {
 | 
				
			|||
    format!("unix:{}:", self.as_ref().to_str().unwrap())
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
pub struct LocalTcpSocket(usize);
 | 
				
			|||
 | 
				
			|||
impl LocalTcpSocket {
 | 
				
			|||
  pub const fn new(x: usize) -> Self {
 | 
				
			|||
    Self(x)
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl SocketSpec for LocalTcpSocket {
 | 
				
			|||
  fn to_nginx(&self) -> String {
 | 
				
			|||
    format!("localhost:{}", self.0)
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<'a, C: CommandRunner> NginxServer<'a, C, String, PathBuf> {
 | 
				
			|||
  pub fn new_redir(domain: &'a str, target: &'a str, command_runner: &'a C) -> Self {
 | 
				
			|||
    let content = server_config(
 | 
				
			|||
      domain,
 | 
				
			|||
      &format!(
 | 
				
			|||
        "location / {{
 | 
				
			|||
  return 301 $scheme://{}$request_uri;
 | 
				
			|||
}}",
 | 
				
			|||
        target
 | 
				
			|||
      ),
 | 
				
			|||
    );
 | 
				
			|||
    Self::new(domain, content, command_runner)
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn new_proxy<S: SocketSpec, STATIC: AsRef<Path>>(
 | 
				
			|||
    domain: &'a str,
 | 
				
			|||
    socket_path: &S,
 | 
				
			|||
    static_path: STATIC,
 | 
				
			|||
    command_runner: &'a C,
 | 
				
			|||
  ) -> Self {
 | 
				
			|||
    let proxy_content = format!(
 | 
				
			|||
      "location / {{
 | 
				
			|||
  try_files             $uri @proxy;
 | 
				
			|||
}}
 | 
				
			|||
 | 
				
			|||
location @proxy {{
 | 
				
			|||
  include               fastcgi_params;
 | 
				
			|||
  proxy_pass            http://{};
 | 
				
			|||
  proxy_redirect        off;
 | 
				
			|||
}}",
 | 
				
			|||
      socket_path.to_nginx()
 | 
				
			|||
    );
 | 
				
			|||
 | 
				
			|||
    let content = server_config(
 | 
				
			|||
      domain,
 | 
				
			|||
      &format!(
 | 
				
			|||
        "
 | 
				
			|||
    root                    {};
 | 
				
			|||
    {}
 | 
				
			|||
",
 | 
				
			|||
        static_path.as_ref().to_str().unwrap(),
 | 
				
			|||
        proxy_content
 | 
				
			|||
      ),
 | 
				
			|||
    );
 | 
				
			|||
    Self::new(domain, content, command_runner)
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn new_php<SOCKET: AsRef<Path>, STATIC: AsRef<Path>>(
 | 
				
			|||
    domain: &'a str,
 | 
				
			|||
    socket_path: SOCKET,
 | 
				
			|||
    static_path: STATIC,
 | 
				
			|||
    command_runner: &'a C,
 | 
				
			|||
    additional_config: &'a str,
 | 
				
			|||
  ) -> Self {
 | 
				
			|||
    let content = server_config(
 | 
				
			|||
      domain,
 | 
				
			|||
      &(php_server_config_snippet(socket_path, static_path) + additional_config),
 | 
				
			|||
    );
 | 
				
			|||
    Self::new(domain, content, command_runner)
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn new_static<S: AsRef<Path>>(
 | 
				
			|||
    domain: &'a str,
 | 
				
			|||
    static_path: S,
 | 
				
			|||
    command_runner: &'a C,
 | 
				
			|||
  ) -> Self {
 | 
				
			|||
    let content = server_config(
 | 
				
			|||
      domain,
 | 
				
			|||
      &format!(
 | 
				
			|||
        "
 | 
				
			|||
    root                    {};
 | 
				
			|||
    try_files $uri $uri/ $uri.html =404;
 | 
				
			|||
",
 | 
				
			|||
        static_path.as_ref().to_str().unwrap()
 | 
				
			|||
      ),
 | 
				
			|||
    );
 | 
				
			|||
    Self::new(domain, content, command_runner)
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  pub fn new(domain: &'a str, content: String, command_runner: &'a C) -> Self {
 | 
				
			|||
    let file_path: PathBuf = ["/etc/nginx/sites-enabled/", domain].iter().collect();
 | 
				
			|||
    NginxServer {
 | 
				
			|||
      command_runner,
 | 
				
			|||
      file: FileSymbol::new(file_path, content),
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<C: CommandRunner, T: AsRef<str>, P: AsRef<Path>> Symbol for NginxServer<'_, C, T, P> {
 | 
				
			|||
  fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
 | 
				
			|||
    if !self.file.target_reached()? {
 | 
				
			|||
      return Ok(false);
 | 
				
			|||
    }
 | 
				
			|||
    // TODO: Could try to find out if the server is in the live config
 | 
				
			|||
    Ok(true)
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn execute(&self) -> Result<(), Box<dyn Error>> {
 | 
				
			|||
    self.file.execute()?;
 | 
				
			|||
    self
 | 
				
			|||
      .command_runner
 | 
				
			|||
      .run_successfully("systemctl", args!["reload-or-restart", "nginx"])
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn get_prerequisites(&self) -> Vec<Resource> {
 | 
				
			|||
    self.file.get_prerequisites()
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
 | 
				
			|||
    Box::new(SymbolAction::new(runner, self))
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn into_action<'b>(self: Box<Self>, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b>
 | 
				
			|||
  where
 | 
				
			|||
    Self: 'b,
 | 
				
			|||
  {
 | 
				
			|||
    Box::new(OwnedSymbolAction::new(runner, *self))
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<C: CommandRunner, T: AsRef<str>, P: AsRef<Path>> fmt::Display for NginxServer<'_, C, T, P> {
 | 
				
			|||
  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
 | 
				
			|||
    write!(f, "Nginx server config")
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
@ -1,31 +0,0 @@ | 
				
			|||
use std::error::Error;
 | 
				
			|||
use std::fmt::{self, Display};
 | 
				
			|||
 | 
				
			|||
use crate::symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
 | 
				
			|||
 | 
				
			|||
pub struct NoopSymbol;
 | 
				
			|||
 | 
				
			|||
impl Symbol for NoopSymbol {
 | 
				
			|||
  fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
 | 
				
			|||
    Ok(true)
 | 
				
			|||
  }
 | 
				
			|||
  fn execute(&self) -> Result<(), Box<dyn Error>> {
 | 
				
			|||
    Ok(())
 | 
				
			|||
  }
 | 
				
			|||
  fn as_action<'a>(&'a self, runner: &'a dyn SymbolRunner) -> Box<dyn Action + 'a> {
 | 
				
			|||
    Box::new(SymbolAction::new(runner, self))
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn into_action<'a>(self: Box<Self>, runner: &'a dyn SymbolRunner) -> Box<dyn Action + 'a>
 | 
				
			|||
  where
 | 
				
			|||
    Self: 'a,
 | 
				
			|||
  {
 | 
				
			|||
    Box::new(OwnedSymbolAction::new(runner, *self))
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl Display for NoopSymbol {
 | 
				
			|||
  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
 | 
				
			|||
    write!(f, "Noop")
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
@ -1,3 +1,7 @@ | 
				
			|||
pub mod reload;
 | 
				
			|||
pub mod user_service;
 | 
				
			|||
pub mod user_session;
 | 
				
			|||
mod reload;
 | 
				
			|||
mod user_service;
 | 
				
			|||
mod user_session;
 | 
				
			|||
 | 
				
			|||
pub use reload::ReloadService;
 | 
				
			|||
pub use user_service::UserService;
 | 
				
			|||
pub use user_session::UserSession;
 | 
				
			|||
@ -1,49 +1,35 @@ | 
				
			|||
use std::error::Error;
 | 
				
			|||
use std::fmt;
 | 
				
			|||
 | 
				
			|||
use crate::command_runner::CommandRunner;
 | 
				
			|||
use crate::symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
 | 
				
			|||
use crate::symbols::Symbol;
 | 
				
			|||
use std::borrow::Borrow;
 | 
				
			|||
use std::error::Error;
 | 
				
			|||
use std::marker::PhantomData;
 | 
				
			|||
 | 
				
			|||
pub struct ReloadService<'a, S, C: CommandRunner> {
 | 
				
			|||
#[derive(Debug)]
 | 
				
			|||
pub struct ReloadService<_C, C, S> {
 | 
				
			|||
  service: S,
 | 
				
			|||
  command_runner: &'a C,
 | 
				
			|||
  command_runner: C,
 | 
				
			|||
  phantom: PhantomData<_C>,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<'a, S, C: CommandRunner> ReloadService<'a, S, C> {
 | 
				
			|||
  pub fn new(service: S, command_runner: &'a C) -> Self {
 | 
				
			|||
    ReloadService {
 | 
				
			|||
impl<_C, C, S> ReloadService<_C, C, S> {
 | 
				
			|||
  pub fn new(command_runner: C, service: S) -> Self {
 | 
				
			|||
    Self {
 | 
				
			|||
      service,
 | 
				
			|||
      command_runner,
 | 
				
			|||
      phantom: PhantomData::default(),
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<S: AsRef<str>, C: CommandRunner> Symbol for ReloadService<'_, S, C> {
 | 
				
			|||
impl<S: AsRef<str>, _C: CommandRunner, C: Borrow<_C>> Symbol for ReloadService<_C, C, S> {
 | 
				
			|||
  fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
 | 
				
			|||
    Ok(true)
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn execute(&self) -> Result<(), Box<dyn Error>> {
 | 
				
			|||
    self.command_runner.run_successfully(
 | 
				
			|||
    self.command_runner.borrow().run_successfully(
 | 
				
			|||
      "systemctl",
 | 
				
			|||
      args!["reload-or-restart", self.service.as_ref()],
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
 | 
				
			|||
    Box::new(SymbolAction::new(runner, self))
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn into_action<'b>(self: Box<Self>, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b>
 | 
				
			|||
  where
 | 
				
			|||
    Self: 'b,
 | 
				
			|||
  {
 | 
				
			|||
    Box::new(OwnedSymbolAction::new(runner, *self))
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<S: AsRef<str>, C: CommandRunner> fmt::Display for ReloadService<'_, S, C> {
 | 
				
			|||
  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
 | 
				
			|||
    write!(f, "Reload service {}", self.service.as_ref())
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
@ -1,7 +1,5 @@ | 
				
			|||
mod csr;
 | 
				
			|||
mod key;
 | 
				
			|||
mod self_signed_cert;
 | 
				
			|||
 | 
				
			|||
pub use self::csr::TlsCsr;
 | 
				
			|||
pub use self::key::TlsKey;
 | 
				
			|||
pub use self::self_signed_cert::SelfSignedTlsCert;
 | 
				
			|||
pub use self::csr::Csr;
 | 
				
			|||
pub use self::key::Key;
 | 
				
			|||
@ -1,119 +0,0 @@ | 
				
			|||
use std::error::Error;
 | 
				
			|||
use std::fmt;
 | 
				
			|||
use std::path::PathBuf;
 | 
				
			|||
 | 
				
			|||
use crate::command_runner::CommandRunner;
 | 
				
			|||
use crate::resources::Resource;
 | 
				
			|||
use crate::symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
 | 
				
			|||
 | 
				
			|||
pub struct SelfSignedTlsCert<'a, D: AsRef<str>, C: CommandRunner> {
 | 
				
			|||
  domain: D,
 | 
				
			|||
  command_runner: &'a C,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<'a, D: AsRef<str>, C: CommandRunner> SelfSignedTlsCert<'a, D, C> {
 | 
				
			|||
  pub fn new(domain: D, command_runner: &'a C) -> Self {
 | 
				
			|||
    SelfSignedTlsCert {
 | 
				
			|||
      domain,
 | 
				
			|||
      command_runner,
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn get_key_path(&self) -> PathBuf {
 | 
				
			|||
    format!("/etc/ssl/private/{}.key", self.domain.as_ref()).into()
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn get_cert_path(&self) -> PathBuf {
 | 
				
			|||
    format!("/etc/ssl/local_certs/{}.chained.crt", self.domain.as_ref()).into()
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<D: AsRef<str>, C: CommandRunner> fmt::Display for SelfSignedTlsCert<'_, D, C> {
 | 
				
			|||
  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
				
			|||
    write!(f, "SelfSignedTlsCert {}", self.domain.as_ref())
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
const DAYS_IN_SECONDS: u32 = 24 * 60 * 60;
 | 
				
			|||
 | 
				
			|||
impl<D: AsRef<str>, C: CommandRunner> Symbol for SelfSignedTlsCert<'_, D, C> {
 | 
				
			|||
  fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
 | 
				
			|||
    if !self.get_cert_path().exists() {
 | 
				
			|||
      return Ok(false);
 | 
				
			|||
    }
 | 
				
			|||
    let output = self.command_runner.run_with_args(
 | 
				
			|||
      "openssl",
 | 
				
			|||
      args![
 | 
				
			|||
        "x509",
 | 
				
			|||
        "-in",
 | 
				
			|||
        self.get_cert_path(),
 | 
				
			|||
        "-noout",
 | 
				
			|||
        "-subject",
 | 
				
			|||
        "-checkend",
 | 
				
			|||
        (30 * DAYS_IN_SECONDS).to_string(),
 | 
				
			|||
      ],
 | 
				
			|||
    )?;
 | 
				
			|||
    println!("{}", output.status.code().unwrap());
 | 
				
			|||
    match output.status.code() {
 | 
				
			|||
      Some(0) => Ok(
 | 
				
			|||
        output.stdout
 | 
				
			|||
          == format!(
 | 
				
			|||
            "subject=CN = {}\nCertificate will not expire\n",
 | 
				
			|||
            self.domain.as_ref()
 | 
				
			|||
          )
 | 
				
			|||
          .as_bytes(),
 | 
				
			|||
      ),
 | 
				
			|||
      Some(_) => {
 | 
				
			|||
        if output.stdout
 | 
				
			|||
          == format!(
 | 
				
			|||
            "subject=CN = {}\nCertificate will expire\n",
 | 
				
			|||
            self.domain.as_ref()
 | 
				
			|||
          )
 | 
				
			|||
          .as_bytes()
 | 
				
			|||
        {
 | 
				
			|||
          Ok(false)
 | 
				
			|||
        } else {
 | 
				
			|||
          Err("Exit code non-zero, but wrong stdout".to_string().into())
 | 
				
			|||
        }
 | 
				
			|||
      }
 | 
				
			|||
      _ => Err("Apparently killed by signal".to_string().into()),
 | 
				
			|||
    }
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn execute(&self) -> Result<(), Box<dyn Error>> {
 | 
				
			|||
    self.command_runner.run_successfully(
 | 
				
			|||
      "openssl",
 | 
				
			|||
      args![
 | 
				
			|||
        "req",
 | 
				
			|||
        "-x509",
 | 
				
			|||
        "-sha256",
 | 
				
			|||
        "-days",
 | 
				
			|||
        "90",
 | 
				
			|||
        "-key",
 | 
				
			|||
        self.get_key_path(),
 | 
				
			|||
        "-out",
 | 
				
			|||
        self.get_cert_path(),
 | 
				
			|||
        "-subj",
 | 
				
			|||
        format!("/CN={}", self.domain.as_ref()),
 | 
				
			|||
      ],
 | 
				
			|||
    )
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn get_prerequisites(&self) -> Vec<Resource> {
 | 
				
			|||
    vec![Resource::new("file", self.get_key_path().to_str().unwrap())]
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
 | 
				
			|||
    Box::new(SymbolAction::new(runner, self))
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  fn into_action<'b>(self: Box<Self>, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b>
 | 
				
			|||
  where
 | 
				
			|||
    Self: 'b,
 | 
				
			|||
  {
 | 
				
			|||
    Box::new(OwnedSymbolAction::new(runner, *self))
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[cfg(test)]
 | 
				
			|||
mod test {}
 | 
				
			|||
@ -1,2 +1,5 @@ | 
				
			|||
pub mod plugin;
 | 
				
			|||
pub mod translation;
 | 
				
			|||
mod plugin;
 | 
				
			|||
mod translation;
 | 
				
			|||
 | 
				
			|||
pub use plugin::Plugin;
 | 
				
			|||
pub use translation::Translation;
 | 
				
			|||
@ -0,0 +1,3 @@ | 
				
			|||
pub mod nginx;
 | 
				
			|||
pub mod php;
 | 
				
			|||
pub mod systemd;
 | 
				
			|||
@ -0,0 +1,13 @@ | 
				
			|||
mod server;
 | 
				
			|||
pub use server::*;
 | 
				
			|||
 | 
				
			|||
use std::path::Path;
 | 
				
			|||
pub fn acme_challenges_snippet<P: AsRef<Path>>(path: P) -> String {
 | 
				
			|||
  format!(
 | 
				
			|||
    "location ^~ /.well-known/acme-challenge/ {{
 | 
				
			|||
  alias {}/;
 | 
				
			|||
  try_files $uri =404;
 | 
				
			|||
}}",
 | 
				
			|||
    path.as_ref().to_str().unwrap()
 | 
				
			|||
  )
 | 
				
			|||
}
 | 
				
			|||
@ -0,0 +1,202 @@ | 
				
			|||
use std::fmt::Display;
 | 
				
			|||
use std::path::Path;
 | 
				
			|||
 | 
				
			|||
pub fn default_server<P: AsRef<Path>>(challenges_snippet_path: P) -> String {
 | 
				
			|||
  format!(
 | 
				
			|||
    "server {{
 | 
				
			|||
      listen                  80 default_server;
 | 
				
			|||
      listen                  [::]:80 default_server;
 | 
				
			|||
      include                 \"{}\";
 | 
				
			|||
  }}",
 | 
				
			|||
    challenges_snippet_path.as_ref().to_str().unwrap()
 | 
				
			|||
  )
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
pub fn server_config<D: Display, C: AsRef<Path>, K: AsRef<Path>, T: Display, S: AsRef<Path>>(
 | 
				
			|||
  domain: D,
 | 
				
			|||
  cert_path: C,
 | 
				
			|||
  key_path: K,
 | 
				
			|||
  content: T,
 | 
				
			|||
  challenges_snippet_path: S,
 | 
				
			|||
) -> String {
 | 
				
			|||
  format!(
 | 
				
			|||
    "server {{
 | 
				
			|||
    listen                  443 ssl http2;
 | 
				
			|||
    listen                  [::]:443 ssl http2;
 | 
				
			|||
    server_name             {};
 | 
				
			|||
    include                 \"{}\";
 | 
				
			|||
 | 
				
			|||
    ssl_certificate         {};
 | 
				
			|||
    ssl_certificate_key     {};
 | 
				
			|||
    add_header              Strict-Transport-Security \"max-age=31536000\";
 | 
				
			|||
 | 
				
			|||
    {}
 | 
				
			|||
}}
 | 
				
			|||
 | 
				
			|||
# Redirect all HTTP links to the matching HTTPS page
 | 
				
			|||
server {{
 | 
				
			|||
    listen                  80;
 | 
				
			|||
    listen                  [::]:80;
 | 
				
			|||
    server_name             {0};
 | 
				
			|||
    include                 \"{1}\";
 | 
				
			|||
 | 
				
			|||
    location / {{
 | 
				
			|||
      return 301 https://$host$request_uri;
 | 
				
			|||
    }}
 | 
				
			|||
}}
 | 
				
			|||
",
 | 
				
			|||
    domain,
 | 
				
			|||
    challenges_snippet_path.as_ref().to_str().unwrap(),
 | 
				
			|||
    cert_path.as_ref().to_str().unwrap(),
 | 
				
			|||
    key_path.as_ref().to_str().unwrap(),
 | 
				
			|||
    content
 | 
				
			|||
  )
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
pub fn php_snippet<SOCKET: AsRef<Path>, STATIC: AsRef<Path>>(
 | 
				
			|||
  index: &'static str,
 | 
				
			|||
  socket_path: SOCKET,
 | 
				
			|||
  static_path: STATIC,
 | 
				
			|||
) -> String {
 | 
				
			|||
  format!(
 | 
				
			|||
    "root                {};
 | 
				
			|||
    index               {};
 | 
				
			|||
    location ~ [^/]\\.php(/|$) {{
 | 
				
			|||
      fastcgi_pass      unix:{};
 | 
				
			|||
      include           \"snippets/fastcgi-php.conf\";
 | 
				
			|||
    }}",
 | 
				
			|||
    static_path.as_ref().to_str().unwrap(),
 | 
				
			|||
    index,
 | 
				
			|||
    socket_path.as_ref().to_str().unwrap()
 | 
				
			|||
  )
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
pub fn redir_snippet(target: &str) -> String {
 | 
				
			|||
  format!(
 | 
				
			|||
    "location / {{
 | 
				
			|||
      return 301 $scheme://{}$request_uri;
 | 
				
			|||
    }}",
 | 
				
			|||
    target
 | 
				
			|||
  )
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
pub trait SocketSpec {
 | 
				
			|||
  fn to_nginx(&self) -> String;
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<T: AsRef<Path>> SocketSpec for T {
 | 
				
			|||
  fn to_nginx(&self) -> String {
 | 
				
			|||
    format!("unix:{}:", self.as_ref().to_str().unwrap())
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
pub struct LocalTcpSocket(usize);
 | 
				
			|||
 | 
				
			|||
impl LocalTcpSocket {
 | 
				
			|||
  pub const fn new(x: usize) -> Self {
 | 
				
			|||
    Self(x)
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl SocketSpec for LocalTcpSocket {
 | 
				
			|||
  fn to_nginx(&self) -> String {
 | 
				
			|||
    format!("localhost:{}", self.0)
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
pub fn proxy_snippet<S: SocketSpec, STATIC: AsRef<Path>>(
 | 
				
			|||
  socket_path: &S,
 | 
				
			|||
  static_path: STATIC,
 | 
				
			|||
) -> String {
 | 
				
			|||
  format!(
 | 
				
			|||
    "root                    {};
 | 
				
			|||
    location / {{
 | 
				
			|||
      try_files             $uri @proxy;
 | 
				
			|||
    }}
 | 
				
			|||
 | 
				
			|||
    location @proxy {{
 | 
				
			|||
      include               fastcgi_params;
 | 
				
			|||
      proxy_pass            http://{};
 | 
				
			|||
      proxy_redirect        off;
 | 
				
			|||
    }}",
 | 
				
			|||
    static_path.as_ref().to_str().unwrap(),
 | 
				
			|||
    socket_path.to_nginx()
 | 
				
			|||
  )
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
pub fn static_snippet<S: AsRef<Path>>(static_path: S) -> String {
 | 
				
			|||
  format!(
 | 
				
			|||
    "root                    {};
 | 
				
			|||
      try_files $uri $uri/ $uri.html =404;
 | 
				
			|||
    ",
 | 
				
			|||
    static_path.as_ref().to_str().unwrap()
 | 
				
			|||
  )
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
pub fn dokuwiki_snippet() -> String {
 | 
				
			|||
  "
 | 
				
			|||
  location ~ /(data/|conf/|bin/|inc/|install.php) { deny all; }
 | 
				
			|||
 | 
				
			|||
  location / { try_files $uri $uri/ @dokuwiki; }
 | 
				
			|||
 | 
				
			|||
  location @dokuwiki {
 | 
				
			|||
      # rewrites \"doku.php/\" out of the URLs if you set the userewrite setting to .htaccess in dokuwiki config page
 | 
				
			|||
      rewrite ^/_media/(.*) /lib/exe/fetch.php?media=$1 last;
 | 
				
			|||
      rewrite ^/_detail/(.*) /lib/exe/detail.php?media=$1 last;
 | 
				
			|||
      rewrite ^/_export/([^/]+)/(.*) /doku.php?do=export_$1&id=$2 last;
 | 
				
			|||
      rewrite ^/(.*) /doku.php?id=$1&$args last;
 | 
				
			|||
  }".into()
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
pub fn nextcloud_snippet() -> String {
 | 
				
			|||
  "
 | 
				
			|||
  client_max_body_size 500M;
 | 
				
			|||
 | 
				
			|||
  # Disable gzip to avoid the removal of the ETag header
 | 
				
			|||
  gzip off;
 | 
				
			|||
 | 
				
			|||
  rewrite ^/caldav(.*)$ /remote.php/caldav$1 redirect;
 | 
				
			|||
  rewrite ^/carddav(.*)$ /remote.php/carddav$1 redirect;
 | 
				
			|||
  rewrite ^/webdav(.*)$ /remote.php/webdav$1 redirect;
 | 
				
			|||
 | 
				
			|||
  error_page 403 /core/templates/403.php;
 | 
				
			|||
  error_page 404 /core/templates/404.php;
 | 
				
			|||
 | 
				
			|||
  location = /robots.txt {
 | 
				
			|||
    allow all;
 | 
				
			|||
    log_not_found off;
 | 
				
			|||
    access_log off;
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  location ~ ^/(?:\\.htaccess|data|config|db_structure\\.xml|README) {
 | 
				
			|||
    deny all;
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  location / {
 | 
				
			|||
    # The following 2 rules are only needed with webfinger
 | 
				
			|||
    rewrite ^/.well-known/host-meta /public.php?service=host-meta last;
 | 
				
			|||
    rewrite ^/.well-known/host-meta.json /public.php?service=host-meta-json last;
 | 
				
			|||
 | 
				
			|||
    rewrite ^/.well-known/carddav /remote.php/carddav/ redirect;
 | 
				
			|||
    rewrite ^/.well-known/caldav /remote.php/caldav/ redirect;
 | 
				
			|||
 | 
				
			|||
    rewrite ^(/core/doc/[^\\/]+/)$ $1/index.html;
 | 
				
			|||
 | 
				
			|||
    try_files $uri $uri/ /index.php;
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  # Adding the cache control header for js and css files
 | 
				
			|||
  # Make sure it is BELOW the location ~ \\.php(?:$|/) { block
 | 
				
			|||
  location ~* \\.(?:css|js)$ {
 | 
				
			|||
    add_header Cache-Control \"public, max-age=7200\";
 | 
				
			|||
    # Optional: Don't log access to assets
 | 
				
			|||
    access_log off;
 | 
				
			|||
  }
 | 
				
			|||
 | 
				
			|||
  # Optional: Don't log access to other assets
 | 
				
			|||
  location ~* \\.(?:jpg|jpeg|gif|bmp|ico|png|swf)$ {
 | 
				
			|||
    access_log off;
 | 
				
			|||
  }
 | 
				
			|||
  "
 | 
				
			|||
  .into()
 | 
				
			|||
}
 | 
				
			|||
@ -0,0 +1,24 @@ | 
				
			|||
use std::path::Path;
 | 
				
			|||
 | 
				
			|||
pub fn fpm_pool_config<U: AsRef<str>, S: AsRef<Path>>(
 | 
				
			|||
  user_name: U,
 | 
				
			|||
  socket_path: S,
 | 
				
			|||
  max_children: usize,
 | 
				
			|||
) -> String {
 | 
				
			|||
  format!(
 | 
				
			|||
    "[{0}]
 | 
				
			|||
 | 
				
			|||
user = {0}
 | 
				
			|||
group = www-data
 | 
				
			|||
listen = {1}
 | 
				
			|||
listen.owner = www-data
 | 
				
			|||
pm = ondemand
 | 
				
			|||
pm.max_children = {2}
 | 
				
			|||
catch_workers_output = yes
 | 
				
			|||
env[PATH] = /usr/local/bin:/usr/bin:/bin
 | 
				
			|||
",
 | 
				
			|||
    user_name.as_ref(),
 | 
				
			|||
    socket_path.as_ref().to_str().unwrap(),
 | 
				
			|||
    max_children,
 | 
				
			|||
  )
 | 
				
			|||
}
 | 
				
			|||
@ -0,0 +1,45 @@ | 
				
			|||
use std::path::Path;
 | 
				
			|||
 | 
				
			|||
pub fn socket_service(
 | 
				
			|||
  socket_path: impl AsRef<Path>,
 | 
				
			|||
  exec: &str,
 | 
				
			|||
  dir: impl AsRef<Path>,
 | 
				
			|||
  additional: &str,
 | 
				
			|||
) -> String {
 | 
				
			|||
  format!(
 | 
				
			|||
    "[Service]
 | 
				
			|||
ExecStartPre=/bin/rm -f {}
 | 
				
			|||
ExecStart={}
 | 
				
			|||
WorkingDirectory={}
 | 
				
			|||
Restart=always
 | 
				
			|||
{}
 | 
				
			|||
 | 
				
			|||
[Install]
 | 
				
			|||
WantedBy=default.target
 | 
				
			|||
  ",
 | 
				
			|||
    socket_path.as_ref().to_str().unwrap(),
 | 
				
			|||
    exec,
 | 
				
			|||
    dir.as_ref().to_str().unwrap(),
 | 
				
			|||
    additional
 | 
				
			|||
  )
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
pub fn nodejs_service<N: AsRef<Path>, S: AsRef<Path>>(
 | 
				
			|||
  nodejs_path: N,
 | 
				
			|||
  socket_path: S,
 | 
				
			|||
  dir: impl AsRef<Path>,
 | 
				
			|||
) -> String {
 | 
				
			|||
  socket_service(
 | 
				
			|||
    &socket_path,
 | 
				
			|||
    &format!("/usr/bin/nodejs {}", nodejs_path.as_ref().to_str().unwrap()),
 | 
				
			|||
    dir,
 | 
				
			|||
    &format!(
 | 
				
			|||
      "Environment=NODE_ENV=production
 | 
				
			|||
Environment=PORT={0}
 | 
				
			|||
ExecStartPost=/bin/sh -c 'sleep 1 && chmod 666 {0}'
 | 
				
			|||
#RuntimeDirectory=service
 | 
				
			|||
#RuntimeDirectoryMode=766",
 | 
				
			|||
      socket_path.as_ref().to_str().unwrap()
 | 
				
			|||
    ),
 | 
				
			|||
  )
 | 
				
			|||
}
 | 
				
			|||
@ -0,0 +1,25 @@ | 
				
			|||
use crate::resources::Resource;
 | 
				
			|||
 | 
				
			|||
pub trait ToArtifact {
 | 
				
			|||
  type Artifact;
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
macro_rules! to_artifact {
 | 
				
			|||
    ( $($name:ident)* ) => (
 | 
				
			|||
        #[allow(non_snake_case)]
 | 
				
			|||
        impl<$($name: Resource,)*> ToArtifact for ($($name,)*)
 | 
				
			|||
        {
 | 
				
			|||
          type Artifact = ($($name::Artifact,)*);
 | 
				
			|||
        }
 | 
				
			|||
    );
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
for_each_tuple!(to_artifact);
 | 
				
			|||
 | 
				
			|||
impl<T: Resource> ToArtifact for Option<T> {
 | 
				
			|||
  type Artifact = Option<T::Artifact>;
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl<T: Resource> ToArtifact for T {
 | 
				
			|||
  type Artifact = T::Artifact;
 | 
				
			|||
}
 | 
				
			|||
@ -0,0 +1,27 @@ | 
				
			|||
-----BEGIN CERTIFICATE----- | 
				
			|||
MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ | 
				
			|||
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT | 
				
			|||
DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow | 
				
			|||
SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT | 
				
			|||
GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC | 
				
			|||
AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF | 
				
			|||
q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8 | 
				
			|||
SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0 | 
				
			|||
Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA | 
				
			|||
a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj | 
				
			|||
/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T | 
				
			|||
AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG | 
				
			|||
CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv | 
				
			|||
bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k | 
				
			|||
c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw | 
				
			|||
VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC | 
				
			|||
ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz | 
				
			|||
MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu | 
				
			|||
Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF | 
				
			|||
AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo | 
				
			|||
uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/ | 
				
			|||
wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu | 
				
			|||
X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG | 
				
			|||
PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6 | 
				
			|||
KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== | 
				
			|||
-----END CERTIFICATE----- | 
				
			|||
@ -0,0 +1,72 @@ | 
				
			|||
use schematics::resources::{Cert, Csr, GitCheckout};
 | 
				
			|||
use schematics::schema::SymbolRunner;
 | 
				
			|||
use schematics::symbols::Symbol;
 | 
				
			|||
use schematics::Setup;
 | 
				
			|||
use std::cell::RefCell;
 | 
				
			|||
use std::error::Error;
 | 
				
			|||
use std::fmt::Debug;
 | 
				
			|||
use std::rc::Rc;
 | 
				
			|||
 | 
				
			|||
struct TestSymbolRunner {
 | 
				
			|||
  count: Rc<RefCell<usize>>,
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
impl SymbolRunner for TestSymbolRunner {
 | 
				
			|||
  fn run_symbol<S: Symbol + Debug>(
 | 
				
			|||
    &self,
 | 
				
			|||
    _symbol: &S,
 | 
				
			|||
    _force: bool,
 | 
				
			|||
  ) -> Result<bool, Box<dyn Error>> {
 | 
				
			|||
    *self.count.borrow_mut() += 1;
 | 
				
			|||
    Ok(false)
 | 
				
			|||
  }
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[test]
 | 
				
			|||
fn runs_only_once() {
 | 
				
			|||
  let count = Rc::new(RefCell::new(0));
 | 
				
			|||
  let runner = TestSymbolRunner {
 | 
				
			|||
    count: Rc::clone(&count),
 | 
				
			|||
  };
 | 
				
			|||
  let mut setup = Setup::new(runner);
 | 
				
			|||
  assert_eq!(
 | 
				
			|||
    (setup.add(Csr("somehost")).unwrap().0).0.to_str().unwrap(),
 | 
				
			|||
    "/etc/ssl/local_certs/somehost.csr",
 | 
				
			|||
  );
 | 
				
			|||
  assert_eq!(
 | 
				
			|||
    (setup.add(Csr("somehost")).unwrap().0).0.to_str().unwrap(),
 | 
				
			|||
    "/etc/ssl/local_certs/somehost.csr",
 | 
				
			|||
  );
 | 
				
			|||
  assert_eq!(*count.borrow(), 2 + 5); // Key and CSR + 5 dirs
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[test]
 | 
				
			|||
fn can_create_an_acme_cert() {
 | 
				
			|||
  let count = Rc::new(RefCell::new(0));
 | 
				
			|||
  let runner = TestSymbolRunner {
 | 
				
			|||
    count: Rc::clone(&count),
 | 
				
			|||
  };
 | 
				
			|||
  let mut setup = Setup::new(runner);
 | 
				
			|||
  assert_eq!(
 | 
				
			|||
    (setup.add(Cert("somehost")).unwrap().0).0.to_str().unwrap(),
 | 
				
			|||
    "/etc/ssl/local_certs/somehost.crt",
 | 
				
			|||
  );
 | 
				
			|||
  assert_eq!(*count.borrow(), 15);
 | 
				
			|||
}
 | 
				
			|||
 | 
				
			|||
#[test]
 | 
				
			|||
fn can_create_a_git_checkout() {
 | 
				
			|||
  let count = Rc::new(RefCell::new(0));
 | 
				
			|||
  let runner = TestSymbolRunner {
 | 
				
			|||
    count: Rc::clone(&count),
 | 
				
			|||
  };
 | 
				
			|||
  let mut setup = Setup::new(runner);
 | 
				
			|||
  setup
 | 
				
			|||
    .add(GitCheckout(
 | 
				
			|||
      "/tmp/somepath".into(),
 | 
				
			|||
      "/tmp/some_src_repo",
 | 
				
			|||
      "master",
 | 
				
			|||
    ))
 | 
				
			|||
    .unwrap();
 | 
				
			|||
  assert_eq!(*count.borrow(), 3);
 | 
				
			|||
}
 | 
				
			|||
						Write
						Preview
					
					
					Loading…
					
					Cancel
						Save
					
		Reference in new issue