sal/buildah_debug_implementation_plan.md
2025-04-05 11:03:58 +02:00

6.7 KiB

Buildah Debug Implementation Plan

Current State

  1. The Builder struct already has a debug field and methods to get and set it (debug() and set_debug()).
  2. There's a thread-local DEBUG variable with functions to get and set it.
  3. The execute_buildah_command function checks the thread-local debug flag and outputs some debug information.
  4. There's an unused execute_buildah_command_with_debug function that takes a Builder reference.

Requirements

  1. Use only the Builder's debug flag, not the thread-local debug flag.
  2. When debug is true, output stdout/stderr regardless of whether the command succeeds or fails.
  3. If debug is false but there's an error, still output all information.

Implementation Plan

1. Keep the Existing Thread-Local DEBUG Variable

We'll keep the existing thread-local DEBUG variable that's already in the code:

// Thread-local storage for debug flag
thread_local! {
    static DEBUG: std::cell::RefCell<bool> = std::cell::RefCell::new(false);
}

2. Modify the Builder Methods to Set/Clear the Thread-Local Debug Flag

We'll modify each Builder method to set the thread-local debug flag from the Builder's debug flag before calling execute_buildah_command and restore it afterward:

pub fn run(&self, command: &str) -> Result<CommandResult, BuildahError> {
    if let Some(container_id) = &self.container_id {
        // Save the current debug flag
        let previous_debug = thread_local_debug();
        
        // Set the thread-local debug flag from the Builder's debug flag
        set_thread_local_debug(self.debug);
        
        // Execute the command
        let result = execute_buildah_command(&["run", container_id, "sh", "-c", command]);
        
        // Restore the previous debug flag
        set_thread_local_debug(previous_debug);
        
        result
    } else {
        Err(BuildahError::Other("No container ID available".to_string()))
    }
}

3. Keep the Existing execute_buildah_command Function

The existing execute_buildah_command function already checks the thread-local debug flag, so we don't need to modify it:

pub fn execute_buildah_command(args: &[&str]) -> Result<CommandResult, BuildahError> {
    // Get the debug flag from thread-local storage
    let debug = thread_local_debug();
    
    if debug {
        println!("Executing buildah command: buildah {}", args.join(" "));
    }
    
    // ... rest of the function ...
}

4. Update the execute_buildah_command Function to Output stdout/stderr

We need to modify the execute_buildah_command function to output stdout/stderr when debug is true, regardless of success/failure:

pub fn execute_buildah_command(args: &[&str]) -> Result<CommandResult, BuildahError> {
    // Get the debug flag from thread-local storage
    let debug = thread_local_debug();
    
    if debug {
        println!("Executing buildah command: buildah {}", args.join(" "));
    }
    
    let output = Command::new("buildah")
        .args(args)
        .output();
    
    match output {
        Ok(output) => {
            let stdout = String::from_utf8_lossy(&output.stdout).to_string();
            let stderr = String::from_utf8_lossy(&output.stderr).to_string();
            
            let result = CommandResult {
                stdout,
                stderr,
                success: output.status.success(),
                code: output.status.code().unwrap_or(-1),
            };
            
            // Always output stdout/stderr when debug is true
            if debug {
                if !result.stdout.is_empty() {
                    println!("Command stdout: {}", result.stdout);
                }
                
                if !result.stderr.is_empty() {
                    println!("Command stderr: {}", result.stderr);
                }
                
                if result.success {
                    println!("Command succeeded with code {}", result.code);
                } else {
                    println!("Command failed with code {}", result.code);
                }
            }
            
            if result.success {
                Ok(result)
            } else {
                // If command failed and debug is false, output stderr
                if !debug {
                    println!("Command failed with code {}: {}", result.code, result.stderr.trim());
                }
                Err(BuildahError::CommandFailed(format!("Command failed with code {}: {}", 
                    result.code, result.stderr.trim())))
            }
        },
        Err(e) => {
            // Always output error information
            println!("Command execution failed: {}", e);
            Err(BuildahError::CommandExecutionFailed(e))
        }
    }
}

5. Handle Static Methods

For static methods, we'll just call execute_buildah_command directly, which will use the thread-local debug flag:

pub fn images() -> Result<Vec<Image>, BuildahError> {
    let result = execute_buildah_command(&["images", "--json"])?;
    // Rest of the method...
}

If we want to support debugging in static methods, we could add an optional debug parameter:

pub fn images(debug: bool) -> Result<Vec<Image>, BuildahError> {
    // Save the current debug flag
    let previous_debug = thread_local_debug();
    
    // Set the thread-local debug flag
    set_thread_local_debug(debug);
    
    // Execute the command
    let result = execute_buildah_command(&["images", "--json"]);
    
    // Restore the previous debug flag
    set_thread_local_debug(previous_debug);
    
    // Process the result
    match result {
        Ok(cmd_result) => {
            // Parse JSON and return images...
        },
        Err(e) => Err(e),
    }
}

// Backward compatibility method
pub fn images() -> Result<Vec<Image>, BuildahError> {
    Self::images(false)
}

6. Update Rhai Bindings

We still need to update the Rhai bindings to expose the debug functionality:

// Add a debug getter and setter
engine.register_get("debug", |builder: &mut Builder| builder.debug());
engine.register_set("debug", |builder: &mut Builder, debug: bool| { builder.set_debug(debug); });

7. Remove Unused Code

We can remove the unused execute_buildah_command_with_debug function.

Implementation Flow

  1. Modify the execute_buildah_command function to output stdout/stderr when debug is true
  2. Update all Builder methods to set/restore the thread-local debug flag
  3. Update static methods to optionally accept a debug parameter
  4. Update Rhai bindings to expose the debug functionality
  5. Remove unused code

Benefits

  1. Minimal changes to the existing codebase
  2. No changes to function signatures
  3. Backward compatibility with existing code
  4. Improved debugging capabilities