diff --git a/kube-client/src/client/auth/mod.rs b/kube-client/src/client/auth/mod.rs index d3a46f0ae..80d6b162f 100644 --- a/kube-client/src/client/auth/mod.rs +++ b/kube-client/src/client/auth/mod.rs @@ -18,7 +18,7 @@ use thiserror::Error; use tokio::sync::{Mutex, RwLock}; use tower::{filter::AsyncPredicate, BoxError}; -use crate::config::{AuthInfo, AuthProviderConfig, ExecConfig}; +use crate::config::{AuthInfo, AuthProviderConfig, ExecConfig, ExecInteractiveMode}; #[cfg(feature = "oauth")] mod oauth; #[cfg(feature = "oauth")] pub use oauth::Error as OAuthError; @@ -66,6 +66,10 @@ pub enum Error { #[error("failed to parse auth exec output: {0}")] AuthExecParse(#[source] serde_json::Error), + /// Fail to serialize input + #[error("failed to serialize input: {0}")] + AuthExecSerialize(#[source] serde_json::Error), + /// Failed to exec auth #[error("failed exec auth: {0}")] AuthExec(String), @@ -461,13 +465,17 @@ pub struct ExecCredential { #[serde(rename = "apiVersion")] pub api_version: Option, pub spec: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub status: Option, } /// ExecCredenitalSpec holds request and runtime specific information provided /// by transport. #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ExecCredentialSpec {} +pub struct ExecCredentialSpec { + #[serde(skip_serializing_if = "Option::is_none")] + interactive: Option, +} /// ExecCredentialStatus holds credentials for the transport to use. #[derive(Clone, Debug, Serialize, Deserialize)] @@ -500,6 +508,25 @@ fn auth_exec(auth: &ExecConfig) -> Result { cmd.envs(envs); } + let interactive = auth.interactive_mode != Some(ExecInteractiveMode::Never); + if interactive { + cmd.stdin(std::process::Stdio::inherit()); + } else { + cmd.stdin(std::process::Stdio::piped()); + } + + // Provide exec info to child process + let exec_info = serde_json::to_string(&ExecCredential { + api_version: auth.api_version.clone(), + kind: None, + spec: Some(ExecCredentialSpec { + interactive: Some(interactive), + }), + status: None, + }) + .map_err(Error::AuthExecSerialize)?; + cmd.env("KUBERNETES_EXEC_INFO", exec_info); + if let Some(envs) = &auth.drop_env { for env in envs { cmd.env_remove(env); diff --git a/kube-client/src/config/file_config.rs b/kube-client/src/config/file_config.rs index 0b34b8d9c..e23b780a2 100644 --- a/kube-client/src/config/file_config.rs +++ b/kube-client/src/config/file_config.rs @@ -255,6 +255,23 @@ pub struct ExecConfig { /// It has been suggested in client-go via https://github.com/kubernetes/client-go/issues/1177 #[serde(skip)] pub drop_env: Option>, + + /// Interative mode of the auth plugins + #[serde(rename = "interactiveMode")] + #[serde(skip_serializing_if = "Option::is_none")] + pub interactive_mode: Option, +} + +/// ExecInteractiveMode define the interactity of the child process +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[cfg_attr(test, derive(Eq))] +pub enum ExecInteractiveMode { + /// Never get interactive + Never, + /// If available et interactive + IfAvailable, + /// Alwayes get interactive + Always, } /// NamedContext associates name with context. diff --git a/kube-client/src/config/mod.rs b/kube-client/src/config/mod.rs index 27dcd381a..a325abc2a 100644 --- a/kube-client/src/config/mod.rs +++ b/kube-client/src/config/mod.rs @@ -401,8 +401,8 @@ const DEFAULT_READ_TIMEOUT: Duration = Duration::from_secs(295); // Expose raw config structs pub use file_config::{ - AuthInfo, AuthProviderConfig, Cluster, Context, ExecConfig, Kubeconfig, NamedAuthInfo, NamedCluster, - NamedContext, NamedExtension, Preferences, + AuthInfo, AuthProviderConfig, Cluster, Context, ExecConfig, ExecInteractiveMode, Kubeconfig, + NamedAuthInfo, NamedCluster, NamedContext, NamedExtension, Preferences, }; #[cfg(test)]