Skip to content

Commit

Permalink
Add --bootc-disk-size option
Browse files Browse the repository at this point in the history
It allows settings the disk size of the VM image that is generated from
a bootc container image.

Also improve the default disk size by basing it on the container image
size.

Signed-off-by: Alberto Faria <[email protected]>
  • Loading branch information
albertofaria committed May 8, 2024
1 parent 1c63aee commit 5c399d6
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 8 deletions.
4 changes: 4 additions & 0 deletions docs/2-podman-docker.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ $ podman run \
Internally, crun-vm generates a VM image from the bootable container and then
boots it.

By default, the VM image is given a disk size roughly double the size of the
bootc container image. To change this, use the `--bootc-disk-size <size>[KMGT]`
option.

## First-boot customization

### cloud-init
Expand Down
16 changes: 15 additions & 1 deletion embed/bootc/prepare.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ engine=$1
container_id=$2
original_root=$3
priv_dir=$4
disk_size=$5

__step() {
printf "\033[36m%s\033[0m\n" "$*"
Expand Down Expand Up @@ -79,7 +80,20 @@ else

# run bootc-install under krun

truncate --size 10G "$bootc_dir/image.raw" # TODO: allow adjusting disk size
if [[ -z "$disk_size" ]]; then
container_image_size=$(
"$engine" image inspect --format '{{.VirtualSize}}' "$image_id"
)

# use double the container image size to allow for in-place updates
disk_size=$(( container_image_size * 2 ))

# round up to 1 MiB
alignment=$(( 2**20 ))
disk_size=$(( (disk_size + alignment - 1) / alignment * alignment ))
fi

truncate --size "$disk_size" "$bootc_dir/image.raw"

trap 'krun delete --force "$container_name" >/dev/null 2>&1 || true' EXIT
krun run --config "$bootc_dir/config.json" "$container_name" </dev/ptmx
Expand Down
3 changes: 3 additions & 0 deletions src/commands/create/custom_opts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ pub struct CustomOptions {
#[clap(long, help = "Use system emulation rather than KVM")]
pub emulated: bool,

#[clap(long)]
pub bootc_disk_size: Option<String>,

#[clap(long)]
pub cloud_init: Option<Utf8PathBuf>,

Expand Down
41 changes: 34 additions & 7 deletions src/commands/create/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ use std::process::{Command, Stdio};

use anyhow::{anyhow, bail, ensure, Context, Result};
use camino::{Utf8Path, Utf8PathBuf};
use lazy_static::lazy_static;
use nix::sys::stat::{major, makedev, minor, mknod, Mode, SFlag};
use regex::Regex;
use rust_embed::RustEmbed;

use crate::commands::create::custom_opts::CustomOptions;
Expand All @@ -38,12 +40,7 @@ pub fn create(args: &liboci_cli::Create, raw_args: &[impl AsRef<OsStr>]) -> Resu

let engine = Engine::detect(&args.container_id, bundle_path, &spec, &original_root_path)?;
let custom_options = CustomOptions::from_spec(&spec, engine)?;
let is_bootc_container = is_bootc_container(&original_root_path, engine)?;

ensure!(
!is_bootc_container || !custom_options.emulated,
"--emulated is incompatible with bootable containers"
);
let is_bootc_container = is_bootc_container(&original_root_path, &custom_options, engine)?;

// We include container_id in our paths to ensure no overlap with the user container's contents.
let priv_dir_path = original_root_path.join(format!("crun-vm-{}", args.container_id));
Expand Down Expand Up @@ -121,6 +118,7 @@ pub fn create(args: &liboci_cli::Create, raw_args: &[impl AsRef<OsStr>]) -> Resu
.arg(&args.container_id)
.arg(&original_root_path)
.arg(&priv_dir_path)
.arg(custom_options.bootc_disk_size.unwrap_or_default())
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
Expand Down Expand Up @@ -152,14 +150,43 @@ fn ensure_unprivileged(spec: &oci_spec::runtime::Spec) -> Result<()> {
Ok(())
}

fn is_bootc_container(original_root_path: &Utf8Path, engine: Engine) -> Result<bool> {
fn is_bootc_container(
original_root_path: &Utf8Path,
custom_options: &CustomOptions,
engine: Engine,
) -> Result<bool> {
let is_bootc_container = original_root_path.join("usr/lib/bootc/install").is_dir();

ensure!(
!is_bootc_container || engine == Engine::Podman || engine == Engine::Docker,
"bootc containers are only supported with Podman and Docker"
);

ensure!(
!is_bootc_container || !custom_options.emulated,
"--emulated is incompatible with bootable containers"
);

if let Some(size) = &custom_options.bootc_disk_size {
lazy_static! {
static ref SIZE_PATTERN: Regex = Regex::new(r"^[0-9]+[KMGT]?$").unwrap();
}

ensure!(
SIZE_PATTERN.is_match(size),
concat!(
"--bootc-disk-size value must be a number followed by an optional suffix K",
" (kilobyte, 1024), M (megabyte, 1024k), G (gigabyte, 1024M), or T (terabyte,",
" 1024G)",
)
);

ensure!(
is_bootc_container,
"--bootc-disk-size only applies to bootable containers"
);
}

Ok(is_bootc_container)
}

Expand Down
23 changes: 23 additions & 0 deletions tests/t/bootc-disk-size.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# SPDX-License-Identifier: GPL-2.0-or-later

image="${TEST_IMAGES[fedora-bootc]}"
user="${TEST_IMAGES_DEFAULT_USER[fedora-bootc]}"

__run() {
__engine run --detach --name bootc-disk-size "$image" --bootc-disk-size "$1"
}

! RUST_LIB_BACKTRACE=0 __run 0
__engine rm bootc-disk-size

for size in 1K 1M; do
__run "$size"
! __engine exec bootc-disk-size --as "$user"
__engine rm --force bootc-disk-size
done

for size in 1G 1T 1024T; do
__run "$size"
__engine exec bootc-disk-size --as "$user"
__engine rm --force bootc-disk-size
done

0 comments on commit 5c399d6

Please sign in to comment.