You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
483 lines
13 KiB
483 lines
13 KiB
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, PostgresqlDatabase,
|
|
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;
|
|
use std::rc::Rc;
|
|
|
|
pub trait Policy {
|
|
#[must_use]
|
|
fn acme_user() -> &'static str {
|
|
"acme"
|
|
}
|
|
|
|
#[must_use]
|
|
fn user_home(user_name: &str) -> Rc<Path> {
|
|
Path::new("/home").join(user_name).into()
|
|
}
|
|
|
|
#[must_use]
|
|
fn user_name_for_domain(domain_name: &'_ str) -> Rc<str> {
|
|
domain_name.split('.').rev().fold(String::new(), |result, part| if result.is_empty() { result } else { result + "_" } + part).into()
|
|
}
|
|
|
|
#[must_use]
|
|
fn php_version() -> &'static str {
|
|
"7.0"
|
|
}
|
|
|
|
#[must_use]
|
|
fn path_for_data(name: impl Display) -> Rc<Path> {
|
|
Path::new("/root/data").join(format!("_{name}")).into()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
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;
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct DefaultLocator<P = DefaultPolicy> {
|
|
phantom: PhantomData<P>,
|
|
}
|
|
|
|
impl<P, D: AsRef<str>> ResourceLocator<Key<D>> for DefaultLocator<P> {
|
|
type Prerequisites = Dir<Rc<Path>>;
|
|
fn locate(resource: &Key<D>) -> (<Key<D> as Resource>::Artifact, Self::Prerequisites) {
|
|
(
|
|
PathArtifact::from(format!("/etc/ssl/private/{}.key", resource.0.as_ref())),
|
|
Dir::new("/etc/ssl/private"),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl<P, D: AsRef<str>> ResourceLocator<Csr<D>> for DefaultLocator<P> {
|
|
type Prerequisites = Dir<Rc<Path>>;
|
|
fn locate(resource: &Csr<D>) -> (<Csr<D> as Resource>::Artifact, Self::Prerequisites) {
|
|
(
|
|
PathArtifact::from(format!("/etc/ssl/local_certs/{}.csr", resource.0.as_ref())),
|
|
Dir::new("/etc/ssl/local_certs"),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl<P, D: AsRef<str>> ResourceLocator<Cert<D>> for DefaultLocator<P> {
|
|
type Prerequisites = Dir<Rc<Path>>;
|
|
fn locate(resource: &Cert<D>) -> (<Cert<D> as Resource>::Artifact, Self::Prerequisites) {
|
|
(
|
|
PathArtifact::from(format!("/etc/ssl/local_certs/{}.crt", resource.0.as_ref())),
|
|
Dir::new("/etc/ssl/local_certs"),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl<P, D: AsRef<str>> ResourceLocator<CertChain<D>> for DefaultLocator<P> {
|
|
type Prerequisites = Dir<Rc<Path>>;
|
|
fn locate(
|
|
resource: &CertChain<D>,
|
|
) -> (<CertChain<D> as Resource>::Artifact, Self::Prerequisites) {
|
|
(
|
|
PathArtifact::from(format!(
|
|
"/etc/ssl/local_certs/{}.chained.crt",
|
|
resource.0.as_ref()
|
|
)),
|
|
Dir::new("/etc/ssl/local_certs"),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl<P, D: AsRef<str>> ResourceLocator<KeyAndCertBundle<D>> for DefaultLocator<P> {
|
|
type Prerequisites = Dir<Rc<Path>>;
|
|
fn locate(
|
|
resource: &KeyAndCertBundle<D>,
|
|
) -> (
|
|
<KeyAndCertBundle<D> as Resource>::Artifact,
|
|
Self::Prerequisites,
|
|
) {
|
|
(
|
|
PathArtifact::from(format!(
|
|
"/etc/ssl/private/{}.with_key.crt",
|
|
resource.0.as_ref()
|
|
)),
|
|
Dir::new("/etc/ssl/private"),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl<POLICY, P: AsRef<Path>> ResourceLocator<File<P>> for DefaultLocator<POLICY> {
|
|
type Prerequisites = Dir<Rc<Path>>;
|
|
fn locate(resource: &File<P>) -> (<File<P> as Resource>::Artifact, Self::Prerequisites) {
|
|
((), Dir::new(resource.0.as_ref().parent().unwrap()))
|
|
}
|
|
}
|
|
|
|
impl<'a, POLICY, P: AsRef<Path>> ResourceLocator<GitCheckout<'a, P>> for DefaultLocator<POLICY> {
|
|
type Prerequisites = Dir<Rc<Path>>;
|
|
fn locate(
|
|
resource: &GitCheckout<'a, P>,
|
|
) -> (
|
|
<GitCheckout<'a, P> as Resource>::Artifact,
|
|
Self::Prerequisites,
|
|
) {
|
|
((), Dir::new(resource.0.as_ref().parent().unwrap()))
|
|
}
|
|
}
|
|
|
|
impl<POLICY, P: Clone + AsRef<Path>> ResourceLocator<Dir<P>> for DefaultLocator<POLICY> {
|
|
type Prerequisites = Option<Dir<Rc<Path>>>;
|
|
fn locate(resource: &Dir<P>) -> (<Dir<P> as Resource>::Artifact, Self::Prerequisites) {
|
|
((), resource.0.as_ref().parent().map(Dir::new))
|
|
}
|
|
}
|
|
|
|
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::from(POLICY::path_for_data(resource.0)), ())
|
|
}
|
|
}
|
|
|
|
impl<POLICY: Policy, P: AsRef<Path>> ResourceLocator<LoadedDirectory<P>>
|
|
for DefaultLocator<POLICY>
|
|
{
|
|
type Prerequisites = Dir<Rc<Path>>;
|
|
fn locate(
|
|
resource: &LoadedDirectory<P>,
|
|
) -> (
|
|
<LoadedDirectory<P> as Resource>::Artifact,
|
|
Self::Prerequisites,
|
|
) {
|
|
(
|
|
PathArtifact::from(POLICY::path_for_data(resource.0)),
|
|
Dir::new(resource.1.as_ref().parent().unwrap()),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl<P: Policy> ResourceLocator<AcmeAccountKey> for DefaultLocator<P> {
|
|
type Prerequisites = Dir<Rc<Path>>;
|
|
fn locate(
|
|
_resource: &AcmeAccountKey,
|
|
) -> (<AcmeAccountKey as Resource>::Artifact, Self::Prerequisites) {
|
|
let acme_user = P::acme_user();
|
|
let home = P::user_home(acme_user);
|
|
(PathArtifact::from(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<Rc<Path>>;
|
|
fn locate(
|
|
_resource: &AcmeChallengesDir,
|
|
) -> (
|
|
<AcmeChallengesDir as Resource>::Artifact,
|
|
Self::Prerequisites,
|
|
) {
|
|
let acme_user = P::acme_user();
|
|
let home = P::user_home(acme_user);
|
|
(PathArtifact::from(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::from("/etc/nginx/snippets/acme-challenge.conf"),
|
|
(),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl<P: Policy> ResourceLocator<AcmeRootCert> for DefaultLocator<P> {
|
|
type Prerequisites = Dir<Rc<Path>>;
|
|
fn locate(
|
|
_resource: &AcmeRootCert,
|
|
) -> (<AcmeRootCert as Resource>::Artifact, Self::Prerequisites) {
|
|
let acme_user = P::acme_user();
|
|
let home = P::user_home(acme_user);
|
|
(
|
|
PathArtifact::from(home.join("lets_encrypt_r3.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::from(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::from("/etc/nginx/sites-enabled/default"), ())
|
|
}
|
|
}
|
|
|
|
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::from(Path::new("/etc/nginx/sites-enabled/").join(&resource.0)),
|
|
(),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl<D: AsRef<Path>, P, C, POLICY> ResourceLocator<ServePhp<D, P, C>> for DefaultLocator<POLICY> {
|
|
type Prerequisites = ();
|
|
fn locate(
|
|
resource: &ServePhp<D, P, C>,
|
|
) -> (
|
|
<ServePhp<D, P, C> as Resource>::Artifact,
|
|
Self::Prerequisites,
|
|
) {
|
|
(
|
|
PathArtifact::from(Path::new("/etc/nginx/sites-enabled/").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::from(Path::new("/etc/nginx/sites-enabled/").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::from(Path::new("/etc/nginx/sites-enabled/").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::from(Path::new("/etc/nginx/sites-enabled/").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::from(format!("/run/php/{}.sock", user.0)),
|
|
PathArtifact::from(format!(
|
|
"/etc/php/{}/fpm/pool.d/{}.conf",
|
|
php_version, user.0
|
|
)),
|
|
user,
|
|
ServiceNameArtifact(format!("php{php_version}-fpm").into()),
|
|
),
|
|
(),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl<D: Clone + AsRef<str>, P, POLICY: Policy> ResourceLocator<SystemdSocketService<D, P>>
|
|
for DefaultLocator<POLICY>
|
|
{
|
|
type Prerequisites = Dir<Rc<Path>>;
|
|
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.as_ref().join(".config");
|
|
let service_dir_path = config.join("systemd/user");
|
|
(
|
|
(
|
|
PathArtifact::from(format!("/var/tmp/{}-{}.socket", user_name.0, resource.1)),
|
|
PathArtifact::from(service_dir_path.join(format!("{}.service", resource.1))),
|
|
user_name,
|
|
),
|
|
Dir::new(service_dir_path),
|
|
)
|
|
}
|
|
}
|
|
|
|
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::from(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<D: AsRef<str>, P: Policy> ResourceLocator<PostgresqlDatabase<D>> for DefaultLocator<P> {
|
|
type Prerequisites = ();
|
|
fn locate(
|
|
resource: &PostgresqlDatabase<D>,
|
|
) -> (
|
|
<PostgresqlDatabase<D> as Resource>::Artifact,
|
|
Self::Prerequisites,
|
|
) {
|
|
let ((user_name, _), ()) = Self::locate(&UserForDomain(&resource.0));
|
|
(
|
|
(
|
|
DatabaseNameArtifact(user_name.0.clone()),
|
|
PathArtifact::from(P::path_for_data(format!("{}.sql", user_name.0))),
|
|
),
|
|
(),
|
|
)
|
|
}
|
|
}
|
|
|
|
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) {
|
|
((), ())
|
|
}
|
|
}
|