diff --git a/common/src/command.rs b/common/src/command.rs index 5d37bee..48c6315 100644 --- a/common/src/command.rs +++ b/common/src/command.rs @@ -21,14 +21,16 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // -use std::{fmt::{Display, Write}, process::{Command, Output, CommandArgs}, ffi::OsStr}; +use std::fmt::{Display, Write}; +use std::process::{Command, Output, CommandArgs}; +use std::ffi::OsStr; use thiserror::Error; pub trait CommandExtTrait { - /// Execute this command and return: + /// Execute command via output() and return: /// /// 1. An IO Error if the command could not be run - /// 2. An Execution Error if the Command was not successfull + /// 2. An ExecutionError if the Command was not successfull /// 3. The [Output] of the Command if the command was executed successfully /// /// Attaches all args to the resulting error @@ -44,7 +46,9 @@ impl CommandExtTrait for Command { } } -pub fn handle_output(maybe_output: Result, args: CommandArgs) -> Result { +pub fn handle_output( + maybe_output: Result, args: CommandArgs +) -> Result { let out = maybe_output.map_err(ProcessError::IO); let error: ProcessError = match out { @@ -57,21 +61,25 @@ pub fn handle_output(maybe_output: Result, args: Command } Err(error) => error, }; - - Err(CommandError { - base: error, - args: args - .flat_map(OsStr::to_str) - .map(ToOwned::to_owned) - .collect(), - }) + // Provide caller arguments in addition to the error + Err( + CommandError { + base: error, + args: args + .flat_map(OsStr::to_str) + .map(ToOwned::to_owned) + .collect(), + } + ) } #[derive(Debug, Error)] pub enum ProcessError { + // The command could not be called #[error(transparent)] IO(#[from] std::io::Error), - // The Command terminated correctly but with unwanted results (e.g. wrong return code) + + // The Command could be called but has a non zero exit status #[error("The process failed with status {}", .0.status)] ExecutionError(std::process::Output), } @@ -95,37 +103,20 @@ impl CommandError { base, } } - - pub fn with(&mut self, arg: String) -> &mut Self { - self.args.push(arg); - self - } } impl Display for CommandError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_char('"')?; for arg in self.args.iter() { f.write_str(arg)?; f.write_char(' ')?; } + f.write_char('"')?; + f.write_char(':')?; + f.write_char(' ')?; + f.write_str(format!("{:?}", self.base).as_str())?; + f.write_char(' ')?; std::fmt::Display::fmt(&self.base, f) } } - -impl From for CommandError { - fn from(value: std::process::Output) -> Self { - Self { - base: value.into(), - args: Default::default(), - } - } -} - -impl From for CommandError { - fn from(value: ProcessError) -> Self { - Self { - base: value, - args: Default::default(), - } - } -} diff --git a/podman-pilot/Cargo.toml b/podman-pilot/Cargo.toml index aab3190..4047d76 100644 --- a/podman-pilot/Cargo.toml +++ b/podman-pilot/Cargo.toml @@ -14,4 +14,5 @@ spinoff = { version = "0.7" } lazy_static = { version = "1.4" } serde = { version = "1.0", features = ["derive"]} serde_yaml = { version = "0.9" } +regex = { version = "1.9" } flakes = { version = "3.0.1", path = "../common" } diff --git a/podman-pilot/src/podman.rs b/podman-pilot/src/podman.rs index 546a244..3a956d5 100644 --- a/podman-pilot/src/podman.rs +++ b/podman-pilot/src/podman.rs @@ -21,25 +21,28 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // +use crate::defaults; +use crate::config::{RuntimeSection, config}; + use flakes::user::{User, chmod, mkdir}; use flakes::lookup::Lookup; use flakes::io::IO; -use spinoff::{Spinner, spinners, Color}; +use flakes::error::FlakeError; +use flakes::command::{CommandError, CommandExtTrait}; +use flakes::config::get_podman_ids_dir; + use std::path::Path; -use std::process::{Command, Stdio}; +use std::process::{Command, Output, Stdio}; use std::env; use std::fs; -use crate::config::{RuntimeSection, config}; -use flakes::error::FlakeError; -use flakes::command::{CommandError, CommandExtTrait}; -use tempfile::tempfile; use std::io::{Write, Read}; use std::fs::File; use std::io::Seek; use std::io::SeekFrom; -use flakes::config::get_podman_ids_dir; -use crate::defaults; +use spinoff::{Spinner, spinners, Color}; +use tempfile::tempfile; +use regex::Regex; pub fn create( program_name: &String @@ -143,9 +146,7 @@ pub fn create( if Path::new(&container_cid_file).exists() && gc_cid_file(&container_cid_file, runas)? && (resume || attach) { // resume or attach mode is active and container exists // report ID value and its ID file name - let cid = fs::read_to_string(&container_cid_file)?; - return Ok((cid, container_cid_file)); } @@ -165,7 +166,7 @@ pub fn create( if !resume { app.arg("--rm"); } - app.arg("-ti"); + app.arg("--tty").arg("--interactive"); } // setup container name to use @@ -223,11 +224,32 @@ fn run_podman_creation(mut app: Command) -> Result { /*! Create and provision container prior start !*/ - let output = app.perform()?; + let RuntimeSection { runas, resume, .. } = config().runtime(); - let cid = String::from_utf8_lossy(&output.stdout).trim_end_matches('\n').to_owned(); + let output: Output = match app.perform() { + Ok(output) => { + output + } + Err(error) => { + if resume { + // Cleanup potentially left over container instance from an + // inconsistent state, e.g powerfail + if Lookup::is_debug() { + debug!("Force cleanup container instance..."); + } + let error_pattern = Regex::new(r"in use by (.*)\.").unwrap(); + if let Some(captures) = error_pattern.captures(&format!("{:?}", error.base)) { + let cid = captures.get(1).unwrap().as_str(); + call_instance("rm_force", cid, "none", runas)?; + } + app.perform()? + } else { + return Err(FlakeError::CommandError(error)) + } + } + }; - let runas = config().runtime().runas; + let cid = String::from_utf8_lossy(&output.stdout).trim_end_matches('\n').to_owned(); let is_delta_container = config().container.base_container.is_some(); let has_includes = !config().tars().is_empty() || !config().paths().is_empty(); @@ -357,11 +379,15 @@ pub fn call_instance( let RuntimeSection { resume, .. } = config().runtime(); let mut call = user.run("podman"); - if action == "create" || action == "rm" { + if action == "rm" || action == "rm_force" { call.stderr(Stdio::null()); call.stdout(Stdio::null()); } - call.arg(action); + if action == "rm_force" { + call.arg("rm").arg("--force"); + } else { + call.arg(action); + } if action == "exec" { call.arg("--interactive"); call.arg("--tty");