Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

install: Add /usr/share/doc/bootc/baseimage #988

Merged
merged 2 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ install:
fi; \
done
install -D -m 0644 -t $(DESTDIR)/$(prefix)/lib/systemd/system systemd/*.service systemd/*.timer systemd/*.path systemd/*.target
install -D -m 0644 -t $(DESTDIR)/$(prefix)/share/doc/bootc/baseimage/base/usr/lib/ostree/ baseimage/base/usr/lib/ostree/prepare-root.conf
install -d -m 755 $(DESTDIR)/$(prefix)/share/doc/bootc/baseimage/base/sysroot
cp -PfT baseimage/base/ostree $(DESTDIR)/$(prefix)/share/doc/bootc/baseimage/base/ostree

# Run this to also take over the functionality of `ostree container` for example.
# Only needed for OS/distros that have callers invoking `ostree container` and not bootc.
Expand Down
10 changes: 10 additions & 0 deletions baseimage/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Recommended image content

The subdirectories here are recommended to be installed alongside
bootc in `/usr/share/doc/bootc/baseimage` - they act as reference
sources of content.

- [base](base): At the current time the content here is effectively
a hard requirement. It's not much, just an ostree configuration
enabling composefs, plus the default `sysroot` directory (which
may go away in the future) and the `ostree` symlink into `sysroot`.
1 change: 1 addition & 0 deletions baseimage/base/ostree
3 changes: 3 additions & 0 deletions baseimage/base/sysroot/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# A trick to keep an empty directory in git
*
!.gitignore
2 changes: 2 additions & 0 deletions baseimage/base/usr/lib/ostree/prepare-root.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[composefs]
enabled = true
1 change: 1 addition & 0 deletions contrib/packaging/bootc.spec
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ BuildRequires: libzstd-devel
%{_prefix}/lib/systemd/system-generators/*
%{_prefix}/lib/bootc
%{_unitdir}/*
%{_docdir}/bootc/*
%{_mandir}/man*/bootc*

%prep
Expand Down
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(())
}
}
Loading