mod config;
mod delivery;
mod event_loop;
pub mod rules;
pub mod sinks;
mod source;
#[cfg(test)]
mod testing;
pub use config::BarkConfig;
use std::sync::Arc;
use config::rules_for;
use event_loop::run_loop;
use shep_client::dogs::Stop;
use super::DogRuntime;
use crate::exit::ExitCode;
pub async fn run(runtime: DogRuntime) -> ExitCode {
let config = match runtime.config::<BarkConfig>() {
Ok(config) => config,
Err(err) => {
eprintln!("shep dog bark: {err}; see `shep dogs`");
return ExitCode::InvalidConfig;
}
};
let rules = match rules_for(&config) {
Ok(rules) => rules,
Err(err) => {
eprintln!("shep dog bark: {err}");
return ExitCode::InvalidConfig;
}
};
let dog = runtime.identity().section().to_owned();
let topics = vec!["process.*".to_owned(), format!("config.dog.{dog}")];
let barks_path = runtime.paths().barks.clone();
let (events, shepherd) = match source::subscribe(runtime.into_client(), dog, topics).await {
Ok(subscribed) => subscribed,
Err(err) => {
eprintln!("shep dog bark: could not subscribe to the shepherd's bus: {err}");
return ExitCode::from(&err);
}
};
run_loop(
events,
Arc::clone(&shepherd),
rules,
&config,
&barks_path,
shepherd,
Stop::on_stop_signals(),
)
.await
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use shep_client::testing::{Handshake, fake_daemon_across_handovers, sample_ack};
use shep_core::protocol::Request;
use crate::dog::run_dog;
use crate::dog::tests::test_paths;
#[tokio::test]
async fn bark_subscribes_to_its_own_config_topic() {
let dir = tempfile::tempdir().unwrap();
let socket = shep_client::testing::control_address(dir.path());
let daemon = fake_daemon_across_handovers(&socket, vec![Handshake::Accept(sample_ack())]);
daemon.reply_to_dog_config(
"[sinks.ops]\nkind = \"json\"\nurl = \"http://127.0.0.1:1/hook\"\n",
);
let paths = test_paths(dir.path(), socket);
let task = tokio::spawn(run_dog("bark", paths));
let topics =
tokio::time::timeout(Duration::from_secs(5), async {
loop {
let subscribed = daemon.envelopes().into_iter().find_map(|(_, envelope)| {
match envelope.body {
Request::Subscribe { topics } => Some(topics),
_ => None,
}
});
if let Some(topics) = subscribed {
break topics;
}
tokio::task::yield_now().await;
}
})
.await
.expect("bark must subscribe once its config parses");
assert!(
topics.iter().any(|topic| topic == "config.dog.bark"),
"bark must ask for its own config topic: {topics:?}"
);
assert!(
topics.iter().any(|topic| topic == "process.*"),
"the lifecycle topics every rule reads must survive: {topics:?}"
);
task.abort();
}
}