Skip to content

Commit cc025ed

Browse files
authored
Merge pull request #1101 from cgwalters/tmpfiles
lints: Add var-tmpfiles
2 parents c34b5e5 + 659218a commit cc025ed

File tree

9 files changed

+762
-10
lines changed

9 files changed

+762
-10
lines changed

Cargo.lock

+18
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+3-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ members = [
77
"utils",
88
"blockdev",
99
"xtask",
10-
"tests-integration"
10+
"tests-integration",
11+
"tmpfiles"
1112
]
1213
resolver = "2"
1314

@@ -59,6 +60,7 @@ similar-asserts = "1.5.0"
5960
static_assertions = "1.1.0"
6061
tempfile = "3.10.1"
6162
tracing = "0.1.40"
63+
thiserror = "2.0.11"
6264
tokio = ">= 1.37.0"
6365
tokio-util = { features = ["io-util"], version = "0.7.10" }
6466

hack/Containerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,4 @@ RUN tar -C / --zstd -xvf /tmp/bootc.tar.zst && rm -vrf /tmp/*
3434
# Also copy over arbitrary bits from the target root
3535
COPY --from=build /build/target/dev-rootfs/ /
3636
# Test our own linting
37-
RUN bootc container lint
37+
RUN bootc container lint --fatal-warnings

hack/provision-derived.sh

+19-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,24 @@ touch ~/.config/nushell/env.nu
1616
dnf -y install nu
1717
dnf clean all
1818
# Stock extra cleaning of logs and caches in general (mostly dnf)
19-
rm /var/log/* /var/cache /var/lib/dnf /var/lib/rpm-state -rf
19+
rm /var/log/* /var/cache /var/lib/{dnf,rpm-state,rhsm} -rf
2020
# And clean root's homedir
2121
rm /var/roothome/.config -rf
22+
23+
# Fast track tmpfiles.d content from the base image, xref
24+
# https://gitlab.com/fedora/bootc/base-images/-/merge_requests/92
25+
if test '!' -f /usr/lib/tmpfiles.d/bootc-base-rpmstate.conf; then
26+
cat >/usr/lib/tmpfiles.d/bootc-base-rpmstate.conf <<'EOF'
27+
# Workaround for https://bugzilla.redhat.com/show_bug.cgi?id=771713
28+
d /var/lib/rpm-state 0755 - - -
29+
EOF
30+
fi
31+
if ! grep -q -r var/roothome/buildinfo /usr/lib/tmpfiles.d; then
32+
cat > /usr/lib/tmpfiles.d/bootc-contentsets.conf <<'EOF'
33+
# Workaround for https://github.com/konflux-ci/build-tasks-dockerfiles/pull/243
34+
d /var/roothome/buildinfo 0755 - - -
35+
d /var/roothome/buildinfo/content_manifests 0755 - - -
36+
# Note we don't actually try to recreate the content; this just makes the linter ignore it
37+
f /var/roothome/buildinfo/content_manifests/content-sets.json 0644 - - -
38+
EOF
39+
fi

lib/Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ anstyle = "1.0.6"
1818
anyhow = { workspace = true }
1919
bootc-utils = { path = "../utils" }
2020
bootc-blockdev = { path = "../blockdev" }
21+
bootc-tmpfiles = { path = "../tmpfiles" }
2122
camino = { workspace = true, features = ["serde1"] }
2223
ostree-ext = { path = "../ostree-ext", features = ["bootc"] }
2324
chrono = { workspace = true, features = ["serde"] }
@@ -51,7 +52,7 @@ xshell = { version = "0.2.6", optional = true }
5152
uuid = { version = "1.8.0", features = ["v4"] }
5253
tini = "1.3.0"
5354
comfy-table = "7.1.1"
54-
thiserror = "2.0.11"
55+
thiserror = { workspace = true }
5556

5657
[dev-dependencies]
5758
similar-asserts = { workspace = true }

lib/src/lints.rs

+64-6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
use std::collections::BTreeSet;
99
use std::env::consts::ARCH;
10+
use std::fmt::Write as WriteFmt;
1011
use std::os::unix::ffi::OsStrExt;
1112

1213
use anyhow::Result;
@@ -467,6 +468,53 @@ fn check_varlog(root: &Dir) -> LintResult {
467468
lint_err(format!("Found non-empty logfile: {first}{others}"))
468469
}
469470

471+
#[distributed_slice(LINTS)]
472+
static LINT_VAR_TMPFILES: Lint = Lint {
473+
name: "var-tmpfiles",
474+
ty: LintType::Warning,
475+
description: indoc! { r#"
476+
Check for content in /var that does not have corresponding systemd tmpfiles.d entries.
477+
This can cause a problem across upgrades because content in /var from the container
478+
image will only be applied on the initial provisioning.
479+
480+
Instead, it's recommended to have /var effectively empty in the container image,
481+
and use systemd tmpfiles.d to generate empty directories and compatibility symbolic links
482+
as part of each boot.
483+
"#},
484+
f: check_var_tmpfiles,
485+
root_type: Some(RootType::Running),
486+
};
487+
fn check_var_tmpfiles(_root: &Dir) -> LintResult {
488+
let r = bootc_tmpfiles::find_missing_tmpfiles_current_root()?;
489+
if r.tmpfiles.is_empty() && r.unsupported.is_empty() {
490+
return lint_ok();
491+
}
492+
let mut msg = String::new();
493+
if let Some((samples, rest)) =
494+
bootc_utils::iterator_split_nonempty_rest_count(r.tmpfiles.iter(), 5)
495+
{
496+
msg.push_str("Found content in /var missing systemd tmpfiles.d entries:\n");
497+
for elt in samples {
498+
writeln!(msg, " {elt}")?;
499+
}
500+
if rest > 0 {
501+
writeln!(msg, " ...and {} more", rest)?;
502+
}
503+
}
504+
if let Some((samples, rest)) =
505+
bootc_utils::iterator_split_nonempty_rest_count(r.unsupported.iter(), 5)
506+
{
507+
msg.push_str("Found non-directory/non-symlink files in /var:\n");
508+
for elt in samples {
509+
writeln!(msg, " {elt:?}")?;
510+
}
511+
if rest > 0 {
512+
writeln!(msg, " ...and {} more", rest)?;
513+
}
514+
}
515+
lint_err(msg)
516+
}
517+
470518
#[distributed_slice(LINTS)]
471519
static LINT_NONEMPTY_BOOT: Lint = Lint::new_warning(
472520
"nonempty-boot",
@@ -498,8 +546,17 @@ fn check_boot(root: &Dir) -> LintResult {
498546

499547
#[cfg(test)]
500548
mod tests {
549+
use std::sync::LazyLock;
550+
501551
use super::*;
502552

553+
static ALTROOT_LINTS: LazyLock<usize> = LazyLock::new(|| {
554+
LINTS
555+
.iter()
556+
.filter(|lint| lint.root_type != Some(RootType::Running))
557+
.count()
558+
});
559+
503560
fn fixture() -> Result<cap_std_ext::cap_tempfile::TempDir> {
504561
let tempdir = cap_std_ext::cap_tempfile::tempdir(cap_std::ambient_authority())?;
505562
Ok(tempdir)
@@ -557,26 +614,27 @@ mod tests {
557614
let mut out = Vec::new();
558615
let root_type = RootType::Alternative;
559616
let r = lint_inner(root, root_type, [], &mut out).unwrap();
560-
assert_eq!(r.passed, LINTS.len());
617+
let running_only_lints = LINTS.len().checked_sub(*ALTROOT_LINTS).unwrap();
618+
assert_eq!(r.passed, *ALTROOT_LINTS);
561619
assert_eq!(r.fatal, 0);
562-
assert_eq!(r.skipped, 0);
620+
assert_eq!(r.skipped, running_only_lints);
563621
assert_eq!(r.warnings, 0);
564622

565623
let r = lint_inner(root, root_type, ["var-log"], &mut out).unwrap();
566624
// Trigger a failure in var-log
567625
root.create_dir_all("var/log/dnf")?;
568626
root.write("var/log/dnf/dnf.log", b"dummy dnf log")?;
569-
assert_eq!(r.passed, LINTS.len().checked_sub(1).unwrap());
627+
assert_eq!(r.passed, ALTROOT_LINTS.checked_sub(1).unwrap());
570628
assert_eq!(r.fatal, 0);
571-
assert_eq!(r.skipped, 1);
629+
assert_eq!(r.skipped, running_only_lints + 1);
572630
assert_eq!(r.warnings, 0);
573631

574632
// But verify that not skipping it results in a warning
575633
let mut out = Vec::new();
576634
let r = lint_inner(root, root_type, [], &mut out).unwrap();
577-
assert_eq!(r.passed, LINTS.len().checked_sub(1).unwrap());
635+
assert_eq!(r.passed, ALTROOT_LINTS.checked_sub(1).unwrap());
578636
assert_eq!(r.fatal, 0);
579-
assert_eq!(r.skipped, 0);
637+
assert_eq!(r.skipped, running_only_lints);
580638
assert_eq!(r.warnings, 1);
581639
Ok(())
582640
}

tmpfiles/Cargo.toml

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
[package]
2+
name = "bootc-tmpfiles"
3+
version = "0.1.0"
4+
license = "MIT OR Apache-2.0"
5+
edition = "2021"
6+
publish = false
7+
8+
[dependencies]
9+
camino = { workspace = true }
10+
fn-error-context = { workspace = true }
11+
cap-std-ext = { version = "4" }
12+
thiserror = { workspace = true }
13+
tempfile = { workspace = true }
14+
bootc-utils = { path = "../utils" }
15+
rustix = { workspace = true }
16+
uzers = "0.12"
17+
18+
[dev-dependencies]
19+
anyhow = { workspace = true }
20+
indoc = { workspace = true }
21+
similar-asserts = { workspace = true }
22+
23+
[lints]
24+
workspace = true

tmpfiles/src/command.rs

Whitespace-only changes.

0 commit comments

Comments
 (0)