1
0
mirror of https://github.com/containers/bootc.git synced 2026-02-05 15:45:53 +01:00

cli: add container lint

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 <jmarrero@redhat.com>
Co-authored-by: Huijing Hei <hhei@redhat.com>
Co-authored-by: Yasmin de Souza <ydesouza@redhat.com>

Signed-off-by: Steven Presti <spresti@redhat.com>
Signed-off-by: Colin Walters <walters@verbum.org>
This commit is contained in:
Steven Presti
2024-03-21 10:37:04 -04:00
committed by Colin Walters
parent 94ddb2f372
commit 858acbe5dd
7 changed files with 159 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
# NAME
bootc-container-lint - Perform relatively inexpensive static analysis
checks as part of a container build
# SYNOPSIS
**bootc container lint** \[**-h**\|**\--help**\]
# DESCRIPTION
Perform relatively inexpensive static analysis checks as part of a
container build
# OPTIONS
**-h**, **\--help**
: Print help
# VERSION
v0.1.11

View File

@@ -0,0 +1,33 @@
# NAME
bootc-container - Operations which can be executed as part of a
container build
# SYNOPSIS
**bootc container** \[**-h**\|**\--help**\] \<*subcommands*\>
# DESCRIPTION
Operations which can be executed as part of a container build
# OPTIONS
**-h**, **\--help**
: Print help
# SUBCOMMANDS
bootc-container-lint(8)
: Perform relatively inexpensive static analysis checks as part of a
container build
bootc-container-help(8)
: Print this message or the help of the given subcommand(s)
# VERSION
v0.1.11

View File

@@ -62,6 +62,10 @@ bootc-install(8)
: Install the running container to a target
bootc-container(8)
: Operations which can be executed as part of a container build
bootc-help(8)
: Print this message or the help of the given subcommand(s)

View File

@@ -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

View File

@@ -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;
@@ -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 {
@@ -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)]
@@ -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,

View File

@@ -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;

72
lib/src/lints.rs Normal file
View File

@@ -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(())
}