Rename domain::Config to domain::Domain, plus other moving

This commit is contained in:
Brian Picciano 2023-07-09 14:25:01 +02:00
parent 9c1bdc1e8a
commit 80e96c47fb
8 changed files with 137 additions and 127 deletions

View File

@ -1,80 +1,29 @@
pub mod acme;
pub mod checker;
pub mod config;
pub mod manager;
mod name;
pub mod store;
use std::fmt;
use std::str::FromStr;
pub use name::*;
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use trust_dns_client::rr as trust_dns_rr;
use crate::error::unexpected::{self, Mappable};
use crate::origin;
#[derive(Debug, Clone)]
/// Validated representation of a domain name
pub struct Name {
inner: trust_dns_rr::Name,
utf8_str: String,
use hex::ToHex;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
/// Defines how a domain will behave when it is accessed. These are configured by the owner of the
/// domain during setup.
pub struct Domain {
pub origin_descr: origin::Descr,
}
impl Name {
pub fn as_str(&self) -> &str {
self.utf8_str.as_str()
}
}
impl fmt::Display for Name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.utf8_str)
}
}
impl FromStr for Name {
type Err = <trust_dns_rr::Name as FromStr>::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut n = trust_dns_rr::Name::from_str(s)?;
let utf8_str = n.clone().to_utf8();
n.set_fqdn(true);
Ok(Name { inner: n, utf8_str })
}
}
impl Serialize for Name {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.as_str())
}
}
struct NameVisitor;
impl<'de> de::Visitor<'de> for NameVisitor {
type Value = Name;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "a valid domain name")
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
match Name::from_str(s) {
Ok(n) => Ok(n),
Err(e) => Err(E::custom(format!("invalid domain name: {}", e))),
}
}
}
impl<'de> Deserialize<'de> for Name {
fn deserialize<D>(deserializer: D) -> Result<Name, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(NameVisitor)
impl Domain {
pub fn hash(&self) -> Result<String, unexpected::Error> {
let mut h = Sha256::new();
serde_json::to_writer(&mut h, self).or_unexpected()?;
Ok(h.finalize().encode_hex::<String>())
}
}

View File

@ -62,7 +62,7 @@ impl DNSChecker {
domain: &domain::Name,
challenge_token: &str,
) -> Result<(), CheckDomainError> {
let domain = &domain.inner;
let domain = domain.as_rr();
// check that the A is installed correctly on the domain
{

View File

@ -1,4 +1,4 @@
use crate::domain::{self, acme, checker, config};
use crate::domain::{self, acme, checker, store};
use crate::error::unexpected::{self, Mappable};
use crate::origin;
use crate::util;
@ -15,11 +15,11 @@ pub enum GetConfigError {
Unexpected(#[from] unexpected::Error),
}
impl From<config::GetError> for GetConfigError {
fn from(e: config::GetError) -> GetConfigError {
impl From<store::GetError> for GetConfigError {
fn from(e: store::GetError) -> GetConfigError {
match e {
config::GetError::NotFound => GetConfigError::NotFound,
config::GetError::Unexpected(e) => GetConfigError::Unexpected(e),
store::GetError::NotFound => GetConfigError::NotFound,
store::GetError::Unexpected(e) => GetConfigError::Unexpected(e),
}
}
}
@ -36,11 +36,11 @@ pub enum GetFileError {
Unexpected(#[from] unexpected::Error),
}
impl From<config::GetError> for GetFileError {
fn from(e: config::GetError) -> Self {
impl From<store::GetError> for GetFileError {
fn from(e: store::GetError) -> Self {
match e {
config::GetError::NotFound => Self::DomainNotFound,
config::GetError::Unexpected(e) => Self::Unexpected(e),
store::GetError::NotFound => Self::DomainNotFound,
store::GetError::Unexpected(e) => Self::Unexpected(e),
}
}
}
@ -69,11 +69,11 @@ pub enum SyncError {
Unexpected(#[from] unexpected::Error),
}
impl From<config::GetError> for SyncError {
fn from(e: config::GetError) -> SyncError {
impl From<store::GetError> for SyncError {
fn from(e: store::GetError) -> SyncError {
match e {
config::GetError::NotFound => SyncError::NotFound,
config::GetError::Unexpected(e) => SyncError::Unexpected(e),
store::GetError::NotFound => SyncError::NotFound,
store::GetError::Unexpected(e) => SyncError::Unexpected(e),
}
}
}
@ -122,10 +122,10 @@ impl From<checker::CheckDomainError> for SyncWithConfigError {
}
}
impl From<config::SetError> for SyncWithConfigError {
fn from(e: config::SetError) -> SyncWithConfigError {
impl From<store::SetError> for SyncWithConfigError {
fn from(e: store::SetError) -> SyncWithConfigError {
match e {
config::SetError::Unexpected(e) => SyncWithConfigError::Unexpected(e),
store::SetError::Unexpected(e) => SyncWithConfigError::Unexpected(e),
}
}
}
@ -134,7 +134,7 @@ pub type GetAcmeHttp01ChallengeKeyError = acme::manager::GetHttp01ChallengeKeyEr
//#[mockall::automock]
pub trait Manager: Sync + Send + rustls::server::ResolvesServerCert {
fn get_config(&self, domain: &domain::Name) -> Result<config::Config, GetConfigError>;
fn get_config(&self, domain: &domain::Name) -> Result<domain::Domain, GetConfigError>;
fn get_file<'store>(
&'store self,
@ -150,7 +150,7 @@ pub trait Manager: Sync + Send + rustls::server::ResolvesServerCert {
fn sync_with_config<'mgr>(
&'mgr self,
domain: domain::Name,
config: config::Config,
config: domain::Domain,
) -> util::BoxFuture<'mgr, Result<(), SyncWithConfigError>>;
fn get_acme_http01_challenge_key(
@ -163,7 +163,7 @@ pub trait Manager: Sync + Send + rustls::server::ResolvesServerCert {
pub struct ManagerImpl {
origin_store: Box<dyn origin::Store + Send + Sync>,
domain_config_store: Box<dyn config::Store + Send + Sync>,
domain_config_store: Box<dyn store::Store + Send + Sync>,
domain_checker: checker::DNSChecker,
acme_manager: Option<Box<dyn acme::manager::Manager + Send + Sync>>,
}
@ -171,7 +171,7 @@ pub struct ManagerImpl {
impl ManagerImpl {
pub fn new<
OriginStore: origin::Store + Send + Sync + 'static,
DomainConfigStore: config::Store + Send + Sync + 'static,
DomainConfigStore: store::Store + Send + Sync + 'static,
AcmeManager: acme::manager::Manager + Send + Sync + 'static,
>(
task_stack: &mut util::TaskStack<unexpected::Error>,
@ -224,7 +224,7 @@ impl ManagerImpl {
}
impl Manager for ManagerImpl {
fn get_config(&self, domain: &domain::Name) -> Result<config::Config, GetConfigError> {
fn get_config(&self, domain: &domain::Name) -> Result<domain::Domain, GetConfigError> {
Ok(self.domain_config_store.get(domain)?)
}
@ -254,7 +254,7 @@ impl Manager for ManagerImpl {
fn sync_with_config<'mgr>(
&'mgr self,
domain: domain::Name,
config: config::Config,
config: domain::Domain,
) -> util::BoxFuture<'mgr, Result<(), SyncWithConfigError>> {
Box::pin(async move {
let config_hash = config

79
src/domain/name.rs Normal file
View File

@ -0,0 +1,79 @@
use std::fmt;
use std::str::FromStr;
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use trust_dns_client::rr as trust_dns_rr;
#[derive(Debug, Clone)]
/// Validated representation of a domain name
pub struct Name {
inner: trust_dns_rr::Name,
utf8_str: String,
}
impl Name {
pub fn as_str(&self) -> &str {
self.utf8_str.as_str()
}
pub fn as_rr(&self) -> &trust_dns_rr::Name {
&self.inner
}
}
impl fmt::Display for Name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.utf8_str)
}
}
impl FromStr for Name {
type Err = <trust_dns_rr::Name as FromStr>::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut n = trust_dns_rr::Name::from_str(s)?;
let utf8_str = n.clone().to_utf8();
n.set_fqdn(true);
Ok(Name { inner: n, utf8_str })
}
}
impl Serialize for Name {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.as_str())
}
}
struct NameVisitor;
impl<'de> de::Visitor<'de> for NameVisitor {
type Value = Name;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "a valid domain name")
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
match Name::from_str(s) {
Ok(n) => Ok(n),
Err(e) => Err(E::custom(format!("invalid domain name: {}", e))),
}
}
}
impl<'de> Deserialize<'de> for Name {
fn deserialize<D>(deserializer: D) -> Result<Name, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(NameVisitor)
}
}

View File

@ -2,26 +2,8 @@ use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::{fs, io};
use crate::domain;
use crate::error::unexpected::{self, Intoable, Mappable};
use crate::{domain, origin};
use hex::ToHex;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
/// Values which the owner of a domain can configure when they install a domain.
pub struct Config {
pub origin_descr: origin::Descr,
}
impl Config {
pub fn hash(&self) -> Result<String, unexpected::Error> {
let mut h = Sha256::new();
serde_json::to_writer(&mut h, self).or_unexpected()?;
Ok(h.finalize().encode_hex::<String>())
}
}
#[derive(thiserror::Error, Debug)]
pub enum GetError {
@ -40,8 +22,8 @@ pub enum SetError {
#[mockall::automock]
pub trait Store {
fn get(&self, domain: &domain::Name) -> Result<Config, GetError>;
fn set(&self, domain: &domain::Name, config: &Config) -> Result<(), SetError>;
fn get(&self, domain: &domain::Name) -> Result<domain::Domain, GetError>;
fn set(&self, domain: &domain::Name, config: &domain::Domain) -> Result<(), SetError>;
fn all_domains(&self) -> Result<Vec<domain::Name>, unexpected::Error>;
}
@ -67,7 +49,7 @@ impl FSStore {
}
impl Store for FSStore {
fn get(&self, domain: &domain::Name) -> Result<Config, GetError> {
fn get(&self, domain: &domain::Name) -> Result<domain::Domain, GetError> {
let path = self.config_file_path(domain);
let config_file = fs::File::open(path.as_path()).map_err(|e| match e.kind() {
io::ErrorKind::NotFound => GetError::NotFound,
@ -81,7 +63,7 @@ impl Store for FSStore {
Ok(config)
}
fn set(&self, domain: &domain::Name, config: &Config) -> Result<(), SetError> {
fn set(&self, domain: &domain::Name, config: &domain::Domain) -> Result<(), SetError> {
let dir_path = self.config_dir_path(domain);
fs::create_dir_all(dir_path.as_path())
.map_unexpected_while(|| format!("creating dir {}", dir_path.display()))?;
@ -132,7 +114,7 @@ mod tests {
let domain = domain::Name::from_str("foo.com").expect("domain parsed");
let config = Config {
let config = domain::Domain {
origin_descr: Descr::Git {
url: "bar".to_string(),
branch_name: "baz".to_string(),
@ -141,13 +123,13 @@ mod tests {
assert!(matches!(
store.get(&domain),
Err::<Config, GetError>(GetError::NotFound)
Err::<domain::Domain, GetError>(GetError::NotFound)
));
store.set(&domain, &config).expect("config set");
assert_eq!(config, store.get(&domain).expect("config retrieved"));
let new_config = Config {
let new_config = domain::Domain {
origin_descr: Descr::Git {
url: "BAR".to_string(),
branch_name: "BAZ".to_string(),

View File

@ -88,7 +88,7 @@ async fn main() {
.expect("domain checker initialization failed");
let domain_config_store =
domani::domain::config::FSStore::new(&config.domain_config_store_dir_path)
domani::domain::store::FSStore::new(&config.domain_config_store_dir_path)
.expect("domain config store initialization failed");
let domain_acme_manager = if config.https_listen_addr.is_some() {

View File

@ -192,7 +192,7 @@ impl<'svc> Service {
#[derive(Serialize)]
struct Response {
domain: domain::Name,
config: Option<domain::config::Config>,
config: Option<domain::Domain>,
}
let config = match self.domain_manager.get_config(&args.domain) {
@ -231,7 +231,7 @@ impl<'svc> Service {
challenge_token: String,
}
let config: domain::config::Config = match domain_config.try_into() {
let config: domain::Domain = match domain_config.try_into() {
Ok(Some(config)) => config,
Ok(None) => return self.render_error_page(400, "domain config is required"),
Err(e) => {
@ -268,7 +268,7 @@ impl<'svc> Service {
return self.render_error_page(401, "Incorrect passphrase");
}
let config: domain::config::Config = match domain_config.try_into() {
let config: domain::Domain = match domain_config.try_into() {
Ok(Some(config)) => config,
Ok(None) => return self.render_error_page(400, "domain config is required"),
Err(e) => {

View File

@ -11,7 +11,7 @@ pub struct FlatConfig {
config_origin_descr_git_branch_name: Option<String>,
}
impl TryFrom<FlatConfig> for Option<domain::config::Config> {
impl TryFrom<FlatConfig> for Option<domain::Domain> {
type Error = String;
fn try_from(v: FlatConfig) -> Result<Self, Self::Error> {
@ -21,7 +21,7 @@ impl TryFrom<FlatConfig> for Option<domain::config::Config> {
.as_str()
{
"" => Ok(None),
"git" => Ok(Some(domain::config::Config {
"git" => Ok(Some(domain::Domain {
origin_descr: origin::Descr::Git {
url: v
.config_origin_descr_git_url
@ -36,8 +36,8 @@ impl TryFrom<FlatConfig> for Option<domain::config::Config> {
}
}
impl From<domain::config::Config> for FlatConfig {
fn from(v: domain::config::Config) -> Self {
impl From<domain::Domain> for FlatConfig {
fn from(v: domain::Domain) -> Self {
match v.origin_descr {
origin::Descr::Git { url, branch_name } => FlatConfig {
config_origin_descr_kind: Some("git".to_string()),