From 67c7ce30ff9cdf1fd4cda60d002cba3c9e43e815 Mon Sep 17 00:00:00 2001 From: Nick Lanham Date: Sat, 28 May 2022 13:18:47 -0700 Subject: [PATCH 1/3] use config specified kubectl binary --- src/command/exec.rs | 24 +++++++++++++++++++----- src/command/portforwards.rs | 27 +++++++++++++++++++++------ src/config/click.rs | 4 ++++ 3 files changed, 44 insertions(+), 11 deletions(-) diff --git a/src/command/exec.rs b/src/command/exec.rs index 2267922..ea94bc0 100644 --- a/src/command/exec.rs +++ b/src/command/exec.rs @@ -47,6 +47,11 @@ fn do_exec( writer: &mut ClickWriter, ) -> Result<(), ClickError> { let ns = pod.namespace.as_ref().unwrap(); + let kubectl_binary = env + .click_config + .kubectl_binary + .as_deref() + .unwrap_or("kubectl"); if do_terminal { let terminal = if let Some(t) = term_opt { t @@ -57,7 +62,7 @@ fn do_exec( }; let mut targs: Vec<&str> = terminal.split_whitespace().collect(); let mut kubectl_args = vec![ - "kubectl", + kubectl_binary, "--namespace", ns, "--context", @@ -77,7 +82,7 @@ fn do_exec( duct::cmd(targs[0], &targs[1..]).start()?; Ok(()) } else { - let mut command = Command::new("kubectl"); + let mut command = Command::new(kubectl_binary); command .arg("--namespace") .arg(ns) @@ -103,9 +108,18 @@ fn do_exec( } Err(e) => { if let io::ErrorKind::NotFound = e.kind() { - Err(ClickError::CommandError( - "Could not find kubectl binary. Is it in your PATH?".to_string(), - )) + let msg = if kubectl_binary.starts_with('/') { + format!( + "Could not find kubectl binary: '{}'. Does it exist?", + kubectl_binary + ) + } else { + format!( + "Could not find kubectl binary: '{}'. Is it in your PATH?", + kubectl_binary + ) + }; + Err(ClickError::CommandError(msg)) } else { Err(ClickError::Io(e)) } diff --git a/src/command/portforwards.rs b/src/command/portforwards.rs index 33a9e32..8cdf309 100644 --- a/src/command/portforwards.rs +++ b/src/command/portforwards.rs @@ -102,7 +102,12 @@ Examples: return Err(ClickError::CommandError("No active context".to_string())); }; - match Command::new("kubectl") + let kubectl_binary = env + .click_config + .kubectl_binary + .as_deref() + .unwrap_or("kubectl"); + match Command::new(kubectl_binary) .arg("--namespace") .arg(ns) .arg("--context") @@ -151,11 +156,21 @@ Examples: } Err(e) => match e.kind() { io::ErrorKind::NotFound => { - writeln!( - stderr(), - "Could not find kubectl binary. Is it in your PATH?" - ) - .unwrap_or(()); + if kubectl_binary.starts_with('/') { + writeln!( + stderr(), + "Could not find kubectl binary '{}'. Does it exist?", + kubectl_binary + ) + .unwrap_or(()); + } else { + writeln!( + stderr(), + "Could not find kubectl binary '{}'. Is it in your PATH.", + kubectl_binary + ) + .unwrap_or(()); + } } _ => { write!( diff --git a/src/config/click.rs b/src/config/click.rs index 4e57e30..f855d7c 100644 --- a/src/config/click.rs +++ b/src/config/click.rs @@ -112,6 +112,7 @@ pub struct ClickConfig { pub context: Option, pub editor: Option, pub terminal: Option, + pub kubectl_binary: Option, #[serde(default = "EditMode::default")] pub editmode: EditMode, #[serde(default = "CompletionType::default")] @@ -137,6 +138,7 @@ impl Default for ClickConfig { context: None, editor: None, terminal: None, + kubectl_binary: None, editmode: EditMode::default(), completiontype: CompletionType::default(), aliases: vec![], @@ -196,6 +198,7 @@ pub mod tests { namespace: ns context: ctx editor: emacs +kubectl_binary: /opt/bin/kubectl terminal: alacritty -e editmode: Vi completiontype: List @@ -216,6 +219,7 @@ aliases: assert_eq!(config.context, Some("ctx".to_owned())); assert_eq!(config.editor, Some("emacs".to_owned())); assert_eq!(config.terminal, Some("alacritty -e".to_owned())); + assert_eq!(config.kubectl_binary, Some("/opt/bin/kubectl".to_owned())); assert_eq!(config.editmode, EditMode::Vi); assert_eq!(config.completiontype, CompletionType::List); assert_eq!(config.aliases.len(), 1); From 302d31378ade31b6ef095fcef48f9695e9aaf797 Mon Sep 17 00:00:00 2001 From: Nick Lanham Date: Sun, 29 May 2022 09:26:11 -0700 Subject: [PATCH 2/3] print full path to kubectl binary in env --- src/env.rs | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/env.rs b/src/env.rs index fe39471..a4c9615 100644 --- a/src/env.rs +++ b/src/env.rs @@ -473,6 +473,26 @@ impl Env { impl fmt::Display for Env { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let kubectl_binary = self + .click_config + .kubectl_binary + .as_deref() + .unwrap_or("kubectl"); + let kubectl_path = std::process::Command::new("which") + .arg(kubectl_binary) + .output() + .map(|output| { + std::str::from_utf8(&output.stdout) + .unwrap_or("Failed to parse 'which' output") + .to_string() + }) + .unwrap_or_else(|_| { + if kubectl_binary.starts_with('/') { + format!("{} not found. Does it exist?", kubectl_binary) + } else { + format!("{} not found. Is it in your PATH?", kubectl_binary) + } + }); write!( f, "Env {{ @@ -483,6 +503,7 @@ impl fmt::Display for Env { Edit Mode: {} Editor: {} Terminal: {} + kubectl Binary: {} Range Separator: {} Describe Shows Events: {} }}", @@ -504,17 +525,16 @@ impl fmt::Display for Env { self.styles.config_val( self.click_config .editor - .as_ref() - .unwrap_or(&"".to_owned()) - .as_str() + .as_deref() + .unwrap_or("") ), self.styles.config_val( self.click_config .terminal - .as_ref() - .unwrap_or(&"".to_owned()) - .as_str() + .as_deref() + .unwrap_or("") ), + self.styles.config_val(&kubectl_path), self.styles .config_val(self.click_config.range_separator.as_str()), self.styles.config_val( From 848d2413d0862ada5a316b7e019596db2157db80 Mon Sep 17 00:00:00 2001 From: Nick Lanham Date: Sun, 29 May 2022 09:41:06 -0700 Subject: [PATCH 3/3] allow set/unset of kubectl_binary --- src/command/click.rs | 51 ++++++++++++++++++++++++++++++++++++++++ src/command_processor.rs | 1 + src/completer.rs | 4 ++++ src/config/click.rs | 2 +- src/config/mod.rs | 1 + src/env.rs | 21 ++++++++++++----- 6 files changed, 73 insertions(+), 7 deletions(-) diff --git a/src/command/click.rs b/src/command/click.rs index f9c5a2c..d5737cd 100644 --- a/src/command/click.rs +++ b/src/command/click.rs @@ -169,6 +169,7 @@ pub const SET_OPTS: &[&str] = &[ "completion_type", "edit_mode", "editor", + "kubectl_binary", "terminal", "range_separator", "describe_include_events", @@ -240,6 +241,9 @@ Example: "terminal" => { env.set_terminal(Some(value)); } + "kubectl_binary" => { + env.set_kubectl_binary(Some(value)); + } "range_separator" => { env.click_config.range_separator = value.to_string(); } @@ -266,6 +270,53 @@ Example: } ); +pub const UNSET_OPTS: &[&str] = &["editor", "kubectl_binary", "terminal", "range_separator"]; + +command!( + UnSetCmd, + "unset", + "Unset a click option. This returns to option to its default value.", + |clap: ClapCommand<'static>| { + clap.arg( + Arg::new("option") + .help("The click option to unset") + .required(true) + .index(1) + .possible_values(UNSET_OPTS), + ) + }, + vec!["unset"], + vec![&completer::unsetoptions_values_completer], + no_named_complete!(), + |matches, env, writer| { + let option = matches.value_of("option").unwrap(); // safe, required + let mut failed = false; + match option { + "editor" => { + env.set_editor(None); + } + "terminal" => { + env.set_terminal(None); + } + "kubectl_binary" => { + env.set_kubectl_binary(None); + } + "range_separator" => { + env.click_config.range_separator = crate::config::default_range_sep(); + } + _ => { + // this shouldn't happen + write!(stderr(), "Invalid option\n").unwrap_or(()); + failed = true; + } + } + if !failed { + clickwriteln!(writer, "Unset {}", option); + } + Ok(()) + } +); + command!( UtcCmd, "utc", diff --git a/src/command_processor.rs b/src/command_processor.rs index b818978..efed77d 100644 --- a/src/command_processor.rs +++ b/src/command_processor.rs @@ -182,6 +182,7 @@ impl CommandProcessor { Box::new(crate::command::click::Quit::new()), Box::new(crate::command::click::Range::new()), Box::new(crate::command::click::SetCmd::new()), + Box::new(crate::command::click::UnSetCmd::new()), Box::new(crate::command::click::UtcCmd::new()), Box::new(crate::command::configmaps::ConfigMaps::new()), Box::new(crate::command::copy::Copy::new()), diff --git a/src/completer.rs b/src/completer.rs index 4a5ac9e..bab3add 100644 --- a/src/completer.rs +++ b/src/completer.rs @@ -328,6 +328,10 @@ macro_rules! possible_values_completer { } possible_values_completer!(setoptions_values_completer, crate::command::click::SET_OPTS); +possible_values_completer!( + unsetoptions_values_completer, + crate::command::click::UNSET_OPTS +); possible_values_completer!( portforwardaction_values_completer, diff --git a/src/config/click.rs b/src/config/click.rs index f855d7c..4b955f0 100644 --- a/src/config/click.rs +++ b/src/config/click.rs @@ -90,7 +90,7 @@ impl From<&CompletionType> for String { } } -fn default_range_sep() -> String { +pub fn default_range_sep() -> String { "--- {name} ---".to_string() } diff --git a/src/config/mod.rs b/src/config/mod.rs index 62dbb75..2ccb679 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -16,6 +16,7 @@ mod click; mod kube; mod kubefile; +pub use self::click::default_range_sep; pub use self::click::Alias; pub use self::click::ClickConfig; pub use self::click::CompletionType; diff --git a/src/env.rs b/src/env.rs index a4c9615..ef16fd9 100644 --- a/src/env.rs +++ b/src/env.rs @@ -200,6 +200,10 @@ impl Env { self.click_config.terminal = terminal.map(|s| s.to_string()); } + pub fn set_kubectl_binary(&mut self, kubectl_binary: Option<&str>) { + self.click_config.kubectl_binary = kubectl_binary.map(|s| s.to_string()); + } + pub fn set_completion_type(&mut self, comptype: config::CompletionType) { self.click_config.completiontype = comptype; self.need_new_editor = true; @@ -482,16 +486,21 @@ impl fmt::Display for Env { .arg(kubectl_binary) .output() .map(|output| { - std::str::from_utf8(&output.stdout) - .unwrap_or("Failed to parse 'which' output") - .to_string() - }) - .unwrap_or_else(|_| { - if kubectl_binary.starts_with('/') { + if output.status.success() { + std::str::from_utf8(&output.stdout) + .unwrap_or("Failed to parse 'which' output") + .to_string() + } else if kubectl_binary.starts_with('/') { format!("{} not found. Does it exist?", kubectl_binary) } else { format!("{} not found. Is it in your PATH?", kubectl_binary) } + }) + .unwrap_or_else(|e| { + format!( + "Error searching for kubectl_binary (which is set to {}): {}", + kubectl_binary, e + ) }); write!( f,