Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions fact/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::{
path::{Path, PathBuf},
str::FromStr,
sync::LazyLock,
time::Duration,
};

use anyhow::{bail, Context};
Expand Down Expand Up @@ -31,6 +32,7 @@ pub struct FactConfig {
json: Option<bool>,
ringbuf_size: Option<u32>,
hotreload: Option<bool>,
scan_interval: Option<Duration>,
}

impl FactConfig {
Expand Down Expand Up @@ -95,6 +97,10 @@ impl FactConfig {
if let Some(hotreload) = from.hotreload {
self.hotreload = Some(hotreload);
}

if let Some(scan_interval) = from.scan_interval {
self.scan_interval = Some(scan_interval);
}
}

pub fn paths(&self) -> &[PathBuf] {
Expand All @@ -117,6 +123,10 @@ impl FactConfig {
self.hotreload.unwrap_or(true)
}

pub fn scan_interval(&self) -> Duration {
self.scan_interval.unwrap_or(Duration::from_secs(30))
}

#[cfg(test)]
pub fn set_paths(&mut self, paths: Vec<PathBuf>) {
self.paths = Some(paths);
Expand Down Expand Up @@ -216,6 +226,21 @@ impl TryFrom<Vec<Yaml>> for FactConfig {
};
config.hotreload = Some(hotreload);
}
"scan_interval" => {
if let Some(scan_interval) = v.as_f64() {
if scan_interval <= 0.0 {
bail!("invalid scan_interval: {scan_interval}");
}
config.scan_interval = Some(Duration::from_secs_f64(scan_interval));
} else if let Some(scan_interval) = v.as_i64() {
if scan_interval <= 0 {
bail!("invalid scan_interval: {scan_interval}");
}
config.scan_interval = Some(Duration::from_secs(scan_interval as u64))
} else {
bail!("scan_interval field has incorrect type: {v:?}");
}
}
name => bail!("Invalid field '{name}' with value: {v:?}"),
}
}
Expand Down Expand Up @@ -429,6 +454,15 @@ pub struct FactCli {
hotreload: bool,
#[arg(long, overrides_with = "hotreload", hide(true))]
no_hotreload: bool,

/// Interval at which scanning of monitored directories should
/// happen in seconds.
///
/// The seconds can use a decimal point for fractions of seconds.
///
/// Default value is 30 seconds
#[arg(long, short, env = "FACT_SCAN_INTERVAL")]
scan_interval: Option<f64>,
}

impl FactCli {
Expand All @@ -448,6 +482,7 @@ impl FactCli {
json: resolve_bool_arg(self.json, self.no_json),
ringbuf_size: self.ringbuf_size,
hotreload: resolve_bool_arg(self.hotreload, self.no_hotreload),
scan_interval: self.scan_interval.map(Duration::from_secs_f64),
}
}
}
Expand Down
20 changes: 20 additions & 0 deletions fact/src/config/reloader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub struct Reloader {
grpc: watch::Sender<GrpcConfig>,
paths: watch::Sender<Vec<PathBuf>>,
files: HashMap<&'static str, i64>,
scan_interval: watch::Sender<Duration>,
trigger: Arc<Notify>,
}

Expand Down Expand Up @@ -75,6 +76,12 @@ impl Reloader {
self.paths.subscribe()
}

/// Subscribe to get notifications when scan_interval configuration
/// is changed.
pub fn scan_interval(&self) -> watch::Receiver<Duration> {
self.scan_interval.subscribe()
}

/// Get a reference to the internal trigger for manual reloading of
/// configuration.
///
Expand Down Expand Up @@ -171,6 +178,17 @@ impl Reloader {
}
});

self.scan_interval.send_if_modified(|old| {
let new = new.scan_interval();
if *old != new {
debug!("Sending new scan interval configuration...");
*old = new;
true
} else {
false
}
});

if self.config.hotreload() != new.hotreload() {
warn!("Changes to the hotreload field only take effect on startup");
}
Expand Down Expand Up @@ -203,13 +221,15 @@ impl From<FactConfig> for Reloader {
let (endpoint, _) = watch::channel(config.endpoint.clone());
let (grpc, _) = watch::channel(config.grpc.clone());
let (paths, _) = watch::channel(config.paths().to_vec());
let (scan_interval, _) = watch::channel(config.scan_interval());
let trigger = Arc::new(Notify::new());

Reloader {
config,
endpoint,
grpc,
paths,
scan_interval,
files,
trigger,
}
Expand Down
106 changes: 106 additions & 0 deletions fact/src/config/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,20 @@ fn parsing() {
..Default::default()
},
),
(
"scan_interval: 60",
FactConfig {
scan_interval: Some(Duration::from_secs(60)),
..Default::default()
},
),
(
"scan_interval: 30.5",
FactConfig {
scan_interval: Some(Duration::from_secs_f64(30.5)),
..Default::default()
},
),
(
r#"
paths:
Expand All @@ -218,6 +232,7 @@ fn parsing() {
json: false
ringbuf_size: 8192
hotreload: false
scan_interval: 60
"#,
FactConfig {
paths: Some(vec![PathBuf::from("/etc")]),
Expand All @@ -234,6 +249,7 @@ fn parsing() {
json: Some(false),
ringbuf_size: Some(8192),
hotreload: Some(false),
scan_interval: Some(Duration::from_secs(60)),
},
),
];
Expand Down Expand Up @@ -387,6 +403,14 @@ paths:
"hotreload: 4",
"hotreload field has incorrect type: Integer(4)",
),
(
"scan_interval: true",
"scan_interval field has incorrect type: Boolean(true)",
),
("scan_interval: 0", "invalid scan_interval: 0"),
("scan_interval: 0.0", "invalid scan_interval: 0"),
("scan_interval: -128", "invalid scan_interval: -128"),
("scan_interval: -128.5", "invalid scan_interval: -128.5"),
("unknown:", "Invalid field 'unknown' with value: Null"),
];
for (input, expected) in tests {
Expand Down Expand Up @@ -726,6 +750,36 @@ fn update() {
..Default::default()
},
),
(
"ringbuf_size: 16384",
FactConfig::default(),
FactConfig {
ringbuf_size: Some(16384),
..Default::default()
},
),
(
"ringbuf_size: 16384",
FactConfig {
ringbuf_size: Some(8192),
..Default::default()
},
FactConfig {
ringbuf_size: Some(16384),
..Default::default()
},
),
(
"ringbuf_size: 16384",
FactConfig {
ringbuf_size: Some(16384),
..Default::default()
},
FactConfig {
ringbuf_size: Some(16384),
..Default::default()
},
),
(
"hotreload: false",
FactConfig::default(),
Expand Down Expand Up @@ -756,6 +810,55 @@ fn update() {
..Default::default()
},
),
(
"scan_interval: 60",
FactConfig::default(),
FactConfig {
scan_interval: Some(Duration::from_secs(60)),
..Default::default()
},
),
(
"scan_interval: 0.5",
FactConfig::default(),
FactConfig {
scan_interval: Some(Duration::from_secs_f64(0.5)),
..Default::default()
},
),
(
"scan_interval: 60",
FactConfig {
scan_interval: Some(Duration::from_secs(30)),
..Default::default()
},
FactConfig {
scan_interval: Some(Duration::from_secs(60)),
..Default::default()
},
),
(
"scan_interval: 25.5",
FactConfig {
scan_interval: Some(Duration::from_secs(30)),
..Default::default()
},
FactConfig {
scan_interval: Some(Duration::from_secs_f64(25.5)),
..Default::default()
},
),
(
"scan_interval: 60",
FactConfig {
scan_interval: Some(Duration::from_secs(60)),
..Default::default()
},
FactConfig {
scan_interval: Some(Duration::from_secs(60)),
..Default::default()
},
),
(
r#"
paths:
Expand All @@ -771,6 +874,7 @@ fn update() {
json: false
ringbuf_size: 16384
hotreload: false
scan_interval: 60
"#,
FactConfig {
paths: Some(vec![PathBuf::from("/etc"), PathBuf::from("/bin")]),
Expand All @@ -787,6 +891,7 @@ fn update() {
json: Some(true),
ringbuf_size: Some(64),
hotreload: Some(true),
scan_interval: Some(Duration::from_secs(30)),
},
FactConfig {
paths: Some(vec![PathBuf::from("/etc")]),
Expand All @@ -803,6 +908,7 @@ fn update() {
json: Some(false),
ringbuf_size: Some(16384),
hotreload: Some(false),
scan_interval: Some(Duration::from_secs(60)),
},
),
];
Expand Down
Loading