From dc6206c55e7b9bc21ecc0fbb89a8267f414c97f6 Mon Sep 17 00:00:00 2001 From: Jack Wills <32690432+mrjackwills@users.noreply.github.com> Date: Tue, 27 Jan 2026 16:32:20 +0000 Subject: [PATCH] fix: DOCKER_HOST env priority, config.toml fix --- example_config/example.config.jsonc | 4 +- example_config/example.config.toml | 4 +- src/config/config.toml | 4 +- src/config/parse_config_file.rs | 4 + src/main.rs | 34 +++++--- src/ui/draw_blocks/error.rs | 85 +++++++++++++++++-- ...rror_docker_connect_error_custom_host.snap | 13 +++ src/ui/gui_state.rs | 4 +- src/ui/mod.rs | 12 ++- 9 files changed, 132 insertions(+), 32 deletions(-) create mode 100644 src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__error__tests__draw_blocks_error_docker_connect_error_custom_host.snap diff --git a/example_config/example.config.jsonc b/example_config/example.config.jsonc index 3b1f8fd..2ea607e 100644 --- a/example_config/example.config.jsonc +++ b/example_config/example.config.jsonc @@ -18,8 +18,8 @@ "show_timestamp": true, // Don't draw gui - for debugging - mostly pointless "gui": true, - // Docker host location - "host": "/var/run/docker.sock", + // Docker host location. Will take priority over a DOCKER_HOST env. + // "host": "/var/run/docker.sock", // Display the timestamp in a custom format, if given option is invalid, it will default to %Y-%m-%dT%H:%M:%S.%8f -> 2025-02-18T12:34:56.01234567 // *Should* accept any valid strftime string up to 32 chars, see https://strftime.org/ "timestamp_format": "%Y-%m-%dT%H:%M:%S.%8f", diff --git a/example_config/example.config.toml b/example_config/example.config.toml index 8d5f1db..8547375 100644 --- a/example_config/example.config.toml +++ b/example_config/example.config.toml @@ -24,8 +24,8 @@ show_timestamp = true # Don't draw gui - for debugging - mostly pointless gui = true -# Docker host location -host = "/var/run/docker.sock" +# Docker host location. Will take priority over a DOCKER_HOST env. +# host = "/var/run/docker.sock" # Display the container logs timestamp with a given timezone, if timezone is unknown, defaults to UTC timezone = "Etc/UTC" diff --git a/src/config/config.toml b/src/config/config.toml index 8d5f1db..8547375 100644 --- a/src/config/config.toml +++ b/src/config/config.toml @@ -24,8 +24,8 @@ show_timestamp = true # Don't draw gui - for debugging - mostly pointless gui = true -# Docker host location -host = "/var/run/docker.sock" +# Docker host location. Will take priority over a DOCKER_HOST env. +# host = "/var/run/docker.sock" # Display the container logs timestamp with a given timezone, if timezone is unknown, defaults to UTC timezone = "Etc/UTC" diff --git a/src/config/parse_config_file.rs b/src/config/parse_config_file.rs index d61991a..0874b25 100644 --- a/src/config/parse_config_file.rs +++ b/src/config/parse_config_file.rs @@ -79,6 +79,8 @@ pub struct ConfigFile { pub use_cli: Option, } + + impl ConfigFile { /// Attempt to create a config.toml file, will attempt to recursively create the directories as well fn crate_config_file(in_container: bool) -> Result<(), AppError> { @@ -119,6 +121,8 @@ impl ConfigFile { toml::from_str::(input).map_err(|i| AppError::Parse(i.message().to_owned())) } } + + // TODO if on windows, omit the docker_host? } /// Read the config file path to string, then attempt to parse diff --git a/src/main.rs b/src/main.rs index efdf182..4a42ce1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -42,12 +42,16 @@ fn setup_tracing() { tracing_subscriber::fmt().with_max_level(Level::INFO).init(); } -/// Read the optional docker_host path, the cli args take priority over the DOCKER_HOST env +/// Read the optional docker_host path, the DOCKER_HOST env take priority over cli or config +/// Bollard will use DOCKER_HOST env, so might be pointless here, although it will fix it's priority over any config setting fn read_docker_host(config: &Config) -> Option { - config - .host - .as_ref() - .map_or_else(|| std::env::var(DOCKER_HOST).ok(), |x| Some(x.clone())) + if let Ok(env) = std::env::var(DOCKER_HOST) + && !env.trim().is_empty() + { + Some(env) + } else { + config.host.as_ref().cloned() + } } /// Create docker daemon handler, and only spawn up the docker data handler if a ping returns non-error @@ -59,11 +63,11 @@ async fn docker_init( ) { let host = read_docker_host(&app_data.lock().config); - let connection = host.map_or_else(Docker::connect_with_socket_defaults, |host| { - Docker::connect_with_socket(&host, 120, API_DEFAULT_VERSION) - }); - - if let Ok(docker) = connection + if let Ok(docker) = host + .as_ref() + .map_or_else(Docker::connect_with_defaults, |host| { + Docker::connect_with_socket(host, 120, API_DEFAULT_VERSION) + }) && docker.ping().await.is_ok() { tokio::spawn(DockerData::start( @@ -73,11 +77,13 @@ async fn docker_init( docker_tx, Arc::clone(gui_state), )); - return; + } else { + app_data.lock().set_error( + AppError::DockerConnect, + gui_state, + Status::DockerConnect(host), + ); } - app_data - .lock() - .set_error(AppError::DockerConnect, gui_state, Status::DockerConnect); } /// Create data for, and then spawn a tokio thread, for the input handler diff --git a/src/ui/draw_blocks/error.rs b/src/ui/draw_blocks/error.rs index 0d69f12..c22d94f 100644 --- a/src/ui/draw_blocks/error.rs +++ b/src/ui/draw_blocks/error.rs @@ -22,6 +22,7 @@ pub fn draw( colors: AppColors, error: &AppError, f: &mut Frame, + host: Option, keymap: &Keymap, seconds: Option, ) { @@ -32,11 +33,17 @@ pub fn draw( .borders(Borders::ALL); let to_push = if matches!(error, AppError::DockerConnect) { + let s = if let Some(host) = host { + format!(" @ \"{host}\"") + } else { + String::new() + }; format!( - "\n\n {}::v{} closing in {:02} seconds", + "{}\n\n {}::v{} closing in {:02} seconds", + s, NAME, VERSION, - seconds.unwrap_or(5) + seconds.unwrap_or(5), ) } else { let clear_text = if keymap.clear == Keymap::new().clear { @@ -107,6 +114,37 @@ mod tests { /// Test that the error popup is centered, red background, white border, white text, and displays the correct text fn test_draw_blocks_error_docker_connect_error() { let mut setup = test_setup(46, 9, true, true); + setup + .terminal + .draw(|f| { + super::draw( + AppColors::new(), + &AppError::DockerConnect, + f, + None, + &Keymap::new(), + Some(4), + ); + }) + .unwrap(); + assert_snapshot!(setup.terminal.backend()); + for (row_index, result_row) in get_result(&setup) { + for (result_cell_index, result_cell) in result_row.iter().enumerate() { + if let (0 | 8, _) = (row_index, result_cell_index) { + assert_eq!(result_cell.bg, Color::Reset); + assert_eq!(result_cell.fg, Color::Reset); + } else { + assert_eq!(result_cell.bg, Color::Red); + assert_eq!(result_cell.fg, Color::White); + } + } + } + } + + #[test] + /// Test that the error popup is centered, red background, white border, white text, and displays the correct text with the custom docker host address + fn test_draw_blocks_error_docker_connect_error_custom_host() { + let mut setup = test_setup(46, 9, true, true); setup .terminal @@ -115,6 +153,7 @@ mod tests { AppColors::new(), &AppError::DockerConnect, f, + Some("/test/host.sock".to_owned()), &Keymap::new(), Some(4), ); @@ -146,6 +185,8 @@ mod tests { AppColors::new(), &AppError::DockerExec, f, + // TODO test me + None, &Keymap::new(), Some(4), ); @@ -183,7 +224,15 @@ mod tests { setup .terminal .draw(|f| { - super::draw(colors, &AppError::DockerExec, f, &Keymap::new(), Some(4)); + // TODO test me + super::draw( + colors, + &AppError::DockerExec, + f, + None, + &Keymap::new(), + Some(4), + ); }) .unwrap(); @@ -218,7 +267,15 @@ mod tests { setup .terminal .draw(|f| { - super::draw(AppColors::new(), &AppError::DockerExec, f, &keymap, None); + // TODO test me + super::draw( + AppColors::new(), + &AppError::DockerExec, + f, + None, + &keymap, + None, + ); }) .unwrap(); assert_snapshot!(setup.terminal.backend()); @@ -235,7 +292,15 @@ mod tests { setup .terminal .draw(|f| { - super::draw(AppColors::new(), &AppError::DockerExec, f, &keymap, None); + // TODO test me + super::draw( + AppColors::new(), + &AppError::DockerExec, + f, + None, + &keymap, + None, + ); }) .unwrap(); assert_snapshot!(setup.terminal.backend()); @@ -252,7 +317,15 @@ mod tests { setup .terminal .draw(|f| { - super::draw(AppColors::new(), &AppError::DockerExec, f, &keymap, None); + // TODO test me + super::draw( + AppColors::new(), + &AppError::DockerExec, + f, + None, + &keymap, + None, + ); }) .unwrap(); assert_snapshot!(setup.terminal.backend()); diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__error__tests__draw_blocks_error_docker_connect_error_custom_host.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__error__tests__draw_blocks_error_docker_connect_error_custom_host.snap new file mode 100644 index 0000000..0cc412b --- /dev/null +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__error__tests__draw_blocks_error_docker_connect_error_custom_host.snap @@ -0,0 +1,13 @@ +--- +source: src/ui/draw_blocks/error.rs +expression: setup.terminal.backend() +--- +" " +"╭────────────────── Error ───────────────────╮" +"│ │" +"│Unable to access docker daemon @ "/test/host│" +"│ │" +"│ oxker::v0.00.000 closing in 04 seconds │" +"│ │" +"╰────────────────────────────────────────────╯" +" " diff --git a/src/ui/gui_state.rs b/src/ui/gui_state.rs index 9b598c4..6addccf 100644 --- a/src/ui/gui_state.rs +++ b/src/ui/gui_state.rs @@ -160,10 +160,10 @@ const FRAMES_LEN: u8 = 9; /// The application gui state can be in multiple of these four states at the same time /// Various functions (e.g input handler), operate differently depending upon current Status -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +#[derive(Debug, Clone, Hash, PartialEq, Eq)] pub enum Status { DeleteConfirm, - DockerConnect, + DockerConnect(Option), Error, Exec, Filter, diff --git a/src/ui/mod.rs b/src/ui/mod.rs index c1182c3..c11118a 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -132,7 +132,7 @@ impl Ui { } /// Draw the the error message ui, for 5 seconds, with a countdown - fn err_loop(&mut self) -> Result<(), AppError> { + fn err_loop(&mut self, host: Option) -> Result<(), AppError> { let mut seconds = 5; let colors = self.app_data.lock().config.app_colors; let keymap = self.app_data.lock().config.keymap.clone(); @@ -155,6 +155,7 @@ impl Ui { colors, &AppError::DockerConnect, f, + host.clone(), &keymap, Some(seconds), ); @@ -273,8 +274,11 @@ impl Ui { /// Draw either the Error, or main oxker ui, to the terminal async fn draw_ui(&mut self) -> Result<(), AppError> { let status = self.gui_state.lock().get_status(); - if status.contains(&Status::DockerConnect) { - self.err_loop()?; + if let Some(Status::DockerConnect(msg)) = status + .iter() + .find(|s| matches!(s, Status::DockerConnect(_))) + { + self.err_loop(msg.clone())?; } else { self.gui_loop().await?; } @@ -463,6 +467,6 @@ fn draw_frame( } if let Some(error) = fd.has_error.as_ref() { - draw_blocks::error::draw(colors, error, f, keymap, None); + draw_blocks::error::draw(colors, error, f, None, keymap, None); } }