1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
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(())
}
}
|