From c742fb0a5d790ae20a1f81fdde8a2aa693bbc8ce Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Tue, 15 Sep 2020 14:31:10 +0200 Subject: [PATCH] Better configuration stuff --- Cargo.toml | 5 ++++- src/access.rs | 22 +++++++++---------- src/auth.rs | 4 ++-- src/config.rs | 51 +++++++++++++++++++------------------------ src/error.rs | 10 +++++++++ src/log.rs | 4 ++-- src/machine.rs | 24 +++----------------- src/main.rs | 4 ++-- src/modules.rs | 4 ++-- src/modules/shelly.rs | 16 +++++++------- 10 files changed, 67 insertions(+), 77 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9878499..0928f43 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,10 +24,13 @@ capnp = "0.13" capnp-rpc = "0.13" capnp-futures = "0.13" -toml = "0.5" serde = { version = "1.0", features = ["derive"] } flexbuffers = "0.1" +glob = "0.3" +toml = "0.5" +config = { version = "0.10", default-features = false, features = ["toml"] } + uuid = { version = "0.8", features = ["serde", "v4"] } clap = "2.33" diff --git a/src/access.rs b/src/access.rs index 07b19e7..84b6751 100644 --- a/src/access.rs +++ b/src/access.rs @@ -15,7 +15,7 @@ use serde::{Serialize, Deserialize}; use slog::Logger; use lmdb::{Transaction, RwTransaction, Cursor}; -use crate::config::Config; +use crate::config::Settings; use crate::error::Result; // FIXME: fabinfra/fabaccess/bffh#3 @@ -196,11 +196,11 @@ impl PermissionsProvider { let (kbytes, _rest) = kbuf.split_at(std::mem::size_of::()); let roleID = u64::from_ne_bytes(kbytes.try_into().unwrap()); let role: Role = flexbuffers::from_slice(vbuf)?; - let filename = format!("{:x}.toml", roleID); + let filename = format!("{:x}.yml", roleID); path.set_file_name(filename); let mut fp = std::fs::File::create(&path)?; - let toml = toml::to_vec(&role)?; - fp.write_all(&toml)?; + let out = toml::to_vec(&role)?; + fp.write_all(&out)?; } Ok(()) @@ -213,11 +213,11 @@ impl PermissionsProvider { let (kbytes, _rest) = kbuf.split_at(std::mem::size_of::()); let permID = u64::from_ne_bytes(kbytes.try_into().unwrap()); let perm: Perm = flexbuffers::from_slice(vbuf)?; - let filename = format!("{:x}.toml", permID); + let filename = format!("{:x}.yml", permID); path.set_file_name(filename); let mut fp = std::fs::File::create(&path)?; - let toml = toml::to_vec(&perm)?; - fp.write_all(&toml)?; + let out = toml::to_vec(&perm)?; + fp.write_all(&out)?; } Ok(()) @@ -230,11 +230,11 @@ impl PermissionsProvider { let (kbytes, _rest) = kbuf.split_at(std::mem::size_of::()); let userID = u64::from_ne_bytes(kbytes.try_into().unwrap()); let user: User = flexbuffers::from_slice(vbuf)?; - let filename = format!("{:x}.toml", userID); + let filename = format!("{:x}.yml", userID); path.set_file_name(filename); let mut fp = std::fs::File::create(&path)?; - let toml = toml::to_vec(&user)?; - fp.write_all(&toml)?; + let out = toml::to_vec(&user)?; + fp.write_all(&out)?; } Ok(()) @@ -408,7 +408,7 @@ impl PermissionsProvider { } /// This line documents init -pub fn init(log: Logger, config: &Config, env: &lmdb::Environment) -> std::result::Result { +pub fn init(log: Logger, config: &Settings, env: &lmdb::Environment) -> std::result::Result { let mut flags = lmdb::DatabaseFlags::empty(); flags.set(lmdb::DatabaseFlags::INTEGER_KEY, true); let roledb = env.create_db(Some("role"), flags)?; diff --git a/src/auth.rs b/src/auth.rs index 52af411..703f566 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -9,7 +9,7 @@ use rsasl::{SASL, Property, Session, ReturnCode}; use rsasl::sys::{Gsasl, Gsasl_session}; use crate::error::Result; -use crate::config::Config; +use crate::config::Settings; pub mod auth_capnp { include!(concat!(env!("OUT_DIR"), "/schema/auth_capnp.rs")); @@ -53,6 +53,6 @@ impl Auth { } } -pub async fn init(log: Logger, config: Config) -> Result { +pub async fn init(log: Logger, config: Settings) -> Result { Ok(Auth::new()) } diff --git a/src/config.rs b/src/config.rs index dee4bbd..d3c1a52 100644 --- a/src/config.rs +++ b/src/config.rs @@ -8,29 +8,29 @@ use crate::error::Result; use std::default::Default; -pub fn read(path: &Path) -> Result { - let mut fp = File::open(path)?; - let mut contents = String::new(); - fp.read_to_string(&mut contents)?; +use std::collections::HashMap; - let config = toml::from_str(&contents)?; - Ok(config) +use config::Config; +pub use config::ConfigError; +use glob::glob; + +pub fn read(path: &Path) -> Result { + let mut settings = Config::default(); + settings + .merge(config::File::from(path)).unwrap(); + + Ok(settings.try_into()?) } #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Config { - pub db: PathBuf, - pub machinedb: PathBuf, - pub passdb: PathBuf, - pub(crate) access: Access, - pub listen: Box<[Listen]>, - pub mqtt_url: String, +pub struct Settings { + pub listens: Box<[Listen]>, + pub shelly: Option } #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Access { - pub(crate) model: PathBuf, - pub(crate) policy: PathBuf +pub struct ShellyCfg { + pub mqtt_url: String } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -39,25 +39,20 @@ pub struct Listen { pub port: Option, } -impl Default for Config { +impl Default for Settings { fn default() -> Self { - Config { - db: PathBuf::from_str("/tmp/bffh.db").unwrap(), - machinedb: PathBuf::from_str("/tmp/machines.db").unwrap(), - access: Access { - model: PathBuf::from_str("/tmp/model.conf").unwrap(), - policy: PathBuf::from_str("/tmp/policy.csv").unwrap(), - }, - passdb: PathBuf::from_str("/tmp/passwd.db").unwrap(), - listen: Box::new([Listen { + Settings { + listens: Box::new([Listen { address: "127.0.0.1".to_string(), port: Some(DEFAULT_PORT) }, Listen { address: "::1".to_string(), port: Some(DEFAULT_PORT) - }]), - mqtt_url: "127.0.0.1:1883".to_string(), + }]), + shelly: Some(ShellyCfg { + mqtt_url: "127.0.0.1:1883".to_string() + }), } } } diff --git a/src/error.rs b/src/error.rs index 8bbeb96..7c8e537 100644 --- a/src/error.rs +++ b/src/error.rs @@ -22,6 +22,7 @@ pub enum Error { FlexbuffersSer(flexbuffers::SerializationError), FuturesSpawn(futures::SpawnError), MQTT(mqtt::Error), + Config(config::ConfigError), } impl fmt::Display for Error { @@ -60,6 +61,9 @@ impl fmt::Display for Error { Error::MQTT(e) => { write!(f, "Paho MQTT encountered an error: {}", e) }, + Error::Config(e) => { + write!(f, "Failed to parse config: {}", e) + } } } } @@ -130,4 +134,10 @@ impl From for Error { } } +impl From for Error { + fn from(e: config::ConfigError) -> Error { + Error::Config(e) + } +} + pub(crate) type Result = std::result::Result; diff --git a/src/log.rs b/src/log.rs index 34c7b2c..0e1643a 100644 --- a/src/log.rs +++ b/src/log.rs @@ -1,9 +1,9 @@ use slog::{Drain, Logger}; use slog_async; use slog_term::{TermDecorator, FullFormat}; -use crate::config::Config; +use crate::config::Settings; -pub fn init(_config: &Config) -> Logger { +pub fn init(_config: &Settings) -> Logger { let decorator = TermDecorator::new().build(); let drain = FullFormat::new(decorator).build().fuse(); let drain = slog_async::Async::new(drain).build().fuse(); diff --git a/src/machine.rs b/src/machine.rs index d382ad3..05c40f6 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -5,13 +5,12 @@ use std::io::{Read, Write}; use slog::Logger; use serde::{Serialize, Deserialize}; -use toml; use std::sync::Arc; use smol::lock::RwLock; use crate::error::Result; -use crate::config::Config; +use crate::config::Settings; use capnp::Error; @@ -161,23 +160,6 @@ impl Machine { pub type MachineDB = HashMap; -pub async fn init(log: Logger, config: &Config) -> Result { - let mdb = if config.machinedb.is_file() { - let mut fp = File::open(&config.machinedb)?; - let mut content = String::new(); - fp.read_to_string(&mut content)?; - let map = toml::from_str(&content)?; - map - } else { - HashMap::new() - }; - - Ok(MachinesProvider::new(log, mdb)) -} - -pub fn save(config: &Config, mdb: &MachineDB) -> Result<()> { - let mut fp = File::create(&config.machinedb)?; - let toml = toml::to_string(mdb)?; - fp.write_all(&toml.as_bytes())?; - Ok(()) +pub async fn init(log: Logger, config: &Settings) -> Result { + unimplemented!() } diff --git a/src/main.rs b/src/main.rs index 3939fc0..19ae35c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -88,7 +88,7 @@ fn main() -> Result<(), Error> { // Check for the --print-default option first because we don't need to do anything else in that // case. if matches.is_present("print default") { - let config = config::Config::default(); + let config = config::Settings::default(); let encoded = toml::to_vec(&config)?; // Direct writing to fd 1 is faster but also prevents any print-formatting that could @@ -177,7 +177,7 @@ fn main() -> Result<(), Error> { // Bind to each address in config.listen. // This is a Stream over Futures so it will do absolutely nothing unless polled to completion let listeners_s: futures::stream::Collect<_, Vec> - = stream::iter((&config).listen.iter()) + = stream::iter((&config).listens.iter()) .map(|l| { let addr = l.address.clone(); let port = l.port.unwrap_or(config::DEFAULT_PORT); diff --git a/src/modules.rs b/src/modules.rs index b64f6e0..8faaf35 100644 --- a/src/modules.rs +++ b/src/modules.rs @@ -12,12 +12,12 @@ mod shelly; use futures::prelude::*; use futures::task::LocalSpawn; -use crate::config::Config; +use crate::config::Settings; use crate::error::Result; use crate::registries::Registries; // spawner is a type that allows 'tasks' to be spawned on it, running them to completion. -pub fn init(log: Logger, config: &Config, spawner: &S, registries: Registries) -> Result<()> { +pub fn init(log: Logger, config: &Settings, spawner: &S, registries: Registries) -> Result<()> { let f = Box::new(shelly::run(log.clone(), config.clone(), registries.clone())); spawner.spawn_local_obj(f.into())?; diff --git a/src/modules/shelly.rs b/src/modules/shelly.rs index c9ba979..732782c 100644 --- a/src/modules/shelly.rs +++ b/src/modules/shelly.rs @@ -1,6 +1,6 @@ use slog::Logger; -use crate::config::Config; +use crate::config::Settings; use crate::registries::{Registries, Actuator, ActBox}; use crate::error::Result; @@ -14,7 +14,7 @@ use paho_mqtt as mqtt; // TODO: Late config parsing. Right now the config is validated at the very startup in its // entirety. This works reasonably enough for this static modules here but if we do dynamic loading // via dlopen(), lua API, python API etc it will not. -pub async fn run(log: Logger, config: Config, registries: Registries) { +pub async fn run(log: Logger, config: Settings, registries: Registries) { let shelly_r = Shelly::new(config).await; if let Err(e) = shelly_r { error!(log, "Shelly module errored: {}", e); @@ -36,8 +36,8 @@ struct Shelly { } impl Shelly { - pub async fn new(config: Config) -> Result { - let client = mqtt::AsyncClient::new(config.mqtt_url)?; + pub async fn new(config: Settings) -> Result { + let client = mqtt::AsyncClient::new(config.shelly.unwrap().mqtt_url)?; client.connect(mqtt::ConnectOptions::new()).await?; @@ -49,14 +49,14 @@ impl Shelly { #[async_trait] impl Actuator for Shelly { async fn power_on(&mut self, name: String) { - let topic = ""; - let msg = mqtt::Message::new(topic, "1", 0); + let topic = format!("shellies/{}/relay/0/command", name); + let msg = mqtt::Message::new(topic, "on", 0); self.client.publish(msg).map(|_| ()).await } async fn power_off(&mut self, name: String) { - let topic = ""; - let msg = mqtt::Message::new(topic, "0", 0); + let topic = format!("shellies/{}/relay/0/command", name); + let msg = mqtt::Message::new(topic, "off", 0); self.client.publish(msg).map(|_| ()).await } }