Skip to content

Commit

Permalink
cli: add container lint
Browse files Browse the repository at this point in the history
Add an entrypoint that basically everyone can start
adding to their builds which performs some basic
static analysis for known problems.

Closes : #216

Co-authored-by: Joseph Marrero <[email protected]>
Co-authored-by: Huijing Hei <[email protected]>
Co-authored-by: Yasmin de Souza <[email protected]>

Signed-off-by: Steven Presti <[email protected]>
Signed-off-by: Colin Walters <[email protected]>
  • Loading branch information
prestist authored and cgwalters committed May 30, 2024
1 parent 94ddb2f commit 28f53cb
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 0 deletions.
1 change: 1 addition & 0 deletions hack/Containerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ FROM $base
COPY --from=build /out/bootc.tar.zst /tmp
COPY --from=build /build/target/dev-rootfs/ /
RUN tar -C / --zstd -xvf /tmp/bootc.tar.zst && rm -vf /tmp/*
RUN bootc container lint
25 changes: 25 additions & 0 deletions lib/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use std::os::unix::process::CommandExt;
use std::process::Command;

use crate::deploy::RequiredHostSpec;
use crate::lints;
use crate::spec::Host;
use crate::spec::ImageReference;
use crate::utils::sigpolicy_from_opts;
Expand Down Expand Up @@ -143,6 +144,14 @@ pub(crate) struct ManOpts {
pub(crate) directory: Utf8PathBuf,
}

/// Subcommands which can be executed as part of a container build.
#[derive(Debug, clap::Subcommand, PartialEq, Eq)]
pub(crate) enum ContainerOpts {
/// Perform relatively inexpensive static analysis checks as part of a container
/// build.
Lint,
}

/// Hidden, internal only options
#[derive(Debug, clap::Subcommand, PartialEq, Eq)]
pub(crate) enum InternalsOpts {
Expand Down Expand Up @@ -292,6 +301,9 @@ pub(crate) enum Opt {
#[clap(subcommand)]
#[cfg(feature = "install")]
Install(InstallOpts),
/// Operations which can be executed as part of a container build.
#[clap(subcommand)]
Container(ContainerOpts),
/// Execute the given command in the host mount namespace
#[cfg(feature = "install")]
#[clap(hide = true)]
Expand Down Expand Up @@ -662,6 +674,19 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
Opt::Rollback(opts) => rollback(opts).await,
Opt::Edit(opts) => edit(opts).await,
Opt::UsrOverlay => usroverlay().await,
Opt::Container(opts) => match opts {
ContainerOpts::Lint => {
if !ostree_ext::container_utils::is_ostree_container()? {
anyhow::bail!(
"Not in a ostree container, this command only verifies ostree containers."
);
}

let root = cap_std::fs::Dir::open_ambient_dir("/", cap_std::ambient_authority())?;
lints::lint(&root)?;
Ok(())
}
},
#[cfg(feature = "install")]
Opt::Install(opts) => match opts {
InstallOpts::ToDisk(opts) => crate::install::install_to_disk(opts).await,
Expand Down
1 change: 1 addition & 0 deletions lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub mod cli;
pub(crate) mod deploy;
pub(crate) mod generator;
pub(crate) mod journal;
mod lints;
mod lsm;
pub(crate) mod metadata;
mod reboot;
Expand Down
72 changes: 72 additions & 0 deletions lib/src/lints.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//! # Implementation of container build lints
//!
//! This module implements `bootc container lint`.

use anyhow::Result;
use cap_std::fs::Dir;
use cap_std_ext::cap_std;
use cap_std_ext::dirext::CapStdExtDirExt as _;
use fn_error_context::context;

/// 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.
#[context("Linting")]
pub(crate) fn lint(root: &Dir) -> Result<()> {
let lints = [check_var_run, check_kernel];
for lint in lints {
lint(&root)?;
}
println!("Checks passed: {}", lints.len());
Ok(())
}

fn check_var_run(root: &Dir) -> Result<()> {
if let Some(meta) = root.symlink_metadata_optional("var/run")? {
if !meta.is_symlink() {
anyhow::bail!("Not a symlink: var/run");
}
}
Ok(())
}

fn check_kernel(root: &Dir) -> Result<()> {
let result = ostree_ext::bootabletree::find_kernel_dir_fs(&root)?;
tracing::debug!("Found kernel: {:?}", result);
Ok(())
}

#[cfg(test)]
fn fixture() -> Result<cap_std_ext::cap_tempfile::TempDir> {
let tempdir = cap_std_ext::cap_tempfile::tempdir(cap_std::ambient_authority())?;
Ok(tempdir)
}

#[test]
fn test_var_run() -> Result<()> {
let root = &fixture()?;
// This one should pass
check_var_run(root).unwrap();
root.create_dir_all("var/run/foo")?;
assert!(check_var_run(root).is_err());
root.remove_dir_all("var/run")?;
// Now we should pass again
check_var_run(root).unwrap();
Ok(())
}

#[test]
fn test_kernel_lint() -> Result<()> {
let root = &fixture()?;
// This one should pass
check_kernel(root).unwrap();
root.create_dir_all("usr/lib/modules/5.7.2")?;
root.write("usr/lib/modules/5.7.2/vmlinuz", "old vmlinuz")?;
root.create_dir_all("usr/lib/modules/6.3.1")?;
root.write("usr/lib/modules/6.3.1/vmlinuz", "new vmlinuz")?;
assert!(check_kernel(root).is_err());
root.remove_dir_all("usr/lib/modules/5.7.2")?;
// Now we should pass again
check_kernel(root).unwrap();
Ok(())
}

0 comments on commit 28f53cb

Please sign in to comment.