@@ -46,18 +46,29 @@ fn space_or_equals_delimited_option_value<'a>(
4646 } )
4747}
4848
49+ /// Returns the value of a given `key` from a list of environment variables formatted as
50+ /// `KEY=VALUE`.
51+ fn env_var_value < ' a > ( env_vars : & ' a [ String ] , key : & str ) -> Option < & ' a str > {
52+ let prefix = format ! ( "{key}=" ) ;
53+ env_vars. iter ( ) . find_map ( |env| env. strip_prefix ( & prefix) )
54+ }
55+
4956/// Returns a command string to run the given `subcommand` string with the same `--namespace` and/or
5057/// `--kubeconfig` values as specified in the incomplete command being entered (`tokens`), which
5158/// scopes down suggestions to be more helpful based on the already-specified namespace or
52- /// kubeconfig file.
59+ /// kubeconfig file. Also reads the `KUBECONFIG` environment variable if `--kubeconfig` is not
60+ /// explicitly specified in the tokens.
5361fn kubectl_script (
5462 env_vars : & [ String ] ,
5563 tokens : & [ & str ] ,
5664 subcommand : CommandBuilder ,
5765) -> CommandBuilder {
5866 let kubeconfig_value = space_or_equals_delimited_option_value ( tokens, "--kubeconfig" )
67+ . or_else ( || env_var_value ( env_vars, "KUBECONFIG" ) )
5968 . map ( |value| format ! ( "--kubeconfig={value} " ) )
60- . unwrap_or_else ( || "" . to_owned ( ) ) ;
69+ // Fall back to the $KUBECONFIG shell variable, which is set when session environment
70+ // variables are forwarded to the child process.
71+ . unwrap_or_else ( || r#"${KUBECONFIG:+--kubeconfig="$KUBECONFIG"} "# . to_owned ( ) ) ;
6172 let namespace_value = space_or_equals_delimited_option_value ( tokens, "--namespace" )
6273 . or ( space_or_equals_delimited_option_value ( tokens, "-n" ) )
6374 . map ( |value| format ! ( "--namespace={value} " ) )
@@ -254,3 +265,97 @@ pub fn generator() -> CommandSignatureGenerators {
254265 KUBECTL_BUILTIN_COMPLETION . clone ( ) ,
255266 )
256267}
268+
269+ #[ cfg( test) ]
270+ mod tests {
271+ use super :: * ;
272+ use warp_completion_metadata:: Shell ;
273+
274+ #[ test]
275+ fn test_kubeconfig_from_flag_in_tokens ( ) {
276+ let env_vars = vec ! [ ] ;
277+ let tokens = vec ! [
278+ "kubectl" ,
279+ "--kubeconfig" ,
280+ "/path/to/config" ,
281+ "config" ,
282+ "use-context" ,
283+ ] ;
284+ let cmd = kubectl_script (
285+ & env_vars,
286+ & tokens,
287+ CommandBuilder :: single_command ( "config get-contexts -o name" ) ,
288+ ) ;
289+ let built = cmd. build ( Shell :: Posix ) ;
290+ assert ! (
291+ built. contains( "--kubeconfig=/path/to/config" ) ,
292+ "Expected --kubeconfig flag from tokens, got: {built}"
293+ ) ;
294+ }
295+
296+ #[ test]
297+ fn test_kubeconfig_from_env_vars ( ) {
298+ let env_vars = vec ! [ "KUBECONFIG=/tmp/kube-test/config" . to_string( ) ] ;
299+ let tokens = vec ! [ "kubectl" , "config" , "use-context" ] ;
300+ let cmd = kubectl_script (
301+ & env_vars,
302+ & tokens,
303+ CommandBuilder :: single_command ( "config get-contexts -o name" ) ,
304+ ) ;
305+ let built = cmd. build ( Shell :: Posix ) ;
306+ assert ! (
307+ built. contains( "--kubeconfig=/tmp/kube-test/config" ) ,
308+ "Expected --kubeconfig from KUBECONFIG env var, got: {built}"
309+ ) ;
310+ }
311+
312+ #[ test]
313+ fn test_kubeconfig_flag_takes_precedence_over_env_var ( ) {
314+ let env_vars = vec ! [ "KUBECONFIG=/env/path/config" . to_string( ) ] ;
315+ let tokens = vec ! [
316+ "kubectl" ,
317+ "--kubeconfig" ,
318+ "/flag/path/config" ,
319+ "config" ,
320+ "use-context" ,
321+ ] ;
322+ let cmd = kubectl_script (
323+ & env_vars,
324+ & tokens,
325+ CommandBuilder :: single_command ( "config get-contexts -o name" ) ,
326+ ) ;
327+ let built = cmd. build ( Shell :: Posix ) ;
328+ assert ! (
329+ built. contains( "--kubeconfig=/flag/path/config" ) ,
330+ "Expected --kubeconfig from flag (not env var), got: {built}"
331+ ) ;
332+ assert ! (
333+ !built. contains( "--kubeconfig=/env/path/config" ) ,
334+ "Should not contain env var value when flag is present, got: {built}"
335+ ) ;
336+ }
337+
338+ #[ test]
339+ fn test_kubeconfig_fallback_to_shell_variable ( ) {
340+ let env_vars: Vec < String > = vec ! [ ] ;
341+ let tokens = vec ! [ "kubectl" , "config" , "use-context" ] ;
342+ let cmd = kubectl_script (
343+ & env_vars,
344+ & tokens,
345+ CommandBuilder :: single_command ( "config get-contexts -o name" ) ,
346+ ) ;
347+ let built = cmd. build ( Shell :: Posix ) ;
348+ assert ! (
349+ built. contains( "${KUBECONFIG:+--kubeconfig=" ) ,
350+ "Expected $KUBECONFIG shell variable fallback, got: {built}"
351+ ) ;
352+ }
353+
354+ #[ test]
355+ fn test_env_var_value_finds_key ( ) {
356+ let env_vars = vec ! [ "FOO=bar" . to_string( ) , "KUBECONFIG=/my/config" . to_string( ) ] ;
357+ assert_eq ! ( env_var_value( & env_vars, "KUBECONFIG" ) , Some ( "/my/config" ) ) ;
358+ assert_eq ! ( env_var_value( & env_vars, "FOO" ) , Some ( "bar" ) ) ;
359+ assert_eq ! ( env_var_value( & env_vars, "MISSING" ) , None ) ;
360+ }
361+ }
0 commit comments