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

cli: add container lint #381

Merged
merged 1 commit into from
Jun 3, 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
23 changes: 23 additions & 0 deletions docs/src/man/bootc-container-lint.md
Original file line number Diff line number Diff line change
@@ -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
33 changes: 33 additions & 0 deletions docs/src/man/bootc-container.md
Original file line number Diff line number Diff line change
@@ -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
4 changes: 4 additions & 0 deletions docs/src/man/bootc.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
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(())
}
Loading