summaryrefslogtreecommitdiff
path: root/src/server.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/server.rs')
-rw-r--r--src/server.rs122
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(())
+ }
+}