From e5b64f49685bc2d62edb42abcc90a04f3449e30b Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Wed, 10 May 2023 13:34:33 +0200 Subject: [PATCH] initial implementation of domain manager --- src/domain.rs | 1 + src/domain/manager.rs | 199 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 200 insertions(+) create mode 100644 src/domain/manager.rs diff --git a/src/domain.rs b/src/domain.rs index fd51183..e19a35f 100644 --- a/src/domain.rs +++ b/src/domain.rs @@ -1,2 +1,3 @@ pub mod checker; pub mod config; +pub mod manager; diff --git a/src/domain/manager.rs b/src/domain/manager.rs new file mode 100644 index 0000000..1ae4a55 --- /dev/null +++ b/src/domain/manager.rs @@ -0,0 +1,199 @@ +use crate::domain::{checker, config}; +use crate::origin; +use std::error::Error; + +#[derive(thiserror::Error, Debug)] +pub enum GetConfigError { + #[error("not found")] + NotFound, + + #[error(transparent)] + Unexpected(Box), +} + +impl From 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)] + Unexpected(Box), +} + +impl From 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, + + #[error(transparent)] + Unexpected(Box), +} + +impl From 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, + + #[error("invalid domain name")] + InvalidDomainName, + + #[error("target CNAME not set")] + TargetCNAMENotSet, + + #[error("challenge token not set")] + ChallengeTokenNotSet, + + #[error(transparent)] + Unexpected(Box), +} + +impl From for SyncWithConfigError { + fn from(e: origin::store::SyncError) -> SyncWithConfigError { + match e { + origin::store::SyncError::InvalidURL => SyncWithConfigError::InvalidURL, + origin::store::SyncError::InvalidBranchName => SyncWithConfigError::InvalidBranchName, + origin::store::SyncError::Unexpected(e) => SyncWithConfigError::Unexpected(e), + } + } +} + +impl From for SyncWithConfigError { + fn from(e: checker::CheckDomainError) -> SyncWithConfigError { + match e { + checker::CheckDomainError::InvalidDomainName => SyncWithConfigError::InvalidDomainName, + checker::CheckDomainError::TargetCNAMENotSet => SyncWithConfigError::TargetCNAMENotSet, + checker::CheckDomainError::ChallengeTokenNotSet => { + SyncWithConfigError::ChallengeTokenNotSet + } + checker::CheckDomainError::Unexpected(e) => SyncWithConfigError::Unexpected(e), + } + } +} + +impl From for SyncWithConfigError { + fn from(e: config::SetError) -> SyncWithConfigError { + match e { + config::SetError::Unexpected(e) => SyncWithConfigError::Unexpected(e), + } + } +} + +#[mockall::automock] +pub trait Manager { + fn get_config(&self, domain: &str) -> Result; + fn get_origin(&self, domain: &str) -> Result, GetOriginError>; + fn sync(&self, domain: &str) -> Result<(), SyncError>; + fn sync_with_config( + &self, + domain: &str, + config: &config::Config, + ) -> Result<(), SyncWithConfigError>; +} + +pub fn new_manager( + origin_store: OriginStore, + domain_config_store: DomainConfigStore, + domain_checker: DomainChecker, +) -> impl Manager +where + OriginStore: for<'a> origin::store::Store<'a>, + DomainConfigStore: config::Store, + DomainChecker: checker::Checker, +{ + ManagerImpl { + origin_store, + domain_config_store, + domain_checker, + } +} + +struct ManagerImpl +where + OriginStore: for<'a> origin::store::Store<'a>, + DomainConfigStore: config::Store, + DomainChecker: checker::Checker, +{ + origin_store: OriginStore, + domain_config_store: DomainConfigStore, + domain_checker: DomainChecker, +} + +impl Manager + for ManagerImpl +where + OriginStore: for<'a> origin::store::Store<'a>, + DomainConfigStore: config::Store, + DomainChecker: checker::Checker, +{ + fn get_config(&self, domain: &str) -> Result { + Ok(self.domain_config_store.get(domain)?) + } + + fn get_origin(&self, domain: &str) -> Result, GetOriginError> { + 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 + .map_err(|e| GetOriginError::Unexpected(Box::from(e)))?; + Ok(origin) + } + + fn sync(&self, domain: &str) -> Result<(), SyncError> { + let config = self.domain_config_store.get(domain)?; + self.origin_store + .sync(&config.origin_descr, origin::store::Limits {}) + // if there's a config there should be an origin, any error here is unexpected + .map_err(|e| SyncError::Unexpected(Box::from(e)))?; + Ok(()) + } + + fn sync_with_config( + &self, + domain: &str, + config: &config::Config, + ) -> Result<(), SyncWithConfigError> { + let config_hash = config + .hash() + .map_err(|e| SyncWithConfigError::Unexpected(Box::from(e)))?; + + self.domain_checker.check_domain(domain, &config_hash)?; + + self.origin_store + .sync(&config.origin_descr, origin::store::Limits {})?; + + self.domain_config_store.set(domain, config)?; + + Ok(()) + } +}