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
|
use crate::config::Configuration;
use log::debug;
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::{Path, PathBuf};
#[derive(Debug, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
struct InputSpecification {
pub name: String,
#[serde(default)]
pub ssh_key_files: Vec<PathBuf>,
pub rsa_host_key: Option<String>,
pub rsa_host_cert: Option<String>,
pub dsa_host_key: Option<String>,
pub dsa_host_cert: Option<String>,
pub ecdsa_host_key: Option<String>,
pub ecdsa_host_cert: Option<String>,
pub ed25519_host_key: Option<String>,
pub ed25519_host_cert: Option<String>,
pub base: Option<PathBuf>,
pub image: PathBuf,
pub image_size_gib: u64,
pub memory_mib: u64,
pub cpus: u64,
}
#[derive(Debug)]
pub struct Specification {
pub name: String,
pub ssh_keys: Vec<String>,
pub rsa_host_key: Option<String>,
pub rsa_host_cert: Option<String>,
pub dsa_host_key: Option<String>,
pub dsa_host_cert: Option<String>,
pub ecdsa_host_key: Option<String>,
pub ecdsa_host_cert: Option<String>,
pub ed25519_host_key: Option<String>,
pub ed25519_host_cert: Option<String>,
pub base: PathBuf,
pub image: PathBuf,
pub image_size_gib: u64,
pub memory_mib: u64,
pub cpus: u64,
}
#[derive(Debug, thiserror::Error)]
pub enum SpecificationError {
#[error("No base image or default base image specified: {0}")]
NoBaseImage(PathBuf),
#[error("Failed to read SSH public key file {0}")]
SshKeyRead(PathBuf, #[source] std::io::Error),
#[error(transparent)]
IoError(#[from] std::io::Error),
#[error(transparent)]
FromUtf8Error(#[from] std::string::FromUtf8Error),
#[error(transparent)]
YamlError(#[from] serde_yaml::Error),
}
impl Specification {
pub fn from_file(
config: &Configuration,
filename: &Path,
) -> Result<Specification, SpecificationError> {
debug!("reading specification from {}", filename.display());
let spec = fs::read(filename)?;
let input: InputSpecification = serde_yaml::from_slice(&spec)?;
debug!("specification as read from file: {:#?}", input);
let base = if let Some(base) = input.base {
base.to_path_buf()
} else if let Some(ref base) = config.default_base_image {
base.to_path_buf()
} else {
return Err(SpecificationError::NoBaseImage(filename.to_path_buf()));
};
let spec = Specification {
name: input.name.clone(),
ssh_keys: ssh_keys(&input.ssh_key_files)?,
rsa_host_key: input.rsa_host_key,
rsa_host_cert: input.rsa_host_cert,
dsa_host_key: input.dsa_host_key,
dsa_host_cert: input.dsa_host_cert,
ecdsa_host_key: input.ecdsa_host_key,
ecdsa_host_cert: input.ecdsa_host_cert,
ed25519_host_key: input.ed25519_host_key,
ed25519_host_cert: input.ed25519_host_cert,
base,
image: input.image.clone(),
image_size_gib: input.image_size_gib,
memory_mib: input.memory_mib,
cpus: input.cpus,
};
debug!("specification as with defaults applied: {:#?}", spec);
Ok(spec)
}
}
fn ssh_keys(filenames: &[PathBuf]) -> Result<Vec<String>, SpecificationError> {
let mut keys = vec![];
for filename in filenames {
let key = std::fs::read(filename)
.map_err(|e| SpecificationError::SshKeyRead(filename.to_path_buf(), e))?;
let key = String::from_utf8(key)?;
let key = key.strip_suffix("\n").or(Some(&key)).unwrap();
keys.push(key.to_string());
}
Ok(keys)
}
|