Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLI #92

Merged
merged 12 commits into from
Jul 29, 2022
77 changes: 47 additions & 30 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion limitador-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ actix-rt = "2"
paperclip = { version = "0.7", features = ["actix4"] }
serde = { version = "1", features = ["derive"] }
notify = "5.0.0-pre.15"
serial_test = "0.7.0"
clap = "3.2.8"

[build-dependencies]
tonic-build = "0.6"
179 changes: 41 additions & 138 deletions limitador-server/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,31 +18,44 @@
// HTTP_API_HOST: host // just to become HTTP_API_HOST:HTTP_API_PORT as &str
// HTTP_API_PORT: port

use std::env;
use log::LevelFilter;

#[derive(Debug)]
pub struct Configuration {
pub limits_file: Option<String>,
pub limits_file: String,
pub storage: StorageConfiguration,
rls_host: String,
rls_port: u16,
http_host: String,
http_port: u16,
pub limit_name_in_labels: bool,
pub log_level: Option<LevelFilter>,
}

impl Configuration {
pub fn from_env() -> Result<Self, ()> {
let rls_port = env::var("ENVOY_RLS_PORT").unwrap_or_else(|_| "8081".to_string());
let http_port = env::var("HTTP_API_PORT").unwrap_or_else(|_| "8080".to_string());
Ok(Self {
limits_file: env::var("LIMITS_FILE").ok(),
storage: storage_config_from_env()?,
rls_host: env::var("ENVOY_RLS_HOST").unwrap_or_else(|_| "0.0.0.0".to_string()),
rls_port: rls_port.parse().expect("Expected a port number!"),
http_host: env::var("HTTP_API_HOST").unwrap_or_else(|_| "0.0.0.0".to_string()),
http_port: http_port.parse().expect("Expected a port number!"),
limit_name_in_labels: env_option_is_enabled("LIMIT_NAME_IN_PROMETHEUS_LABELS"),
})
pub const DEFAULT_RLS_PORT: &'static str = "8081";
pub const DEFAULT_HTTP_PORT: &'static str = "8080";
pub const DEFAULT_IP_BIND: &'static str = "0.0.0.0";

pub fn with(
storage: StorageConfiguration,
limits_file: String,
rls_host: String,
rls_port: u16,
http_host: String,
http_port: u16,
limit_name_in_labels: bool,
) -> Self {
Self {
limits_file,
storage,
rls_host,
rls_port,
http_host,
http_port,
limit_name_in_labels,
log_level: None,
}
}

pub fn rlp_address(&self) -> String {
Expand All @@ -54,48 +67,19 @@ impl Configuration {
}
}

fn storage_config_from_env() -> Result<StorageConfiguration, ()> {
let redis_url = env::var("REDIS_URL");
let infinispan_url = env::var("INFINISPAN_URL");

match (redis_url, infinispan_url) {
(Ok(_), Ok(_)) => Err(()),
(Ok(url), Err(_)) => Ok(StorageConfiguration::Redis(RedisStorageConfiguration {
url,
cache: if env_option_is_enabled("REDIS_LOCAL_CACHE_ENABLED") {
Some(RedisStorageCacheConfiguration {
flushing_period: env::var("REDIS_LOCAL_CACHE_FLUSHING_PERIOD_MS")
.unwrap_or_else(|_| "1".to_string())
.parse()
.expect("Expected an i64"),
max_ttl: env::var("REDIS_LOCAL_CACHE_MAX_TTL_CACHED_COUNTERS_MS")
.unwrap_or_else(|_| "5000".to_string())
.parse()
.expect("Expected an u64"),
ttl_ratio: env::var("REDIS_LOCAL_CACHE_TTL_RATIO_CACHED_COUNTERS")
.unwrap_or_else(|_| "10".to_string())
.parse()
.expect("Expected an u64"),
})
} else {
None
},
})),
(Err(_), Ok(url)) => Ok(StorageConfiguration::Infinispan(
InfinispanStorageConfiguration {
url,
cache: env::var("INFINISPAN_CACHE_NAME").ok(),
consistency: env::var("INFINISPAN_COUNTERS_CONSISTENCY").ok(),
},
)),
_ => Ok(StorageConfiguration::InMemory),
}
}

fn env_option_is_enabled(env_name: &str) -> bool {
match env::var(env_name) {
Ok(value) => value == "1",
Err(_) => false,
#[cfg(test)]
impl Default for Configuration {
fn default() -> Self {
Configuration {
limits_file: "".to_string(),
storage: StorageConfiguration::InMemory,
rls_host: "".to_string(),
rls_port: 0,
http_host: "".to_string(),
http_port: 0,
limit_name_in_labels: false,
log_level: None,
}
}
}

Expand All @@ -117,6 +101,7 @@ pub struct RedisStorageCacheConfiguration {
pub flushing_period: i64,
pub max_ttl: u64,
pub ttl_ratio: u64,
pub max_counters: usize,
}

#[derive(PartialEq, Debug)]
Expand All @@ -125,85 +110,3 @@ pub struct InfinispanStorageConfiguration {
pub cache: Option<String>,
pub consistency: Option<String>,
}

#[cfg(test)]
mod tests {
use crate::config::{Configuration, StorageConfiguration};
use serial_test::serial;
use std::env;

struct VarEnvCleaner {
vars: Vec<String>,
}

impl VarEnvCleaner {
pub fn new() -> Self {
Self { vars: Vec::new() }
}

pub fn set_var(&mut self, k: &str, v: &str) {
self.vars.insert(0, k.to_string());
env::set_var(k, v);
}
}

impl Drop for VarEnvCleaner {
fn drop(&mut self) {
for var in &self.vars {
env::remove_var(var);
}
}
}

#[test]
#[serial]
fn test_config_defaults() {
let config = Configuration::from_env().unwrap();
assert_eq!(config.limits_file, None);
assert_eq!(config.storage, StorageConfiguration::InMemory);
assert_eq!(config.http_address(), "0.0.0.0:8080".to_string());
assert_eq!(config.rlp_address(), "0.0.0.0:8081".to_string());
assert_eq!(config.limit_name_in_labels, false);
}

#[test]
#[serial]
fn test_config_redis_defaults() {
let mut vars = VarEnvCleaner::new();
let url = "redis://127.0.1.1:7654";
vars.set_var("REDIS_URL", url);

let config = Configuration::from_env().unwrap();
assert_eq!(config.limits_file, None);
if let StorageConfiguration::Redis(ref redis_config) = config.storage {
assert_eq!(redis_config.url, url);
assert_eq!(redis_config.cache, None);
} else {
panic!("Should be a Redis config!");
}
assert_eq!(config.http_address(), "0.0.0.0:8080".to_string());
assert_eq!(config.rlp_address(), "0.0.0.0:8081".to_string());
assert_eq!(config.limit_name_in_labels, false);
}

#[test]
#[serial]
fn test_config_infinispan_defaults() {
let mut vars = VarEnvCleaner::new();

let url = "127.0.2.2:9876";
vars.set_var("INFINISPAN_URL", url);
let config = Configuration::from_env().unwrap();
assert_eq!(config.limits_file, None);
if let StorageConfiguration::Infinispan(ref infinispan_config) = config.storage {
assert_eq!(infinispan_config.url, url);
assert_eq!(infinispan_config.cache, None);
assert_eq!(infinispan_config.consistency, None);
} else {
panic!("Should be an Infinispan config!");
}
assert_eq!(config.http_address(), "0.0.0.0:8080".to_string());
assert_eq!(config.rlp_address(), "0.0.0.0:8081".to_string());
assert_eq!(config.limit_name_in_labels, false);
}
}
8 changes: 2 additions & 6 deletions limitador-server/src/envoy_rls/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,9 +196,7 @@ mod tests {
async fn test_returns_ok_when_no_limits_apply() {
// No limits saved
let rate_limiter = MyRateLimiter::new(Arc::new(
Limiter::new(Configuration::from_env().unwrap())
.await
.unwrap(),
Limiter::new(Configuration::default()).await.unwrap(),
));

let req = RateLimitRequest {
Expand Down Expand Up @@ -228,9 +226,7 @@ mod tests {
#[tokio::test]
async fn test_returns_unknown_when_domain_is_empty() {
let rate_limiter = MyRateLimiter::new(Arc::new(
Limiter::new(Configuration::from_env().unwrap())
.await
.unwrap(),
Limiter::new(Configuration::default()).await.unwrap(),
));

let req = RateLimitRequest {
Expand Down
Loading