Skip to content

Commit 3382d98

Browse files
authored
Postinstall disk advisory check (#107)
This runs an advisory disk free check on `postinstall` to warn if you are lacking sufficient disk space in `/var/lib` for things like image pulls and large caches.
1 parent 715143c commit 3382d98

3 files changed

Lines changed: 121 additions & 1 deletion

File tree

src/checkers/postinstall/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ pub mod guest_type;
22
pub mod kernel;
33
pub mod kube;
44
pub mod services;
5+
pub mod system;

src/checkers/postinstall/system.rs

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
use crate::helpers::{
2+
CheckGroup, CheckGroupCategory, CheckGroupResult, CheckResult,
3+
CheckResultValue::{Errored, Failed, Passed},
4+
host_executor::HostNamespaceExecutor,
5+
};
6+
7+
use async_trait::async_trait;
8+
use bytesize::ByteSize;
9+
use futures::{FutureExt, future::join_all};
10+
use log::debug;
11+
use sysinfo::Disks;
12+
13+
const GROUP_IDENTIFIER: &str = "system";
14+
const NAME: &str = "System Checks";
15+
const RECOMMENDED_DISK_VAR: u64 = 20 * 1024 * 1024 * 1024; // 5GB
16+
17+
pub struct SystemChecks {
18+
host_executor: HostNamespaceExecutor,
19+
}
20+
21+
impl SystemChecks {
22+
pub fn new(host_executor: HostNamespaceExecutor) -> Self {
23+
SystemChecks { host_executor }
24+
}
25+
pub async fn run_all(&self) -> CheckGroupResult {
26+
let results = join_all([self
27+
.enough_disk("/var/lib".into(), RECOMMENDED_DISK_VAR)
28+
.boxed()])
29+
.await;
30+
31+
let mut group_result = Passed;
32+
for res in results.iter() {
33+
// Set group result to Failed if we failed and aren't already in an Errored state
34+
if !matches!(group_result, Errored(_)) && matches!(res.result, Failed(_)) {
35+
group_result = Failed("".into());
36+
}
37+
38+
if matches!(res.result, Errored(_)) {
39+
group_result = Errored("".into());
40+
}
41+
}
42+
43+
CheckGroupResult {
44+
name: NAME.to_string(),
45+
result: group_result,
46+
results,
47+
}
48+
}
49+
50+
async fn enough_disk(&self, mount_path: String, free_thresh: u64) -> CheckResult {
51+
let name = format!("Enough Disk Space for {}", &mount_path);
52+
let result = match self
53+
.host_executor
54+
.spawn_in_host_ns(async move {
55+
let disks = Disks::new_with_refreshed_list();
56+
match Self::available_space_for_path(&disks, &mount_path) {
57+
Some(avail) if avail >= free_thresh => Passed,
58+
Some(avail) => Failed(format!(
59+
"{} has {} free, at least {} is recommended for ",
60+
mount_path,
61+
ByteSize(avail),
62+
ByteSize(free_thresh),
63+
)),
64+
None => Failed(format!(
65+
"no mounted filesystem found covering {}",
66+
mount_path
67+
)),
68+
}
69+
})
70+
.await
71+
{
72+
Ok(result) => result,
73+
Err(e) => Errored(e.to_string()),
74+
};
75+
CheckResult::new(&name, result)
76+
}
77+
78+
/// Finds available bytes for the filesystem that best covers `path`
79+
fn available_space_for_path(disks: &Disks, path: &str) -> Option<u64> {
80+
let target = std::path::Path::new(path);
81+
disks
82+
.iter()
83+
.filter(|d| target.starts_with(d.mount_point()))
84+
.max_by_key(|d| d.mount_point().as_os_str().len())
85+
.map(|d| {
86+
debug!(
87+
"Best mount for {}: {} ({} bytes available)",
88+
path,
89+
d.mount_point().display(),
90+
d.available_space()
91+
);
92+
d.available_space()
93+
})
94+
}
95+
}
96+
97+
#[async_trait]
98+
impl CheckGroup for SystemChecks {
99+
fn id(&self) -> &str {
100+
GROUP_IDENTIFIER
101+
}
102+
103+
fn name(&self) -> &str {
104+
NAME
105+
}
106+
107+
fn description(&self) -> &str {
108+
"System checks"
109+
}
110+
111+
async fn run(&self) -> CheckGroupResult {
112+
self.run_all().await
113+
}
114+
115+
fn category(&self) -> CheckGroupCategory {
116+
CheckGroupCategory::Advisory
117+
}
118+
}

src/postinstall_cmd.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use edera_check::checkers::postinstall::{
22
guest_type::GuestTypeChecks, kernel::PostinstallKernelChecks, kube::KubeChecks,
3-
services::ServiceChecks,
3+
services::ServiceChecks, system::SystemChecks,
44
};
55

66
use crate::{booted_under_edera, create_base_path, create_gzip_from, write_group_report};
@@ -36,6 +36,7 @@ pub async fn do_postinstall(
3636
Box::new(PostinstallKernelChecks::new(host_executor.clone())),
3737
Box::new(ServiceChecks::new(host_executor.clone())),
3838
Box::new(KubeChecks::new(host_executor.clone())),
39+
Box::new(SystemChecks::new(host_executor.clone())),
3940
];
4041

4142
if record_hostinfo {

0 commit comments

Comments
 (0)