@@ -8,9 +8,9 @@ use std::process::Command;
88/// dir that greenboot looks for the health check and other scripts
99static GREENBOOT_INSTALL_PATHS : [ & str ; 2 ] = [ "/usr/lib/greenboot" , "/etc/greenboot" ] ;
1010
11- /// runs all the scripts in required.d and wanted.d
11+ /// run required.d and wanted.d scripts.
12+ /// If a required script fails, log the error, and skip remaining checks.
1213pub fn run_diagnostics ( skipped : Vec < String > ) -> Result < Vec < String > > {
13- let mut required_script_failure = false ;
1414 let mut path_exists = false ;
1515 let mut all_skipped = HashSet :: new ( ) ;
1616
@@ -31,7 +31,7 @@ pub fn run_diagnostics(skipped: Vec<String>) -> Result<Vec<String>> {
3131 if !result. errors . is_empty ( ) {
3232 log:: error!( "required script error:" ) ;
3333 result. errors . iter ( ) . for_each ( |e| log:: error!( "{e}" ) ) ;
34- required_script_failure = true ;
34+ bail ! ( "required health-check failed, skipping remaining scripts" ) ;
3535 }
3636 }
3737
@@ -64,9 +64,6 @@ pub fn run_diagnostics(skipped: Vec<String>) -> Result<Vec<String>> {
6464 ) ;
6565 }
6666
67- if required_script_failure {
68- bail ! ( "health-check failed!" ) ;
69- }
7067 Ok ( missing_disabled)
7168}
7269
@@ -149,13 +146,18 @@ fn run_scripts(name: &str, path: &str, disabled_scripts: Option<&[String]>) -> S
149146 String :: from_utf8_lossy( & o. stdout) ,
150147 String :: from_utf8_lossy( & o. stderr)
151148 ) ;
152- result. errors . push ( Box :: new ( std:: io:: Error :: new (
153- std:: io:: ErrorKind :: Other ,
154- error_msg,
155- ) ) ) ;
149+ result
150+ . errors
151+ . push ( Box :: new ( std:: io:: Error :: other ( error_msg) ) ) ;
152+ if name == "required" {
153+ break ;
154+ }
156155 }
157156 Err ( e) => {
158157 result. errors . push ( Box :: new ( e) ) ;
158+ if name == "required" {
159+ break ;
160+ }
159161 }
160162 }
161163 }
@@ -167,13 +169,28 @@ fn run_scripts(name: &str, path: &str, disabled_scripts: Option<&[String]>) -> S
167169mod test {
168170 use super :: * ;
169171 use anyhow:: { Context , Result } ;
170- use std:: fs;
172+ use std:: fs:: File ;
173+ use std:: io:: Write ;
174+ use std:: sync:: Once ;
175+ use std:: { fs, os:: unix:: fs:: PermissionsExt } ;
176+
177+ static INIT : Once = Once :: new ( ) ;
178+
179+ fn init_logger ( ) {
180+ INIT . call_once ( || {
181+ env_logger:: builder ( ) . is_test ( true ) . try_init ( ) . ok ( ) ;
182+ } ) ;
183+ }
171184
172185 static GREENBOOT_INSTALL_PATHS : [ & str ; 2 ] = [ "/usr/lib/greenboot" , "/etc/greenboot" ] ;
173186
174187 /// validate when the required folder is not found
175188 #[ test]
176- fn missing_required_folder ( ) {
189+ fn test_missing_required_folder ( ) {
190+ let required_path = format ! ( "{}/check/required.d" , GREENBOOT_INSTALL_PATHS [ 1 ] ) ;
191+ if Path :: new ( & required_path) . exists ( ) {
192+ fs:: remove_dir_all ( & required_path) . unwrap ( ) ;
193+ }
177194 assert_eq ! (
178195 run_diagnostics( vec![ ] ) . unwrap_err( ) . to_string( ) ,
179196 String :: from( "cannot find any required.d folder" )
@@ -191,13 +208,52 @@ mod test {
191208 }
192209
193210 #[ test]
194- fn test_failed_diagnostics ( ) {
211+ fn test_required_script_failure_exit_early ( ) {
212+ init_logger ( ) ;
195213 setup_folder_structure ( false )
196214 . context ( "Test setup failed" )
197215 . unwrap ( ) ;
198- let failed_msg = run_diagnostics ( vec ! [ ] ) . unwrap_err ( ) . to_string ( ) ;
199- assert_eq ! ( failed_msg, String :: from( "health-check failed!" ) ) ;
200- tear_down ( ) . context ( "Test teardown failed" ) . unwrap ( ) ;
216+
217+ let base_path = GREENBOOT_INSTALL_PATHS [ 1 ] ;
218+
219+ let counter_file = format ! ( "{}/fail_counter.txt" , base_path) ;
220+ let mut file = File :: create ( & counter_file) . expect ( "Failed to create counter file" ) ;
221+ writeln ! ( file, "0" ) . unwrap ( ) ;
222+
223+ // Inject counter logic into the failing scripts
224+ for name in [ "01_failing_script" , "02_failing_script" ] {
225+ let path = format ! ( "{}/check/required.d/{}.sh" , base_path, name) ;
226+ let mut script = File :: create ( & path) . unwrap ( ) ;
227+ writeln ! (
228+ script,
229+ "#!/bin/bash\n COUNTER_FILE=\" {}\" \n count=$(cat $COUNTER_FILE)\n echo $((count + 1)) >| $COUNTER_FILE\n exit 1" ,
230+ counter_file
231+ ) . unwrap ( ) ;
232+ std:: fs:: set_permissions ( & path, std:: fs:: Permissions :: from_mode ( 0o755 ) ) . unwrap ( ) ;
233+ }
234+
235+ let result = run_diagnostics ( vec ! [ ] ) ;
236+ log:: debug!( "Diagnostics result: {:?}" , result) ;
237+
238+ assert ! ( result. is_err( ) ) ;
239+ assert_eq ! (
240+ result. unwrap_err( ) . to_string( ) ,
241+ "required health-check failed, skipping remaining scripts"
242+ ) ;
243+
244+ log:: info!( "Health check failed as expected." ) ;
245+
246+ let fail_script_count = fs:: read_to_string ( counter_file)
247+ . unwrap ( )
248+ . trim ( )
249+ . parse :: < u32 > ( )
250+ . unwrap ( ) ;
251+ assert_eq ! (
252+ fail_script_count, 1 ,
253+ "Only one failing script should have executed"
254+ ) ;
255+
256+ tear_down ( ) . expect ( "teardown failed" ) ;
201257 }
202258
203259 #[ test]
@@ -207,7 +263,7 @@ mod test {
207263 . context ( "Test setup failed" )
208264 . unwrap ( ) ;
209265
210- // Try to skip a script that doesn't exist
266+ // Try to run a script that doesn't exist
211267 let state = run_diagnostics ( vec ! [ nonexistent_script_name. clone( ) ] ) ;
212268 assert ! (
213269 state. unwrap( ) . contains( & nonexistent_script_name) ,
@@ -218,16 +274,20 @@ mod test {
218274 }
219275
220276 #[ test]
221- fn test_skip_failing_script ( ) {
277+ fn test_skip_disabled_script ( ) {
222278 setup_folder_structure ( false )
223279 . context ( "Test setup failed" )
224280 . unwrap ( ) ;
225281
226- // Skip the failing script in required.d
227- let state = run_diagnostics ( vec ! [ "failing_script.sh" . to_string( ) ] ) ;
282+ // Skip the disabled script in required.d ,since there are two
283+ // failing- scripts passing them both so that this test passes.
284+ let state = run_diagnostics ( vec ! [
285+ "01_failing_script.sh" . to_string( ) ,
286+ "02_failing_script.sh" . to_string( ) ,
287+ ] ) ;
228288 assert ! (
229289 state. is_ok( ) ,
230- "Should pass when skipping failing required script"
290+ "Should pass when skipping disabled required script"
231291 ) ;
232292
233293 tear_down ( ) . context ( "Test teardown failed" ) . unwrap ( ) ;
@@ -263,12 +323,17 @@ mod test {
263323 . context ( "unable to copy failing script to wanted.d" ) ?;
264324
265325 if !passing {
266- // Create failing script in required.d for failure cases
326+ // Create multiple failing script in required.d for failure cases
267327 fs:: copy (
268328 failing_test_scripts,
269- format ! ( "{}/failing_script .sh" , & required_path) ,
329+ format ! ( "{}/01_failing_script .sh" , & required_path) ,
270330 )
271331 . context ( "unable to copy failing script to required.d" ) ?;
332+ fs:: copy (
333+ failing_test_scripts,
334+ format ! ( "{}/02_failing_script.sh" , & required_path) ,
335+ )
336+ . context ( "unable to copy another failing script to required.d" ) ?;
272337 }
273338 Ok ( ( ) )
274339 }
0 commit comments