Skip to content

Commit

Permalink
lints: Check base image content
Browse files Browse the repository at this point in the history
This is mainly a sanity check for people building custom base
images.

But it also unit tests our own reference content.

Signed-off-by: Colin Walters <[email protected]>
  • Loading branch information
cgwalters committed Dec 20, 2024
1 parent 1ba69d1 commit 7d9a160
Showing 1 changed file with 87 additions and 1 deletion.
88 changes: 87 additions & 1 deletion lib/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@
//! This module implements `bootc container lint`.
use std::env::consts::ARCH;
use std::os::unix::ffi::OsStrExt;

use anyhow::{bail, ensure, Result};
use anyhow::{bail, ensure, Context, Result};
use cap_std::fs::Dir;
use cap_std_ext::cap_std;
use cap_std_ext::dirext::CapStdExtDirExt as _;
use fn_error_context::context;

use crate::utils::openat2_with_retry;

/// Reference to embedded default baseimage content that should exist.
const BASEIMAGE_REF: &str = "usr/share/doc/bootc/baseimage/base";

/// check for the existence of the /var/run directory
/// if it exists we need to check that it links to /run if not error
/// if it does not exist error.
Expand All @@ -23,6 +27,7 @@ pub(crate) fn lint(root: &Dir) -> Result<()> {
check_parse_kargs,
check_usretc,
check_utf8,
check_baseimage_root,
];
for lint in lints {
lint(&root)?;
Expand Down Expand Up @@ -116,6 +121,57 @@ fn check_utf8(dir: &Dir) -> Result<()> {
Ok(())
}

/// Check for a few files and directories we expect in the base image.
fn check_baseimage_root_norecurse(dir: &Dir) -> Result<()> {
// Check /sysroot
let meta = dir.symlink_metadata_optional("sysroot")?;
match meta {
Some(meta) if !meta.is_dir() => {
anyhow::bail!("Expected a directory for /sysroot")
}
None => anyhow::bail!("Missing /sysroot"),
_ => {}
}

// Check /ostree -> sysroot/ostree
let Some(meta) = dir.symlink_metadata_optional("ostree")? else {
anyhow::bail!("Missing ostree -> sysroot/ostree link")
};
if !meta.is_symlink() {
anyhow::bail!("/ostree should be a symlink");
}
let link = dir.read_link_contents("ostree")?;
let expected = "sysroot/ostree";
if link.as_os_str().as_bytes() != expected.as_bytes() {
anyhow::bail!("Expected /ostree -> {expected}, not {link:?}");
}

// Check the prepare-root config
let prepareroot_path = "usr/lib/ostree/prepare-root.conf";
let config_data = dir
.read_to_string(prepareroot_path)
.context(prepareroot_path)?;
let config = ostree_ext::glib::KeyFile::new();
config.load_from_data(&config_data, ostree_ext::glib::KeyFileFlags::empty())?;

if !ostree_ext::ostree_prepareroot::overlayfs_enabled_in_config(&config)? {
anyhow::bail!("{prepareroot_path} does not have composefs enabled")
}

Ok(())
}

/// Check ostree-related base image content.
fn check_baseimage_root(dir: &Dir) -> Result<()> {
check_baseimage_root_norecurse(dir)?;
// If we have our own documentation with the expected root contents
// embedded, then check that too! Mostly just because recursion is fun.
if let Some(dir) = dir.open_dir_optional(BASEIMAGE_REF)? {
check_baseimage_root_norecurse(&dir)?;
}
Ok(())
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -260,4 +316,34 @@ mod tests {
root.remove_file(badfile).unwrap(); // Get rid of the problem
check_utf8(root).unwrap(); // Check it
}

#[test]
fn test_baseimage_root() -> Result<()> {
use bootc_utils::CommandRunExt;
use cap_std_ext::cmdext::CapStdExtCommandExt;
use std::path::Path;

let td = fixture()?;

// An empty root should fail our test
assert!(check_baseimage_root(&td).is_err());

// Copy our reference base image content from the source dir
let manifest = std::env::var_os("CARGO_MANIFEST_PATH").unwrap();
let srcdir = Path::new(&manifest)
.parent()
.unwrap()
.join("../baseimage/base");
for ent in std::fs::read_dir(srcdir)? {
let ent = ent?;
std::process::Command::new("cp")
.cwd_dir(td.try_clone()?)
.arg("-pr")
.arg(ent.path())
.arg(".")
.run()?;
}
check_baseimage_root(&td).unwrap();
Ok(())
}
}

0 comments on commit 7d9a160

Please sign in to comment.