use std::sync::Arc;
use std::time::Duration;
use chrono::Local;
use tokio::sync::watch;
use tracing::{info, warn};
use crate::config::{CredentialStore, NotificationConfig, ResolvedSettings};
use crate::docker::Docker;
use crate::errors::AppError;
use crate::health::{HealthConfig, TokioClock};
use crate::notify::Dispatcher;
use crate::registry::digest::OciRegistry;
use crate::scheduler::{self, SchedulerConfig};
#[allow(clippy::too_many_arguments)]
pub async fn run(
interval: u64,
tick: u64,
stop_timeout: u64,
credentials: Arc<CredentialStore>,
notifications: NotificationConfig,
settings: ResolvedSettings,
) -> Result<(), AppError> {
let docker = Docker::connect(credentials.clone())?;
let registry = OciRegistry::new(credentials);
let dispatcher = Dispatcher::from_config(notifications, crate::http::client());
let tick_secs = tick.max(1);
let interval_secs = interval.max(1).max(tick_secs);
let cfg = SchedulerConfig {
poll_interval: Duration::from_secs(interval_secs),
tick: Duration::from_secs(tick_secs),
health: HealthConfig::default(),
};
let (tx, rx) = watch::channel(false);
let mut deadline_rx = rx.clone();
tokio::spawn(async move {
wait_for_signal().await;
info!("shutdown signal received; finishing in-flight work then exiting");
let _ = tx.send(true);
});
let stop_timeout = Duration::from_secs(stop_timeout);
tokio::select! {
res = scheduler::run_with(&docker, ®istry, &cfg, &TokioClock, Local::now, rx, &dispatcher, settings) => res,
_ = async {
let _ = deadline_rx.wait_for(|v| *v).await;
tokio::time::sleep(stop_timeout).await;
} => {
warn!(timeout_s = stop_timeout.as_secs(), "shutdown drain exceeded stop-timeout; forcing exit");
Ok(())
}
}
}
async fn wait_for_signal() {
#[cfg(unix)]
{
use tokio::signal::unix::{SignalKind, signal};
match signal(SignalKind::terminate()) {
Ok(mut term) => {
tokio::select! {
_ = tokio::signal::ctrl_c() => {}
_ = term.recv() => {}
}
}
Err(_) => {
let _ = tokio::signal::ctrl_c().await;
}
}
}
#[cfg(not(unix))]
{
let _ = tokio::signal::ctrl_c().await;
}
}