diff options
Diffstat (limited to 'src/server.rs')
-rw-r--r-- | src/server.rs | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/src/server.rs b/src/server.rs new file mode 100644 index 0000000..9a809a5 --- /dev/null +++ b/src/server.rs @@ -0,0 +1,122 @@ +use crate::daemon::{Daemon, DaemonError, DaemonManager}; +use crate::tlsgen::{Tls, TlsError}; +use log::debug; +use rand::random; +use serde::Serialize; +use std::ops::Range; +use std::path::{Path, PathBuf}; +use std::time::Instant; +use tempfile::{tempdir, TempDir}; + +const PORT_RANGE: Range<PortNumber> = 2048..32000; +const TIMEOUT_MS: u128 = 10_000; + +type PortNumber = u16; + +#[derive(Debug, thiserror::Error)] +pub enum ObnamServerError { + #[error("took too long to pick a random port for server")] + Port, + + #[error("failed to create temporary directory for server: {0}")] + TempDir(std::io::Error), + + #[error("failed to write server configuration to {0}: {1}")] + WriteConfig(PathBuf, std::io::Error), + + #[error("failed to create TLS certificate: {0}")] + Tls(TlsError), + + #[error("failed to write TLS certificate: {0}")] + WriteTls(PathBuf, std::io::Error), + + #[error("failed to start Obnam server: {0}")] + Daemon(DaemonError), +} + +#[derive(Debug)] +pub struct ObnamServer { + #[allow(dead_code)] + tempdir: TempDir, + chunks: PathBuf, + daemon: Option<Daemon>, +} + +impl ObnamServer { + pub fn new(manager: &DaemonManager) -> Result<Self, ObnamServerError> { + debug!("creating ObamServer"); + let tempdir = tempdir().map_err(ObnamServerError::TempDir)?; + let config_filename = tempdir.path().join("server.yaml"); + let chunks = tempdir.path().join("chunks"); + let tls_key = tempdir.path().join("tls_key"); + let tls_cert = tempdir.path().join("tls_cert"); + + let tls = Tls::new().map_err(ObnamServerError::Tls)?; + write(&tls_key, tls.key())?; + write(&tls_cert, tls.cert())?; + + let port = pick_port()?; + + let config = ServerConfig::new(port, chunks.clone(), tls_key, tls_cert); + config.write(&config_filename)?; + + let daemon = manager + .start(&["/bin/sleep", "1000"]) + .map_err(ObnamServerError::Daemon)?; + + Ok(Self { + tempdir, + chunks, + daemon: Some(daemon), + }) + } + + pub fn stop(&mut self) { + self.daemon.take(); + } + + pub fn chunks(&self) -> &Path { + &self.chunks + } +} + +fn write(filename: &Path, data: &[u8]) -> Result<(), ObnamServerError> { + std::fs::write(filename, data) + .map_err(|err| ObnamServerError::WriteTls(filename.to_path_buf(), err)) +} + +fn pick_port() -> Result<PortNumber, ObnamServerError> { + let started = Instant::now(); + while started.elapsed().as_millis() < TIMEOUT_MS { + let port: PortNumber = random(); + if PORT_RANGE.contains(&port) { + return Ok(port); + } + } + Err(ObnamServerError::Port) +} + +#[derive(Debug, Serialize)] +pub struct ServerConfig { + address: String, + chunks: PathBuf, + tls_key: PathBuf, + tls_cert: PathBuf, +} + +impl ServerConfig { + fn new(port: u16, chunks: PathBuf, tls_key: PathBuf, tls_cert: PathBuf) -> Self { + Self { + address: format!("localhost:{}", port), + chunks, + tls_key, + tls_cert, + } + } + + fn write(&self, filename: &Path) -> Result<(), ObnamServerError> { + std::fs::write(filename, serde_yaml::to_string(self).unwrap()) + .map_err(|err| ObnamServerError::WriteConfig(filename.to_path_buf(), err))?; + Ok(()) + } +} |