From 9d72601e57eeb821314b6188e60817137baf223d Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Tue, 10 Nov 2020 08:18:19 +0100 Subject: Split configuration module in multiple files Signed-off-by: Matthias Beyer --- src/config/configuration.rs | 40 +++++++ src/config/container_config.rs | 11 ++ src/config/docker_config.rs | 42 ++++++++ src/config/endpoint_config.rs | 33 ++++++ src/config/mod.rs | 233 +++++------------------------------------ src/config/not_validated.rs | 79 ++++++++++++++ src/config/util.rs | 17 +++ 7 files changed, 250 insertions(+), 205 deletions(-) create mode 100644 src/config/configuration.rs create mode 100644 src/config/container_config.rs create mode 100644 src/config/docker_config.rs create mode 100644 src/config/endpoint_config.rs create mode 100644 src/config/not_validated.rs create mode 100644 src/config/util.rs (limited to 'src') diff --git a/src/config/configuration.rs b/src/config/configuration.rs new file mode 100644 index 0000000..294c5cb --- /dev/null +++ b/src/config/configuration.rs @@ -0,0 +1,40 @@ +use std::collections::BTreeMap; +use std::ops::Deref; +use std::path::PathBuf; + +use anyhow::Context; +use anyhow::Result; +use handlebars::Handlebars; + +use crate::config::NotValidatedConfiguration; + +#[derive(Debug)] +pub struct Configuration<'reg> { + pub (in crate::config) inner: NotValidatedConfiguration, + pub (in crate::config) hb: Handlebars<'reg>, +} + +impl<'reg> Deref for Configuration<'reg> { + type Target = NotValidatedConfiguration; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl<'reg> Configuration<'reg> { + /// Get the path to the releases directory, interpolate every variable used in the config + pub fn releases_directory(&self, hm: &BTreeMap) -> Result { + self.hb.render("releases", hm) + .map(PathBuf::from) + .context("Interpolating variables into 'release' setting from configuration") + } + + /// Get the path to the staging directory, interpolate every variable used in the config + pub fn staging_directory(&self, hm: &BTreeMap) -> Result { + self.hb.render("staging", hm) + .map(PathBuf::from) + .context("Interpolating variables into 'staging' setting from configuration") + } +} + diff --git a/src/config/container_config.rs b/src/config/container_config.rs new file mode 100644 index 0000000..aad17e8 --- /dev/null +++ b/src/config/container_config.rs @@ -0,0 +1,11 @@ +use getset::Getters; +use serde::Deserialize; + +use crate::util::EnvironmentVariableName; + +#[derive(Debug, Getters, Deserialize)] +pub struct ContainerConfig { + #[getset(get = "pub")] + allowed_env: Vec, +} + diff --git a/src/config/docker_config.rs b/src/config/docker_config.rs new file mode 100644 index 0000000..fcc605f --- /dev/null +++ b/src/config/docker_config.rs @@ -0,0 +1,42 @@ +use getset::{CopyGetters, Getters}; +use serde::Deserialize; + +use crate::config::Endpoint; +use crate::util::docker::ImageName; + +#[derive(Debug, Getters, CopyGetters, Deserialize)] +pub struct DockerConfig { + /// The required docker version + /// + /// If not set, it will not be checked, which might result in weird things? + /// + /// # Note + /// + /// Because the docker API returns strings, not a version object, each compatible version must + /// be listed. + #[getset(get = "pub")] + docker_versions: Option>, + + /// The required docker api version + /// + /// If not set, it will not be checked, which might result in weird things? + /// + /// # Note + /// + /// Because the docker API returns strings, not a version object, each compatible version must + /// be listed. + #[getset(get = "pub")] + docker_api_versions: Option>, + + /// Whether the program should verify that the required images are present. + /// You want this to be true normally. + #[getset(get_copy = "pub")] + verify_images_present: bool, + + #[getset(get = "pub")] + images: Vec, + + #[getset(get = "pub")] + endpoints: Vec, +} + diff --git a/src/config/endpoint_config.rs b/src/config/endpoint_config.rs new file mode 100644 index 0000000..2146910 --- /dev/null +++ b/src/config/endpoint_config.rs @@ -0,0 +1,33 @@ +use getset::{CopyGetters, Getters}; +use serde::Deserialize; + +#[derive(Clone, Debug, Getters, CopyGetters, Deserialize)] +pub struct Endpoint { + #[getset(get = "pub")] + name: String, + + #[getset(get = "pub")] + uri: String, + + #[getset(get = "pub")] + endpoint_type: EndpointType, + + /// Relative speed to other endpoints + /// + /// So if you have two servers, one with 12 cores and one with 24, you want to set "1" for the + /// first and "2" for the second (or "12" for the first and "24" for the second - the ratio is + /// the thing here)! + #[getset(get_copy = "pub")] + speed: usize, + + /// Maximum number of jobs which are allowed on this endpoint + #[getset(get_copy = "pub")] + maxjobs: usize, +} + +#[derive(Clone, Debug, Deserialize, Eq, PartialEq)] +pub enum EndpointType { + Socket, + Http, +} + diff --git a/src/config/mod.rs b/src/config/mod.rs index 4bd0df8..629b274 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,205 +1,28 @@ -use std::collections::BTreeMap; -use std::fmt::Debug; -use std::ops::Deref; -use std::path::PathBuf; - -use anyhow::Context; -use anyhow::Result; -use getset::CopyGetters; -use getset::Getters; -use handlebars::Handlebars; -use serde::Deserialize; - -use crate::phase::PhaseName; -use crate::util::EnvironmentVariableName; -use crate::util::docker::ImageName; - -#[derive(Debug, Getters, Deserialize)] -pub struct NotValidatedConfiguration { - #[getset(get = "pub")] - repository: PathBuf, - - #[serde(default = "default_progress_format")] - #[getset(get = "pub")] - progress_format: String, - - #[serde(default = "default_package_print_format")] - #[getset(get = "pub")] - package_print_format: String, - - #[serde(rename = "releases")] - releases_directory: String, - - #[serde(rename = "staging")] - staging_directory: String, - - #[getset(get = "pub")] - #[serde(rename = "database_host")] - database_host: String, - - #[getset(get = "pub")] - #[serde(rename = "database_port")] - database_port: String, - - #[getset(get = "pub")] - #[serde(rename = "database_user")] - database_user: String, - - #[getset(get = "pub")] - #[serde(rename = "database_password")] - database_password: String, - - #[getset(get = "pub")] - #[serde(rename = "database_name")] - database_name: String, - - #[getset(get = "pub")] - docker: DockerConfig, - - #[getset(get = "pub")] - containers: ContainerConfig, - - #[getset(get = "pub")] - available_phases: Vec, -} - -impl<'reg> NotValidatedConfiguration { - pub fn validate(self) -> Result> { - // TODO: Implement proper validation - - let hb = { - let mut hb = Handlebars::new(); - hb.register_template_string("releases", &self.releases_directory)?; - hb.register_template_string("staging", &self.staging_directory)?; - hb - }; - - Ok(Configuration { - inner: self, - hb, - }) - } -} - -#[derive(Debug)] -pub struct Configuration<'reg> { - inner: NotValidatedConfiguration, - hb: Handlebars<'reg>, -} - -impl<'reg> Deref for Configuration<'reg> { - type Target = NotValidatedConfiguration; - - fn deref(&self) -> &Self::Target { - &self.inner - } -} - -impl<'reg> Configuration<'reg> { - /// Get the path to the releases directory, interpolate every variable used in the config - pub fn releases_directory(&self, hm: &BTreeMap) -> Result { - self.hb.render("releases", hm) - .map(PathBuf::from) - .context("Interpolating variables into 'release' setting from configuration") - } - - /// Get the path to the staging directory, interpolate every variable used in the config - pub fn staging_directory(&self, hm: &BTreeMap) -> Result { - self.hb.render("staging", hm) - .map(PathBuf::from) - .context("Interpolating variables into 'staging' setting from configuration") - } -} - - -#[derive(Debug, Getters, CopyGetters, Deserialize)] -pub struct DockerConfig { - /// The required docker version - /// - /// If not set, it will not be checked, which might result in weird things? - /// - /// # Note - /// - /// Because the docker API returns strings, not a version object, each compatible version must - /// be listed. - #[getset(get = "pub")] - docker_versions: Option>, - - /// The required docker api version - /// - /// If not set, it will not be checked, which might result in weird things? - /// - /// # Note - /// - /// Because the docker API returns strings, not a version object, each compatible version must - /// be listed. - #[getset(get = "pub")] - docker_api_versions: Option>, - - /// Whether the program should verify that the required images are present. - /// You want this to be true normally. - #[getset(get_copy = "pub")] - verify_images_present: bool, - - #[getset(get = "pub")] - images: Vec, - - #[getset(get = "pub")] - endpoints: Vec, -} - -#[derive(Debug, Getters, Deserialize)] -pub struct ContainerConfig { - #[getset(get = "pub")] - allowed_env: Vec, -} - - -#[derive(Clone, Debug, Getters, CopyGetters, Deserialize)] -pub struct Endpoint { - #[getset(get = "pub")] - name: String, - - #[getset(get = "pub")] - uri: String, - - #[getset(get = "pub")] - endpoint_type: EndpointType, - - /// Relative speed to other endpoints - /// - /// So if you have two servers, one with 12 cores and one with 24, you want to set "1" for the - /// first and "2" for the second (or "12" for the first and "24" for the second - the ratio is - /// the thing here)! - #[getset(get_copy = "pub")] - speed: usize, - - /// Maximum number of jobs which are allowed on this endpoint - #[getset(get_copy = "pub")] - maxjobs: usize, -} - -#[derive(Clone, Debug, Deserialize, Eq, PartialEq)] -pub enum EndpointType { - Socket, - Http, -} - - -fn default_progress_format() -> String { - String::from("[{elapsed_precise}] ({percent:>3}%): {bar:40.cyan/blue} | {msg}") -} - -fn default_package_print_format() -> String { - String::from(indoc::indoc!(r#" - {{i}} - {{name}} : {{version}} - Source: {{source_url}} - Hash ({{source_hash_type}}): {{source_hash}}" - {{#if print_system_deps}}System Deps: {{ system_deps }} {{/if}} - {{#if print_system_runtime_deps}}System runtime Deps: {{ system_runtime_deps }} {{/if}} - {{#if print_build_deps}}Build Deps: {{ build_deps }} {{/if}} - {{#if print_runtime_deps}}Runtime Deps: {{ runtime_deps }} {{/if}} - - "#)) -} - +//! The configuration handling code +//! +//! This module contains all code for the configuration of butido itself. +//! +//! Please note that the `not_validated` module is the "entry point". +//! A "NotValidatedConfiguration" is loaded from the filesystem and then transformed into a +//! `Configuration` object via the `validate()` method. +//! +//! This mechanism is chosen because we might want to be able to do validation on the configuration +//! that is not possible to do with TOML itself. +//! + +mod configuration; +pub use configuration::*; + +mod container_config; +pub use container_config::*; + +mod docker_config; +pub use docker_config::*; + +mod endpoint_config; +pub use endpoint_config::*; + +mod not_validated; +pub use not_validated::*; + +mod util; diff --git a/src/config/not_validated.rs b/src/config/not_validated.rs new file mode 100644 index 0000000..6764578 --- /dev/null +++ b/src/config/not_validated.rs @@ -0,0 +1,79 @@ +use std::path::PathBuf; +use anyhow::Result; +use getset::Getters; +use handlebars::Handlebars; +use serde::Deserialize; + +use crate::config::Configuration; +use crate::config::ContainerConfig; +use crate::config::DockerConfig; +use crate::config::util::*; +use crate::phase::PhaseName; + +#[derive(Debug, Getters, Deserialize)] +pub struct NotValidatedConfiguration { + #[getset(get = "pub")] + repository: PathBuf, + + #[serde(default = "default_progress_format")] + #[getset(get = "pub")] + progress_format: String, + + #[serde(default = "default_package_print_format")] + #[getset(get = "pub")] + package_print_format: String, + + #[serde(rename = "releases")] + releases_directory: String, + + #[serde(rename = "staging")] + staging_directory: String, + + #[getset(get = "pub")] + #[serde(rename = "database_host")] + database_host: String, + + #[getset(get = "pub")] + #[serde(rename = "database_port")] + database_port: String, + + #[getset(get = "pub")] + #[serde(rename = "database_user")] + database_user: String, + + #[getset(get = "pub")] + #[serde(rename = "database_password")] + database_password: String, + + #[getset(get = "pub")] + #[serde(rename = "database_name")] + database_name: String, + + #[getset(get = "pub")] + docker: DockerConfig, + + #[getset(get = "pub")] + containers: ContainerConfig, + + #[getset(get = "pub")] + available_phases: Vec, +} + +impl<'reg> NotValidatedConfiguration { + pub fn validate(self) -> Result> { + // TODO: Implement proper validation + + let hb = { + let mut hb = Handlebars::new(); + hb.register_template_string("releases", &self.releases_directory)?; + hb.register_template_string("staging", &self.staging_directory)?; + hb + }; + + Ok(Configuration { + inner: self, + hb, + }) + } +} + diff --git a/src/config/util.rs b/src/config/util.rs new file mode 100644 index 0000000..8cae2d7 --- /dev/null +++ b/src/config/util.rs @@ -0,0 +1,17 @@ +pub fn default_progress_format() -> String { + String::from("[{elapsed_precise}] ({percent:>3}%): {bar:40.cyan/blue} | {msg}") +} + +pub fn default_package_print_format() -> String { + String::from(indoc::indoc!(r#" + {{i}} - {{name}} : {{version}} + Source: {{source_url}} + Hash ({{source_hash_type}}): {{source_hash}}" + {{#if print_system_deps}}System Deps: {{ system_deps }} {{/if}} + {{#if print_system_runtime_deps}}System runtime Deps: {{ system_runtime_deps }} {{/if}} + {{#if print_build_deps}}Build Deps: {{ build_deps }} {{/if}} + {{#if print_runtime_deps}}Runtime Deps: {{ runtime_deps }} {{/if}} + + "#)) +} + -- cgit v1.2.3