Skip to content

Commit c1baa21

Browse files
committed
WIP: Add osbuild-disk
This mirrors `to-disk`, except instead of having the bootc container install itself, we're adding osbuild/bib in the middle. I think this is pretty close to working, but right now it dies deep in the osbuild stack failing to fetch the image. But this all works with `to-disk` where bootc-in-container fetches it. Assisted-by: Claude Code Signed-off-by: Colin Walters <[email protected]>
1 parent 8a3dd72 commit c1baa21

File tree

5 files changed

+755
-0
lines changed

5 files changed

+755
-0
lines changed

crates/integration-tests/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ mod tests {
2020
pub mod libvirt_upload_disk;
2121
pub mod libvirt_verb;
2222
pub mod mount_feature;
23+
pub mod osbuild_disk;
2324
pub mod run_ephemeral;
2425
pub mod run_ephemeral_ssh;
2526
pub mod to_disk;
Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
//! Integration tests for osbuild-disk command
2+
//!
3+
//! ⚠️ **CRITICAL INTEGRATION TEST POLICY** ⚠️
4+
//!
5+
//! INTEGRATION TESTS MUST NEVER "warn and continue" ON FAILURES!
6+
//!
7+
//! If something is not working:
8+
//! - Use `todo!("reason why this doesn't work yet")`
9+
//! - Use `panic!("clear error message")`
10+
//! - Use `assert!()` and `unwrap()` to fail hard
11+
//!
12+
//! NEVER use patterns like:
13+
//! - "Note: test failed - likely due to..."
14+
//! - "This is acceptable in CI/testing environments"
15+
//! - Warning and continuing on failures
16+
17+
use camino::Utf8PathBuf;
18+
use color_eyre::Result;
19+
use linkme::distributed_slice;
20+
use std::process::Command;
21+
use tempfile::TempDir;
22+
23+
use crate::{run_bcvk, IntegrationTest, INTEGRATION_TESTS, INTEGRATION_TEST_LABEL};
24+
25+
#[distributed_slice(INTEGRATION_TESTS)]
26+
static TEST_OSBUILD_DISK_QCOW2: IntegrationTest =
27+
IntegrationTest::new("osbuild_disk_qcow2", test_osbuild_disk_qcow2);
28+
29+
/// Test building a qcow2 disk image with bootc-image-builder
30+
fn test_osbuild_disk_qcow2() -> Result<()> {
31+
let temp_dir = TempDir::new().expect("Failed to create temp directory");
32+
let output_dir =
33+
Utf8PathBuf::try_from(temp_dir.path().to_path_buf()).expect("temp path is not UTF-8");
34+
35+
let output = run_bcvk(&[
36+
"osbuild-disk",
37+
"--label",
38+
INTEGRATION_TEST_LABEL,
39+
"quay.io/centos-bootc/centos-bootc:stream10",
40+
output_dir.as_str(),
41+
])
42+
.expect("Failed to run bcvk osbuild-disk");
43+
44+
assert!(
45+
output.success(),
46+
"osbuild-disk failed with exit code: {:?}. stdout: {}, stderr: {}",
47+
output.exit_code(),
48+
output.stdout,
49+
output.stderr
50+
);
51+
52+
// Verify output directory contains qcow2 subdirectory
53+
let qcow2_dir = output_dir.join("qcow2");
54+
assert!(
55+
qcow2_dir.exists(),
56+
"qcow2 output directory not found at {}",
57+
qcow2_dir
58+
);
59+
60+
// Verify disk.qcow2 file exists
61+
let disk_path = qcow2_dir.join("disk.qcow2");
62+
assert!(
63+
disk_path.exists(),
64+
"disk.qcow2 file not found at {}",
65+
disk_path
66+
);
67+
68+
let metadata = std::fs::metadata(&disk_path).expect("Failed to get disk metadata");
69+
assert!(metadata.len() > 0, "Disk image is empty");
70+
71+
// Verify the file is actually qcow2 format using qemu-img info
72+
let qemu_img_output = Command::new("qemu-img")
73+
.args(["info", disk_path.as_str()])
74+
.output()
75+
.expect("Failed to run qemu-img info");
76+
77+
let qemu_img_stdout = String::from_utf8_lossy(&qemu_img_output.stdout);
78+
79+
assert!(
80+
qemu_img_output.status.success(),
81+
"qemu-img info failed with exit code: {:?}",
82+
qemu_img_output.status.code()
83+
);
84+
85+
assert!(
86+
qemu_img_stdout.contains("file format: qcow2"),
87+
"qemu-img info doesn't show qcow2 format. Output was:\n{}",
88+
qemu_img_stdout
89+
);
90+
91+
assert!(
92+
output.stdout.contains("Build completed successfully!")
93+
|| output.stderr.contains("Build completed successfully!"),
94+
"No 'Build completed successfully!' message found in output. stdout: {}, stderr: {}",
95+
output.stdout,
96+
output.stderr
97+
);
98+
99+
Ok(())
100+
}
101+
102+
#[distributed_slice(INTEGRATION_TESTS)]
103+
static TEST_OSBUILD_DISK_WITH_CONFIG: IntegrationTest =
104+
IntegrationTest::new("osbuild_disk_with_config", test_osbuild_disk_with_config);
105+
106+
/// Test building with a custom config file
107+
fn test_osbuild_disk_with_config() -> Result<()> {
108+
let temp_dir = TempDir::new().expect("Failed to create temp directory");
109+
let output_dir =
110+
Utf8PathBuf::try_from(temp_dir.path().join("output")).expect("temp path is not UTF-8");
111+
std::fs::create_dir_all(&output_dir).expect("Failed to create output directory");
112+
113+
// Create a simple config file with user customization
114+
let config_path = temp_dir.path().join("config.toml");
115+
let config_content = r#"
116+
[[customizations.user]]
117+
name = "testuser"
118+
password = "testpass"
119+
groups = ["wheel"]
120+
"#;
121+
std::fs::write(&config_path, config_content).expect("Failed to write config file");
122+
123+
let config_path_str = config_path.to_str().expect("Config path is not UTF-8");
124+
125+
let output = run_bcvk(&[
126+
"osbuild-disk",
127+
"--label",
128+
INTEGRATION_TEST_LABEL,
129+
"--config",
130+
config_path_str,
131+
"quay.io/centos-bootc/centos-bootc:stream10",
132+
output_dir.as_str(),
133+
])
134+
.expect("Failed to run bcvk osbuild-disk with config");
135+
136+
assert!(
137+
output.success(),
138+
"osbuild-disk with config failed with exit code: {:?}. stdout: {}, stderr: {}",
139+
output.exit_code(),
140+
output.stdout,
141+
output.stderr
142+
);
143+
144+
// Verify output directory contains qcow2 subdirectory
145+
let qcow2_dir = output_dir.join("qcow2");
146+
assert!(
147+
qcow2_dir.exists(),
148+
"qcow2 output directory not found at {}",
149+
qcow2_dir
150+
);
151+
152+
// Verify disk.qcow2 file exists
153+
let disk_path = qcow2_dir.join("disk.qcow2");
154+
assert!(
155+
disk_path.exists(),
156+
"disk.qcow2 file not found at {}",
157+
disk_path
158+
);
159+
160+
let metadata = std::fs::metadata(&disk_path).expect("Failed to get disk metadata");
161+
assert!(metadata.len() > 0, "Disk image is empty");
162+
163+
assert!(
164+
output.stdout.contains("Build completed successfully!")
165+
|| output.stderr.contains("Build completed successfully!"),
166+
"No 'Build completed successfully!' message found in output. stdout: {}, stderr: {}",
167+
output.stdout,
168+
output.stderr
169+
);
170+
171+
Ok(())
172+
}
173+
174+
#[distributed_slice(INTEGRATION_TESTS)]
175+
static TEST_OSBUILD_DISK_RAW: IntegrationTest =
176+
IntegrationTest::new("osbuild_disk_raw", test_osbuild_disk_raw);
177+
178+
/// Test building a raw disk image
179+
fn test_osbuild_disk_raw() -> Result<()> {
180+
let temp_dir = TempDir::new().expect("Failed to create temp directory");
181+
let output_dir =
182+
Utf8PathBuf::try_from(temp_dir.path().to_path_buf()).expect("temp path is not UTF-8");
183+
184+
let output = run_bcvk(&[
185+
"osbuild-disk",
186+
"--label",
187+
INTEGRATION_TEST_LABEL,
188+
"--type",
189+
"raw",
190+
"quay.io/centos-bootc/centos-bootc:stream10",
191+
output_dir.as_str(),
192+
])
193+
.expect("Failed to run bcvk osbuild-disk with raw format");
194+
195+
assert!(
196+
output.success(),
197+
"osbuild-disk with raw format failed with exit code: {:?}. stdout: {}, stderr: {}",
198+
output.exit_code(),
199+
output.stdout,
200+
output.stderr
201+
);
202+
203+
// Verify output directory contains image subdirectory (raw images go here)
204+
let image_dir = output_dir.join("image");
205+
assert!(
206+
image_dir.exists(),
207+
"image output directory not found at {}",
208+
image_dir
209+
);
210+
211+
// Verify disk.raw file exists
212+
let disk_path = image_dir.join("disk.raw");
213+
assert!(
214+
disk_path.exists(),
215+
"disk.raw file not found at {}",
216+
disk_path
217+
);
218+
219+
let metadata = std::fs::metadata(&disk_path).expect("Failed to get disk metadata");
220+
assert!(metadata.len() > 0, "Disk image is empty");
221+
222+
// Verify the file is raw format using qemu-img info
223+
let qemu_img_output = Command::new("qemu-img")
224+
.args(["info", disk_path.as_str()])
225+
.output()
226+
.expect("Failed to run qemu-img info");
227+
228+
let qemu_img_stdout = String::from_utf8_lossy(&qemu_img_output.stdout);
229+
230+
assert!(
231+
qemu_img_output.status.success(),
232+
"qemu-img info failed with exit code: {:?}",
233+
qemu_img_output.status.code()
234+
);
235+
236+
assert!(
237+
qemu_img_stdout.contains("file format: raw"),
238+
"qemu-img info doesn't show raw format. Output was:\n{}",
239+
qemu_img_stdout
240+
);
241+
242+
assert!(
243+
output.stdout.contains("Build completed successfully!")
244+
|| output.stderr.contains("Build completed successfully!"),
245+
"No 'Build completed successfully!' message found in output. stdout: {}, stderr: {}",
246+
output.stdout,
247+
output.stderr
248+
);
249+
250+
Ok(())
251+
}

crates/kit/src/main.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ mod images;
2121
mod install_options;
2222
mod libvirt;
2323
mod libvirt_upload_disk;
24+
mod osbuild_disk;
2425
#[allow(dead_code)]
2526
mod podman;
2627
#[allow(dead_code)]
@@ -116,6 +117,10 @@ enum Commands {
116117
#[clap(name = "to-disk")]
117118
ToDisk(to_disk::ToDiskOpts),
118119

120+
/// Build disk images using bootc-image-builder
121+
#[clap(name = "osbuild-disk")]
122+
OsbuildDisk(osbuild_disk::OsbuildDiskOpts),
123+
119124
/// Manage libvirt integration for bootc containers
120125
Libvirt {
121126
/// Hypervisor connection URI (e.g., qemu:///system, qemu+ssh://host/system)
@@ -194,6 +199,9 @@ fn main() -> Result<(), Report> {
194199
Commands::ToDisk(opts) => {
195200
to_disk::run(opts)?;
196201
}
202+
Commands::OsbuildDisk(opts) => {
203+
osbuild_disk::run(opts)?;
204+
}
197205
Commands::Libvirt { connect, command } => {
198206
let options = libvirt::LibvirtOptions { connect };
199207
match command {

0 commit comments

Comments
 (0)