### Building and Pushing an OCI Image Source: https://docs.rs/dockdash/0.2.0/dockdash/index.html?search=std%3A%3Avec This example demonstrates how to create a layer from a file, build an OCI image with specific platform and entrypoint configurations, and push the resulting image to a registry. ```APIDOC ## Rust SDK: Building and Pushing Images ### Description Use the `Layer::builder` and `Image::builder` to construct an OCI image programmatically. Once built, the `image.push` method is used to upload the image to a specified registry. ### Usage Example ```rust use dockdash::{Arch, Image, Layer, PushOptions, Result}; // Create a layer from a local file let layer = Layer::builder()? .file("./my-binary", "/app", Some(0o755))? .build() .await?; // Build the image let (image, _) = Image::builder() .from("ubuntu:latest") .platform("linux", &Arch::ARM64) .layer(layer) .entrypoint(vec!["/app".to_string()]) .build() .await?; // Push to registry image.push("registry.example.com/my-app:latest", &PushOptions::default()).await?; ``` ``` -------------------------------- ### Get configuration digest Source: https://docs.rs/dockdash/0.2.0/dockdash/struct.Image.html?search= Returns the digest of the image configuration. ```rust pub fn config_digest(&self) -> &str ``` -------------------------------- ### Get Runtime Command Source: https://docs.rs/dockdash/0.2.0/dockdash/struct.ImageMetadata.html Concatenates the entrypoint and command fields to determine the final execution command. ```rust pub fn runtime_command(&self) -> Vec ``` -------------------------------- ### Initialize BlobCache Source: https://docs.rs/dockdash/0.2.0/dockdash/struct.BlobCache.html Demonstrates creating a BlobCache instance using either the default system location or a custom directory path. ```rust use dockdash::BlobCache; // Use the default location (~/.dockdash/cache/blobs) let cache = BlobCache::new().unwrap(); // Use a custom directory let cache = BlobCache::with_path("/tmp/my-cache".into()).unwrap(); ``` -------------------------------- ### Image::get_metadata Source: https://docs.rs/dockdash/0.2.0/dockdash/struct.Image.html?search=u32+-%3E+bool Gets runtime metadata from the OCI image config. ```APIDOC ## pub fn get_metadata(&self) -> Result ### Description Gets runtime metadata from the OCI image config. Extracts the entrypoint, cmd, and working directory from the image configuration. ### Returns ImageMetadata containing entrypoint, cmd, and working_dir ``` -------------------------------- ### Test Layer with Files and Directories Source: https://docs.rs/dockdash/0.2.0/src/dockdash/layer.rs.html?search= Demonstrates building a layer from disk sources and verifying the resulting archive structure. ```rust #[tokio::test] async fn test_layer_with_file_and_dir() -> Result<()> { let temp_root = tempdir().map_err(|e| Error::Io { message: "failed to create tempdir".to_string(), source: e, })?; // Create a directory on disk to add let source_dir_disk = temp_root.path().join("mydir_on_disk"); fs::create_dir(&source_dir_disk).map_err(|e| Error::Io { message: "failed to create source_dir_disk".to_string(), source: e, })?; fs::write(source_dir_disk.join("file1.txt"), "content1").map_err(|e| Error::Io { message: "failed to write to file1.txt".to_string(), source: e, })?; fs::write(source_dir_disk.join("file2.txt"), "content2").map_err(|e| Error::Io { message: "failed to write to file2.txt".to_string(), source: e, })?; // Add a subdirectory with a file to test recursion let sub_dir_disk = source_dir_disk.join("my_subdir"); fs::create_dir(&sub_dir_disk).map_err(|e| Error::Io { message: "failed to create sub_dir_disk".to_string(), source: e, })?; fs::write(sub_dir_disk.join("nested_file.txt"), "nested_content").map_err(|e| { Error::Io { message: "failed to write to nested_file.txt".to_string(), source: e, } })?; // Create an individual file on disk to add let source_file_disk = temp_root.path().join("individual_on_disk.txt"); fs::write(&source_file_disk, "individual_content").map_err(|e| Error::Io { message: "failed to write to individual_on_disk.txt".to_string(), source: e, })?; let layer = Layer::builder()? .directory(&source_dir_disk, "archived_dir")? // Add dir to "archived_dir/" in layer .file( &source_file_disk, "archived_dir/renamed_individual.txt", None, )? // Add file into "archived_dir/" .data("root_file.txt", b"root data", None)? // Add data to root with default mode .build() .await?; assert!(layer.diff_id().starts_with("sha256:")); assert!(layer.path().exists()); // Verify tar content let compressed_file = File::open(layer.path()).map_err(|e| Error::Io { message: "failed to open layer path".to_string(), source: e, })?; let tar_reader = zstd::Decoder::new(compressed_file).map_err(|e| Error::Io { message: "failed to create zstd decoder".to_string(), source: e, })?; let mut archive = Archive::new(tar_reader); let mut entries_found = std::collections::HashMap::new(); for entry_result in archive.entries().map_err(|e| Error::Io { message: "failed to get archive entries".to_string(), source: e, })? { let mut entry = entry_result.map_err(|e| Error::Io { message: "failed to get entry".to_string(), source: e, })?; let path_str = entry .path() .map_err(|e| Error::Io { message: "failed to get entry path".to_string(), source: e, })? ``` -------------------------------- ### Get image metadata Source: https://docs.rs/dockdash/0.2.0/dockdash/struct.Image.html?search= Extracts runtime metadata from the OCI image configuration. ```rust pub fn get_metadata(&self) -> Result ``` -------------------------------- ### Initialize BlobCache with custom path Source: https://docs.rs/dockdash/0.2.0/dockdash/struct.BlobCache.html Shows how to instantiate a BlobCache at a specific directory path, which will be created if it does not exist. ```rust let cache = BlobCache::with_path("/var/cache/my-tool/blobs".into()).unwrap(); ``` -------------------------------- ### Get OCI archive path Source: https://docs.rs/dockdash/0.2.0/dockdash/struct.Image.html?search= Returns the path to the OCI archive tarball. ```rust pub fn path(&self) -> &Path ``` -------------------------------- ### Build and Push an OCI Image Source: https://docs.rs/dockdash/0.2.0/dockdash/index.html?search=u32+-%3E+bool Demonstrates creating a layer from a file, building an image with a specific platform and entrypoint, and pushing it to a registry. ```rust use dockdash::{Arch, Image, Layer, PushOptions, Result}; use std::path::PathBuf; let layer = Layer::builder()? .file("./my-binary", "./app", Some(0o755))? .build() .await?; let (image, _) = Image::builder() .from("ubuntu:latest") .platform("linux", &Arch::ARM64) .layer(layer) .entrypoint(vec!["/app".to_string()]) .build() .await?; image.push("registry.example.com/my-app:latest", &PushOptions::default()).await?; ``` -------------------------------- ### BlobCache unit tests Source: https://docs.rs/dockdash/0.2.0/src/dockdash/blobcache.rs.html Tests for putting, getting, and removing blobs from the cache using temporary directories. ```rust #[tokio::test] async fn test_put_get_blob() -> Result<()> { let temp_dir = tempdir().map_err(|e| Error::Io { message: "Failed to create temp dir".to_string(), source: e, })?; let cache = BlobCache::with_path(temp_dir.path().to_path_buf())?; let digest = "sha256:testdigest123"; let data = b"hello cache data"; cache.put_blob(digest, data).await?; let retrieved_data = cache.get_blob(digest).await?.expect("Blob should be found"); assert_eq!(retrieved_data, data); Ok(()) } ``` ```rust #[tokio::test] async fn test_get_non_existent_blob() -> Result<()> { let temp_dir = tempdir().map_err(|e| Error::Io { message: "Failed to create temp dir".to_string(), source: e, })?; let cache = BlobCache::with_path(temp_dir.path().to_path_buf())?; let digest = "sha256:nonexistent123"; let retrieved_data = cache.get_blob(digest).await?; assert!(retrieved_data.is_none()); Ok(()) } ``` ```rust #[tokio::test] async fn test_put_remove_get_blob() -> Result<()> { let temp_dir = tempdir().map_err(|e| Error::Io { message: "Failed to create temp dir".to_string(), source: e, })?; let cache = BlobCache::with_path(temp_dir.path().to_path_buf())?; let digest = "sha256:toberemoved456"; let data = b"this will be removed"; cache.put_blob(digest, data).await?; let retrieved_data_before_remove = cache .get_blob(digest) .await? .expect("Blob should be found before remove"); assert_eq!(retrieved_data_before_remove, data); cache.remove_blob(digest).await?; let retrieved_data_after_remove = cache.get_blob(digest).await?; assert!(retrieved_data_after_remove.is_none()); // Removing again should be fine cache.remove_blob(digest).await?; Ok(()) } ``` -------------------------------- ### Verify Directory Archiving Source: https://docs.rs/dockdash/0.2.0/src/dockdash/layer.rs.html?search= Demonstrates how to build a layer from a source directory and verify the contents of the resulting compressed archive. ```rust #[tokio::test] async fn test_directory_to_archive_root() -> Result<()> { let temp_root = tempdir().map_err(|e| Error::Io { message: "failed to create tempdir".to_string(), source: e, })?; // 1. Create a source directory on disk with some files let source_dir_on_disk = temp_root.path().join("my_app_files"); fs::create_dir(&source_dir_on_disk).map_err(|e| Error::Io { message: "failed to create source_dir_on_disk".to_string(), source: e, })?; fs::write(source_dir_on_disk.join("app.js"), "console.log('app');").map_err(|e| { Error::Io { message: "failed to write to app.js".to_string(), source: e, } })?; fs::write( source_dir_on_disk.join("styles.css"), "body { color: blue; }", ) .map_err(|e| Error::Io { message: "failed to write to styles.css".to_string(), source: e, })?; // 2. Build the layer, copying the directory to the archive root "." let layer = Layer::builder()? .directory(&source_dir_on_disk, ".")? .build() .await?; assert!(layer.diff_id().starts_with("sha256:")); assert!(layer.path().exists()); // 3. Verify tar content let compressed_file = File::open(layer.path()).map_err(|e| Error::Io { message: "failed to open layer path".to_string(), source: e, })?; let tar_reader = zstd::Decoder::new(compressed_file).map_err(|e| Error::Io { message: "failed to create zstd decoder".to_string(), source: e, })?; let mut archive = Archive::new(tar_reader); let mut entries_found = std::collections::HashMap::new(); for entry_result in archive.entries().map_err(|e| Error::Io { message: "failed to get archive entries".to_string(), source: e, })? { let mut entry = entry_result.map_err(|e| Error::Io { message: "failed to get entry".to_string(), source: e, })?; let path_str = entry .path() .map_err(|e| Error::Io { message: "failed to get entry path".to_string(), source: e, })? .to_string_lossy() ``` -------------------------------- ### Building and Pushing an OCI Image Source: https://docs.rs/dockdash/0.2.0/dockdash/index.html Demonstrates the process of creating a layer from a file, building an image with specific platform and entrypoint configurations, and pushing the resulting image to a registry. ```APIDOC ## Rust SDK: Building and Pushing an Image ### Description This example shows how to use the `LayerBuilder` and `ImageBuilder` to construct an OCI image and push it to a remote registry using `image.push`. ### Usage ```rust use dockdash::{Arch, Image, Layer, PushOptions, Result}; use std::path::PathBuf; // Create a layer from a file let layer = Layer::builder()? .file("./my-binary", "./app", Some(0o755))? .build() .await?; // Build the image let (image, _) = Image::builder() .from("ubuntu:latest") .platform("linux", &Arch::ARM64) .layer(layer) .entrypoint(vec!["/app".to_string()]) .build() .await?; // Push to registry image.push("registry.example.com/my-app:latest", &PushOptions::default()).await?; ``` ``` -------------------------------- ### Building and Pushing an OCI Image Source: https://docs.rs/dockdash/0.2.0/dockdash/index.html?search= Demonstrates the core workflow of creating a layer from a file, building an image with specific platform and entrypoint configurations, and pushing the resulting image to a registry. ```APIDOC ## Rust SDK: Building and Pushing Images ### Description This workflow uses the `LayerBuilder` and `ImageBuilder` to construct an OCI image from local files and push it to a remote registry. ### Usage ```rust use dockdash::{Arch, Image, Layer, PushOptions, Result}; use std::path::PathBuf; // Create a layer from a local file let layer = Layer::builder()? .file("./my-binary", "./app", Some(0o755))? .build() .await?; // Build the image let (image, _) = Image::builder() .from("ubuntu:latest") .platform("linux", &Arch::ARM64) .layer(layer) .entrypoint(vec!["/app".to_string()]) .build() .await?; // Push the image to a registry image.push("registry.example.com/my-app:latest", &PushOptions::default()).await?; ``` ``` -------------------------------- ### Configure Image Builder Policies Source: https://docs.rs/dockdash/0.2.0/src/dockdash/image.rs.html?search= Methods for configuring push policies and progress tracking on an image builder. ```rust /// Sets the monolithic push policy to never use monolithic push (use chunked upload). pub fn with_chunked_upload(mut self) -> Self { self.monolithic_push = MonolithicPushPolicy::Never; self } /// Sets the monolithic push policy to automatically determine based on the registry. pub fn with_auto_push_mode(mut self) -> Self { self.monolithic_push = MonolithicPushPolicy::Auto; self } /// Sets a progress callback to receive push progress updates. pub fn with_progress_callback(mut self, callback: Box) -> Self { self.progress_callback = Some(callback); self } } ``` -------------------------------- ### build() Source: https://docs.rs/dockdash/0.2.0/src/dockdash/image.rs.html?search=std%3A%3Avec Executes the image build process using the configured parameters. ```APIDOC ## build() -> Result<(Image, BuildDiagnostics)> ### Description Builds the OCI image. This process involves pulling the base image (if necessary), applying layers and configuration, and creating an OCI tarball layout. ### Returns - **Result<(Image, BuildDiagnostics)>** - Returns the built Image and associated build diagnostics upon success, or an error if the build fails. ``` -------------------------------- ### Building and Pushing an OCI Image Source: https://docs.rs/dockdash/0.2.0/src/dockdash/lib.rs.html?search=std%3A%3Avec Demonstrates the core workflow of creating a layer from a file, building an image from a base, and pushing it to a registry. ```APIDOC ## SDK Usage: Image Creation and Push ### Description This workflow shows how to construct an OCI image programmatically using the `Layer` and `Image` builders, and subsequently push the resulting image to a remote OCI registry. ### Usage ```rust use dockdash::{Arch, Image, Layer, PushOptions, Result}; // 1. Create a layer from a local file let layer = Layer::builder()? .file("./my-binary", "/app", Some(0o755))? .build() .await?; // 2. Build the image from a base and add the layer let (image, _) = Image::builder() .from("ubuntu:latest") .platform("linux", &Arch::ARM64) .layer(layer) .entrypoint(vec!["/app".to_string()]) .build() .await?; // 3. Push the image to a registry image.push("registry.example.com/my-app:latest", &PushOptions::default()).await?; ``` ``` -------------------------------- ### Define Image Metadata and Runtime Command Source: https://docs.rs/dockdash/0.2.0/src/dockdash/image.rs.html?search= Defines the structure for OCI image configuration metadata and provides a method to concatenate entrypoint and command arguments. ```rust /// Runtime metadata extracted from an OCI image configuration. #[derive(Debug, Clone)] pub struct ImageMetadata { /// Entrypoint from the image config pub entrypoint: Option>, /// Cmd from the image config pub cmd: Option>, /// Working directory from the image config pub working_dir: Option, } impl ImageMetadata { /// Gets the full runtime command (entrypoint + cmd concatenated). /// This is what should actually be executed. pub fn runtime_command(&self) -> Vec { let mut command = Vec::new(); if let Some(ref entrypoint) = self.entrypoint { command.extend(entrypoint.iter().cloned()); } if let Some(ref cmd) = self.cmd { command.extend(cmd.iter().cloned()); } command } } ``` -------------------------------- ### ImageBuilder Configuration and Build Source: https://docs.rs/dockdash/0.2.0/src/dockdash/image.rs.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Methods for configuring the ImageBuilder instance and executing the image build process. ```APIDOC ## ImageBuilder Configuration Methods ### working_dir(working_dir: &str) -> Self Sets the working directory for the image, overriding the base image setting. ### output_to(path: PathBuf) -> Self Specifies the destination path for the OCI archive tarball. ### blob_cache(cache: blobcache::BlobCache) -> Self Configures a specific BlobCache instance for the builder. ### output_name_and_tag(name_and_tag: &str) -> Self Sets the name and tag for the image configuration. ### pull_policy(policy: PullPolicy) -> Self Sets the pull policy for the base image manifest. ### protocol(protocol: ClientProtocol) -> Self Sets the protocol (Http or Https) for registry communication. ### auth(auth: RegistryAuth) -> Self Sets the authentication credentials for pulling the base image. ### build() -> Result<(Image, BuildDiagnostics)> Executes the image build process, pulling the base image if necessary, applying layers, and creating the OCI tarball. ``` -------------------------------- ### Initialize and Load OCI Images Source: https://docs.rs/dockdash/0.2.0/src/dockdash/image.rs.html?search= Methods for creating an image builder or loading an existing OCI tarball from the filesystem. ```rust impl Image { /// Returns a builder to construct an `Image`. pub fn builder() -> ImageBuilder { ImageBuilder { base_image_ref: None, platform_os: None, platform_arch: None, layers: Vec::new(), entrypoint: None, cmd: None, working_dir: None, output_path: None, blob_cache: None, output_image_name_and_tag: None, pull_policy: None, auth: None, protocol: ClientProtocol::Https, } } /// Loads an existing OCI tarball from disk. /// /// This is useful for pushing pre-built images without rebuilding them. /// /// # Arguments /// * `tarball_path` - Path to the OCI tarball file /// /// # Returns /// A loaded Image instance on success #[instrument(fields(tarball_path = %tarball_path.as_ref().display()))] pub fn from_tarball(tarball_path: impl AsRef) -> Result { let tarball_path = tarball_path.as_ref(); if !tarball_path.exists() { return Err(Error::Generic { message: format!("OCI tarball not found at {}", tarball_path.display()), source: None, }); } info!("Loading OCI image from tarball: {}", tarball_path.display()); // Load the OCI artifact to get the config digest let mut archive = OciArtifact::from_oci_archive(tarball_path).map_err(|e| Error::OciArchive { message: format!( "Failed to load OCI artifact from {}", tarball_path.display() ), source: Some(e.into()), })?; ``` -------------------------------- ### Test layer creation with raw data Source: https://docs.rs/dockdash/0.2.0/src/dockdash/layer.rs.html?search= Shows how to add raw byte data to a layer and verify the resulting layer properties. ```rust #[tokio::test] async fn test_layer_with_data() -> Result<()> { let content = b"hello world"; let layer = Layer::builder()? .data("hello.txt", content, Some(0o755))? .build() .await?; assert!(layer.diff_id().starts_with("sha256:")); assert!(layer.path().exists()); // Verify tar content let compressed_file = File::open(layer.path()).map_err(|e| Error::Io { message: "failed to open layer path".to_string(), source: e, })?; let tar_reader = zstd::Decoder::new(compressed_file).map_err(|e| Error::Io { message: "failed to create zstd decoder".to_string(), source: e, })?; ``` -------------------------------- ### PushOptions Source: https://docs.rs/dockdash/0.2.0/src/dockdash/image.rs.html?search=u32+-%3E+bool Configuration options for pushing an OCI image to a registry. ```APIDOC ## struct PushOptions ### Description Options for pushing an image, including authentication, protocol, and push policy. ### Methods - **with_monolithic_push(mut self) -> Self** - Sets the monolithic push policy to always use monolithic push. ``` -------------------------------- ### Prepare OCI Client Configuration Source: https://docs.rs/dockdash/0.2.0/src/dockdash/image.rs.html?search= Extracts configuration data from an OCI archive and maps it to an OciClientConfig structure for registry pushing. ```rust let (cfg_desc, cfg_bytes) = archive.get_config().map_err(|e| { warn!(error = %e, "Failed to get config from OCI artifact."); Error::OciArchive { message: "Failed to get config from OCI artifact".to_string(), source: Some(e.into()), } })?; let cfg_for_push = OciClientConfig { data: cfg_bytes, media_type: cfg_desc.media_type().to_string(), annotations: cfg_desc .annotations() .clone() .map(|h| h.into_iter().collect()), }; ``` -------------------------------- ### Build Image Configuration Source: https://docs.rs/dockdash/0.2.0/src/dockdash/image.rs.html Constructs an ImageConfiguration by either extending an existing base image or creating a new one from scratch using OCI builders. ```rust } else { // Building from scratch (no base image) info!("Building from scratch (no base image)"); base_image_data = None; manifest_source = ManifestSource::NotApplicable; resolved_manifest_digest_str = String::new(); default_image_name = "scratch:latest".to_string(); } let build_artifacts_dir = tempfile::tempdir().map_err(|e| Error::Io { message: "Failed to create temporary directory for image build artifacts".to_string(), source: e, })?; // Build image configuration (either from base or from scratch) let current_config: ImageConfiguration = if let Some(ref base_data) = base_image_data { // Start from base image config let mut config: ImageConfiguration = serde_json::from_slice(&base_data.config.data) .map_err(|e| Error::ImageConfig { message: "Failed to parse base image configuration".to_string(), source: Some(Box::new(e)), })?; // Add new layers to diff_ids let mut all_diff_ids: Vec = config.rootfs().diff_ids().clone(); for new_layer in &self.layers { all_diff_ids.push(new_layer.diff_id().to_string()); } *config.rootfs_mut().diff_ids_mut() = all_diff_ids; // Update process config let mut proc_config = config.config().clone().unwrap_or_default(); if let Some(entrypoint) = self.entrypoint { proc_config.set_entrypoint(Some(entrypoint)); // Per Docker/OCI convention, setting entrypoint resets cmd // unless the user also explicitly set cmd if self.cmd.is_none() { proc_config.set_cmd(None); } } if let Some(cmd) = self.cmd { proc_config.set_cmd(Some(cmd)); } if let Some(working_dir) = self.working_dir { proc_config.set_working_dir(Some(working_dir)); } config.set_os(target_os_for_build.as_str().into()); config.set_architecture(target_arch_for_build.to_string().as_str().into()); config.set_config(Some(proc_config)); config } else { // Build from scratch - create minimal config use oci_spec::image::{ConfigBuilder, ImageConfigurationBuilder, RootFsBuilder}; let diff_ids: Vec = self .layers .iter() .map(|layer| layer.diff_id().to_string()) .collect(); let rootfs = RootFsBuilder::default() .typ("layers") .diff_ids(diff_ids) .build() .map_err(|e| Error::ImageConfig { message: format!("Failed to build rootfs: {}", e), source: Some(Box::new(e)), })?; let mut config_builder = ConfigBuilder::default(); if let Some(entrypoint) = self.entrypoint { config_builder = config_builder.entrypoint(entrypoint); } if let Some(cmd) = self.cmd { config_builder = config_builder.cmd(cmd); } if let Some(working_dir) = self.working_dir { config_builder = config_builder.working_dir(working_dir); } let proc_config = config_builder.build().map_err(|e| Error::ImageConfig { message: format!("Failed to build config: {}", e), source: Some(Box::new(e)), })?; ImageConfigurationBuilder::default() .os(target_os_for_build.as_str()) .architecture(target_arch_for_build.to_string().as_str()) .rootfs(rootfs) .config(proc_config) .build() .map_err(|e| Error::ImageConfig { message: format!("Failed to build image configuration: {}", e), source: Some(Box::new(e)), })? }; let config_json_bytes = serde_json::to_vec(¤t_config).map_err(|e| Error::ImageConfig { message: "Failed to serialize new image configuration".to_string(), source: Some(Box::new(e)), })?; let config_digest_sha256 = { let mut hasher = Sha256::new(); hasher.update(&config_json_bytes); ``` -------------------------------- ### PushOptions Configuration Methods Source: https://docs.rs/dockdash/0.2.0/dockdash/struct.PushOptions.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Methods available on the PushOptions struct to configure push behavior. ```APIDOC ## pub fn with_monolithic_push(self) -> Self Sets the monolithic push policy to always use monolithic push. ## pub fn with_chunked_upload(self) -> Self Sets the monolithic push policy to never use monolithic push (use chunked upload). ## pub fn with_auto_push_mode(self) -> Self Sets the monolithic push policy to automatically determine based on the registry. ## pub fn with_progress_callback(self, callback: Box) -> Self Sets a progress callback to receive push progress updates. ``` -------------------------------- ### PushOptions Configuration Methods Source: https://docs.rs/dockdash/0.2.0/dockdash/struct.PushOptions.html?search=std%3A%3Avec Methods available on the PushOptions struct to configure the push behavior. ```APIDOC ## with_monolithic_push ### Description Sets the monolithic push policy to always use monolithic push. ## with_chunked_upload ### Description Sets the monolithic push policy to never use monolithic push (use chunked upload). ## with_auto_push_mode ### Description Sets the monolithic push policy to automatically determine based on the registry. ## with_progress_callback ### Description Sets a progress callback to receive push progress updates. ### Parameters - **callback** (Box) - Required - The callback function to handle progress updates. ``` -------------------------------- ### Test Directory Archiving Source: https://docs.rs/dockdash/0.2.0/src/dockdash/layer.rs.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Demonstrates how to archive directories into a layer, including handling absolute paths and root-level mapping. ```rust #[tokio::test] async fn test_layer_with_directory_absolute_path() -> Result<()> { let temp_root = tempdir().map_err(|e| Error::Io { message: "failed to create tempdir".to_string(), source: e, })?; let source_dir_disk = temp_root.path().join("my_dir_abs"); fs::create_dir_all(source_dir_disk.join("sub")).map_err(|e| Error::Io { message: "failed to create_dir_all".to_string(), source: e, })?; fs::write( source_dir_disk.join("sub/file_in_dir.txt"), "dir content abs", ) .map_err(|e| Error::Io { message: "failed to write to file_in_dir.txt".to_string(), source: e, })?; let layer = Layer::builder()? .directory(&source_dir_disk, "/archived_dir_abs")? .build() .await?; let entries = list_tar_contents(layer.path())?; assert!(entries.contains_key("archived_dir_abs/sub/")); assert_eq!( entries.get("archived_dir_abs/sub/file_in_dir.txt").unwrap(), "dir content abs" ); Ok(()) } ``` ```rust #[tokio::test] async fn test_layer_with_directory_root_path() -> Result<()> { // Adding a directory with archive_path "/" means its *contents* go to the layer's root. let temp_root = tempdir().map_err(|e| Error::Io { message: "failed to create tempdir".to_string(), source: e, })?; let source_dir_disk = temp_root.path().join("my_dir_for_root"); fs::create_dir_all(source_dir_disk.join("sub_at_root")).map_err(|e| Error::Io { message: "failed to create_dir_all".to_string(), source: e, })?; fs::write(source_dir_disk.join("file_at_root.txt"), "root dir content").map_err(|e| { Error::Io { message: "failed to write to file_at_root.txt".to_string(), source: e, } })?; fs::write( source_dir_disk.join("sub_at_root/nested.txt"), "nested root", ) .map_err(|e| Error::Io { message: "failed to write to nested.txt".to_string(), source: e, })?; let layer = Layer::builder()? .directory(&source_dir_disk, "/")? // Contents of my_dir_for_root should be at archive root .build() .await?; let entries = list_tar_contents(layer.path())?; assert!(entries.contains_key("sub_at_root/")); assert_eq!(entries.get("file_at_root.txt").unwrap(), "root dir content"); assert_eq!( entries.get("sub_at_root/nested.txt").unwrap(), "nested root" ); // The directory "my_dir_for_root" itself should not appear in the path. assert!(!entries.contains_key(&format!( "{}/file_at_root.txt", source_dir_disk.file_name().unwrap().to_string_lossy() ))); Ok(()) } ``` -------------------------------- ### file Source: https://docs.rs/dockdash/0.2.0/dockdash/struct.LayerBuilder.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Adds a single file from the local filesystem to the layer. ```APIDOC ## pub fn file(self, disk_path: impl AsRef, archive_path: impl AsRef, mode: Option) -> Result ### Description Adds a single file from the local filesystem to the layer. ### Parameters - **disk_path** (impl AsRef) - Path to the source file on the local filesystem. - **archive_path** (impl AsRef) - Path where the file will be placed within the layer’s tar archive. - **mode** (Option) - Optional. The file mode (permissions) to set. If `None`, uses the source file’s mode. ``` -------------------------------- ### file Source: https://docs.rs/dockdash/0.2.0/dockdash/struct.LayerBuilder.html?search=std%3A%3Avec Adds a single file from the local filesystem to the layer. ```APIDOC ## pub fn file(self, disk_path: impl AsRef, archive_path: impl AsRef, mode: Option) -> Result ### Description Adds a single file from the local filesystem to the layer. ### Parameters - **disk_path** (impl AsRef) - Required - Path to the source file on the local filesystem. - **archive_path** (impl AsRef) - Required - Path where the file will be placed within the layer’s tar archive. - **mode** (Option) - Optional - The file mode (permissions) to set. If `None`, uses the source file’s mode. ``` -------------------------------- ### Configure Platform Resolver and Pull Manifest Source: https://docs.rs/dockdash/0.2.0/src/dockdash/image.rs.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Sets up a custom platform resolver for OCI clients and implements logic to pull or retrieve cached image manifests. ```rust if let (Some(os_filter_val), Some(arch_filter_val)) = (&self.platform_os, &self.platform_arch) { let os_filter_cloned = os_filter_val.clone(); let arch_filter_cloned = arch_filter_val.to_string(); client_cfg.platform_resolver = Some(Box::new( move |index_entries: &[ImageIndexEntry]| { info!(target_os = %os_filter_cloned, target_arch = %arch_filter_cloned, num_index_entries = index_entries.len(), "Platform resolver: Attempting to find match."); for entry in index_entries { if let Some(p) = entry.platform.as_ref() { if p.os == os_filter_cloned && p.architecture == arch_filter_cloned { info!(resolver_selected_digest = %entry.digest, "Platform resolver: Found a match."); return Some(entry.digest.clone()); } } } warn!(target_os = %os_filter_cloned, target_arch = %arch_filter_cloned, "Platform resolver: No match found."); None }, )); } else { info!("Platform resolver not configured as os/arch were not explicitly provided to ImageBuilder. Default oci-client resolver will be used if necessary."); // If no platform is specified by the user, oci-client's default resolver (current_platform_resolver) will be used. // We don't need to explicitly set it to None here, as ClientConfig::default() already sets a default resolver. } let oci_client = Client::new(client_cfg); info!(base_image_ref = %base_image_ref_str, pull_policy = ?pull_policy, "Attempting to pull/load resolved base image manifest."); let pull_err_mapper = |e: OciDistributionError| { warn!(base_image_ref = %base_image_ref_str, error = %e, "Failed to pull and resolve base image manifest."); Error::ImagePull { image_ref: base_image_ref_str.to_string(), message: format!( "Failed to pull/resolve base image manifest ({}): {}", base_image_ref_str, e ), source: Some(Box::new(e)), } }; let manifest_cache_key = format!( "manifest-v2:{}:{}:{}", base_ref.whole(), target_os_for_build.as_str(), target_arch_for_build, ); let manifest_source_temp: ManifestSource; let (base_image_manifest_resolved, resolved_manifest_digest_str_temp) = if pull_policy == PullPolicy::Missing { debug!(key = %manifest_cache_key, "PullPolicy::Missing. Attempting to load manifest from cache."); match cache.get_blob(&manifest_cache_key).await { Ok(Some(cached_data)) => { match serde_json::from_slice::<(OciImageManifest, String)>(&cached_data) { Ok((manifest, digest)) => { info!(key = %manifest_cache_key, resolved_digest = %digest, "Manifest cache hit and deserialized successfully."); manifest_source_temp = ManifestSource::FromCache; (manifest, digest) } Err(e) => { warn!(key = %manifest_cache_key, error = %e, "Failed to deserialize cached manifest. Will pull from registry."); // Fallback: Pull and cache manifest_source_temp = ManifestSource::FromRegistry; let (pulled_manifest, pulled_digest) = oci_client .pull_image_manifest(&base_ref, &pull_auth) .await .map_err(pull_err_mapper)?; match serde_json::to_vec(&( pulled_manifest.clone(), pulled_digest.clone(), )) { Ok(data_to_cache) => { if let Err(cache_err) = cache ``` -------------------------------- ### pub async fn build Source: https://docs.rs/dockdash/0.2.0/src/dockdash/layer.rs.html?search= Finalizes the layer construction by completing the tar archive, calculating the diff_id, and compressing the archive. ```APIDOC ## Method: build ### Description Finalizes the layer construction. This method completes the uncompressed tar archive, calculates its diff_id (SHA256 hash), zstd-compresses the archive into a new temporary file, and returns the resulting Layer. ### Returns - **Result** - Returns the finalized Layer object or an error if the build process fails. ``` -------------------------------- ### BlobCache::new Source: https://docs.rs/dockdash/0.2.0/dockdash/struct.BlobCache.html?search= Creates a new BlobCache instance using the default cache location. ```APIDOC ## pub fn new() -> Result ### Description Creates a new `BlobCache` using the default cache location (~/.dockdash/cache/blobs, or /tmp/.dockdash/cache/blobs in constrained environments). ``` -------------------------------- ### BlobCache::new Source: https://docs.rs/dockdash/0.2.0/dockdash/struct.BlobCache.html Creates a new BlobCache instance using the default cache location. ```APIDOC ## pub fn new() -> Result ### Description Creates a new `BlobCache` using the default cache location (`~/.dockdash/cache/blobs`, or `/tmp/.dockdash/cache/blobs` in constrained environments). ``` -------------------------------- ### Execute Image Build Source: https://docs.rs/dockdash/0.2.0/src/dockdash/image.rs.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E The build method handles base image pulling, cache initialization, and OCI tarball creation. ```rust #[instrument(skip_all, fields( base_image_ref = ?self.base_image_ref, platform_os = ?self.platform_os, platform_arch = ?self.platform_arch, num_layers_to_add = self.layers.len(), output_path = ?self.output_path ))] pub async fn build(mut self) -> Result<(Image, BuildDiagnostics)> { info!("Starting image build."); let target_os_for_build = self .platform_os .clone() .unwrap_or_else(|| "linux".to_string()); let target_arch_for_build = self.platform_arch.clone().unwrap_or(Arch::Amd64); // Conditionally pull base image if specified let base_image_data: Option; let manifest_source: ManifestSource; let resolved_manifest_digest_str: String; let default_image_name: String; if let Some(base_image_ref_str) = &self.base_image_ref { // Pull base image info!("Building from base image: {}", base_image_ref_str); let base_ref = Reference::try_from(base_image_ref_str.as_str()).map_err(|e| Error::ImagePull { image_ref: base_image_ref_str.to_string(), message: format!("Invalid base image reference format: {}", e), source: Some(Box::new(e)), })?; // Determine authentication for pulling base image // Use provided auth if available, otherwise determine from environment variables let pull_auth = self .auth .take() .unwrap_or_else(|| determine_registry_auth(&base_ref)); let pull_policy = self.pull_policy.take().unwrap_or_default(); // Get policy or default // Initialize cache instance ONCE. // If self.blob_cache is None, ImageBuilder creates its own. // If it's Some, it means ImageBuilder was configured with an external cache. let cache = match self.blob_cache.take() { // Take ownership from self Some(c) => c, None => { debug!("No BlobCache provided to ImageBuilder, creating a default one."); blobcache::BlobCache::new()? } }; let mut client_cfg = ClientConfig { protocol: self.protocol.clone(), ..Default::default() }; ``` -------------------------------- ### Push Config and Manifest Only Source: https://docs.rs/dockdash/0.2.0/src/dockdash/image.rs.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Handles the case where all layers are already present in the registry, pushing only the configuration and manifest. ```rust oci_client .push( &push_ref, &Vec::new(), // Empty layers since they're all already mounted cfg_for_push, &options.auth, Some(dist_mani), ) .await .map_err(|e| { warn!(error = %e, "OCI client push failed."); Error::Generic { message: format!("OCI client push to {} failed: {}", push_ref, e), source: Some(Box::new(e)), } })?; ``` -------------------------------- ### directory Source: https://docs.rs/dockdash/0.2.0/dockdash/struct.LayerBuilder.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Adds an entire directory recursively from the local filesystem to the layer. ```APIDOC ## pub fn directory(self, disk_path: impl AsRef, archive_path: impl AsRef) -> Result ### Description Adds an entire directory (recursively) from the local filesystem to the layer. ### Parameters - **disk_path** (impl AsRef) - Path to the source directory on the local filesystem. - **archive_path** (impl AsRef) - Path under which the directory contents will be placed within the layer’s tar archive. ``` -------------------------------- ### directory Source: https://docs.rs/dockdash/0.2.0/dockdash/struct.LayerBuilder.html?search=std%3A%3Avec Adds an entire directory recursively from the local filesystem to the layer. ```APIDOC ## pub fn directory(self, disk_path: impl AsRef, archive_path: impl AsRef) -> Result ### Description Adds an entire directory (recursively) from the local filesystem to the layer. ### Parameters - **disk_path** (impl AsRef) - Required - Path to the source directory on the local filesystem. - **archive_path** (impl AsRef) - Required - Path under which the directory contents will be placed within the layer’s tar archive. ``` -------------------------------- ### context Source: https://docs.rs/dockdash/0.2.0/dockdash/type.Result.html Compatibility re-export of wrap_err for interop with anyhow. ```APIDOC ## fn context(self, msg: D) -> Result ### Description Compatibility re-export of `wrap_err()` for interop with `anyhow`. ``` -------------------------------- ### ImageBuilder Configuration Methods Source: https://docs.rs/dockdash/0.2.0/src/dockdash/image.rs.html?search= Fluent API methods for configuring the ImageBuilder instance. ```rust impl ImageBuilder { /// Sets the base image reference (e.g., "marketplace.gcr.io/google/ubuntu2404:latest", "ghcr.io/user/image:tag"). pub fn from(mut self, base_image_ref: &str) -> Self { self.base_image_ref = Some(base_image_ref.to_string()); self } /// Sets the target platform for the image. pub fn platform(mut self, os: &str, arch: &Arch) -> Self { self.platform_os = Some(os.to_string()); self.platform_arch = Some(arch.clone()); self } /// Adds a `Layer` to be included in the image. Layers are applied in the order they are added. pub fn layer(mut self, layer: Layer) -> Self { self.layers.push(layer); self } /// Sets the entrypoint for the image. Overrides the entrypoint from the base image. pub fn entrypoint(mut self, entrypoint: Vec) -> Self { self.entrypoint = Some(entrypoint); self } /// Sets the command (Cmd) for the image. Overrides the command from the base image. pub fn cmd(mut self, cmd: Vec) -> Self { self.cmd = Some(cmd); ``` -------------------------------- ### runtime_command Source: https://docs.rs/dockdash/0.2.0/dockdash/struct.ImageMetadata.html?search=Option%3CT%3E%2C+%28T+-%3E+U%29+-%3E+Option%3CU%3E Retrieves the full runtime command by concatenating the entrypoint and command fields. ```APIDOC ## fn runtime_command(&self) -> Vec ### Description Gets the full runtime command (entrypoint + cmd concatenated). This is what should actually be executed. ``` -------------------------------- ### BlobCache::with_path Source: https://docs.rs/dockdash/0.2.0/dockdash/struct.BlobCache.html Creates a new BlobCache instance at a specified directory path. ```APIDOC ## pub fn with_path(path: PathBuf) -> Result ### Description Creates a new `BlobCache` at the given directory path. The directory will be created if it does not exist. ``` -------------------------------- ### Configure Pull and Extract Options Source: https://docs.rs/dockdash/0.2.0/src/dockdash/image.rs.html?search= Configuration struct for defining platform, authentication, and protocol settings when pulling images. ```rust /// Options for pulling and extracting an image. #[derive(Debug, Clone, Default)] pub struct PullAndExtractOptions { /// The platform OS to pull (e.g., "linux") pub platform_os: Option, /// The platform architecture to pull (e.g., Arch::Amd64) pub platform_arch: Option, /// Pull policy for the image manifest pub pull_policy: PullPolicy, /// Optional blob cache to use pub blob_cache: Option, /// Optional authentication for the registry pub auth: Option, /// Protocol to use (Http or Https). Defaults to Https. pub protocol: ClientProtocol, } ```