implement domain config store
This commit is contained in:
parent
7b60814681
commit
563f072e09
@ -7,6 +7,7 @@ edition = "2021"
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
clippy = "0.0.302"
|
clippy = "0.0.302"
|
||||||
|
tempdir = "0.3.7"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
sha2 = "0.10.6"
|
sha2 = "0.10.6"
|
||||||
@ -15,7 +16,6 @@ gix = { version = "0.44.1", features = [
|
|||||||
"blocking-network-client",
|
"blocking-network-client",
|
||||||
"blocking-http-transport-reqwest-rust-tls",
|
"blocking-http-transport-reqwest-rust-tls",
|
||||||
]}
|
]}
|
||||||
tempdir = "0.3.7"
|
|
||||||
serde = { version = "1.0.162", features = [ "derive" ]}
|
serde = { version = "1.0.162", features = [ "derive" ]}
|
||||||
serde_json = "1.0.96"
|
serde_json = "1.0.96"
|
||||||
trust-dns-client = "0.22.0"
|
trust-dns-client = "0.22.0"
|
||||||
|
@ -1 +1,2 @@
|
|||||||
pub mod checker;
|
pub mod checker;
|
||||||
|
pub mod config;
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use mockall::automock;
|
||||||
use trust_dns_client::client::{Client, SyncClient};
|
use trust_dns_client::client::{Client, SyncClient};
|
||||||
use trust_dns_client::rr::{DNSClass, Name, RData, RecordType};
|
use trust_dns_client::rr::{DNSClass, Name, RData, RecordType};
|
||||||
use trust_dns_client::udp::UdpClientConnection;
|
use trust_dns_client::udp::UdpClientConnection;
|
||||||
|
|
||||||
use mockall::automock;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum NewDNSCheckerError {
|
pub enum NewDNSCheckerError {
|
||||||
InvalidResolverAddress,
|
InvalidResolverAddress,
|
||||||
|
127
src/domain/config.rs
Normal file
127
src/domain/config.rs
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
use std::error::Error;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::{fs, io};
|
||||||
|
|
||||||
|
use crate::origin::Descr;
|
||||||
|
|
||||||
|
use mockall::automock;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
/// Values which the owner of a domain can configure when they install a domain.
|
||||||
|
pub struct Config {
|
||||||
|
pub origin_descr: Descr,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum GetError {
|
||||||
|
NotFound,
|
||||||
|
Unexpected(Box<dyn Error>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Error + 'static> From<E> for GetError {
|
||||||
|
fn from(e: E) -> GetError {
|
||||||
|
GetError::Unexpected(Box::from(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum SetError {
|
||||||
|
NotFound,
|
||||||
|
Unexpected(Box<dyn Error>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Error + 'static> From<E> for SetError {
|
||||||
|
fn from(e: E) -> SetError {
|
||||||
|
SetError::Unexpected(Box::from(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[automock]
|
||||||
|
pub trait Store {
|
||||||
|
fn get(&self, domain: &str) -> Result<Config, GetError>;
|
||||||
|
fn set(&self, domain: &str, config: &Config) -> Result<(), SetError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FSStore {
|
||||||
|
dir_path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FSStore {
|
||||||
|
pub fn new(dir_path: &Path) -> io::Result<FSStore> {
|
||||||
|
fs::create_dir_all(dir_path)?;
|
||||||
|
Ok(FSStore {
|
||||||
|
dir_path: dir_path.into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn config_dir_path(&self, domain: &str) -> PathBuf {
|
||||||
|
self.dir_path.join(domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn config_file_path(&self, domain: &str) -> PathBuf {
|
||||||
|
self.config_dir_path(domain).join("config.json")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Store for FSStore {
|
||||||
|
fn get(&self, domain: &str) -> Result<Config, GetError> {
|
||||||
|
let config_file =
|
||||||
|
fs::File::open(self.config_file_path(domain)).map_err(|e| match e.kind() {
|
||||||
|
io::ErrorKind::NotFound => GetError::NotFound,
|
||||||
|
_ => e.into(),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(serde_json::from_reader(config_file)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set(&self, domain: &str, config: &Config) -> Result<(), SetError> {
|
||||||
|
fs::create_dir_all(self.config_dir_path(domain))?;
|
||||||
|
|
||||||
|
let config_file = fs::File::create(self.config_file_path(domain))?;
|
||||||
|
serde_json::to_writer(config_file, config)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::origin::Descr;
|
||||||
|
use tempdir::TempDir;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic() {
|
||||||
|
let tmp_dir = TempDir::new("domain_config_store").unwrap();
|
||||||
|
|
||||||
|
let store = FSStore::new(tmp_dir.path()).expect("store created");
|
||||||
|
|
||||||
|
let domain = "foo";
|
||||||
|
|
||||||
|
let config = Config {
|
||||||
|
origin_descr: Descr::Git {
|
||||||
|
url: "bar".to_string(),
|
||||||
|
branch_name: "baz".to_string(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
store.get(domain),
|
||||||
|
Err::<Config, GetError>(GetError::NotFound)
|
||||||
|
));
|
||||||
|
|
||||||
|
store.set(domain, &config).expect("config set");
|
||||||
|
assert_eq!(config, store.get(domain).expect("config retrieved"));
|
||||||
|
|
||||||
|
let new_config = Config {
|
||||||
|
origin_descr: Descr::Git {
|
||||||
|
url: "BAR".to_string(),
|
||||||
|
branch_name: "BAZ".to_string(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
store.set(domain, &new_config).expect("config set");
|
||||||
|
assert_eq!(new_config, store.get(domain).expect("config retrieved"));
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
use super::Descr;
|
use super::Descr;
|
||||||
use serde_json;
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
@ -44,7 +43,12 @@ impl<E: Error + 'static> From<E> for AllDescrsError {
|
|||||||
type AllDescrsResult<T> = Result<T, AllDescrsError>;
|
type AllDescrsResult<T> = Result<T, AllDescrsError>;
|
||||||
|
|
||||||
pub trait Store {
|
pub trait Store {
|
||||||
fn write_file<W>(&self, descr: &Descr, path: &str, into: &mut W) -> Result<(), WriteFileError>
|
fn read_file_into<W>(
|
||||||
|
&self,
|
||||||
|
descr: &Descr,
|
||||||
|
path: &str,
|
||||||
|
into: &mut W,
|
||||||
|
) -> Result<(), WriteFileError>
|
||||||
where
|
where
|
||||||
W: std::io::Write + ?Sized;
|
W: std::io::Write + ?Sized;
|
||||||
|
|
||||||
@ -140,7 +144,7 @@ pub mod git {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_file<W>(
|
fn read_file_into<W>(
|
||||||
&self,
|
&self,
|
||||||
descr: &Descr,
|
descr: &Descr,
|
||||||
path: &str,
|
path: &str,
|
||||||
@ -235,7 +239,7 @@ pub mod git {
|
|||||||
let assert_write = |path: &str| {
|
let assert_write = |path: &str| {
|
||||||
let mut into: Vec<u8> = vec![];
|
let mut into: Vec<u8> = vec![];
|
||||||
store
|
store
|
||||||
.write_file(&descr, path, &mut into)
|
.read_file_into(&descr, path, &mut into)
|
||||||
.expect("write should succeed");
|
.expect("write should succeed");
|
||||||
assert!(into.len() > 0);
|
assert!(into.len() > 0);
|
||||||
};
|
};
|
||||||
@ -246,7 +250,7 @@ pub mod git {
|
|||||||
// File doesn't exist
|
// File doesn't exist
|
||||||
let mut into: Vec<u8> = vec![];
|
let mut into: Vec<u8> = vec![];
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
store.write_file(&descr, "DNE", &mut into),
|
store.read_file_into(&descr, "DNE", &mut into),
|
||||||
Err::<(), super::WriteFileError>(super::WriteFileError::FileNotFound),
|
Err::<(), super::WriteFileError>(super::WriteFileError::FileNotFound),
|
||||||
));
|
));
|
||||||
assert_eq!(into.len(), 0);
|
assert_eq!(into.len(), 0);
|
||||||
|
Loading…
Reference in New Issue
Block a user