diff --git a/CHANGELOG.md b/CHANGELOG.md index 63339810f06..3e6afc9c3e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ - Set `sentry.segment.id` and `sentry.segment.name` attributes on OTLP segment spans. ([#5748](https://github.com/getsentry/relay/pull/5748)) - Envelope buffer: Add option to disable flush-to-disk on shutdown. ([#5751](https://github.com/getsentry/relay/pull/5751)) +- Healthcheck: Provide a flag to exit container on healthcheck failure. ([#5754](https://github.com/getsentry/relay/pull/5754)) **Internal**: diff --git a/Cargo.lock b/Cargo.lock index b40f40e8565..9581e38e94e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4168,6 +4168,7 @@ dependencies = [ "hostname 0.4.1", "http", "mimalloc", + "nix", "relay-config", "relay-kafka", "relay-log", diff --git a/relay/Cargo.toml b/relay/Cargo.toml index a4397e4b5c1..a5c493144e7 100644 --- a/relay/Cargo.toml +++ b/relay/Cargo.toml @@ -32,4 +32,5 @@ relay-statsd = { workspace = true } relay-kafka = { workspace = true, optional = true } uuid = { workspace = true } reqwest = { workspace = true, features = ["gzip", "native-tls-vendored"] } +nix = { version = "0.29.0", features = ["signal"] } mimalloc = { workspace = true, features = ["v3", "override", "debug_in_debug"] } diff --git a/relay/src/cliapp.rs b/relay/src/cliapp.rs index 750356058d0..3ecc54fd52a 100644 --- a/relay/src/cliapp.rs +++ b/relay/src/cliapp.rs @@ -344,5 +344,12 @@ pub fn make_app() -> Command { .value_parser(clap::value_parser!(SocketAddr)) .required(false), ) + .arg( + Arg::new("kill-on-fail") + .long("kill-on-fail") + .action(ArgAction::SetTrue) + .help("Send SIGTERM to PID 1 if the healthcheck fails.") + .required(false), + ) ) } diff --git a/relay/src/healthcheck.rs b/relay/src/healthcheck.rs index f6f0e732407..e08809d692b 100644 --- a/relay/src/healthcheck.rs +++ b/relay/src/healthcheck.rs @@ -6,6 +6,9 @@ use clap::ArgMatches; use relay_config::Config; use reqwest::blocking::Client; +use nix::sys::signal::{self, Signal}; +use nix::unistd::Pid; + pub fn healthcheck(config: &Config, matches: &ArgMatches) -> Result<()> { let mode = matches .get_one::("mode") @@ -30,12 +33,20 @@ pub fn healthcheck(config: &Config, matches: &ArgMatches) -> Result<()> { .get(format!("http://{addr}/api/relay/healthcheck/{mode}/")) .send(); + let kill_on_fail = matches.get_flag("kill-on-fail"); + match response { Ok(response) => { if response.status().is_success() { Ok(()) } else { relay_log::error!("Relay is unhealthy. Status code: {}", response.status()); + if kill_on_fail { + relay_log::error!("Sending SIGTERM to PID 1 to exit container."); + if let Err(err) = signal::kill(Pid::from_raw(1), Signal::SIGTERM) { + relay_log::error!("Failed to send SIGTERM to PID 1: {err}"); + } + } Err(format_err!( "Relay is unhealthy. Status code: {}", response.status() @@ -44,6 +55,12 @@ pub fn healthcheck(config: &Config, matches: &ArgMatches) -> Result<()> { } Err(err) => { relay_log::error!("Relay is unhealthy. Error: {err}"); + if kill_on_fail { + relay_log::error!("Sending SIGTERM to PID 1 to exit container."); + if let Err(err) = signal::kill(Pid::from_raw(1), Signal::SIGTERM) { + relay_log::error!("Failed to send SIGTERM to PID 1: {err}"); + } + } Err(err.into()) } }