diff --git a/Cargo.lock b/Cargo.lock index a51eaa14d..1bbbcdd63 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1082,7 +1082,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -1383,9 +1383,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.15" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" +checksum = "839c0e8a181239723652be9062bb56ca5bf5f64011f73b623f6f4fc59086a228" dependencies = [ "atomic-waker", "bytes", @@ -3259,7 +3259,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3316,7 +3316,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -4100,7 +4100,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -4876,7 +4876,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] diff --git a/crates/stackable-operator/CHANGELOG.md b/crates/stackable-operator/CHANGELOG.md index c306526c1..93e8db4c4 100644 --- a/crates/stackable-operator/CHANGELOG.md +++ b/crates/stackable-operator/CHANGELOG.md @@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +### Fixed + +- `wait_for_termination` in `COMMON_BASH_TRAP_FUNCTIONS` now returns the exit status of the process + it waited for instead of always returning `0` and the shell no longer aborts when SIGTERM arrives + before the child process ID is known to ensure graceful shut down. ([#1265]). + +[#1265]: https://github.com/stackabletech/operator-rs/pull/1265 + ## [0.116.0] - 2026-08-14 ### Added diff --git a/crates/stackable-operator/src/crd/git_sync/v1alpha1_impl.rs b/crates/stackable-operator/src/crd/git_sync/v1alpha1_impl.rs index 20cfc7e5f..5ad1bb03b 100644 --- a/crates/stackable-operator/src/crd/git_sync/v1alpha1_impl.rs +++ b/crates/stackable-operator/src/crd/git_sync/v1alpha1_impl.rs @@ -494,7 +494,7 @@ mod tests { handle_term_signal() { - if [ "${term_child_pid}" ]; then + if [ -n "${term_child_pid:-}" ]; then kill -TERM "${term_child_pid}" 2>/dev/null else term_kill_needed="yes" @@ -509,9 +509,14 @@ mod tests { kill -TERM "${term_child_pid}" 2>/dev/null fi wait ${term_child_pid} 2>/dev/null + term_child_status=$? trap - TERM - wait ${term_child_pid} 2>/dev/null + if [ "${term_child_status}" -gt 128 ]; then + wait ${term_child_pid} 2>/dev/null + term_child_status=$? + fi set -e + return ${term_child_status} } prepare_signal_handlers @@ -563,7 +568,7 @@ volumeMounts: handle_term_signal() { - if [ "${term_child_pid}" ]; then + if [ -n "${term_child_pid:-}" ]; then kill -TERM "${term_child_pid}" 2>/dev/null else term_kill_needed="yes" @@ -578,9 +583,14 @@ volumeMounts: kill -TERM "${term_child_pid}" 2>/dev/null fi wait ${term_child_pid} 2>/dev/null + term_child_status=$? trap - TERM - wait ${term_child_pid} 2>/dev/null + if [ "${term_child_status}" -gt 128 ]; then + wait ${term_child_pid} 2>/dev/null + term_child_status=$? + fi set -e + return ${term_child_status} } prepare_signal_handlers @@ -637,7 +647,7 @@ volumeMounts: handle_term_signal() { - if [ "${term_child_pid}" ]; then + if [ -n "${term_child_pid:-}" ]; then kill -TERM "${term_child_pid}" 2>/dev/null else term_kill_needed="yes" @@ -652,9 +662,14 @@ volumeMounts: kill -TERM "${term_child_pid}" 2>/dev/null fi wait ${term_child_pid} 2>/dev/null + term_child_status=$? trap - TERM - wait ${term_child_pid} 2>/dev/null + if [ "${term_child_status}" -gt 128 ]; then + wait ${term_child_pid} 2>/dev/null + term_child_status=$? + fi set -e + return ${term_child_status} } prepare_signal_handlers diff --git a/crates/stackable-operator/src/crd/git_sync/v1alpha2_impl.rs b/crates/stackable-operator/src/crd/git_sync/v1alpha2_impl.rs index eba308bfb..da59054ab 100644 --- a/crates/stackable-operator/src/crd/git_sync/v1alpha2_impl.rs +++ b/crates/stackable-operator/src/crd/git_sync/v1alpha2_impl.rs @@ -625,7 +625,7 @@ mod tests { handle_term_signal() { - if [ "${term_child_pid}" ]; then + if [ -n "${term_child_pid:-}" ]; then kill -TERM "${term_child_pid}" 2>/dev/null else term_kill_needed="yes" @@ -640,9 +640,14 @@ mod tests { kill -TERM "${term_child_pid}" 2>/dev/null fi wait ${term_child_pid} 2>/dev/null + term_child_status=$? trap - TERM - wait ${term_child_pid} 2>/dev/null + if [ "${term_child_status}" -gt 128 ]; then + wait ${term_child_pid} 2>/dev/null + term_child_status=$? + fi set -e + return ${term_child_status} } prepare_signal_handlers @@ -694,7 +699,7 @@ volumeMounts: handle_term_signal() { - if [ "${term_child_pid}" ]; then + if [ -n "${term_child_pid:-}" ]; then kill -TERM "${term_child_pid}" 2>/dev/null else term_kill_needed="yes" @@ -709,9 +714,14 @@ volumeMounts: kill -TERM "${term_child_pid}" 2>/dev/null fi wait ${term_child_pid} 2>/dev/null + term_child_status=$? trap - TERM - wait ${term_child_pid} 2>/dev/null + if [ "${term_child_status}" -gt 128 ]; then + wait ${term_child_pid} 2>/dev/null + term_child_status=$? + fi set -e + return ${term_child_status} } prepare_signal_handlers @@ -768,7 +778,7 @@ volumeMounts: handle_term_signal() { - if [ "${term_child_pid}" ]; then + if [ -n "${term_child_pid:-}" ]; then kill -TERM "${term_child_pid}" 2>/dev/null else term_kill_needed="yes" @@ -783,9 +793,14 @@ volumeMounts: kill -TERM "${term_child_pid}" 2>/dev/null fi wait ${term_child_pid} 2>/dev/null + term_child_status=$? trap - TERM - wait ${term_child_pid} 2>/dev/null + if [ "${term_child_status}" -gt 128 ]; then + wait ${term_child_pid} 2>/dev/null + term_child_status=$? + fi set -e + return ${term_child_status} } prepare_signal_handlers @@ -1080,7 +1095,7 @@ name: content-from-git-2 handle_term_signal() { - if [ "${term_child_pid}" ]; then + if [ -n "${term_child_pid:-}" ]; then kill -TERM "${term_child_pid}" 2>/dev/null else term_kill_needed="yes" @@ -1095,9 +1110,14 @@ name: content-from-git-2 kill -TERM "${term_child_pid}" 2>/dev/null fi wait ${term_child_pid} 2>/dev/null + term_child_status=$? trap - TERM - wait ${term_child_pid} 2>/dev/null + if [ "${term_child_status}" -gt 128 ]; then + wait ${term_child_pid} 2>/dev/null + term_child_status=$? + fi set -e + return ${term_child_status} } prepare_signal_handlers @@ -1290,7 +1310,7 @@ secret: handle_term_signal() { - if [ "${term_child_pid}" ]; then + if [ -n "${term_child_pid:-}" ]; then kill -TERM "${term_child_pid}" 2>/dev/null else term_kill_needed="yes" @@ -1305,9 +1325,14 @@ secret: kill -TERM "${term_child_pid}" 2>/dev/null fi wait ${term_child_pid} 2>/dev/null + term_child_status=$? trap - TERM - wait ${term_child_pid} 2>/dev/null + if [ "${term_child_status}" -gt 128 ]; then + wait ${term_child_pid} 2>/dev/null + term_child_status=$? + fi set -e + return ${term_child_status} } prepare_signal_handlers diff --git a/crates/stackable-operator/src/product_logging/framework.rs b/crates/stackable-operator/src/product_logging/framework.rs index 35c7bab02..3750acf7a 100644 --- a/crates/stackable-operator/src/product_logging/framework.rs +++ b/crates/stackable-operator/src/product_logging/framework.rs @@ -1386,8 +1386,10 @@ sinks: /// {remove_vector_shutdown_file_command} /// prepare_signal_handlers /// my-application start & -/// wait_for_termination $! +/// product_exit_code=0 +/// wait_for_termination $! || product_exit_code=$? /// {create_vector_shutdown_file_command} +/// exit \"${{product_exit_code}}\" /// ", /// remove_vector_shutdown_file_command = /// remove_vector_shutdown_file_command(STACKABLE_LOG_DIR), diff --git a/crates/stackable-operator/src/utils/bash.rs b/crates/stackable-operator/src/utils/bash.rs index b93b77173..e6762e7e5 100644 --- a/crates/stackable-operator/src/utils/bash.rs +++ b/crates/stackable-operator/src/utils/bash.rs @@ -1,7 +1,8 @@ /// This is a bash snippet, which adds two functions out of interest: /// /// 1. `prepare_signal_handlers` call this first to set up the needed traps -/// 2. `wait_for_termination` waits for the PID you passed as the first argument to terminate +/// 2. `wait_for_termination` waits for the PID you passed as the first argument to terminate and +/// returns its exit status /// /// An example use could be /// ```text @@ -9,8 +10,10 @@ /// echo "Run before startup" /// prepare_signal_handlers /// {hadoop_home}/bin/hdfs {role} & -/// wait_for_termination $! +/// product_exit_code=0 +/// wait_for_termination $! || product_exit_code=$? /// echo "Run after termination" +/// exit "${product_exit_code}" /// ``` pub const COMMON_BASH_TRAP_FUNCTIONS: &str = r#" prepare_signal_handlers() @@ -22,7 +25,7 @@ prepare_signal_handlers() handle_term_signal() { - if [ "${term_child_pid}" ]; then + if [ -n "${term_child_pid:-}" ]; then kill -TERM "${term_child_pid}" 2>/dev/null else term_kill_needed="yes" @@ -37,8 +40,55 @@ wait_for_termination() kill -TERM "${term_child_pid}" 2>/dev/null fi wait ${term_child_pid} 2>/dev/null + term_child_status=$? trap - TERM - wait ${term_child_pid} 2>/dev/null + if [ "${term_child_status}" -gt 128 ]; then + wait ${term_child_pid} 2>/dev/null + term_child_status=$? + fi set -e + return ${term_child_status} } "#; + +#[cfg(test)] +mod tests { + use std::process::Command; + + use super::*; + + fn container_command_exit_code(script: &str) -> i32 { + Command::new("/bin/bash") + .args(["-euo", "pipefail", "-c", script]) + .status() + .expect("bash can be executed in the test environment") + .code() + .expect("the shell terminated regularly and not by a signal") + } + + #[test] + fn crash_of_child_process() { + let exit_code = container_command_exit_code(&format!( + "{COMMON_BASH_TRAP_FUNCTIONS} +prepare_signal_handlers +bash -c 'exit 42' & +wait_for_termination $!" + )); + + assert_eq!(42, exit_code); + } + + #[test] + fn graceful_shutdown_of_child_process() { + let exit_code = container_command_exit_code(&format!( + "{COMMON_BASH_TRAP_FUNCTIONS} +prepare_signal_handlers +bash -c 'trap \"exit 7\" TERM; sleep 10 & wait $!' & +child_pid=$! +(sleep 0.2; kill -TERM $$) & +wait_for_termination $child_pid" + )); + + assert_eq!(7, exit_code); + } +}