//! Tool configuration. use crate::util::{check_network_names, expand_optional_pathbuf, expand_optional_pathbufs}; use log::debug; use serde::{Deserialize, Serialize}; use std::default::Default; use std::fs; use std::path::{Path, PathBuf}; /// Configuration from configuration file. #[derive(Default, Debug, Deserialize, Serialize)] #[serde(deny_unknown_fields)] pub struct Configuration { /// Base image, if provided. pub default_base_image: Option, /// Default size of new VM image, in GiB. pub default_image_gib: Option, /// Default memory to give to new VM, in MiB. pub default_memory_mib: Option, /// Default number of CPUs for a new VM. pub default_cpus: Option, /// Should host certificates be generated for new VMs? pub default_generate_host_certificate: Option, /// Should new VM be started automatically on host boot? pub default_autostart: Option, /// List of default networks to add to hosts. pub default_networks: Option>, /// Directory where new VM images should be created, if given. pub image_directory: Option, /// List of path names of SSH public keys to put into the default /// user's `authorized_keys` file. pub authorized_keys: Option>, /// Path name to SSH CA key for creating SSH host certificates. pub ca_key: Option, /// Path name to SSH CA public key for verifying SSH user certificates. pub user_ca_pubkey: Option, /// Should SSH authorized keys files be allowed by default? pub default_allow_authorized_keys: Option, } /// Errors from this module. #[derive(Debug, thiserror::Error)] pub enum ConfigurationError { /// Network name error. #[error(transparent)] NetworkNameError(#[from] crate::util::NetworkNameError), /// Error reading configuration file. #[error("couldn't read configuration file {0}")] ReadError(PathBuf, #[source] std::io::Error), /// YAML parsing error. #[error(transparent)] YamlError(#[from] serde_yaml::Error), /// Error expanding a ~user in a path name. #[error(transparent)] HomeDirError(#[from] home_dir::Error), } impl Configuration { /// Read configuration from named file. pub fn from_file(filename: &Path) -> Result { if filename.exists() { debug!("reading configuration file {}", filename.display()); let config = fs::read(filename) .map_err(|err| ConfigurationError::ReadError(filename.to_path_buf(), err))?; let mut config: Configuration = serde_yaml::from_slice(&config)?; if let Some(ref networks) = config.default_networks { check_network_names(networks)?; } config.expand_tildes()?; config.fill_in_missing_networks(); debug!("config: {:#?}", config); Ok(config) } else { Ok(Self::default()) } } fn fill_in_missing_networks(&mut self) { if self.default_networks.is_none() { self.default_networks = Some(vec!["network=default".to_string()]); } } fn expand_tildes(&mut self) -> Result<(), home_dir::Error> { expand_optional_pathbuf(&mut self.default_base_image)?; expand_optional_pathbuf(&mut self.image_directory)?; expand_optional_pathbuf(&mut self.image_directory)?; expand_optional_pathbuf(&mut self.ca_key)?; expand_optional_pathbuf(&mut self.user_ca_pubkey)?; expand_optional_pathbufs(&mut self.authorized_keys)?; Ok(()) } }