Skip to content

Commit 2c1ba97

Browse files
authored
Merge pull request #1051 from cgwalters/lints-more-2
lints: Add warning level, add a check for /var/log
2 parents 9a58693 + add6cce commit 2c1ba97

File tree

2 files changed

+113
-5
lines changed

2 files changed

+113
-5
lines changed

lib/src/cli.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,10 @@ pub(crate) enum ContainerOpts {
245245
/// Operate on the provided rootfs.
246246
#[clap(long, default_value = "/")]
247247
rootfs: Utf8PathBuf,
248+
249+
/// Make warnings fatal.
250+
#[clap(long)]
251+
fatal_warnings: bool,
248252
},
249253
}
250254

@@ -1011,14 +1015,17 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
10111015
Opt::Edit(opts) => edit(opts).await,
10121016
Opt::UsrOverlay => usroverlay().await,
10131017
Opt::Container(opts) => match opts {
1014-
ContainerOpts::Lint { rootfs } => {
1018+
ContainerOpts::Lint {
1019+
rootfs,
1020+
fatal_warnings,
1021+
} => {
10151022
if !ostree_ext::container_utils::is_ostree_container()? {
10161023
anyhow::bail!(
10171024
"Not in a ostree container, this command only verifies ostree containers."
10181025
);
10191026
}
10201027
let root = &Dir::open_ambient_dir(rootfs, cap_std::ambient_authority())?;
1021-
lints::lint(root)?;
1028+
lints::lint(root, fatal_warnings)?;
10221029
Ok(())
10231030
}
10241031
},

lib/src/lints.rs

Lines changed: 104 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@
22
//!
33
//! This module implements `bootc container lint`.
44
5+
use std::collections::BTreeSet;
56
use std::env::consts::ARCH;
67
use std::os::unix::ffi::OsStrExt;
78

89
use anyhow::{Context, Result};
10+
use camino::{Utf8Path, Utf8PathBuf};
911
use cap_std::fs::Dir;
1012
use cap_std_ext::cap_std;
13+
use cap_std_ext::cap_std::fs::MetadataExt;
1114
use cap_std_ext::dirext::CapStdExtDirExt as _;
1215
use fn_error_context::context;
1316

@@ -53,6 +56,8 @@ enum LintType {
5356
/// If this fails, it is known to be fatal - the system will not install or
5457
/// is effectively guaranteed to fail at runtime.
5558
Fatal,
59+
/// This is not a fatal problem, but something you likely want to fix.
60+
Warning,
5661
}
5762

5863
struct Lint {
@@ -93,31 +98,54 @@ const LINTS: &[Lint] = &[
9398
ty: LintType::Fatal,
9499
f: check_baseimage_root,
95100
},
101+
Lint {
102+
name: "var-log",
103+
ty: LintType::Warning,
104+
f: check_varlog,
105+
},
96106
];
97107

98108
/// check for the existence of the /var/run directory
99109
/// if it exists we need to check that it links to /run if not error
100110
/// if it does not exist error.
101111
#[context("Linting")]
102-
pub(crate) fn lint(root: &Dir) -> Result<()> {
112+
pub(crate) fn lint(root: &Dir, fatal_warnings: bool) -> Result<()> {
103113
let mut fatal = 0usize;
114+
let mut warnings = 0usize;
104115
let mut passed = 0usize;
105116
for lint in LINTS {
106117
let name = lint.name;
107118
let r = match (lint.f)(&root) {
108119
Ok(r) => r,
109120
Err(e) => anyhow::bail!("Unexpected runtime error running lint {name}: {e}"),
110121
};
122+
111123
if let Err(e) = r {
112-
eprintln!("Failed lint: {name}: {e}");
113-
fatal += 1;
124+
match lint.ty {
125+
LintType::Fatal => {
126+
eprintln!("Failed lint: {name}: {e}");
127+
fatal += 1;
128+
}
129+
LintType::Warning => {
130+
eprintln!("Lint warning: {name}: {e}");
131+
warnings += 1;
132+
}
133+
}
114134
} else {
115135
// We'll be quiet for now
116136
tracing::debug!("OK {name} (type={:?})", lint.ty);
117137
passed += 1;
118138
}
119139
}
120140
println!("Checks passed: {passed}");
141+
let fatal = if fatal_warnings {
142+
fatal + warnings
143+
} else {
144+
fatal
145+
};
146+
if warnings > 0 {
147+
println!("Warnings: {warnings}");
148+
}
121149
if fatal > 0 {
122150
anyhow::bail!("Checks failed: {fatal}")
123151
}
@@ -239,6 +267,47 @@ fn check_baseimage_root(dir: &Dir) -> LintResult {
239267
lint_ok()
240268
}
241269

270+
fn collect_nonempty_regfiles(
271+
root: &Dir,
272+
path: &Utf8Path,
273+
out: &mut BTreeSet<Utf8PathBuf>,
274+
) -> Result<()> {
275+
for entry in root.entries_utf8()? {
276+
let entry = entry?;
277+
let ty = entry.file_type()?;
278+
let path = path.join(entry.file_name()?);
279+
if ty.is_file() {
280+
let meta = entry.metadata()?;
281+
if meta.size() > 0 {
282+
out.insert(path);
283+
}
284+
} else if ty.is_dir() {
285+
let d = entry.open_dir()?;
286+
collect_nonempty_regfiles(d.as_cap_std(), &path, out)?;
287+
}
288+
}
289+
Ok(())
290+
}
291+
292+
fn check_varlog(root: &Dir) -> LintResult {
293+
let Some(d) = root.open_dir_optional("var/log")? else {
294+
return lint_ok();
295+
};
296+
let mut nonempty_regfiles = BTreeSet::new();
297+
collect_nonempty_regfiles(&d, "/var/log".into(), &mut nonempty_regfiles)?;
298+
let mut nonempty_regfiles = nonempty_regfiles.into_iter();
299+
let Some(first) = nonempty_regfiles.next() else {
300+
return lint_ok();
301+
};
302+
let others = nonempty_regfiles.len();
303+
let others = if others > 0 {
304+
format!(" (and {others} more)")
305+
} else {
306+
"".into()
307+
};
308+
lint_err(format!("Found non-empty logfile: {first}{others}"))
309+
}
310+
242311
#[cfg(test)]
243312
mod tests {
244313
use super::*;
@@ -301,6 +370,38 @@ mod tests {
301370
Ok(())
302371
}
303372

373+
#[test]
374+
fn test_varlog() -> Result<()> {
375+
let root = &fixture()?;
376+
check_varlog(root).unwrap().unwrap();
377+
root.create_dir_all("var/log")?;
378+
check_varlog(root).unwrap().unwrap();
379+
root.symlink_contents("../../usr/share/doc/systemd/README.logs", "var/log/README")?;
380+
check_varlog(root).unwrap().unwrap();
381+
382+
root.atomic_write("var/log/somefile.log", "log contents")?;
383+
let Err(e) = check_varlog(root).unwrap() else {
384+
unreachable!()
385+
};
386+
assert_eq!(
387+
e.to_string(),
388+
"Found non-empty logfile: /var/log/somefile.log"
389+
);
390+
391+
root.create_dir_all("var/log/someproject")?;
392+
root.atomic_write("var/log/someproject/audit.log", "audit log")?;
393+
root.atomic_write("var/log/someproject/info.log", "info")?;
394+
let Err(e) = check_varlog(root).unwrap() else {
395+
unreachable!()
396+
};
397+
assert_eq!(
398+
e.to_string(),
399+
"Found non-empty logfile: /var/log/somefile.log (and 2 more)"
400+
);
401+
402+
Ok(())
403+
}
404+
304405
#[test]
305406
fn test_non_utf8() {
306407
use std::{ffi::OsStr, os::unix::ffi::OsStrExt};

0 commit comments

Comments
 (0)