Skip to content

Commit f8deb30

Browse files
Armando Ortizdjach7
authored andcommitted
Add support for binary validation upon booting
- Previously only supported script validation - Added an extra test case in greenboot.rs to check that binary files can be skipped - Modified tests within greenboot.rs in order handle both failing scripts and binaries
1 parent 11ffdc8 commit f8deb30

3 files changed

Lines changed: 102 additions & 9 deletions

File tree

src/lib/greenboot.rs

Lines changed: 102 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ use anyhow::{Result, bail};
22
use glob::glob;
33
use std::collections::HashSet;
44
use std::error::Error;
5+
use std::fs;
6+
use std::os::unix::fs::PermissionsExt;
57
use std::path::Path;
68
use std::process::Command;
79

@@ -104,9 +106,23 @@ fn run_scripts(name: &str, path: &str, disabled_scripts: Option<&[String]>) -> S
104106
skipped: Vec::new(),
105107
};
106108

107-
// Handle glob pattern error early
108-
let entries = match glob(&format!("{}*.sh", path)) {
109-
Ok(e) => e,
109+
let entries = match glob(&format!("{}*", path)) {
110+
Ok(e) => {
111+
let valid: Vec<_> = e
112+
.filter_map(Result::ok)
113+
.filter(|entry| {
114+
if let Ok(metadata) = fs::metadata(entry) {
115+
let mode = metadata.permissions().mode();
116+
metadata.is_file()
117+
&& (entry.extension().and_then(|ext| ext.to_str()) == Some("sh")
118+
|| (mode & 0o001 != 0 || mode & 0o010 != 0 || mode & 0o100 != 0))
119+
} else {
120+
false
121+
}
122+
})
123+
.collect();
124+
Some(valid).into_iter()
125+
}
110126
Err(e) => {
111127
result.errors.push(Box::new(e));
112128
return result;
@@ -115,24 +131,28 @@ fn run_scripts(name: &str, path: &str, disabled_scripts: Option<&[String]>) -> S
115131

116132
for entry in entries.flatten() {
117133
// Process script name
118-
let script_name = match entry.file_name().and_then(|n| n.to_str()) {
134+
let file_name = match entry.file_name().and_then(|n| n.to_str()) {
119135
Some(name) => name,
120136
None => continue,
121137
};
122138

123139
// Check if script should be skipped
124140
if let Some(disabled) = disabled_scripts {
125-
if disabled.contains(&script_name.to_string()) {
126-
log::info!("Skipping disabled script: {}", script_name);
127-
result.skipped.push(script_name.to_string());
141+
if disabled.contains(&file_name.to_string()) {
142+
log::info!("Skipping disabled script: {}", file_name);
143+
result.skipped.push(file_name.to_string());
128144
continue;
129145
}
130146
}
131147

132148
log::info!("running {} check {}", name, entry.to_string_lossy());
133149

134-
// Execute script and handle output
135-
let output = Command::new("bash").arg("-C").arg(&entry).output();
150+
// Sort between script and binary since they require different commands to execute properly.
151+
let output = if entry.extension().and_then(|ext| ext.to_str()) == Some("sh") {
152+
Command::new("bash").arg("-C").arg(&entry).output()
153+
} else {
154+
Command::new(&entry).output()
155+
};
136156

137157
match output {
138158
Ok(o) if o.status.success() => {
@@ -213,6 +233,11 @@ mod test {
213233
setup_folder_structure(false)
214234
.context("Test setup failed")
215235
.unwrap();
236+
// Causes errors if these are no removed since they cause an excess amount
237+
// of failure.
238+
let required_path = format!("{}/check/required.d", GREENBOOT_INSTALL_PATHS[1]);
239+
let _ = std::fs::remove_file(format!("{}/01_failing_binary", required_path));
240+
let _ = std::fs::remove_file(format!("{}/02_failing_binary", required_path));
216241

217242
let base_path = GREENBOOT_INSTALL_PATHS[1];
218243

@@ -279,6 +304,12 @@ mod test {
279304
.context("Test setup failed")
280305
.unwrap();
281306

307+
// Removing extra failing binaries because this can cause a
308+
// failure if not added to the skips or removed as done below.
309+
let required_path = format!("{}/check/required.d", GREENBOOT_INSTALL_PATHS[1]);
310+
let _ = std::fs::remove_file(format!("{}/01_failing_binary", required_path));
311+
let _ = std::fs::remove_file(format!("{}/02_failing_binary", required_path));
312+
282313
// Skip the disabled script in required.d ,since there are two
283314
// failing- scripts passing them both so that this test passes.
284315
let state = run_diagnostics(vec![
@@ -293,11 +324,41 @@ mod test {
293324
tear_down().context("Test teardown failed").unwrap();
294325
}
295326

327+
// Since binaries are a separate and later added feature compared to
328+
// scripts, there should be a separate test to ensure they both work.
329+
#[test]
330+
fn test_skip_disabled_binary() {
331+
setup_folder_structure(false)
332+
.context("Test setup failed")
333+
.unwrap();
334+
335+
// Removing extra failing scripts because this can cause a
336+
// failure if not added to the skips or removed as done below
337+
let required_path = format!("{}/check/required.d", GREENBOOT_INSTALL_PATHS[1]);
338+
let _ = std::fs::remove_file(format!("{}/01_failing_script.sh", required_path));
339+
let _ = std::fs::remove_file(format!("{}/02_failing_script.sh", required_path));
340+
341+
// Skip the disabled script in required.d ,since there are two
342+
// failing- scripts passing them both so that this test passes.
343+
let state = run_diagnostics(vec![
344+
"01_failing_binary".to_string(),
345+
"02_failing_binary".to_string(),
346+
]);
347+
assert!(
348+
state.is_ok(),
349+
"Should pass when skipping disabled required binary"
350+
);
351+
352+
tear_down().context("Test teardown failed").unwrap();
353+
}
354+
296355
fn setup_folder_structure(passing: bool) -> Result<()> {
297356
let required_path = format!("{}/check/required.d", GREENBOOT_INSTALL_PATHS[1]);
298357
let wanted_path = format!("{}/check/wanted.d", GREENBOOT_INSTALL_PATHS[1]);
299358
let passing_test_scripts = "testing_assets/passing_script.sh";
300359
let failing_test_scripts = "testing_assets/failing_script.sh";
360+
let passing_test_binary = "testing_assets/passing_binary";
361+
let failing_test_binary = "testing_assets/failing_binary";
301362

302363
fs::create_dir_all(&required_path).expect("cannot create folder");
303364
fs::create_dir_all(&wanted_path).expect("cannot create folder");
@@ -315,13 +376,33 @@ mod test {
315376
)
316377
.context("unable to copy passing script to wanted.d")?;
317378

379+
// Create passing binary in both required and wanted
380+
fs::copy(
381+
passing_test_binary,
382+
format!("{}/passing_binary", &required_path),
383+
)
384+
.context("unable to copy passing binary to required.d")?;
385+
386+
fs::copy(
387+
passing_test_binary,
388+
format!("{}/passing_binary", &wanted_path),
389+
)
390+
.context("unable to copy passing binary to wanted.d")?;
391+
318392
// Create failing script in wanted.d
319393
fs::copy(
320394
failing_test_scripts,
321395
format!("{}/failing_script.sh", &wanted_path),
322396
)
323397
.context("unable to copy failing script to wanted.d")?;
324398

399+
// Create failing binary in wanted.d
400+
fs::copy(
401+
failing_test_binary,
402+
format!("{}/failing_binary", &wanted_path),
403+
)
404+
.context("unable to copy failing binary to wanted.d")?;
405+
325406
if !passing {
326407
// Create multiple failing script in required.d for failure cases
327408
fs::copy(
@@ -334,6 +415,18 @@ mod test {
334415
format!("{}/02_failing_script.sh", &required_path),
335416
)
336417
.context("unable to copy another failing script to required.d")?;
418+
419+
// Create multiple failing binaries in required.d for failure cases
420+
fs::copy(
421+
failing_test_scripts,
422+
format!("{}/01_failing_binary", &required_path),
423+
)
424+
.context("unable to copy failing binary to required.d")?;
425+
fs::copy(
426+
failing_test_scripts,
427+
format!("{}/02_failing_binary", &required_path),
428+
)
429+
.context("unable to copy another failing binary to required.d")?;
337430
}
338431
Ok(())
339432
}

testing_assets/failing_binary

12.1 KB
Binary file not shown.

testing_assets/passing_binary

12.1 KB
Binary file not shown.

0 commit comments

Comments
 (0)