Solved multiple Buildah-related issues in the Buildah SAL + fixed test suite for Buildah
This commit is contained in:
		@@ -249,8 +249,32 @@ impl Builder {
 | 
			
		||||
    /// # Returns
 | 
			
		||||
    ///
 | 
			
		||||
    /// * `Result<CommandResult, BuildahError>` - Command result or error
 | 
			
		||||
    pub fn commit(&self, image_name: &str) -> Result<CommandResult, BuildahError> {
 | 
			
		||||
    pub fn commit(&self, image_name: &str, options: Option<Vec<(String, String)>>) -> Result<CommandResult, BuildahError> {
 | 
			
		||||
        if let Some(container_id) = &self.container_id {
 | 
			
		||||
            let mut args_owned: Vec<String> = Vec::new();
 | 
			
		||||
            args_owned.push("commit".to_string());
 | 
			
		||||
 | 
			
		||||
            // Process options
 | 
			
		||||
            if let Some(options_vec) = options {
 | 
			
		||||
                for (key, value) in options_vec.iter() {
 | 
			
		||||
                    let option_name = if key.len() == 1 {
 | 
			
		||||
                        format!("-{}", key)
 | 
			
		||||
                    } else {
 | 
			
		||||
                        format!("--{}", key)
 | 
			
		||||
                    };
 | 
			
		||||
                    args_owned.push(option_name);
 | 
			
		||||
                    if !value.is_empty() {
 | 
			
		||||
                        args_owned.push(value.clone());
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            args_owned.push(container_id.clone());
 | 
			
		||||
            args_owned.push(image_name.to_string());
 | 
			
		||||
 | 
			
		||||
            // Convert Vec<String> to Vec<&str> for execute_buildah_command
 | 
			
		||||
            let args: Vec<&str> = args_owned.iter().map(|s| s.as_str()).collect();
 | 
			
		||||
 | 
			
		||||
            // Save the current debug flag
 | 
			
		||||
            let previous_debug = thread_local_debug();
 | 
			
		||||
 | 
			
		||||
@@ -258,7 +282,7 @@ impl Builder {
 | 
			
		||||
            set_thread_local_debug(self.debug);
 | 
			
		||||
 | 
			
		||||
            // Execute the command
 | 
			
		||||
            let result = execute_buildah_command(&["commit", container_id, image_name]);
 | 
			
		||||
            let result = execute_buildah_command(&args);
 | 
			
		||||
 | 
			
		||||
            // Restore the previous debug flag
 | 
			
		||||
            set_thread_local_debug(previous_debug);
 | 
			
		||||
@@ -336,16 +360,22 @@ impl Builder {
 | 
			
		||||
    /// # Returns
 | 
			
		||||
    ///
 | 
			
		||||
    /// * `Result<CommandResult, BuildahError>` - Command result or error
 | 
			
		||||
    pub fn config(&self, options: HashMap<String, String>) -> Result<CommandResult, BuildahError> {
 | 
			
		||||
    pub fn config(&self, options: Vec<(String, String)>) -> Result<CommandResult, BuildahError> {
 | 
			
		||||
        if let Some(container_id) = &self.container_id {
 | 
			
		||||
            let mut args_owned: Vec<String> = Vec::new();
 | 
			
		||||
            args_owned.push("config".to_string());
 | 
			
		||||
 | 
			
		||||
            // Process options map
 | 
			
		||||
            // Process options
 | 
			
		||||
            for (key, value) in options.iter() {
 | 
			
		||||
                let option_name = format!("--{}", key);
 | 
			
		||||
                let option_name = if key.len() == 1 {
 | 
			
		||||
                    format!("-{}", key)
 | 
			
		||||
                } else {
 | 
			
		||||
                    format!("--{}", key)
 | 
			
		||||
                };
 | 
			
		||||
                args_owned.push(option_name);
 | 
			
		||||
                args_owned.push(value.clone());
 | 
			
		||||
                if !value.is_empty() {
 | 
			
		||||
                    args_owned.push(value.clone());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            args_owned.push(container_id.clone());
 | 
			
		||||
@@ -380,8 +410,19 @@ impl Builder {
 | 
			
		||||
    /// # Returns
 | 
			
		||||
    ///
 | 
			
		||||
    /// * `Result<CommandResult, BuildahError>` - Command result or error
 | 
			
		||||
    pub fn set_entrypoint(&self, entrypoint: &str) -> Result<CommandResult, BuildahError> {
 | 
			
		||||
    pub fn set_entrypoint(&self, entrypoint: Vec<String>) -> Result<CommandResult, BuildahError> {
 | 
			
		||||
        if let Some(container_id) = &self.container_id {
 | 
			
		||||
            // Serialize the entrypoint vector to a JSON string
 | 
			
		||||
            let entrypoint_json = match serde_json::to_string(&entrypoint) {
 | 
			
		||||
                Ok(json) => json,
 | 
			
		||||
                Err(e) => {
 | 
			
		||||
                    return Err(BuildahError::JsonParseError(format!(
 | 
			
		||||
                        "Failed to serialize entrypoint to JSON: {}",
 | 
			
		||||
                        e
 | 
			
		||||
                    )));
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            // Save the current debug flag
 | 
			
		||||
            let previous_debug = thread_local_debug();
 | 
			
		||||
 | 
			
		||||
@@ -390,7 +431,7 @@ impl Builder {
 | 
			
		||||
 | 
			
		||||
            // Execute the command
 | 
			
		||||
            let result =
 | 
			
		||||
                execute_buildah_command(&["config", "--entrypoint", entrypoint, container_id]);
 | 
			
		||||
                execute_buildah_command(&["config", "--entrypoint", &entrypoint_json, container_id]);
 | 
			
		||||
 | 
			
		||||
            // Restore the previous debug flag
 | 
			
		||||
            set_thread_local_debug(previous_debug);
 | 
			
		||||
@@ -410,8 +451,19 @@ impl Builder {
 | 
			
		||||
    /// # Returns
 | 
			
		||||
    ///
 | 
			
		||||
    /// * `Result<CommandResult, BuildahError>` - Command result or error
 | 
			
		||||
    pub fn set_cmd(&self, cmd: &str) -> Result<CommandResult, BuildahError> {
 | 
			
		||||
    pub fn set_cmd(&self, cmd: Vec<String>) -> Result<CommandResult, BuildahError> {
 | 
			
		||||
        if let Some(container_id) = &self.container_id {
 | 
			
		||||
            // Serialize the cmd vector to a JSON string
 | 
			
		||||
            let cmd_json = match serde_json::to_string(&cmd) {
 | 
			
		||||
                Ok(json) => json,
 | 
			
		||||
                Err(e) => {
 | 
			
		||||
                    return Err(BuildahError::JsonParseError(format!(
 | 
			
		||||
                        "Failed to serialize cmd to JSON: {}",
 | 
			
		||||
                        e
 | 
			
		||||
                    )));
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            // Save the current debug flag
 | 
			
		||||
            let previous_debug = thread_local_debug();
 | 
			
		||||
 | 
			
		||||
@@ -419,7 +471,7 @@ impl Builder {
 | 
			
		||||
            set_thread_local_debug(self.debug);
 | 
			
		||||
 | 
			
		||||
            // Execute the command
 | 
			
		||||
            let result = execute_buildah_command(&["config", "--cmd", cmd, container_id]);
 | 
			
		||||
            let result = execute_buildah_command(&["config", "--cmd", &cmd_json, container_id]);
 | 
			
		||||
 | 
			
		||||
            // Restore the previous debug flag
 | 
			
		||||
            set_thread_local_debug(previous_debug);
 | 
			
		||||
 
 | 
			
		||||
@@ -55,34 +55,44 @@ impl ContentOperations {
 | 
			
		||||
    ///
 | 
			
		||||
    /// * `Result<String, BuildahError>` - File content or error
 | 
			
		||||
    pub fn read_content(container_id: &str, source_path: &str) -> Result<String, BuildahError> {
 | 
			
		||||
        // Create a temporary file
 | 
			
		||||
        // Create a temporary file to store the content from the container.
 | 
			
		||||
        let temp_file = NamedTempFile::new()
 | 
			
		||||
            .map_err(|e| BuildahError::Other(format!("Failed to create temporary file: {}", e)))?;
 | 
			
		||||
 | 
			
		||||
        let temp_path = temp_file.path().to_string_lossy().to_string();
 | 
			
		||||
 | 
			
		||||
        // Copy the file from the container to the temporary file
 | 
			
		||||
        // Use mount to access the container's filesystem
 | 
			
		||||
        let mount_result = execute_buildah_command(&["mount", container_id])?;
 | 
			
		||||
        let mount_point = mount_result.stdout.trim();
 | 
			
		||||
        // In rootless mode, `buildah mount` must run inside `buildah unshare`.
 | 
			
		||||
        // We create a script to mount, copy the file, and unmount, all within the unshare session.
 | 
			
		||||
        let script = format!(
 | 
			
		||||
            r#"
 | 
			
		||||
                set -e
 | 
			
		||||
                mount_point=$(buildah mount '{container_id}')
 | 
			
		||||
                if [ -z "$mount_point" ]; then
 | 
			
		||||
                    echo "Error: Failed to mount container '{container_id}'." >&2
 | 
			
		||||
                    exit 1
 | 
			
		||||
                fi
 | 
			
		||||
                trap 'buildah umount '{container_id}'' EXIT
 | 
			
		||||
                cp "${{mount_point}}{source_path}" '{temp_path}'
 | 
			
		||||
            "#,
 | 
			
		||||
            container_id = container_id,
 | 
			
		||||
            source_path = source_path,
 | 
			
		||||
            temp_path = &temp_path
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Construct the full path to the file in the container
 | 
			
		||||
        let full_source_path = format!("{}{}", mount_point, source_path);
 | 
			
		||||
        let result = execute_buildah_command(&["unshare", "sh", "-c", &script])?;
 | 
			
		||||
 | 
			
		||||
        // Copy the file from the mounted container to the temporary file
 | 
			
		||||
        execute_buildah_command(&["copy", container_id, &full_source_path, &temp_path])?;
 | 
			
		||||
 | 
			
		||||
        // Unmount the container
 | 
			
		||||
        execute_buildah_command(&["umount", container_id])?;
 | 
			
		||||
 | 
			
		||||
        // Read the content from the temporary file
 | 
			
		||||
        let mut file = File::open(temp_file.path())
 | 
			
		||||
            .map_err(|e| BuildahError::Other(format!("Failed to open temporary file: {}", e)))?;
 | 
			
		||||
        if !result.success {
 | 
			
		||||
            return Err(BuildahError::Other(format!(
 | 
			
		||||
                "Failed to execute read_content script in unshare session: {}",
 | 
			
		||||
                result.stderr
 | 
			
		||||
            )));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // The script has copied the file content to our temporary file.
 | 
			
		||||
        // Now, we read it.
 | 
			
		||||
        let mut content = String::new();
 | 
			
		||||
        file.read_to_string(&mut content).map_err(|e| {
 | 
			
		||||
            BuildahError::Other(format!("Failed to read from temporary file: {}", e))
 | 
			
		||||
        })?;
 | 
			
		||||
        File::open(&temp_path)
 | 
			
		||||
            .and_then(|mut f| f.read_to_string(&mut content))
 | 
			
		||||
            .map_err(|e| BuildahError::Other(format!("Failed to read from temporary file: {}", e)))?;
 | 
			
		||||
 | 
			
		||||
        Ok(content)
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -98,20 +98,42 @@ fn bah_error_to_rhai_error<T>(result: Result<T, BuildahError>) -> Result<T, Box<
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Helper function to convert Rhai Map to Rust HashMap
 | 
			
		||||
fn convert_map_to_hashmap(options: Map) -> Result<HashMap<String, String>, Box<EvalAltResult>> {
 | 
			
		||||
    let mut config_options = HashMap::<String, String>::new();
 | 
			
		||||
// Helper function to convert Rhai Array of pairs to a Vec of tuples
 | 
			
		||||
fn convert_array_to_vec(
 | 
			
		||||
    options: Array,
 | 
			
		||||
) -> Result<Vec<(String, String)>, Box<EvalAltResult>> {
 | 
			
		||||
    let mut config_options: Vec<(String, String)> = Vec::new();
 | 
			
		||||
 | 
			
		||||
    for (key, value) in options.iter() {
 | 
			
		||||
        if let Ok(value_str) = value.clone().into_string() {
 | 
			
		||||
            // Convert SmartString to String
 | 
			
		||||
            config_options.insert(key.to_string(), value_str);
 | 
			
		||||
        } else {
 | 
			
		||||
    for option_pair_dynamic in options {
 | 
			
		||||
        let pair = option_pair_dynamic.into_array().map_err(|_e| {
 | 
			
		||||
            Box::new(EvalAltResult::ErrorRuntime(
 | 
			
		||||
                "Each option must be an array of [key, value]".into(),
 | 
			
		||||
                rhai::Position::NONE,
 | 
			
		||||
            ))
 | 
			
		||||
        })?;
 | 
			
		||||
 | 
			
		||||
        if pair.len() != 2 {
 | 
			
		||||
            return Err(Box::new(EvalAltResult::ErrorRuntime(
 | 
			
		||||
                format!("Option '{}' must be a string", key).into(),
 | 
			
		||||
                "Each option must be an array of [key, value] with 2 elements".into(),
 | 
			
		||||
                rhai::Position::NONE,
 | 
			
		||||
            )));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let key = pair[0].clone().into_string().map_err(|_e| {
 | 
			
		||||
            Box::new(EvalAltResult::ErrorRuntime(
 | 
			
		||||
                "Option key must be a string".into(),
 | 
			
		||||
                rhai::Position::NONE,
 | 
			
		||||
            ))
 | 
			
		||||
        })?;
 | 
			
		||||
 | 
			
		||||
        let value = pair[1].clone().into_string().map_err(|_e| {
 | 
			
		||||
            Box::new(EvalAltResult::ErrorRuntime(
 | 
			
		||||
                "Option value must be a string".into(),
 | 
			
		||||
                rhai::Position::NONE,
 | 
			
		||||
            ))
 | 
			
		||||
        })?;
 | 
			
		||||
 | 
			
		||||
        config_options.push((key, value));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(config_options)
 | 
			
		||||
@@ -157,8 +179,10 @@ pub fn builder_add(
 | 
			
		||||
pub fn builder_commit(
 | 
			
		||||
    builder: &mut Builder,
 | 
			
		||||
    image_name: &str,
 | 
			
		||||
    options: Array,
 | 
			
		||||
) -> Result<CommandResult, Box<EvalAltResult>> {
 | 
			
		||||
    bah_error_to_rhai_error(builder.commit(image_name))
 | 
			
		||||
    let commit_options = convert_array_to_vec(options)?;
 | 
			
		||||
    bah_error_to_rhai_error(builder.commit(image_name, Some(commit_options)))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn builder_remove(builder: &mut Builder) -> Result<CommandResult, Box<EvalAltResult>> {
 | 
			
		||||
@@ -167,27 +191,34 @@ pub fn builder_remove(builder: &mut Builder) -> Result<CommandResult, Box<EvalAl
 | 
			
		||||
 | 
			
		||||
pub fn builder_config(
 | 
			
		||||
    builder: &mut Builder,
 | 
			
		||||
    options: Map,
 | 
			
		||||
    options: Array,
 | 
			
		||||
) -> Result<CommandResult, Box<EvalAltResult>> {
 | 
			
		||||
    // Convert Rhai Map to Rust HashMap
 | 
			
		||||
    let config_options = convert_map_to_hashmap(options)?;
 | 
			
		||||
    let config_options = convert_array_to_vec(options)?;
 | 
			
		||||
    bah_error_to_rhai_error(builder.config(config_options))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Set the entrypoint for the container
 | 
			
		||||
pub fn builder_set_entrypoint(
 | 
			
		||||
    builder: &mut Builder,
 | 
			
		||||
    entrypoint: &str,
 | 
			
		||||
    entrypoint: Array,
 | 
			
		||||
) -> Result<CommandResult, Box<EvalAltResult>> {
 | 
			
		||||
    bah_error_to_rhai_error(builder.set_entrypoint(entrypoint))
 | 
			
		||||
    let entrypoint_vec: Vec<String> = entrypoint
 | 
			
		||||
        .into_iter()
 | 
			
		||||
        .map(|item| item.into_string().unwrap_or_default())
 | 
			
		||||
        .collect();
 | 
			
		||||
    bah_error_to_rhai_error(builder.set_entrypoint(entrypoint_vec))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Set the default command for the container
 | 
			
		||||
pub fn builder_set_cmd(
 | 
			
		||||
    builder: &mut Builder,
 | 
			
		||||
    cmd: &str,
 | 
			
		||||
    cmd: Array,
 | 
			
		||||
) -> Result<CommandResult, Box<EvalAltResult>> {
 | 
			
		||||
    bah_error_to_rhai_error(builder.set_cmd(cmd))
 | 
			
		||||
    let cmd_vec: Vec<String> = cmd
 | 
			
		||||
        .into_iter()
 | 
			
		||||
        .map(|item| item.into_string().unwrap_or_default())
 | 
			
		||||
        .collect();
 | 
			
		||||
    bah_error_to_rhai_error(builder.set_cmd(cmd_vec))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Write content to a file in the container
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user