Better configuration stuff

This commit is contained in:
Gregor Reitzenstein 2020-09-15 14:31:10 +02:00
parent ff981a768b
commit c742fb0a5d
10 changed files with 67 additions and 77 deletions

View File

@ -24,10 +24,13 @@ capnp = "0.13"
capnp-rpc = "0.13" capnp-rpc = "0.13"
capnp-futures = "0.13" capnp-futures = "0.13"
toml = "0.5"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
flexbuffers = "0.1" 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"] } uuid = { version = "0.8", features = ["serde", "v4"] }
clap = "2.33" clap = "2.33"

View File

@ -15,7 +15,7 @@ use serde::{Serialize, Deserialize};
use slog::Logger; use slog::Logger;
use lmdb::{Transaction, RwTransaction, Cursor}; use lmdb::{Transaction, RwTransaction, Cursor};
use crate::config::Config; use crate::config::Settings;
use crate::error::Result; use crate::error::Result;
// FIXME: fabinfra/fabaccess/bffh#3 // FIXME: fabinfra/fabaccess/bffh#3
@ -196,11 +196,11 @@ impl PermissionsProvider {
let (kbytes, _rest) = kbuf.split_at(std::mem::size_of::<u64>()); let (kbytes, _rest) = kbuf.split_at(std::mem::size_of::<u64>());
let roleID = u64::from_ne_bytes(kbytes.try_into().unwrap()); let roleID = u64::from_ne_bytes(kbytes.try_into().unwrap());
let role: Role = flexbuffers::from_slice(vbuf)?; let role: Role = flexbuffers::from_slice(vbuf)?;
let filename = format!("{:x}.toml", roleID); let filename = format!("{:x}.yml", roleID);
path.set_file_name(filename); path.set_file_name(filename);
let mut fp = std::fs::File::create(&path)?; let mut fp = std::fs::File::create(&path)?;
let toml = toml::to_vec(&role)?; let out = toml::to_vec(&role)?;
fp.write_all(&toml)?; fp.write_all(&out)?;
} }
Ok(()) Ok(())
@ -213,11 +213,11 @@ impl PermissionsProvider {
let (kbytes, _rest) = kbuf.split_at(std::mem::size_of::<u64>()); let (kbytes, _rest) = kbuf.split_at(std::mem::size_of::<u64>());
let permID = u64::from_ne_bytes(kbytes.try_into().unwrap()); let permID = u64::from_ne_bytes(kbytes.try_into().unwrap());
let perm: Perm = flexbuffers::from_slice(vbuf)?; let perm: Perm = flexbuffers::from_slice(vbuf)?;
let filename = format!("{:x}.toml", permID); let filename = format!("{:x}.yml", permID);
path.set_file_name(filename); path.set_file_name(filename);
let mut fp = std::fs::File::create(&path)?; let mut fp = std::fs::File::create(&path)?;
let toml = toml::to_vec(&perm)?; let out = toml::to_vec(&perm)?;
fp.write_all(&toml)?; fp.write_all(&out)?;
} }
Ok(()) Ok(())
@ -230,11 +230,11 @@ impl PermissionsProvider {
let (kbytes, _rest) = kbuf.split_at(std::mem::size_of::<u64>()); let (kbytes, _rest) = kbuf.split_at(std::mem::size_of::<u64>());
let userID = u64::from_ne_bytes(kbytes.try_into().unwrap()); let userID = u64::from_ne_bytes(kbytes.try_into().unwrap());
let user: User = flexbuffers::from_slice(vbuf)?; let user: User = flexbuffers::from_slice(vbuf)?;
let filename = format!("{:x}.toml", userID); let filename = format!("{:x}.yml", userID);
path.set_file_name(filename); path.set_file_name(filename);
let mut fp = std::fs::File::create(&path)?; let mut fp = std::fs::File::create(&path)?;
let toml = toml::to_vec(&user)?; let out = toml::to_vec(&user)?;
fp.write_all(&toml)?; fp.write_all(&out)?;
} }
Ok(()) Ok(())
@ -408,7 +408,7 @@ impl PermissionsProvider {
} }
/// This line documents init /// This line documents init
pub fn init(log: Logger, config: &Config, env: &lmdb::Environment) -> std::result::Result<PermissionsProvider, crate::error::Error> { pub fn init(log: Logger, config: &Settings, env: &lmdb::Environment) -> std::result::Result<PermissionsProvider, crate::error::Error> {
let mut flags = lmdb::DatabaseFlags::empty(); let mut flags = lmdb::DatabaseFlags::empty();
flags.set(lmdb::DatabaseFlags::INTEGER_KEY, true); flags.set(lmdb::DatabaseFlags::INTEGER_KEY, true);
let roledb = env.create_db(Some("role"), flags)?; let roledb = env.create_db(Some("role"), flags)?;

View File

@ -9,7 +9,7 @@ use rsasl::{SASL, Property, Session, ReturnCode};
use rsasl::sys::{Gsasl, Gsasl_session}; use rsasl::sys::{Gsasl, Gsasl_session};
use crate::error::Result; use crate::error::Result;
use crate::config::Config; use crate::config::Settings;
pub mod auth_capnp { pub mod auth_capnp {
include!(concat!(env!("OUT_DIR"), "/schema/auth_capnp.rs")); include!(concat!(env!("OUT_DIR"), "/schema/auth_capnp.rs"));
@ -53,6 +53,6 @@ impl Auth {
} }
} }
pub async fn init(log: Logger, config: Config) -> Result<Auth> { pub async fn init(log: Logger, config: Settings) -> Result<Auth> {
Ok(Auth::new()) Ok(Auth::new())
} }

View File

@ -8,29 +8,29 @@ use crate::error::Result;
use std::default::Default; use std::default::Default;
pub fn read(path: &Path) -> Result<Config> { use std::collections::HashMap;
let mut fp = File::open(path)?;
let mut contents = String::new();
fp.read_to_string(&mut contents)?;
let config = toml::from_str(&contents)?; use config::Config;
Ok(config) pub use config::ConfigError;
use glob::glob;
pub fn read(path: &Path) -> Result<Settings> {
let mut settings = Config::default();
settings
.merge(config::File::from(path)).unwrap();
Ok(settings.try_into()?)
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config { pub struct Settings {
pub db: PathBuf, pub listens: Box<[Listen]>,
pub machinedb: PathBuf, pub shelly: Option<ShellyCfg>
pub passdb: PathBuf,
pub(crate) access: Access,
pub listen: Box<[Listen]>,
pub mqtt_url: String,
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Access { pub struct ShellyCfg {
pub(crate) model: PathBuf, pub mqtt_url: String
pub(crate) policy: PathBuf
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
@ -39,17 +39,10 @@ pub struct Listen {
pub port: Option<u16>, pub port: Option<u16>,
} }
impl Default for Config { impl Default for Settings {
fn default() -> Self { fn default() -> Self {
Config { Settings {
db: PathBuf::from_str("/tmp/bffh.db").unwrap(), listens: Box::new([Listen {
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 {
address: "127.0.0.1".to_string(), address: "127.0.0.1".to_string(),
port: Some(DEFAULT_PORT) port: Some(DEFAULT_PORT)
}, },
@ -57,7 +50,9 @@ impl Default for Config {
address: "::1".to_string(), address: "::1".to_string(),
port: Some(DEFAULT_PORT) 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()
}),
} }
} }
} }

View File

@ -22,6 +22,7 @@ pub enum Error {
FlexbuffersSer(flexbuffers::SerializationError), FlexbuffersSer(flexbuffers::SerializationError),
FuturesSpawn(futures::SpawnError), FuturesSpawn(futures::SpawnError),
MQTT(mqtt::Error), MQTT(mqtt::Error),
Config(config::ConfigError),
} }
impl fmt::Display for Error { impl fmt::Display for Error {
@ -60,6 +61,9 @@ impl fmt::Display for Error {
Error::MQTT(e) => { Error::MQTT(e) => {
write!(f, "Paho MQTT encountered an error: {}", 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<mqtt::Error> for Error {
} }
} }
impl From<config::ConfigError> for Error {
fn from(e: config::ConfigError) -> Error {
Error::Config(e)
}
}
pub(crate) type Result<T> = std::result::Result<T, Error>; pub(crate) type Result<T> = std::result::Result<T, Error>;

View File

@ -1,9 +1,9 @@
use slog::{Drain, Logger}; use slog::{Drain, Logger};
use slog_async; use slog_async;
use slog_term::{TermDecorator, FullFormat}; 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 decorator = TermDecorator::new().build();
let drain = FullFormat::new(decorator).build().fuse(); let drain = FullFormat::new(decorator).build().fuse();
let drain = slog_async::Async::new(drain).build().fuse(); let drain = slog_async::Async::new(drain).build().fuse();

View File

@ -5,13 +5,12 @@ use std::io::{Read, Write};
use slog::Logger; use slog::Logger;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use toml;
use std::sync::Arc; use std::sync::Arc;
use smol::lock::RwLock; use smol::lock::RwLock;
use crate::error::Result; use crate::error::Result;
use crate::config::Config; use crate::config::Settings;
use capnp::Error; use capnp::Error;
@ -161,23 +160,6 @@ impl Machine {
pub type MachineDB = HashMap<Uuid, Machine>; pub type MachineDB = HashMap<Uuid, Machine>;
pub async fn init(log: Logger, config: &Config) -> Result<MachinesProvider> { pub async fn init(log: Logger, config: &Settings) -> Result<MachinesProvider> {
let mdb = if config.machinedb.is_file() { unimplemented!()
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(())
} }

View File

@ -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 // Check for the --print-default option first because we don't need to do anything else in that
// case. // case.
if matches.is_present("print default") { if matches.is_present("print default") {
let config = config::Config::default(); let config = config::Settings::default();
let encoded = toml::to_vec(&config)?; let encoded = toml::to_vec(&config)?;
// Direct writing to fd 1 is faster but also prevents any print-formatting that could // 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. // Bind to each address in config.listen.
// This is a Stream over Futures so it will do absolutely nothing unless polled to completion // This is a Stream over Futures so it will do absolutely nothing unless polled to completion
let listeners_s: futures::stream::Collect<_, Vec<TcpListener>> let listeners_s: futures::stream::Collect<_, Vec<TcpListener>>
= stream::iter((&config).listen.iter()) = stream::iter((&config).listens.iter())
.map(|l| { .map(|l| {
let addr = l.address.clone(); let addr = l.address.clone();
let port = l.port.unwrap_or(config::DEFAULT_PORT); let port = l.port.unwrap_or(config::DEFAULT_PORT);

View File

@ -12,12 +12,12 @@ mod shelly;
use futures::prelude::*; use futures::prelude::*;
use futures::task::LocalSpawn; use futures::task::LocalSpawn;
use crate::config::Config; use crate::config::Settings;
use crate::error::Result; use crate::error::Result;
use crate::registries::Registries; use crate::registries::Registries;
// spawner is a type that allows 'tasks' to be spawned on it, running them to completion. // spawner is a type that allows 'tasks' to be spawned on it, running them to completion.
pub fn init<S: LocalSpawn>(log: Logger, config: &Config, spawner: &S, registries: Registries) -> Result<()> { pub fn init<S: LocalSpawn>(log: Logger, config: &Settings, spawner: &S, registries: Registries) -> Result<()> {
let f = Box::new(shelly::run(log.clone(), config.clone(), registries.clone())); let f = Box::new(shelly::run(log.clone(), config.clone(), registries.clone()));
spawner.spawn_local_obj(f.into())?; spawner.spawn_local_obj(f.into())?;

View File

@ -1,6 +1,6 @@
use slog::Logger; use slog::Logger;
use crate::config::Config; use crate::config::Settings;
use crate::registries::{Registries, Actuator, ActBox}; use crate::registries::{Registries, Actuator, ActBox};
use crate::error::Result; 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 // 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 // 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. // 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; let shelly_r = Shelly::new(config).await;
if let Err(e) = shelly_r { if let Err(e) = shelly_r {
error!(log, "Shelly module errored: {}", e); error!(log, "Shelly module errored: {}", e);
@ -36,8 +36,8 @@ struct Shelly {
} }
impl Shelly { impl Shelly {
pub async fn new(config: Config) -> Result<ActBox> { pub async fn new(config: Settings) -> Result<ActBox> {
let client = mqtt::AsyncClient::new(config.mqtt_url)?; let client = mqtt::AsyncClient::new(config.shelly.unwrap().mqtt_url)?;
client.connect(mqtt::ConnectOptions::new()).await?; client.connect(mqtt::ConnectOptions::new()).await?;
@ -49,14 +49,14 @@ impl Shelly {
#[async_trait] #[async_trait]
impl Actuator for Shelly { impl Actuator for Shelly {
async fn power_on(&mut self, name: String) { async fn power_on(&mut self, name: String) {
let topic = ""; let topic = format!("shellies/{}/relay/0/command", name);
let msg = mqtt::Message::new(topic, "1", 0); let msg = mqtt::Message::new(topic, "on", 0);
self.client.publish(msg).map(|_| ()).await self.client.publish(msg).map(|_| ()).await
} }
async fn power_off(&mut self, name: String) { async fn power_off(&mut self, name: String) {
let topic = ""; let topic = format!("shellies/{}/relay/0/command", name);
let msg = mqtt::Message::new(topic, "0", 0); let msg = mqtt::Message::new(topic, "off", 0);
self.client.publish(msg).map(|_| ()).await self.client.publish(msg).map(|_| ()).await
} }
} }