Add basic `env` config module

next
Sayan Nandan 3 years ago
parent dff703e843
commit eb037fcd62

@ -252,6 +252,7 @@ impl fmt::Display for BinaryData {
let b = *b;
// See this: https://doc.rust-lang.org/reference/tokens.html#byte-escapes
// this idea was borrowed from the Bytes crate
#[allow(clippy::manual_range_contains)]
if b == b'\n' {
write!(f, "\\n")?;
} else if b == b'\r' {

@ -0,0 +1,107 @@
/*
* Created on Thu Sep 23 2021
*
* This file is a part of Skytable
* Skytable (formerly known as TerrabaseDB or Skybase) is a free and open-source
* NoSQL database written by Sayan Nandan ("the Author") with the
* vision to provide flexibility in data modelling without compromising
* on performance, queryability or scalability.
*
* Copyright (c) 2021, Sayan Nandan <ohsayan@outlook.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
use std::collections::HashMap;
use std::env as std_env;
use std_env::VarError;
type ConfigMap = HashMap<&'static str, Option<String>>;
#[derive(Debug)]
pub enum ConfigStatus {
ParseFailure,
None,
Config(ConfigMap),
}
macro_rules! to_const {
($($(#[$attr:meta])* $v:ident),* $(,)?) => {
$(
$(#[$attr])*
const $v: &str = stringify!($v);
)*
pub fn has_env_config() -> ConfigStatus {
let mut hm = ConfigMap::new();
let mut has_env = false;
$(
match std_env::var($v) {
Ok(var) => {
hm.insert($v, Some(var));
has_env = true;
},
Err(e) => {
match e {
VarError::NotPresent => {},
VarError::NotUnicode {..} => {
return ConfigStatus::ParseFailure;
}
}
}
}
)*
if has_env {
ConfigStatus::Config(hm)
} else {
ConfigStatus::None
}
}
};
}
to_const! {
// system config
/// host addr
SKY_SYSTEM_HOST,
/// port
SKY_SYSTEM_PORT,
/// noart configuration for secure environments
SKY_SYSTEM_NOART,
/// the maximum number of connections
SKY_SYSTEM_MAXCON,
// bgsave
/// enabled/disabled flag for bgsave
SKY_BGSAVE_ENABLED,
/// bgsave interval
SKY_BGSAVE_EVERY,
// snapshot
/// snapshot interval
SKY_SNAPSHOT_EVERY,
/// maximum number of snapshots
SKY_SNAPSHOT_ATMOST,
/// flag to disable writes if snapshot fails
SKY_SNAPSHOT_FAILSAFE,
// TLS
/// the tls private key
SKY_TLS_KEY,
/// the tls cert
SKY_TLS_CERT,
/// the tls port
SKY_TLS_PORT,
/// the tls-only flag
SKY_TLS_ONLY,
/// the tls password stream
SKY_TLS_PASSIN
}

@ -27,18 +27,16 @@
//! This module provides tools to handle configuration files and settings
use crate::dbnet::MAXIMUM_CONNECTION_LIMIT;
#[cfg(test)]
use libsky::TResult;
use serde::Deserialize;
use std::error::Error;
use std::fmt;
use std::fs;
#[cfg(test)]
use std::net::Ipv6Addr;
use std::net::{IpAddr, Ipv4Addr};
const DEFAULT_IPV4: IpAddr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
mod env;
#[cfg(test)]
const DEFAULT_PORT: u16 = 2003;
mod tests;
const DEFAULT_IPV4: IpAddr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
const DEFAULT_SSL_PORT: u16 = 2004;
/// This struct is an _object representation_ used for parsing the TOML file
@ -154,12 +152,12 @@ pub enum PortConfig {
},
}
#[cfg(test)]
impl PortConfig {
#[cfg(test)]
pub const fn default() -> PortConfig {
PortConfig::InsecureOnly {
host: DEFAULT_IPV4,
port: DEFAULT_PORT,
port: tests::DEFAULT_PORT,
}
}
}
@ -342,7 +340,7 @@ impl ParsedConfig {
}
#[cfg(test)]
/// Create a new `ParsedConfig` from a `TOML` string
pub fn new_from_toml_str(tomlstr: String) -> TResult<Self> {
pub fn new_from_toml_str(tomlstr: String) -> tests::TResult<Self> {
Ok(ParsedConfig::from_config(toml::from_str(&tomlstr)?))
}
/// Create a new `ParsedConfig` with all the fields
@ -648,204 +646,3 @@ pub fn get_config_file_or_return_cfg() -> Result<ConfigType<ParsedConfig, String
Ok(ConfigType::Def(ParsedConfig::default(), restorefile))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_config_toml_okayport() {
let file = r#"
[server]
host = "127.0.0.1"
port = 2003
"#
.to_owned();
let cfg = ParsedConfig::new_from_toml_str(file).unwrap();
assert_eq!(cfg, ParsedConfig::default(),);
}
/// Gets a `toml` file from `WORKSPACEROOT/examples/config-files`
fn get_toml_from_examples_dir(filename: String) -> TResult<String> {
use std::path;
let curdir = std::env::current_dir().unwrap();
let workspaceroot = curdir.ancestors().nth(1).unwrap();
let mut fileloc = path::PathBuf::from(workspaceroot);
fileloc.push("examples");
fileloc.push("config-files");
fileloc.push(filename);
Ok(fs::read_to_string(fileloc)?)
}
#[test]
fn test_config_toml_badport() {
let file = r#"
[server]
port = 20033002
"#
.to_owned();
let cfg = ParsedConfig::new_from_toml_str(file);
assert!(cfg.is_err());
}
#[test]
fn test_config_file_ok() {
let file = get_toml_from_examples_dir("skyd.toml".to_owned()).unwrap();
let cfg = ParsedConfig::new_from_toml_str(file).unwrap();
assert_eq!(cfg, ParsedConfig::default());
}
#[test]
fn test_config_file_err() {
let file = get_toml_from_examples_dir("skyd.toml".to_owned()).unwrap();
let cfg = ParsedConfig::new_from_file(file);
assert!(cfg.is_err());
}
#[test]
fn test_args() {
let cmdlineargs = vec!["skyd", "--withconfig", "../examples/config-files/skyd.toml"];
let cfg_layout = load_yaml!("../cli.yml");
let matches = App::from_yaml(cfg_layout).get_matches_from(cmdlineargs);
let filename = matches.value_of("config").unwrap();
assert_eq!("../examples/config-files/skyd.toml", filename);
let cfg =
ParsedConfig::new_from_toml_str(std::fs::read_to_string(filename).unwrap()).unwrap();
assert_eq!(cfg, ParsedConfig::default());
}
#[test]
fn test_config_file_noart() {
let file = get_toml_from_examples_dir("secure-noart.toml".to_owned()).unwrap();
let cfg = ParsedConfig::new_from_toml_str(file).unwrap();
assert_eq!(
cfg,
ParsedConfig {
noart: true,
bgsave: BGSave::default(),
snapshot: SnapshotConfig::default(),
ports: PortConfig::default(),
maxcon: MAXIMUM_CONNECTION_LIMIT
}
);
}
#[test]
fn test_config_file_ipv6() {
let file = get_toml_from_examples_dir("ipv6.toml".to_owned()).unwrap();
let cfg = ParsedConfig::new_from_toml_str(file).unwrap();
assert_eq!(
cfg,
ParsedConfig {
noart: false,
bgsave: BGSave::default(),
snapshot: SnapshotConfig::default(),
ports: PortConfig::new_insecure_only(
IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1)),
DEFAULT_PORT
),
maxcon: MAXIMUM_CONNECTION_LIMIT
}
);
}
#[test]
fn test_config_file_template() {
let file = get_toml_from_examples_dir("template.toml".to_owned()).unwrap();
let cfg = ParsedConfig::new_from_toml_str(file).unwrap();
assert_eq!(
cfg,
ParsedConfig::new(
false,
BGSave::default(),
SnapshotConfig::Enabled(SnapshotPref::new(3600, 4, true)),
PortConfig::new_secure_only(
DEFAULT_IPV4,
SslOpts::new(
"/path/to/keyfile.pem".into(),
"/path/to/chain.pem".into(),
2004,
Some("/path/to/cert/passphrase.txt".to_owned())
)
),
MAXIMUM_CONNECTION_LIMIT
)
);
}
#[test]
fn test_config_file_bad_bgsave_section() {
let file = get_toml_from_examples_dir("badcfg2.toml".to_owned()).unwrap();
let cfg = ParsedConfig::new_from_toml_str(file);
assert!(cfg.is_err());
}
#[test]
fn test_config_file_custom_bgsave() {
let file = get_toml_from_examples_dir("withcustombgsave.toml".to_owned()).unwrap();
let cfg = ParsedConfig::new_from_toml_str(file).unwrap();
assert_eq!(
cfg,
ParsedConfig {
noart: false,
bgsave: BGSave::new(true, 600),
snapshot: SnapshotConfig::default(),
ports: PortConfig::default(),
maxcon: MAXIMUM_CONNECTION_LIMIT
}
);
}
#[test]
fn test_config_file_bgsave_enabled_only() {
/*
* This test demonstrates a case where the user just said that BGSAVE is enabled.
* In that case, we will default to the 120 second duration
*/
let file = get_toml_from_examples_dir("bgsave-justenabled.toml".to_owned()).unwrap();
let cfg = ParsedConfig::new_from_toml_str(file).unwrap();
assert_eq!(
cfg,
ParsedConfig {
noart: false,
bgsave: BGSave::default(),
snapshot: SnapshotConfig::default(),
ports: PortConfig::default(),
maxcon: MAXIMUM_CONNECTION_LIMIT
}
)
}
#[test]
fn test_config_file_bgsave_every_only() {
/*
* This test demonstrates a case where the user just gave the value for every
* In that case, it means BGSAVE is enabled and set to `every` seconds
*/
let file = get_toml_from_examples_dir("bgsave-justevery.toml".to_owned()).unwrap();
let cfg = ParsedConfig::new_from_toml_str(file).unwrap();
assert_eq!(
cfg,
ParsedConfig {
noart: false,
bgsave: BGSave::new(true, 600),
snapshot: SnapshotConfig::default(),
ports: PortConfig::default(),
maxcon: MAXIMUM_CONNECTION_LIMIT
}
)
}
#[test]
fn test_config_file_snapshot() {
let file = get_toml_from_examples_dir("snapshot.toml".to_owned()).unwrap();
let cfg = ParsedConfig::new_from_toml_str(file).unwrap();
assert_eq!(
cfg,
ParsedConfig {
snapshot: SnapshotConfig::Enabled(SnapshotPref::new(3600, 4, true)),
bgsave: BGSave::default(),
noart: false,
ports: PortConfig::default(),
maxcon: MAXIMUM_CONNECTION_LIMIT
}
);
}
}

@ -0,0 +1,232 @@
/*
* Created on Thu Sep 23 2021
*
* This file is a part of Skytable
* Skytable (formerly known as TerrabaseDB or Skybase) is a free and open-source
* NoSQL database written by Sayan Nandan ("the Author") with the
* vision to provide flexibility in data modelling without compromising
* on performance, queryability or scalability.
*
* Copyright (c) 2021, Sayan Nandan <ohsayan@outlook.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
use super::{
BGSave, IpAddr, ParsedConfig, PortConfig, SnapshotConfig, SnapshotPref, SslOpts, DEFAULT_IPV4,
MAXIMUM_CONNECTION_LIMIT,
};
use clap::{load_yaml, App};
pub(super) use libsky::TResult;
use std::fs;
use std::net::Ipv6Addr;
pub(super) const DEFAULT_PORT: u16 = 2003;
#[test]
fn test_config_toml_okayport() {
let file = r#"
[server]
host = "127.0.0.1"
port = 2003
"#
.to_owned();
let cfg = ParsedConfig::new_from_toml_str(file).unwrap();
assert_eq!(cfg, ParsedConfig::default(),);
}
/// Gets a `toml` file from `WORKSPACEROOT/examples/config-files`
fn get_toml_from_examples_dir(filename: String) -> TResult<String> {
use std::path;
let curdir = std::env::current_dir().unwrap();
let workspaceroot = curdir.ancestors().nth(1).unwrap();
let mut fileloc = path::PathBuf::from(workspaceroot);
fileloc.push("examples");
fileloc.push("config-files");
fileloc.push(filename);
Ok(fs::read_to_string(fileloc)?)
}
#[test]
fn test_config_toml_badport() {
let file = r#"
[server]
port = 20033002
"#
.to_owned();
let cfg = ParsedConfig::new_from_toml_str(file);
assert!(cfg.is_err());
}
#[test]
fn test_config_file_ok() {
let file = get_toml_from_examples_dir("skyd.toml".to_owned()).unwrap();
let cfg = ParsedConfig::new_from_toml_str(file).unwrap();
assert_eq!(cfg, ParsedConfig::default());
}
#[test]
fn test_config_file_err() {
let file = get_toml_from_examples_dir("skyd.toml".to_owned()).unwrap();
let cfg = ParsedConfig::new_from_file(file);
assert!(cfg.is_err());
}
#[test]
fn test_args() {
let cmdlineargs = vec!["skyd", "--withconfig", "../examples/config-files/skyd.toml"];
let cfg_layout = load_yaml!("../cli.yml");
let matches = App::from_yaml(cfg_layout).get_matches_from(cmdlineargs);
let filename = matches.value_of("config").unwrap();
assert_eq!("../examples/config-files/skyd.toml", filename);
let cfg = ParsedConfig::new_from_toml_str(std::fs::read_to_string(filename).unwrap()).unwrap();
assert_eq!(cfg, ParsedConfig::default());
}
#[test]
fn test_config_file_noart() {
let file = get_toml_from_examples_dir("secure-noart.toml".to_owned()).unwrap();
let cfg = ParsedConfig::new_from_toml_str(file).unwrap();
assert_eq!(
cfg,
ParsedConfig {
noart: true,
bgsave: BGSave::default(),
snapshot: SnapshotConfig::default(),
ports: PortConfig::default(),
maxcon: MAXIMUM_CONNECTION_LIMIT
}
);
}
#[test]
fn test_config_file_ipv6() {
let file = get_toml_from_examples_dir("ipv6.toml".to_owned()).unwrap();
let cfg = ParsedConfig::new_from_toml_str(file).unwrap();
assert_eq!(
cfg,
ParsedConfig {
noart: false,
bgsave: BGSave::default(),
snapshot: SnapshotConfig::default(),
ports: PortConfig::new_insecure_only(
IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1)),
DEFAULT_PORT
),
maxcon: MAXIMUM_CONNECTION_LIMIT
}
);
}
#[test]
fn test_config_file_template() {
let file = get_toml_from_examples_dir("template.toml".to_owned()).unwrap();
let cfg = ParsedConfig::new_from_toml_str(file).unwrap();
assert_eq!(
cfg,
ParsedConfig::new(
false,
BGSave::default(),
SnapshotConfig::Enabled(SnapshotPref::new(3600, 4, true)),
PortConfig::new_secure_only(
DEFAULT_IPV4,
SslOpts::new(
"/path/to/keyfile.pem".into(),
"/path/to/chain.pem".into(),
2004,
Some("/path/to/cert/passphrase.txt".to_owned())
)
),
MAXIMUM_CONNECTION_LIMIT
)
);
}
#[test]
fn test_config_file_bad_bgsave_section() {
let file = get_toml_from_examples_dir("badcfg2.toml".to_owned()).unwrap();
let cfg = ParsedConfig::new_from_toml_str(file);
assert!(cfg.is_err());
}
#[test]
fn test_config_file_custom_bgsave() {
let file = get_toml_from_examples_dir("withcustombgsave.toml".to_owned()).unwrap();
let cfg = ParsedConfig::new_from_toml_str(file).unwrap();
assert_eq!(
cfg,
ParsedConfig {
noart: false,
bgsave: BGSave::new(true, 600),
snapshot: SnapshotConfig::default(),
ports: PortConfig::default(),
maxcon: MAXIMUM_CONNECTION_LIMIT
}
);
}
#[test]
fn test_config_file_bgsave_enabled_only() {
/*
* This test demonstrates a case where the user just said that BGSAVE is enabled.
* In that case, we will default to the 120 second duration
*/
let file = get_toml_from_examples_dir("bgsave-justenabled.toml".to_owned()).unwrap();
let cfg = ParsedConfig::new_from_toml_str(file).unwrap();
assert_eq!(
cfg,
ParsedConfig {
noart: false,
bgsave: BGSave::default(),
snapshot: SnapshotConfig::default(),
ports: PortConfig::default(),
maxcon: MAXIMUM_CONNECTION_LIMIT
}
)
}
#[test]
fn test_config_file_bgsave_every_only() {
/*
* This test demonstrates a case where the user just gave the value for every
* In that case, it means BGSAVE is enabled and set to `every` seconds
*/
let file = get_toml_from_examples_dir("bgsave-justevery.toml".to_owned()).unwrap();
let cfg = ParsedConfig::new_from_toml_str(file).unwrap();
assert_eq!(
cfg,
ParsedConfig {
noart: false,
bgsave: BGSave::new(true, 600),
snapshot: SnapshotConfig::default(),
ports: PortConfig::default(),
maxcon: MAXIMUM_CONNECTION_LIMIT
}
)
}
#[test]
fn test_config_file_snapshot() {
let file = get_toml_from_examples_dir("snapshot.toml".to_owned()).unwrap();
let cfg = ParsedConfig::new_from_toml_str(file).unwrap();
assert_eq!(
cfg,
ParsedConfig {
snapshot: SnapshotConfig::Enabled(SnapshotPref::new(3600, 4, true)),
bgsave: BGSave::default(),
noart: false,
ports: PortConfig::default(),
maxcon: MAXIMUM_CONNECTION_LIMIT
}
);
}
Loading…
Cancel
Save