# apt-ostree OCI Container Integration ## Overview apt-ostree integrates with OCI (Open Container Initiative) containers to provide container-native deployment capabilities for Debian/Ubuntu systems. This document explains how apt-ostree implements OCI container integration. ## Core OCI Integration ### Container Image Generation apt-ostree can generate OCI container images from OSTree commits: ```rust // OCI integration in src/container.rs use std::fs; use std::path::Path; use serde_json::{json, Value}; pub struct OciManager; impl OciManager { // Generate OCI container image from OSTree commit pub fn generate_oci_image( commit_checksum: &str, image_name: &str, image_tag: &str, ) -> Result> { // 1. Extract OSTree commit to filesystem let ostree_manager = OstreeManager::new()?; let commit_tree = ostree_manager.read_commit(commit_checksum)?; // 2. Create OCI image layers let layers = Self::create_oci_layers(&commit_tree)?; // 3. Generate OCI manifest let manifest = Self::generate_oci_manifest(&layers, image_name, image_tag)?; // 4. Create OCI image archive Self::create_oci_archive(&manifest, &layers, image_name, image_tag) } // Create OCI layers from filesystem fn create_oci_layers( filesystem_tree: &OstreeRepoFile, ) -> Result, Box> { let mut layers = Vec::new(); // Create layer from filesystem let layer_path = Self::create_filesystem_layer(filesystem_tree)?; // Calculate layer digest let layer_digest = Self::calculate_layer_digest(&layer_path)?; // Create layer descriptor let layer_desc = json!({ "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip", "digest": layer_digest, "size": fs::metadata(&layer_path)?.len() }); layers.push(layer_desc); Ok(layers) } // Generate OCI manifest fn generate_oci_manifest( layers: &[Value], image_name: &str, image_tag: &str, ) -> Result> { let manifest = json!({ "schemaVersion": 2, "config": { "mediaType": "application/vnd.oci.image.config.v1+json", "digest": "sha256:config", "size": 0 }, "layers": layers }); Ok(manifest) } // Create filesystem layer fn create_filesystem_layer( filesystem_tree: &OstreeRepoFile, ) -> Result> { // Create temporary directory for layer let layer_path = tempfile::tempdir()?.path().to_path_buf(); // Extract filesystem tree to layer let ostree_manager = OstreeManager::new()?; ostree_manager.extract_tree_to_path(filesystem_tree, &layer_path)?; // Create tar.gz archive let archive_path = layer_path.with_extension("tar.gz"); Self::create_tar_gz(&layer_path, &archive_path)?; Ok(archive_path) } // Calculate layer digest fn calculate_layer_digest(layer_path: &Path) -> Result> { use std::io::Read; use sha2::{Sha256, Digest}; let mut file = fs::File::open(layer_path)?; let mut buffer = Vec::new(); file.read_to_end(&mut buffer)?; let mut hasher = Sha256::new(); hasher.update(&buffer); let result = hasher.finalize(); Ok(format!("sha256:{:x}", result)) } // Create tar.gz archive fn create_tar_gz( source_path: &Path, archive_path: &Path, ) -> Result<(), Box> { use std::process::Command; let output = Command::new("tar") .args(&["-czf", archive_path.to_str().unwrap(), "-C", source_path.to_str().unwrap(), "."]) .output()?; if !output.status.success() { return Err(format!( "Failed to create tar.gz archive: {}", String::from_utf8_lossy(&output.stderr) ).into()); } Ok(()) } } ``` ### Container Registry Integration apt-ostree integrates with OCI container registries: ```rust // Registry integration in src/container.rs use std::process::Command; impl OciManager { // Push OCI image to registry pub fn push_oci_image( image_path: &str, registry_url: &str, image_name: &str, image_tag: &str, username: Option<&str>, password: Option<&str>, ) -> Result<(), Box> { let mut cmd = Command::new("skopeo"); cmd.arg("copy"); // Add credentials if provided if let (Some(user), Some(pass)) = (username, password) { cmd.args(&["--dest-creds", &format!("{}:{}", user, pass)]); } cmd.args(&[ &format!("oci:{}", image_path), &format!("docker://{}/{}/{}:{}", registry_url, image_name, image_tag) ]); let output = cmd.output()?; if !output.status.success() { return Err(format!( "Failed to push OCI image: {}", String::from_utf8_lossy(&output.stderr) ).into()); } Ok(()) } // Pull OCI image from registry pub fn pull_oci_image( registry_url: &str, image_name: &str, image_tag: &str, local_path: &str, username: Option<&str>, password: Option<&str>, ) -> Result<(), Box> { let mut cmd = Command::new("skopeo"); cmd.arg("copy"); // Add credentials if provided if let (Some(user), Some(pass)) = (username, password) { cmd.args(&["--src-creds", &format!("{}:{}", user, pass)]); } cmd.args(&[ &format!("docker://{}/{}/{}:{}", registry_url, image_name, image_tag), &format!("oci:{}", local_path) ]); let output = cmd.output()?; if !output.status.success() { return Err(format!( "Failed to pull OCI image: {}", String::from_utf8_lossy(&output.stderr) ).into()); } Ok(()) } // List available images in registry pub fn list_registry_images( registry_url: &str, username: Option<&str>, password: Option<&str>, ) -> Result, Box> { let mut cmd = Command::new("skopeo"); cmd.args(&["list-tags"]); // Add credentials if provided if let (Some(user), Some(pass)) = (username, password) { cmd.args(&["--creds", &format!("{}:{}", user, pass)]); } cmd.arg(&format!("docker://{}", registry_url)); let output = cmd.output()?; if !output.status.success() { return Err(format!( "Failed to list registry images: {}", String::from_utf8_lossy(&output.stderr) ).into()); } // Parse output to extract image names let output_str = String::from_utf8(output.stdout)?; let images: Vec = output_str .lines() .filter(|line| !line.trim().is_empty()) .map(|line| line.trim().to_string()) .collect(); Ok(images) } } ``` ## Bootc Compatibility ### Bootc Image Generation apt-ostree can generate bootc-compatible images: ```rust // Bootc integration in src/container.rs impl OciManager { // Generate bootc-compatible image pub fn generate_bootc_image( commit_checksum: &str, image_name: &str, image_tag: &str, ) -> Result> { // 1. Generate OCI image let oci_image_path = Self::generate_oci_image(commit_checksum, image_name, image_tag)?; // 2. Add bootc-specific metadata Self::add_bootc_metadata(&oci_image_path, image_name, image_tag)?; Ok(oci_image_path) } // Add bootc-specific metadata fn add_bootc_metadata( image_path: &str, image_name: &str, image_tag: &str, ) -> Result<(), Box> { // Create bootc metadata let bootc_metadata = json!({ "bootc": {}, "deployment": { "type": "ostree", "ref": "ubuntu/24.04/x86_64/desktop" } }); // Write metadata to image Self::write_image_metadata(image_path, &bootc_metadata) } // Write metadata to OCI image fn write_image_metadata( image_path: &str, metadata: &Value, ) -> Result<(), Box> { // Create metadata directory let metadata_path = format!("{}/blobs/sha256/metadata", image_path); fs::create_dir_all(&metadata_path)?; // Write metadata file let metadata_file = format!("{}/bootc.json", metadata_path); fs::write(metadata_file, serde_json::to_string_pretty(metadata)?)?; Ok(()) } } ``` ## mmdebstrap Integration ### Base Image Creation apt-ostree uses mmdebstrap for efficient base image creation: ```rust // mmdebstrap integration in src/container.rs impl OciManager { // Create base system image with mmdebstrap pub fn create_base_image( release: &str, arch: &str, packages: &[String], ) -> Result> { use std::process::Command; // Create temporary directory for base system let base_path = tempfile::tempdir()?.path().to_path_buf(); let mut cmd = Command::new("mmdebstrap"); cmd.args(&["--arch", arch, "--variant", "minbase", release, base_path.to_str().unwrap()]); // Add additional packages if specified if !packages.is_empty() { cmd.arg("--include"); cmd.arg(&packages.join(",")); } let output = cmd.output()?; if !output.status.success() { return Err(format!( "Failed to create base system: {}", String::from_utf8_lossy(&output.stderr) ).into()); } // Create OCI image from base system let image_path = Self::create_oci_from_filesystem(&base_path, "base", "latest")?; Ok(image_path) } // Create OCI image from filesystem fn create_oci_from_filesystem( filesystem_path: &Path, image_name: &str, image_tag: &str, ) -> Result> { // Create OCI image structure let image_path = tempfile::tempdir()?.path().to_path_buf(); let blobs_path = image_path.join("blobs").join("sha256"); fs::create_dir_all(&blobs_path)?; // Create layer from filesystem let layer_path = Self::create_filesystem_layer_from_path(filesystem_path)?; let layer_digest = Self::calculate_layer_digest(&layer_path)?; // Copy layer to blobs directory let blob_path = blobs_path.join(&layer_digest[7..]); // Remove "sha256:" prefix fs::copy(&layer_path, &blob_path)?; // Create manifest let manifest = Self::generate_oci_manifest(&[json!({ "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip", "digest": layer_digest, "size": fs::metadata(&blob_path)?.len() })], image_name, image_tag)?; // Write manifest let manifest_path = image_path.join("manifest.json"); fs::write(manifest_path, serde_json::to_string_pretty(&manifest)?)?; Ok(image_path.to_string_lossy().to_string()) } // Create filesystem layer from path fn create_filesystem_layer_from_path( filesystem_path: &Path, ) -> Result> { // Create temporary directory for layer let layer_path = tempfile::tempdir()?.path().to_path_buf(); // Create tar.gz archive of filesystem let archive_path = layer_path.with_extension("tar.gz"); Self::create_tar_gz(filesystem_path, &archive_path)?; Ok(archive_path) } } ``` ## Future Enhancements ### Planned Features 1. **Enhanced OCI Support**: Full OCI specification compliance 2. **Registry Authentication**: Advanced registry authentication methods 3. **Image Optimization**: Layer optimization and compression 4. **Multi-Architecture Support**: Support for multiple architectures ### Integration Roadmap - **Phase 1**: Basic OCI integration (🔄 In Progress) - **Phase 2**: Registry integration (📋 Planned) - **Phase 3**: Bootc compatibility (📋 Planned) - **Phase 4**: Advanced features (📋 Planned) ## Manual OCI Build Process ### Prerequisites - `oci-image-tool` (from Ubuntu packages) - `skopeo` for image validation - OSTree repository with test commit ### Build Steps 1. **Checkout OSTree Commit**: Extract filesystem content from OSTree commit 2. **Create OCI Image Structure**: Set up OCI directory structure with blobs 3. **Create Filesystem Layer**: Tar and compress filesystem content 4. **Update Configuration**: Add layer digest to OCI config 5. **Create OCI Manifest**: Generate manifest with layer and config references 6. **Create OCI Index**: Generate index for multi-platform support 7. **Validate Image**: Use skopeo to validate OCI compliance ### CLI Commands ```bash # Build OCI image from OSTree commit apt-ostree oci build --source test/oci/demo --output my-image.oci --format oci --repo /path/to/ostree/repo # Build Docker image from OSTree commit apt-ostree oci build --source test/oci/demo --output my-image.tar --format docker --repo /path/to/ostree/repo # Push to registry apt-ostree oci push /tmp/demo-image.oci localhost:5000 demo:latest ``` ### Image Features - **OCI Specification Compliance**: Schema Version 2, SHA256 digests, Gzip compression - **Supported Formats**: OCI directory structure, Docker tar archive - **Advanced Configuration**: Custom labels, environment variables, exposed ports, volume mounts