2023-05-18 20:02:57 +00:00
|
|
|
use crate::domain::{self, acme, checker, config};
|
2023-05-17 12:08:17 +00:00
|
|
|
use crate::error::{MapUnexpected, ToUnexpected};
|
|
|
|
use crate::{error, origin};
|
2023-05-17 10:34:24 +00:00
|
|
|
|
2023-05-18 20:02:57 +00:00
|
|
|
use std::{future, pin, sync};
|
2023-05-10 11:34:33 +00:00
|
|
|
|
|
|
|
#[derive(thiserror::Error, Debug)]
|
|
|
|
pub enum GetConfigError {
|
|
|
|
#[error("not found")]
|
|
|
|
NotFound,
|
|
|
|
|
|
|
|
#[error(transparent)]
|
2023-05-17 12:08:17 +00:00
|
|
|
Unexpected(#[from] error::Unexpected),
|
2023-05-10 11:34:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl From<config::GetError> for GetConfigError {
|
|
|
|
fn from(e: config::GetError) -> GetConfigError {
|
|
|
|
match e {
|
|
|
|
config::GetError::NotFound => GetConfigError::NotFound,
|
|
|
|
config::GetError::Unexpected(e) => GetConfigError::Unexpected(e),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(thiserror::Error, Debug)]
|
|
|
|
pub enum GetOriginError {
|
|
|
|
#[error("not found")]
|
|
|
|
NotFound,
|
|
|
|
|
|
|
|
#[error(transparent)]
|
2023-05-17 12:08:17 +00:00
|
|
|
Unexpected(#[from] error::Unexpected),
|
2023-05-10 11:34:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl From<config::GetError> for GetOriginError {
|
|
|
|
fn from(e: config::GetError) -> GetOriginError {
|
|
|
|
match e {
|
|
|
|
config::GetError::NotFound => GetOriginError::NotFound,
|
|
|
|
config::GetError::Unexpected(e) => GetOriginError::Unexpected(e),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(thiserror::Error, Debug)]
|
|
|
|
pub enum SyncError {
|
|
|
|
#[error("not found")]
|
|
|
|
NotFound,
|
|
|
|
|
2023-05-10 13:12:34 +00:00
|
|
|
#[error("already in progress")]
|
|
|
|
AlreadyInProgress,
|
|
|
|
|
2023-05-10 11:34:33 +00:00
|
|
|
#[error(transparent)]
|
2023-05-17 12:08:17 +00:00
|
|
|
Unexpected(#[from] error::Unexpected),
|
2023-05-10 11:34:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl From<config::GetError> for SyncError {
|
|
|
|
fn from(e: config::GetError) -> SyncError {
|
|
|
|
match e {
|
|
|
|
config::GetError::NotFound => SyncError::NotFound,
|
|
|
|
config::GetError::Unexpected(e) => SyncError::Unexpected(e),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(thiserror::Error, Debug)]
|
|
|
|
pub enum SyncWithConfigError {
|
|
|
|
#[error("invalid url")]
|
|
|
|
InvalidURL,
|
|
|
|
|
|
|
|
#[error("invalid branch name")]
|
|
|
|
InvalidBranchName,
|
|
|
|
|
2023-05-10 13:12:34 +00:00
|
|
|
#[error("already in progress")]
|
|
|
|
AlreadyInProgress,
|
|
|
|
|
2023-05-15 20:16:29 +00:00
|
|
|
#[error("target A/AAAA not set")]
|
|
|
|
TargetANotSet,
|
2023-05-10 11:34:33 +00:00
|
|
|
|
|
|
|
#[error("challenge token not set")]
|
|
|
|
ChallengeTokenNotSet,
|
|
|
|
|
|
|
|
#[error(transparent)]
|
2023-05-17 12:08:17 +00:00
|
|
|
Unexpected(#[from] error::Unexpected),
|
2023-05-10 11:34:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl From<origin::store::SyncError> for SyncWithConfigError {
|
|
|
|
fn from(e: origin::store::SyncError) -> SyncWithConfigError {
|
|
|
|
match e {
|
|
|
|
origin::store::SyncError::InvalidURL => SyncWithConfigError::InvalidURL,
|
|
|
|
origin::store::SyncError::InvalidBranchName => SyncWithConfigError::InvalidBranchName,
|
2023-05-10 13:12:34 +00:00
|
|
|
origin::store::SyncError::AlreadyInProgress => SyncWithConfigError::AlreadyInProgress,
|
2023-05-10 11:34:33 +00:00
|
|
|
origin::store::SyncError::Unexpected(e) => SyncWithConfigError::Unexpected(e),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<checker::CheckDomainError> for SyncWithConfigError {
|
|
|
|
fn from(e: checker::CheckDomainError) -> SyncWithConfigError {
|
|
|
|
match e {
|
2023-05-15 20:16:29 +00:00
|
|
|
checker::CheckDomainError::TargetANotSet => SyncWithConfigError::TargetANotSet,
|
2023-05-10 11:34:33 +00:00
|
|
|
checker::CheckDomainError::ChallengeTokenNotSet => {
|
|
|
|
SyncWithConfigError::ChallengeTokenNotSet
|
|
|
|
}
|
|
|
|
checker::CheckDomainError::Unexpected(e) => SyncWithConfigError::Unexpected(e),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<config::SetError> for SyncWithConfigError {
|
|
|
|
fn from(e: config::SetError) -> SyncWithConfigError {
|
|
|
|
match e {
|
|
|
|
config::SetError::Unexpected(e) => SyncWithConfigError::Unexpected(e),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-18 20:02:57 +00:00
|
|
|
pub type GetAcmeHttp01ChallengeKeyError = acme::manager::GetHttp01ChallengeKeyError;
|
|
|
|
|
2023-05-19 11:26:27 +00:00
|
|
|
pub type AllDomainsResult<T> = config::AllDomainsResult<T>;
|
|
|
|
|
2023-05-17 12:09:16 +00:00
|
|
|
#[mockall::automock(
|
|
|
|
type Origin=origin::MockOrigin;
|
|
|
|
type SyncWithConfigFuture=future::Ready<Result<(), SyncWithConfigError>>;
|
2023-05-17 12:37:23 +00:00
|
|
|
type SyncAllOriginsErrorsIter=Vec<(Option<origin::Descr>,error::Unexpected)>;
|
2023-05-17 12:09:16 +00:00
|
|
|
)]
|
2023-05-16 15:17:47 +00:00
|
|
|
pub trait Manager {
|
2023-05-17 10:34:24 +00:00
|
|
|
type Origin<'mgr>: origin::Origin + 'mgr
|
|
|
|
where
|
|
|
|
Self: 'mgr;
|
|
|
|
|
|
|
|
type SyncWithConfigFuture<'mgr>: future::Future<Output = Result<(), SyncWithConfigError>>
|
|
|
|
+ Send
|
|
|
|
+ Unpin
|
|
|
|
+ 'mgr
|
|
|
|
where
|
|
|
|
Self: 'mgr;
|
|
|
|
|
2023-05-17 12:37:23 +00:00
|
|
|
type SyncAllOriginsErrorsIter<'mgr>: IntoIterator<Item = (Option<origin::Descr>, error::Unexpected)>
|
|
|
|
+ 'mgr
|
|
|
|
where
|
|
|
|
Self: 'mgr;
|
|
|
|
|
2023-05-12 13:19:24 +00:00
|
|
|
fn get_config(&self, domain: &domain::Name) -> Result<config::Config, GetConfigError>;
|
2023-05-17 12:37:23 +00:00
|
|
|
|
2023-05-17 10:34:24 +00:00
|
|
|
fn get_origin(&self, domain: &domain::Name) -> Result<Self::Origin<'_>, GetOriginError>;
|
2023-05-17 12:37:23 +00:00
|
|
|
|
2023-05-10 11:34:33 +00:00
|
|
|
fn sync_with_config(
|
|
|
|
&self,
|
2023-05-15 15:42:32 +00:00
|
|
|
domain: domain::Name,
|
|
|
|
config: config::Config,
|
2023-05-17 10:34:24 +00:00
|
|
|
) -> Self::SyncWithConfigFuture<'_>;
|
2023-05-17 12:37:23 +00:00
|
|
|
|
|
|
|
fn sync_all_origins(&self) -> Result<Self::SyncAllOriginsErrorsIter<'_>, error::Unexpected>;
|
2023-05-18 20:02:57 +00:00
|
|
|
|
|
|
|
fn get_acme_http01_challenge_key(
|
|
|
|
&self,
|
|
|
|
token: &str,
|
|
|
|
) -> Result<String, GetAcmeHttp01ChallengeKeyError>;
|
2023-05-19 11:26:27 +00:00
|
|
|
|
|
|
|
fn all_domains(&self) -> AllDomainsResult<Vec<AllDomainsResult<domain::Name>>>;
|
2023-05-10 11:34:33 +00:00
|
|
|
}
|
|
|
|
|
2023-05-16 15:17:47 +00:00
|
|
|
pub trait BoxedManager: Manager + Send + Sync + Clone {}
|
|
|
|
|
2023-05-18 20:02:57 +00:00
|
|
|
struct ManagerImpl<OriginStore, DomainConfigStore, AcmeManager>
|
2023-05-16 15:17:47 +00:00
|
|
|
where
|
|
|
|
OriginStore: origin::store::BoxedStore,
|
|
|
|
DomainConfigStore: config::BoxedStore,
|
2023-05-18 20:02:57 +00:00
|
|
|
AcmeManager: acme::manager::BoxedManager,
|
2023-05-16 15:17:47 +00:00
|
|
|
{
|
|
|
|
origin_store: OriginStore,
|
|
|
|
domain_config_store: DomainConfigStore,
|
|
|
|
domain_checker: checker::DNSChecker,
|
2023-05-18 20:02:57 +00:00
|
|
|
acme_manager: Option<AcmeManager>,
|
2023-05-16 15:17:47 +00:00
|
|
|
}
|
|
|
|
|
2023-05-18 20:02:57 +00:00
|
|
|
pub fn new<OriginStore, DomainConfigStore, AcmeManager>(
|
2023-05-10 11:34:33 +00:00
|
|
|
origin_store: OriginStore,
|
|
|
|
domain_config_store: DomainConfigStore,
|
2023-05-15 15:42:32 +00:00
|
|
|
domain_checker: checker::DNSChecker,
|
2023-05-18 20:02:57 +00:00
|
|
|
acme_manager: Option<AcmeManager>,
|
2023-05-16 15:17:47 +00:00
|
|
|
) -> impl BoxedManager
|
2023-05-10 11:34:33 +00:00
|
|
|
where
|
2023-05-16 15:17:47 +00:00
|
|
|
OriginStore: origin::store::BoxedStore,
|
|
|
|
DomainConfigStore: config::BoxedStore,
|
2023-05-18 20:02:57 +00:00
|
|
|
AcmeManager: acme::manager::BoxedManager,
|
2023-05-10 11:34:33 +00:00
|
|
|
{
|
2023-05-16 15:17:47 +00:00
|
|
|
sync::Arc::new(ManagerImpl {
|
2023-05-10 11:34:33 +00:00
|
|
|
origin_store,
|
|
|
|
domain_config_store,
|
|
|
|
domain_checker,
|
2023-05-18 20:02:57 +00:00
|
|
|
acme_manager: acme_manager,
|
2023-05-16 15:17:47 +00:00
|
|
|
})
|
2023-05-10 11:34:33 +00:00
|
|
|
}
|
|
|
|
|
2023-05-18 20:02:57 +00:00
|
|
|
impl<OriginStore, DomainConfigStore, AcmeManager> BoxedManager
|
|
|
|
for sync::Arc<ManagerImpl<OriginStore, DomainConfigStore, AcmeManager>>
|
2023-05-10 11:34:33 +00:00
|
|
|
where
|
2023-05-16 15:17:47 +00:00
|
|
|
OriginStore: origin::store::BoxedStore,
|
|
|
|
DomainConfigStore: config::BoxedStore,
|
2023-05-18 20:02:57 +00:00
|
|
|
AcmeManager: acme::manager::BoxedManager,
|
2023-05-10 11:34:33 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2023-05-18 20:02:57 +00:00
|
|
|
impl<OriginStore, DomainConfigStore, AcmeManager> Manager
|
|
|
|
for sync::Arc<ManagerImpl<OriginStore, DomainConfigStore, AcmeManager>>
|
2023-05-10 11:34:33 +00:00
|
|
|
where
|
2023-05-16 15:17:47 +00:00
|
|
|
OriginStore: origin::store::BoxedStore,
|
|
|
|
DomainConfigStore: config::BoxedStore,
|
2023-05-18 20:02:57 +00:00
|
|
|
AcmeManager: acme::manager::BoxedManager,
|
2023-05-10 11:34:33 +00:00
|
|
|
{
|
2023-05-17 10:34:24 +00:00
|
|
|
type Origin<'mgr> = OriginStore::Origin<'mgr>
|
|
|
|
where Self: 'mgr;
|
|
|
|
|
|
|
|
type SyncWithConfigFuture<'mgr> = pin::Pin<Box<dyn future::Future<Output = Result<(), SyncWithConfigError>> + Send + 'mgr>>
|
|
|
|
where Self: 'mgr;
|
|
|
|
|
2023-05-17 12:37:23 +00:00
|
|
|
type SyncAllOriginsErrorsIter<'mgr> = Box<dyn Iterator<Item = (Option<origin::Descr>, error::Unexpected)> + 'mgr>
|
|
|
|
where Self: 'mgr;
|
|
|
|
|
2023-05-12 13:19:24 +00:00
|
|
|
fn get_config(&self, domain: &domain::Name) -> Result<config::Config, GetConfigError> {
|
2023-05-10 11:34:33 +00:00
|
|
|
Ok(self.domain_config_store.get(domain)?)
|
|
|
|
}
|
|
|
|
|
2023-05-17 10:34:24 +00:00
|
|
|
fn get_origin(&self, domain: &domain::Name) -> Result<Self::Origin<'_>, GetOriginError> {
|
2023-05-10 11:34:33 +00:00
|
|
|
let config = self.domain_config_store.get(domain)?;
|
|
|
|
let origin = self
|
|
|
|
.origin_store
|
|
|
|
.get(config.origin_descr)
|
|
|
|
// if there's a config there should be an origin, any error here is unexpected
|
2023-05-17 12:08:17 +00:00
|
|
|
.map_unexpected()?;
|
2023-05-17 10:34:24 +00:00
|
|
|
Ok(origin)
|
2023-05-10 11:34:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn sync_with_config(
|
|
|
|
&self,
|
2023-05-15 15:42:32 +00:00
|
|
|
domain: domain::Name,
|
|
|
|
config: config::Config,
|
2023-05-17 10:34:24 +00:00
|
|
|
) -> Self::SyncWithConfigFuture<'_> {
|
2023-05-15 15:42:32 +00:00
|
|
|
Box::pin(async move {
|
2023-05-17 12:08:17 +00:00
|
|
|
let config_hash = config.hash().map_unexpected()?;
|
2023-05-10 11:34:33 +00:00
|
|
|
|
2023-05-15 15:42:32 +00:00
|
|
|
self.domain_checker
|
|
|
|
.check_domain(&domain, &config_hash)
|
|
|
|
.await?;
|
2023-05-10 11:34:33 +00:00
|
|
|
|
2023-05-15 15:42:32 +00:00
|
|
|
self.origin_store
|
|
|
|
.sync(config.origin_descr.clone(), origin::store::Limits {})?;
|
2023-05-10 11:34:33 +00:00
|
|
|
|
2023-05-15 15:42:32 +00:00
|
|
|
self.domain_config_store.set(&domain, &config)?;
|
2023-05-10 11:34:33 +00:00
|
|
|
|
2023-05-18 20:02:57 +00:00
|
|
|
if let Some(ref acme_manager) = self.acme_manager {
|
|
|
|
acme_manager.sync_domain(domain.clone()).await?;
|
|
|
|
}
|
|
|
|
|
2023-05-15 15:42:32 +00:00
|
|
|
Ok(())
|
|
|
|
})
|
2023-05-10 11:34:33 +00:00
|
|
|
}
|
2023-05-17 12:37:23 +00:00
|
|
|
|
|
|
|
fn sync_all_origins(&self) -> Result<Self::SyncAllOriginsErrorsIter<'_>, error::Unexpected> {
|
|
|
|
let iter = self.origin_store.all_descrs().map_unexpected()?.into_iter();
|
|
|
|
|
|
|
|
Ok(Box::from(iter.filter_map(|descr| {
|
|
|
|
if let Err(err) = descr {
|
|
|
|
return Some((None, err.to_unexpected().into()));
|
|
|
|
}
|
|
|
|
|
|
|
|
let descr = descr.unwrap();
|
|
|
|
|
|
|
|
if let Err(err) = self
|
|
|
|
.origin_store
|
|
|
|
.sync(descr.clone(), origin::store::Limits {})
|
|
|
|
{
|
|
|
|
return Some((Some(descr), err.to_unexpected().into()));
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
})))
|
|
|
|
}
|
2023-05-18 20:02:57 +00:00
|
|
|
|
|
|
|
fn get_acme_http01_challenge_key(
|
|
|
|
&self,
|
|
|
|
token: &str,
|
|
|
|
) -> Result<String, GetAcmeHttp01ChallengeKeyError> {
|
|
|
|
if let Some(ref acme_manager) = self.acme_manager {
|
|
|
|
return acme_manager.get_http01_challenge_key(token);
|
|
|
|
}
|
|
|
|
|
|
|
|
Err(GetAcmeHttp01ChallengeKeyError::NotFound)
|
|
|
|
}
|
2023-05-19 11:26:27 +00:00
|
|
|
|
|
|
|
fn all_domains(&self) -> AllDomainsResult<Vec<AllDomainsResult<domain::Name>>> {
|
|
|
|
self.domain_config_store.all_domains()
|
|
|
|
}
|
2023-05-10 11:34:33 +00:00
|
|
|
}
|