diff options
Diffstat (limited to 'src/config')
-rw-r--r-- | src/config/mod.rs | 6 | ||||
-rw-r--r-- | src/config/options.rs | 86 | ||||
-rw-r--r-- | src/config/options_test.rs | 179 | ||||
-rw-r--r-- | src/config/runtime.rs | 148 |
4 files changed, 215 insertions, 204 deletions
diff --git a/src/config/mod.rs b/src/config/mod.rs index 14926bd..2bf8f66 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -4,8 +4,10 @@ mod options_test; mod runtime; pub use options::{ConfigOpts, ConfigOptsAcme, ConfigOptsBase, ConfigOptsConsul}; -pub use runtime::{RuntimeConfig, RuntimeConfigAcme, RuntimeConfigConsul, RuntimeConfigFirewall, RuntimeConfigIgd}; +pub use runtime::{ + RuntimeConfig, RuntimeConfigAcme, RuntimeConfigConsul, RuntimeConfigFirewall, RuntimeConfigIgd, +}; pub const EXPIRATION_TIME: u16 = 300; pub const REFRESH_TIME: u16 = 60; -pub const CONSUL_URL: &str = "http://127.0.0.1:8500";
\ No newline at end of file +pub const CONSUL_URL: &str = "http://127.0.0.1:8500"; diff --git a/src/config/options.rs b/src/config/options.rs index 36da475..f62d14c 100644 --- a/src/config/options.rs +++ b/src/config/options.rs @@ -11,66 +11,68 @@ use crate::config::RuntimeConfig; /// Base configuration options #[derive(Clone, Default, Deserialize)] pub struct ConfigOptsBase { - /// This node's private IP address [default: None] - pub private_ip: Option<String>, - /// Expiration time for IGD rules [default: 60] - pub expiration_time: Option<u16>, - /// Refresh time for IGD and Firewall rules [default: 300] - pub refresh_time: Option<u16>, + /// This node's private IP address [default: None] + pub private_ip: Option<String>, + /// Expiration time for IGD rules [default: 60] + pub expiration_time: Option<u16>, + /// Refresh time for IGD and Firewall rules [default: 300] + pub refresh_time: Option<u16>, } /// ACME configuration options #[derive(Clone, Default, Deserialize)] pub struct ConfigOptsAcme { - /// Whether ACME is enabled [default: false] - #[serde(default)] - pub enable: bool, + /// Whether ACME is enabled [default: false] + #[serde(default)] + pub enable: bool, - /// The default domain holder's e-mail [default: None] - pub email: Option<String>, + /// The default domain holder's e-mail [default: None] + pub email: Option<String>, } /// Consul configuration options #[derive(Clone, Default, Deserialize)] pub struct ConfigOptsConsul { - /// Consul's node name [default: None] - pub node_name: Option<String>, - /// Consul's REST URL [default: "http://127.0.0.1:8500"] - pub url: Option<String>, + /// Consul's node name [default: None] + pub node_name: Option<String>, + /// Consul's REST URL [default: "http://127.0.0.1:8500"] + pub url: Option<String>, } /// Model of all potential configuration options pub struct ConfigOpts { - pub base: ConfigOptsBase, - pub acme: ConfigOptsAcme, - pub consul: ConfigOptsConsul, + pub base: ConfigOptsBase, + pub acme: ConfigOptsAcme, + pub consul: ConfigOptsConsul, } impl ConfigOpts { - pub fn from_env() -> Result<RuntimeConfig> { - let base: ConfigOptsBase = envy::prefixed("DIPLONAT_").from_env()?; - let consul: ConfigOptsConsul = envy::prefixed("DIPLONAT_CONSUL_").from_env()?; - let acme: ConfigOptsAcme = envy::prefixed("DIPLONAT_ACME_").from_env()?; + pub fn from_env() -> Result<RuntimeConfig> { + let base: ConfigOptsBase = envy::prefixed("DIPLONAT_").from_env()?; + let consul: ConfigOptsConsul = envy::prefixed("DIPLONAT_CONSUL_").from_env()?; + let acme: ConfigOptsAcme = envy::prefixed("DIPLONAT_ACME_").from_env()?; - RuntimeConfig::new(Self { - base: base, - consul: consul, - acme: acme, - }) - } + RuntimeConfig::new(Self { + base: base, + consul: consul, + acme: acme, + }) + } - // Currently only used in tests - #[allow(dead_code)] - pub fn from_iter<Iter: Clone>(iter: Iter) -> Result<RuntimeConfig> - where Iter: IntoIterator<Item = (String, String)> { - let base: ConfigOptsBase = envy::prefixed("DIPLONAT_").from_iter(iter.clone())?; - let consul: ConfigOptsConsul = envy::prefixed("DIPLONAT_CONSUL_").from_iter(iter.clone())?; - let acme: ConfigOptsAcme = envy::prefixed("DIPLONAT_ACME_").from_iter(iter.clone())?; + // Currently only used in tests + #[allow(dead_code)] + pub fn from_iter<Iter: Clone>(iter: Iter) -> Result<RuntimeConfig> + where + Iter: IntoIterator<Item = (String, String)>, + { + let base: ConfigOptsBase = envy::prefixed("DIPLONAT_").from_iter(iter.clone())?; + let consul: ConfigOptsConsul = envy::prefixed("DIPLONAT_CONSUL_").from_iter(iter.clone())?; + let acme: ConfigOptsAcme = envy::prefixed("DIPLONAT_ACME_").from_iter(iter.clone())?; - RuntimeConfig::new(Self { - base: base, - consul: consul, - acme: acme, - }) - } -}
\ No newline at end of file + RuntimeConfig::new(Self { + base: base, + consul: consul, + acme: acme, + }) + } +} diff --git a/src/config/options_test.rs b/src/config/options_test.rs index a6063fd..c0c7367 100644 --- a/src/config/options_test.rs +++ b/src/config/options_test.rs @@ -5,116 +5,125 @@ use crate::config::*; // Environment variables are set for the entire process and // tests are run whithin the same process. -// => We cannot test ConfigOpts::from_env(), +// => We cannot test ConfigOpts::from_env(), // because tests modify each other's environment. // This is why we only test ConfigOpts::from_iter(iter). fn minimal_valid_options() -> HashMap<String, String> { - let mut opts = HashMap::new(); - opts.insert("DIPLONAT_PRIVATE_IP".to_string(), "172.123.43.555".to_string()); - opts.insert("DIPLONAT_CONSUL_NODE_NAME".to_string(), "consul_node".to_string()); - opts + let mut opts = HashMap::new(); + opts.insert( + "DIPLONAT_PRIVATE_IP".to_string(), + "172.123.43.555".to_string(), + ); + opts.insert( + "DIPLONAT_CONSUL_NODE_NAME".to_string(), + "consul_node".to_string(), + ); + opts } fn all_valid_options() -> HashMap<String, String> { - let mut opts = minimal_valid_options(); - opts.insert("DIPLONAT_EXPIRATION_TIME".to_string(), "30".to_string()); - opts.insert("DIPLONAT_REFRESH_TIME".to_string(), "10".to_string()); - opts.insert("DIPLONAT_CONSUL_URL".to_string(), "http://127.0.0.1:9999".to_string()); - opts.insert("DIPLONAT_ACME_ENABLE".to_string(), "true".to_string()); - opts.insert("DIPLONAT_ACME_EMAIL".to_string(), "bozo@bozo.net".to_string()); - opts + let mut opts = minimal_valid_options(); + opts.insert("DIPLONAT_EXPIRATION_TIME".to_string(), "30".to_string()); + opts.insert("DIPLONAT_REFRESH_TIME".to_string(), "10".to_string()); + opts.insert( + "DIPLONAT_CONSUL_URL".to_string(), + "http://127.0.0.1:9999".to_string(), + ); + opts.insert("DIPLONAT_ACME_ENABLE".to_string(), "true".to_string()); + opts.insert( + "DIPLONAT_ACME_EMAIL".to_string(), + "bozo@bozo.net".to_string(), + ); + opts } #[test] #[should_panic] fn err_empty_env() { - std::env::remove_var("DIPLONAT_PRIVATE_IP"); - std::env::remove_var("DIPLONAT_CONSUL_NODE_NAME"); - ConfigOpts::from_env().unwrap(); + std::env::remove_var("DIPLONAT_PRIVATE_IP"); + std::env::remove_var("DIPLONAT_CONSUL_NODE_NAME"); + ConfigOpts::from_env().unwrap(); } #[test] fn ok_from_iter_minimal_valid_options() { - let opts = minimal_valid_options(); - let rt_config = ConfigOpts::from_iter(opts.clone()).unwrap(); + let opts = minimal_valid_options(); + let rt_config = ConfigOpts::from_iter(opts.clone()).unwrap(); - assert!(rt_config.acme.is_none()); - assert_eq!( - &rt_config.consul.node_name, - opts.get(&"DIPLONAT_CONSUL_NODE_NAME".to_string()).unwrap() - ); - assert_eq!( - rt_config.consul.url, - CONSUL_URL.to_string() - ); - assert_eq!( - rt_config.firewall.refresh_time, - Duration::from_secs(REFRESH_TIME.into()) - ); - assert_eq!( - &rt_config.igd.private_ip, - opts.get(&"DIPLONAT_PRIVATE_IP".to_string()).unwrap() - ); - assert_eq!( - rt_config.igd.expiration_time, - Duration::from_secs(EXPIRATION_TIME.into()) - ); - assert_eq!( - rt_config.igd.refresh_time, - Duration::from_secs(REFRESH_TIME.into()) - ); + assert!(rt_config.acme.is_none()); + assert_eq!( + &rt_config.consul.node_name, + opts.get(&"DIPLONAT_CONSUL_NODE_NAME".to_string()).unwrap() + ); + assert_eq!(rt_config.consul.url, CONSUL_URL.to_string()); + assert_eq!( + rt_config.firewall.refresh_time, + Duration::from_secs(REFRESH_TIME.into()) + ); + assert_eq!( + &rt_config.igd.private_ip, + opts.get(&"DIPLONAT_PRIVATE_IP".to_string()).unwrap() + ); + assert_eq!( + rt_config.igd.expiration_time, + Duration::from_secs(EXPIRATION_TIME.into()) + ); + assert_eq!( + rt_config.igd.refresh_time, + Duration::from_secs(REFRESH_TIME.into()) + ); } #[test] #[should_panic] fn err_from_iter_invalid_refresh_time() { - let mut opts = minimal_valid_options(); - opts.insert("DIPLONAT_EXPIRATION_TIME".to_string(), "60".to_string()); - opts.insert("DIPLONAT_REFRESH_TIME".to_string(), "60".to_string()); - ConfigOpts::from_iter(opts).unwrap(); + let mut opts = minimal_valid_options(); + opts.insert("DIPLONAT_EXPIRATION_TIME".to_string(), "60".to_string()); + opts.insert("DIPLONAT_REFRESH_TIME".to_string(), "60".to_string()); + ConfigOpts::from_iter(opts).unwrap(); } #[test] fn ok_from_iter_all_valid_options() { - let opts = all_valid_options(); - let rt_config = ConfigOpts::from_iter(opts.clone()).unwrap(); + let opts = all_valid_options(); + let rt_config = ConfigOpts::from_iter(opts.clone()).unwrap(); - let expiration_time = Duration::from_secs( - opts.get(&"DIPLONAT_EXPIRATION_TIME".to_string()).unwrap() - .parse::<u64>().unwrap() - .into()); - let refresh_time = Duration::from_secs( - opts.get(&"DIPLONAT_REFRESH_TIME".to_string()).unwrap() - .parse::<u64>().unwrap() - .into()); + let expiration_time = Duration::from_secs( + opts + .get(&"DIPLONAT_EXPIRATION_TIME".to_string()) + .unwrap() + .parse::<u64>() + .unwrap() + .into(), + ); + let refresh_time = Duration::from_secs( + opts + .get(&"DIPLONAT_REFRESH_TIME".to_string()) + .unwrap() + .parse::<u64>() + .unwrap() + .into(), + ); - assert!(rt_config.acme.is_some()); - assert_eq!( - &rt_config.acme.unwrap().email, - opts.get(&"DIPLONAT_ACME_EMAIL".to_string()).unwrap()); - assert_eq!( - &rt_config.consul.node_name, - opts.get(&"DIPLONAT_CONSUL_NODE_NAME".to_string()).unwrap() - ); - assert_eq!( - &rt_config.consul.url, - opts.get(&"DIPLONAT_CONSUL_URL".to_string()).unwrap() - ); - assert_eq!( - rt_config.firewall.refresh_time, - refresh_time - ); - assert_eq!( - &rt_config.igd.private_ip, - opts.get(&"DIPLONAT_PRIVATE_IP".to_string()).unwrap() - ); - assert_eq!( - rt_config.igd.expiration_time, - expiration_time - ); - assert_eq!( - rt_config.igd.refresh_time, - refresh_time - ); -}
\ No newline at end of file + assert!(rt_config.acme.is_some()); + assert_eq!( + &rt_config.acme.unwrap().email, + opts.get(&"DIPLONAT_ACME_EMAIL".to_string()).unwrap() + ); + assert_eq!( + &rt_config.consul.node_name, + opts.get(&"DIPLONAT_CONSUL_NODE_NAME".to_string()).unwrap() + ); + assert_eq!( + &rt_config.consul.url, + opts.get(&"DIPLONAT_CONSUL_URL".to_string()).unwrap() + ); + assert_eq!(rt_config.firewall.refresh_time, refresh_time); + assert_eq!( + &rt_config.igd.private_ip, + opts.get(&"DIPLONAT_PRIVATE_IP".to_string()).unwrap() + ); + assert_eq!(rt_config.igd.expiration_time, expiration_time); + assert_eq!(rt_config.igd.refresh_time, refresh_time); +} diff --git a/src/config/runtime.rs b/src/config/runtime.rs index 58c86b9..0d52b15 100644 --- a/src/config/runtime.rs +++ b/src/config/runtime.rs @@ -1,6 +1,6 @@ use std::time::Duration; -use anyhow::{Result, anyhow}; +use anyhow::{anyhow, Result}; use crate::config::{ConfigOpts, ConfigOptsAcme, ConfigOptsBase, ConfigOptsConsul}; @@ -11,111 +11,109 @@ use crate::config::{ConfigOpts, ConfigOptsAcme, ConfigOptsBase, ConfigOptsConsul #[derive(Debug)] pub struct RuntimeConfigAcme { - pub email: String, + pub email: String, } #[derive(Debug)] pub struct RuntimeConfigConsul { - pub node_name: String, - pub url: String, + pub node_name: String, + pub url: String, } #[derive(Debug)] pub struct RuntimeConfigFirewall { - pub refresh_time: Duration, + pub refresh_time: Duration, } #[derive(Debug)] pub struct RuntimeConfigIgd { - pub private_ip: String, - pub expiration_time: Duration, - pub refresh_time: Duration, + pub private_ip: String, + pub expiration_time: Duration, + pub refresh_time: Duration, } #[derive(Debug)] pub struct RuntimeConfig { - pub acme: Option<RuntimeConfigAcme>, - pub consul: RuntimeConfigConsul, - pub firewall: RuntimeConfigFirewall, - pub igd: RuntimeConfigIgd, + pub acme: Option<RuntimeConfigAcme>, + pub consul: RuntimeConfigConsul, + pub firewall: RuntimeConfigFirewall, + pub igd: RuntimeConfigIgd, } impl RuntimeConfig { - pub fn new(opts: ConfigOpts) -> Result<Self> { - let acme = RuntimeConfigAcme::new(opts.acme.clone())?; - let consul = RuntimeConfigConsul::new(opts.consul.clone())?; - let firewall = RuntimeConfigFirewall::new(opts.base.clone())?; - let igd = RuntimeConfigIgd::new(opts.base.clone())?; - - Ok(Self { - acme, - consul, - firewall, - igd, - }) - } + pub fn new(opts: ConfigOpts) -> Result<Self> { + let acme = RuntimeConfigAcme::new(opts.acme.clone())?; + let consul = RuntimeConfigConsul::new(opts.consul.clone())?; + let firewall = RuntimeConfigFirewall::new(opts.base.clone())?; + let igd = RuntimeConfigIgd::new(opts.base.clone())?; + + Ok(Self { + acme, + consul, + firewall, + igd, + }) + } } impl RuntimeConfigAcme { - pub fn new(opts: ConfigOptsAcme) -> Result<Option<Self>> { - if !opts.enable { - return Ok(None); - } - - let email = opts.email.expect( - "'DIPLONAT_ACME_EMAIL' environment variable is required \ - if 'DIPLONAT_ACME_ENABLE' == 'true'"); - - Ok(Some(Self { - email, - })) - } + pub fn new(opts: ConfigOptsAcme) -> Result<Option<Self>> { + if !opts.enable { + return Ok(None); + } + + let email = opts.email.expect( + "'DIPLONAT_ACME_EMAIL' environment variable is required \ + if 'DIPLONAT_ACME_ENABLE' == 'true'", + ); + + Ok(Some(Self { email })) + } } impl RuntimeConfigConsul { - pub(super) fn new(opts: ConfigOptsConsul) -> Result<Self> { - let node_name = opts.node_name.expect( - "'DIPLONAT_CONSUL_NODE_NAME' environment variable is required"); - let url = opts.url.unwrap_or(super::CONSUL_URL.to_string()); - - Ok(Self { - node_name, - url, - }) - } + pub(super) fn new(opts: ConfigOptsConsul) -> Result<Self> { + let node_name = opts + .node_name + .expect("'DIPLONAT_CONSUL_NODE_NAME' environment variable is required"); + let url = opts.url.unwrap_or(super::CONSUL_URL.to_string()); + + Ok(Self { node_name, url }) + } } impl RuntimeConfigFirewall { - pub(super) fn new(opts: ConfigOptsBase) -> Result<Self> { - let refresh_time = Duration::from_secs( - opts.refresh_time.unwrap_or(super::REFRESH_TIME).into()); - - Ok(Self { - refresh_time, - }) - } + pub(super) fn new(opts: ConfigOptsBase) -> Result<Self> { + let refresh_time = Duration::from_secs(opts.refresh_time.unwrap_or(super::REFRESH_TIME).into()); + + Ok(Self { refresh_time }) + } } impl RuntimeConfigIgd { - pub(super) fn new(opts: ConfigOptsBase) -> Result<Self> { - let private_ip = opts.private_ip.expect( - "'DIPLONAT_PRIVATE_IP' environment variable is required"); - let expiration_time = Duration::from_secs( - opts.expiration_time.unwrap_or(super::EXPIRATION_TIME).into()); - let refresh_time = Duration::from_secs( - opts.refresh_time.unwrap_or(super::REFRESH_TIME).into()); - - if refresh_time.as_secs() * 2 > expiration_time.as_secs() { - return Err(anyhow!( + pub(super) fn new(opts: ConfigOptsBase) -> Result<Self> { + let private_ip = opts + .private_ip + .expect("'DIPLONAT_PRIVATE_IP' environment variable is required"); + let expiration_time = Duration::from_secs( + opts + .expiration_time + .unwrap_or(super::EXPIRATION_TIME) + .into(), + ); + let refresh_time = Duration::from_secs(opts.refresh_time.unwrap_or(super::REFRESH_TIME).into()); + + if refresh_time.as_secs() * 2 > expiration_time.as_secs() { + return Err(anyhow!( "IGD expiration time (currently: {}s) must be at least twice bigger than refresh time (currently: {}s)", expiration_time.as_secs(), refresh_time.as_secs())); - } - - Ok(Self { - private_ip, - expiration_time, - refresh_time, - }) - } -}
\ No newline at end of file + } + + Ok(Self { + private_ip, + expiration_time, + refresh_time, + }) + } +} |