Skip to content

Commit ebe1530

Browse files
authored
Merge pull request #381 from prestist/build-lint
cli: add container lint
2 parents f152bfe + 858acbe commit ebe1530

File tree

7 files changed

+159
-0
lines changed

7 files changed

+159
-0
lines changed

docs/src/man/bootc-container-lint.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# NAME
2+
3+
bootc-container-lint - Perform relatively inexpensive static analysis
4+
checks as part of a container build
5+
6+
# SYNOPSIS
7+
8+
**bootc container lint** \[**-h**\|**\--help**\]
9+
10+
# DESCRIPTION
11+
12+
Perform relatively inexpensive static analysis checks as part of a
13+
container build
14+
15+
# OPTIONS
16+
17+
**-h**, **\--help**
18+
19+
: Print help
20+
21+
# VERSION
22+
23+
v0.1.11

docs/src/man/bootc-container.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# NAME
2+
3+
bootc-container - Operations which can be executed as part of a
4+
container build
5+
6+
# SYNOPSIS
7+
8+
**bootc container** \[**-h**\|**\--help**\] \<*subcommands*\>
9+
10+
# DESCRIPTION
11+
12+
Operations which can be executed as part of a container build
13+
14+
# OPTIONS
15+
16+
**-h**, **\--help**
17+
18+
: Print help
19+
20+
# SUBCOMMANDS
21+
22+
bootc-container-lint(8)
23+
24+
: Perform relatively inexpensive static analysis checks as part of a
25+
container build
26+
27+
bootc-container-help(8)
28+
29+
: Print this message or the help of the given subcommand(s)
30+
31+
# VERSION
32+
33+
v0.1.11

docs/src/man/bootc.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ bootc-install(8)
6262

6363
: Install the running container to a target
6464

65+
bootc-container(8)
66+
67+
: Operations which can be executed as part of a container build
68+
6569
bootc-help(8)
6670

6771
: Print this message or the help of the given subcommand(s)

hack/Containerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ FROM $base
1616
COPY --from=build /out/bootc.tar.zst /tmp
1717
COPY --from=build /build/target/dev-rootfs/ /
1818
RUN tar -C / --zstd -xvf /tmp/bootc.tar.zst && rm -vf /tmp/*
19+
RUN bootc container lint

lib/src/cli.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use std::os::unix::process::CommandExt;
1919
use std::process::Command;
2020

2121
use crate::deploy::RequiredHostSpec;
22+
use crate::lints;
2223
use crate::spec::Host;
2324
use crate::spec::ImageReference;
2425
use crate::utils::sigpolicy_from_opts;
@@ -143,6 +144,14 @@ pub(crate) struct ManOpts {
143144
pub(crate) directory: Utf8PathBuf,
144145
}
145146

147+
/// Subcommands which can be executed as part of a container build.
148+
#[derive(Debug, clap::Subcommand, PartialEq, Eq)]
149+
pub(crate) enum ContainerOpts {
150+
/// Perform relatively inexpensive static analysis checks as part of a container
151+
/// build.
152+
Lint,
153+
}
154+
146155
/// Hidden, internal only options
147156
#[derive(Debug, clap::Subcommand, PartialEq, Eq)]
148157
pub(crate) enum InternalsOpts {
@@ -292,6 +301,9 @@ pub(crate) enum Opt {
292301
#[clap(subcommand)]
293302
#[cfg(feature = "install")]
294303
Install(InstallOpts),
304+
/// Operations which can be executed as part of a container build.
305+
#[clap(subcommand)]
306+
Container(ContainerOpts),
295307
/// Execute the given command in the host mount namespace
296308
#[cfg(feature = "install")]
297309
#[clap(hide = true)]
@@ -662,6 +674,19 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
662674
Opt::Rollback(opts) => rollback(opts).await,
663675
Opt::Edit(opts) => edit(opts).await,
664676
Opt::UsrOverlay => usroverlay().await,
677+
Opt::Container(opts) => match opts {
678+
ContainerOpts::Lint => {
679+
if !ostree_ext::container_utils::is_ostree_container()? {
680+
anyhow::bail!(
681+
"Not in a ostree container, this command only verifies ostree containers."
682+
);
683+
}
684+
685+
let root = cap_std::fs::Dir::open_ambient_dir("/", cap_std::ambient_authority())?;
686+
lints::lint(&root)?;
687+
Ok(())
688+
}
689+
},
665690
#[cfg(feature = "install")]
666691
Opt::Install(opts) => match opts {
667692
InstallOpts::ToDisk(opts) => crate::install::install_to_disk(opts).await,

lib/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pub mod cli;
2121
pub(crate) mod deploy;
2222
pub(crate) mod generator;
2323
pub(crate) mod journal;
24+
mod lints;
2425
mod lsm;
2526
pub(crate) mod metadata;
2627
mod reboot;

lib/src/lints.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//! # Implementation of container build lints
2+
//!
3+
//! This module implements `bootc container lint`.
4+
5+
use anyhow::Result;
6+
use cap_std::fs::Dir;
7+
use cap_std_ext::cap_std;
8+
use cap_std_ext::dirext::CapStdExtDirExt as _;
9+
use fn_error_context::context;
10+
11+
/// check for the existence of the /var/run directory
12+
/// if it exists we need to check that it links to /run if not error
13+
/// if it does not exist error.
14+
#[context("Linting")]
15+
pub(crate) fn lint(root: &Dir) -> Result<()> {
16+
let lints = [check_var_run, check_kernel];
17+
for lint in lints {
18+
lint(&root)?;
19+
}
20+
println!("Checks passed: {}", lints.len());
21+
Ok(())
22+
}
23+
24+
fn check_var_run(root: &Dir) -> Result<()> {
25+
if let Some(meta) = root.symlink_metadata_optional("var/run")? {
26+
if !meta.is_symlink() {
27+
anyhow::bail!("Not a symlink: var/run");
28+
}
29+
}
30+
Ok(())
31+
}
32+
33+
fn check_kernel(root: &Dir) -> Result<()> {
34+
let result = ostree_ext::bootabletree::find_kernel_dir_fs(&root)?;
35+
tracing::debug!("Found kernel: {:?}", result);
36+
Ok(())
37+
}
38+
39+
#[cfg(test)]
40+
fn fixture() -> Result<cap_std_ext::cap_tempfile::TempDir> {
41+
let tempdir = cap_std_ext::cap_tempfile::tempdir(cap_std::ambient_authority())?;
42+
Ok(tempdir)
43+
}
44+
45+
#[test]
46+
fn test_var_run() -> Result<()> {
47+
let root = &fixture()?;
48+
// This one should pass
49+
check_var_run(root).unwrap();
50+
root.create_dir_all("var/run/foo")?;
51+
assert!(check_var_run(root).is_err());
52+
root.remove_dir_all("var/run")?;
53+
// Now we should pass again
54+
check_var_run(root).unwrap();
55+
Ok(())
56+
}
57+
58+
#[test]
59+
fn test_kernel_lint() -> Result<()> {
60+
let root = &fixture()?;
61+
// This one should pass
62+
check_kernel(root).unwrap();
63+
root.create_dir_all("usr/lib/modules/5.7.2")?;
64+
root.write("usr/lib/modules/5.7.2/vmlinuz", "old vmlinuz")?;
65+
root.create_dir_all("usr/lib/modules/6.3.1")?;
66+
root.write("usr/lib/modules/6.3.1/vmlinuz", "new vmlinuz")?;
67+
assert!(check_kernel(root).is_err());
68+
root.remove_dir_all("usr/lib/modules/5.7.2")?;
69+
// Now we should pass again
70+
check_kernel(root).unwrap();
71+
Ok(())
72+
}

0 commit comments

Comments
 (0)