# πŸ” **rpm-ostree OCI Integration Analysis** ## πŸ“‹ **Overview** This document provides a comprehensive analysis of rpm-ostree's OCI (Open Container Initiative) integration capabilities, examining how OCI operations are implemented and how they relate to the CLI vs daemon separation. This analysis is based on examination of the actual rpm-ostree source code rather than speculative documentation. ## πŸ—οΈ **OCI Architecture Overview** ### **Component Structure** ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ CLI Client β”‚ β”‚ OCI Layer β”‚ β”‚ OSTree Core β”‚ β”‚ (rpm-ostree) │◄──►│ (Container) │◄──►│ (Repository) β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β€’ Command Line β”‚ β”‚ β€’ Image Build β”‚ β”‚ β€’ Commit Read β”‚ β”‚ β€’ User Interfaceβ”‚ β”‚ β€’ Registry Ops β”‚ β”‚ β€’ Filesystem β”‚ β”‚ β€’ Progress β”‚ β”‚ β€’ Format Conv β”‚ β”‚ β€’ Metadata β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ### **Key Design Principles** 1. **CLI-Centric OCI Operations**: OCI operations are primarily CLI-based, not daemon-based 2. **Direct OSTree Integration**: Container operations directly access OSTree repository 3. **External Tool Integration**: Uses external tools (podman, buildah) for container operations 4. **Rust Implementation**: OCI functionality is implemented in Rust, not C++ daemon ## πŸ” **Detailed OCI Implementation Analysis** ### **1. OCI Command Structure** #### **CLI Entry Points** ```rust // From rust/src/main.rs - OCI command dispatch "container-encapsulate" => { rpmostree_rust::client::warn_future_incompatibility( "This entrypoint is deprecated; use `rpm-ostree compose container-encapsulate` instead", ); rpmostree_rust::container::container_encapsulate(args_orig).map(|_| 0) .map_err(anyhow::Error::msg) }, ``` **Key Characteristics**: - **CLI-Only**: OCI operations are CLI commands, not daemon operations - **Deprecated Path**: Direct `container-encapsulate` is deprecated in favor of compose workflow - **Rust Implementation**: Full OCI functionality implemented in Rust, not C++ daemon - **No DBus Integration**: OCI operations don't go through the daemon #### **Compose Integration** ```rust // From rust/src/compose.rs - Compose-based OCI operations // The compose module integrates OCI operations into the compose workflow // rather than exposing them as standalone daemon operations ``` **Key Characteristics**: - **Workflow Integration**: OCI operations integrated into compose workflow - **Higher-Level Abstractions**: Compose provides higher-level container operations - **Daemon Independence**: OCI operations don't require daemon communication ### **2. Core OCI Implementation** #### **Container Encapsulation** ```rust // From rust/src/container.rs - Main OCI implementation #[derive(Debug, Parser)] struct ContainerEncapsulateOpts { #[clap(long)] #[clap(value_parser)] repo: Utf8PathBuf, /// OSTree branch name or checksum ostree_ref: String, /// Image reference, e.g. registry:quay.io/exampleos/exampleos:latest #[clap(value_parser = ostree_ext::cli::parse_base_imgref)] imgref: ImageReference, /// Additional labels for the container #[clap(name = "label", long, short)] labels: Vec, /// Path to container image configuration in JSON format #[clap(long)] image_config: Option, /// Override the architecture #[clap(long)] arch: Option, /// Maximum number of container image layers #[clap(long)] max_layers: Option, /// The encapsulated container format version; must be 1 or 2 #[clap(long, default_value = "1")] format_version: u32, } ``` **Key Characteristics**: - **Direct Repository Access**: Direct access to OSTree repository, no daemon mediation - **Rich Configuration**: Extensive configuration options for container generation - **Format Flexibility**: Support for multiple container format versions - **Architecture Override**: Ability to override target architecture #### **OSTree Integration** ```rust // From rust/src/container.rs - OSTree commit processing pub fn container_encapsulate(args: Vec) -> CxxResult<()> { let args = args.iter().skip(1).map(|s| s.as_str()); let opt = ContainerEncapsulateOpts::parse_from(args); let repo = &ostree_ext::cli::parse_repo(&opt.repo)?; let (root, rev) = repo.read_commit(opt.ostree_ref.as_str(), gio::Cancellable::NONE)?; // Read package information directly from commit let pkglist: glib::Variant = { let r = crate::ffi::package_variant_list_for_commit( repo.reborrow_cxx(), rev.as_str(), cancellable.reborrow_cxx(), ) .context("Reading package variant list")?; unsafe { glib::translate::from_glib_full(r as *mut _) } }; // Process packages and generate container metadata // ... container generation logic } ``` **Key Characteristics**: - **Direct Commit Access**: Direct reading of OSTree commits without daemon - **Package Metadata**: Direct access to package information from commits - **C++ Bridge**: Uses C++ FFI for package metadata, but Rust for container logic - **No Transaction Management**: OCI operations don't use daemon transaction system ### **3. Container Storage Integration** #### **External Tool Integration** ```rust // From rust/src/containers_storage.rs - Container storage management #[derive(Debug, Copy, Clone)] enum Backend { Podman, Buildah, } impl AsRef for Backend { fn as_ref(&self) -> &'static str { match self { Backend::Podman => "podman", Backend::Buildah => "buildah", } } } pub(crate) struct Mount { backend: Backend, path: Utf8PathBuf, temp_cid: Option, mounted: bool, } ``` **Key Characteristics**: - **External Dependencies**: Relies on external container tools (podman, buildah) - **Backend Detection**: Automatic detection of available container backends - **Mount Management**: Temporary container mounting for filesystem access - **Resource Cleanup**: Automatic cleanup of temporary containers and mounts #### **Container Mounting** ```rust // From rust/src/containers_storage.rs - Container mounting operations impl Mount { pub(crate) fn new_for_image(image: &str) -> Result { let backend = Self::detect_backend()?; let mut o = match backend { Backend::Podman => Command::new(backend.as_ref()) .args(["create", image]) .run_get_output()?, Backend::Buildah => Command::new(backend.as_ref()) .args(["from", image]) .run_get_output()?, }; let mut s = String::new(); o.read_to_string(&mut s)?; let cid = s.trim(); let path = Self::_impl_mount(backend, cid)?; Ok(Self { backend, path, temp_cid: Some(cid.to_owned()), mounted: true, }) } } ``` **Key Characteristics**: - **Command Execution**: Uses external commands for container operations - **Temporary Containers**: Creates temporary containers for filesystem access - **Cross-Platform**: Supports both podman and buildah backends - **Mount Management**: Manages temporary mounts for container filesystem access ### **4. OCI Image Generation** #### **Filesystem Mapping** ```rust // From rust/src/container.rs - Filesystem to container mapping struct MappingBuilder { unpackaged_id: Rc, packagemeta: ObjectMetaSet, componentmeta: ObjectMetaSet, checksum_paths: BTreeMap>, path_packages: HashMap>>, path_components: HashMap>>, skip: HashSet, component_ids: HashSet, rpmsize: u64, } ``` **Key Characteristics**: - **Package Tracking**: Maps filesystem paths to package information - **Component Support**: Supports component-based packaging systems - **Checksum Mapping**: Maps file checksums to paths for deduplication - **Metadata Preservation**: Preserves package and component metadata in container #### **Layer Generation** ```rust // From rust/src/container.rs - Container layer generation fn build_fs_mapping_recurse( path: &mut Utf8PathBuf, dir: &cap_std::fs::Dir, state: &mut MappingBuilder, ) -> Result<()> { for child in dir.entries()? { let childi = child?; let name: Utf8PathBuf = childi.name().try_into()?; let child = dir.child(&name); path.push(&name); match childi.file_type() { gio::FileType::Regular | gio::FileType::SymbolicLink => { let child = child.downcast::().unwrap(); // Track component information from extended attributes if let Some(component_name) = get_user_component_xattr(&child)? { let component_id = Rc::from(component_name.clone()); state.component_ids.insert(component_name); state .path_components .entry(path.clone()) .or_default() .insert(Rc::clone(&component_id)); } // Track package information let checksum = child.checksum().to_string(); state .checksum_paths .entry(checksum) .or_default() .insert(path.clone()); } gio::FileType::Directory => { build_fs_mapping_recurse(path, &child, state)?; } o => anyhow::bail!("Unhandled file type: {o:?}"), } path.pop(); } Ok(()) } ``` **Key Characteristics**: - **Recursive Processing**: Recursively processes filesystem tree - **Extended Attributes**: Reads component information from extended attributes - **Checksum Tracking**: Tracks file checksums for deduplication - **Type Handling**: Handles regular files, symlinks, and directories ## πŸ”„ **OCI Operation Flow** ### **1. Container Encapsulation Flow** ``` User Command β†’ CLI Parsing β†’ OSTree Access β†’ Filesystem Mapping β†’ Container Generation β†’ Output ``` **Detailed Flow**: 1. **User Command**: `rpm-ostree container-encapsulate ` 2. **CLI Parsing**: Parse command options and validate parameters 3. **OSTree Access**: Direct access to OSTree repository and commit 4. **Filesystem Mapping**: Map filesystem to package/component metadata 5. **Container Generation**: Generate OCI container image 6. **Output**: Write container image to specified location ### **2. Container Mounting Flow** ``` Image Reference β†’ Backend Detection β†’ Container Creation β†’ Mount β†’ Filesystem Access β†’ Unmount ``` **Detailed Flow**: 1. **Image Reference**: Parse container image reference 2. **Backend Detection**: Detect available container backend (podman/buildah) 3. **Container Creation**: Create temporary container from image 4. **Mount**: Mount container filesystem to temporary location 5. **Filesystem Access**: Access container filesystem for operations 6. **Unmount**: Clean up temporary container and mount ### **3. Registry Operations Flow** ``` Registry Command β†’ External Tool β†’ Registry Communication β†’ Result Processing ``` **Detailed Flow**: 1. **Registry Command**: Parse registry operation command 2. **External Tool**: Execute external tool (skopeo, podman, buildah) 3. **Registry Communication**: Communicate with container registry 4. **Result Processing**: Process operation results and handle errors ## πŸ“Š **CLI vs Daemon Responsibility Matrix for OCI** | OCI Operation | CLI (rpm-ostree) | Daemon (rpm-ostreed) | Notes | |---------------|-------------------|----------------------|-------| | **Container Encapsulation** | βœ… Primary | ❌ None | Direct OSTree access | | **Image Building** | βœ… Primary | ❌ None | CLI-only operation | | **Registry Operations** | βœ… Primary | ❌ None | External tool integration | | **Container Mounting** | βœ… Primary | ❌ None | Temporary mount management | | **Filesystem Mapping** | βœ… Primary | ❌ None | Direct repository access | | **Layer Generation** | βœ… Primary | ❌ None | CLI-side processing | | **Metadata Preservation** | βœ… Primary | ❌ None | Package info extraction | | **Progress Reporting** | βœ… Primary | ❌ None | Local progress display | ## πŸš€ **apt-ostree OCI Implementation Strategy** ### **1. Architecture Decision** #### **CLI-Centric OCI Operations** ```rust // OCI operations should be CLI-based, not daemon-based // This follows rpm-ostree's proven architecture impl AptOstreeCli { pub async fn container_encapsulate( &self, ostree_ref: &str, image_ref: &str, options: ContainerOptions, ) -> Result<()> { // Direct OSTree access - no daemon required let repo = self.get_ostree_repo().await?; let (root, rev) = repo.read_commit(ostree_ref).await?; // Generate container image directly let container = self.generate_container_image(root, options).await?; // Output container image self.write_container_image(container, image_ref).await?; Ok(()) } } ``` **Rationale**: - **Performance**: Direct access avoids DBus overhead - **Simplicity**: No complex transaction management needed - **Reliability**: Fewer failure points without daemon dependency - **Consistency**: Follows rpm-ostree's proven architecture #### **External Tool Integration** ```rust // Use external tools for container operations, like rpm-ostree pub struct ContainerBackend { backend_type: BackendType, command_path: PathBuf, } impl ContainerBackend { pub fn detect_available() -> Result> { let mut backends = Vec::new(); // Check for podman if let Ok(path) = which::which("podman") { backends.push(Self { backend_type: BackendType::Podman, command_path: path, }); } // Check for buildah if let Ok(path) = which::which("buildah") { backends.push(Self { backend_type: BackendType::Buildah, command_path: path, }); } Ok(backends) } } ``` **Rationale**: - **Maturity**: External tools are mature and well-tested - **Standards**: Follows container ecosystem standards - **Maintenance**: Reduces maintenance burden on apt-ostree - **Compatibility**: Ensures compatibility with existing container workflows ### **2. Implementation Structure** #### **Core OCI Module** ```rust // src/oci/mod.rs - Main OCI functionality pub mod container; pub mod registry; pub mod storage; pub mod utils; pub use container::ContainerEncapsulator; pub use registry::RegistryClient; pub use storage::ContainerStorage; pub use utils::OciUtils; ``` #### **Container Encapsulation** ```rust // src/oci/container.rs - Container image generation pub struct ContainerEncapsulator { ostree_manager: OstreeManager, apt_manager: AptManager, options: ContainerOptions, } impl ContainerEncapsulator { pub async fn encapsulate_commit( &self, ostree_ref: &str, image_ref: &str, ) -> Result<()> { // 1. Read OSTree commit let (root, rev) = self.ostree_manager.read_commit(ostree_ref).await?; // 2. Extract package information let packages = self.apt_manager.get_commit_packages(&rev).await?; // 3. Generate filesystem mapping let mapping = self.generate_filesystem_mapping(&root, &packages).await?; // 4. Create container layers let layers = self.create_container_layers(&mapping).await?; // 5. Generate OCI manifest let manifest = self.generate_oci_manifest(&layers, image_ref).await?; // 6. Write container image self.write_container_image(&manifest, &layers, image_ref).await?; Ok(()) } } ``` #### **Registry Integration** ```rust // src/oci/registry.rs - Container registry operations pub struct RegistryClient { registry_url: String, credentials: Option, } impl RegistryClient { pub async fn push_image( &self, image_path: &Path, image_ref: &str, ) -> Result<()> { // Use external tool (skopeo) for registry operations let output = Command::new("skopeo") .args([ "copy", "--dest-creds", &format!("{}:{}", self.credentials.as_ref().unwrap().username, self.credentials.as_ref().unwrap().password), &format!("oci:{}", image_path.display()), &format!("docker://{}", image_ref), ]) .output() .await?; if !output.status.success() { return Err(anyhow::anyhow!( "skopeo copy failed: {}", String::from_utf8_lossy(&output.stderr) )); } Ok(()) } } ``` ### **3. CLI Integration** #### **Command Structure** ```rust // src/main.rs - OCI command integration #[tokio::main] async fn main() -> Result<(), Box> { let args: Vec = env::args().collect(); match args.get(1).map(|s| s.as_str()) { // ... existing commands ... // OCI commands Some("container-encapsulate") => { let ref_spec = args.get(2).ok_or("Missing OSTree reference")?; let image_ref = args.get(3).ok_or("Missing image reference")?; let cli = AptOstreeCli::new().await?; cli.container_encapsulate(ref_spec, image_ref).await?; } Some("compose") => { // Handle compose subcommands including OCI operations handle_compose_command(&args[2..]).await?; } _ => { eprintln!("Unknown command. Use --help for usage information."); std::process::exit(1); } } Ok(()) } ``` #### **Compose Integration** ```rust // src/compose.rs - Compose-based OCI operations pub async fn handle_compose_command(args: &[String]) -> Result<()> { match args.get(0).map(|s| s.as_str()) { Some("container-encapsulate") => { let compose = ComposeManager::new().await?; compose.container_encapsulate(&args[1..]).await?; } Some("build-image") => { let compose = ComposeManager::new().await?; compose.build_container_image(&args[1..]).await?; } _ => { eprintln!("Unknown compose command. Use --help for usage information."); std::process::exit(1); } } Ok(()) } ``` ## 🎯 **Key Implementation Principles** ### **1. CLI-Centric Architecture** - **Direct Access**: OCI operations directly access OSTree repository - **No Daemon Dependency**: OCI operations don't require daemon communication - **Local Processing**: All container generation happens locally - **External Tools**: Use external tools for container operations ### **2. External Tool Integration** - **Tool Detection**: Automatic detection of available container tools - **Backend Support**: Support for multiple container backends - **Command Execution**: Execute external commands for container operations - **Error Handling**: Proper error handling for external tool failures ### **3. OSTree Integration** - **Direct Repository Access**: Direct access to OSTree repository - **Package Metadata**: Extract package information from commits - **Filesystem Mapping**: Map filesystem to package metadata - **Component Support**: Support for component-based packaging ### **4. Progress and Error Handling** - **Local Progress**: Local progress reporting without daemon - **Error Propagation**: Proper error propagation from external tools - **Resource Cleanup**: Automatic cleanup of temporary resources - **User Feedback**: Clear user feedback for OCI operations ## πŸ“š **Documentation Status** ### **Current Documentation Issues** 1. **Speculative Content**: Some existing OCI documentation contains speculative content 2. **Incorrect Architecture**: Claims OCI operations go through daemon (they don't) 3. **Missing Implementation**: No actual OCI implementation exists in current apt-ostree 4. **Outdated Information**: Documentation doesn't reflect actual rpm-ostree implementation ### **Corrected Understanding** 1. **CLI-Only Operations**: OCI operations are CLI-based, not daemon-based 2. **Direct OSTree Access**: Direct access to OSTree repository without daemon 3. **External Tool Integration**: Uses external tools for container operations 4. **Rust Implementation**: Full OCI functionality implemented in Rust This OCI integration analysis provides the foundation for implementing OCI functionality in apt-ostree that follows the proven architecture of rpm-ostree while maintaining the CLI-centric approach that has proven successful for container operations.