diff --git a/kube-client/src/client/auth/mod.rs b/kube-client/src/client/auth/mod.rs index ba8ed59d7..58b672f4e 100644 --- a/kube-client/src/client/auth/mod.rs +++ b/kube-client/src/client/auth/mod.rs @@ -14,7 +14,7 @@ use jsonpath_lib::select as jsonpath_select; use secrecy::{ExposeSecret, SecretString}; use serde::{Deserialize, Serialize}; use thiserror::Error; -use tokio::sync::Mutex; +use tokio::sync::{Mutex, RwLock}; use tower::{filter::AsyncPredicate, BoxError}; use crate::config::{AuthInfo, AuthProviderConfig, ExecConfig}; @@ -116,10 +116,12 @@ impl TokenFile { Utc::now() + Duration::seconds(10) > self.expires_at } + /// Get the cached token. Returns `None` if it's expiring. fn cached_token(&self) -> Option<&str> { (!self.is_expiring()).then(|| self.token.expose_secret().as_ref()) } + /// Get a token. Reloads from file if the cached token is expiring. fn token(&mut self) -> &str { if self.is_expiring() { // > If reload from file fails, the last-read token should be used to avoid breaking @@ -148,7 +150,7 @@ impl TokenFile { #[derive(Debug, Clone)] pub enum RefreshableToken { Exec(Arc, AuthInfo)>>), - File(Arc>), + File(Arc>), #[cfg(feature = "oauth")] GcpOauth(Arc>), } @@ -206,12 +208,15 @@ impl RefreshableToken { } RefreshableToken::File(token_file) => { - let mut locked = token_file.lock().await; - if let Some(token) = locked.cached_token() { - bearer_header(token) - } else { - bearer_header(locked.token()) + let guard = token_file.read().await; + if let Some(header) = guard.cached_token().map(bearer_header) { + return header; } + // Drop the read guard before a write lock attempt to prevent deadlock. + drop(guard); + // Note that `token()` only reloads if the cached token is expiring. + // A separate method to conditionally reload minimizes the need for an exclusive access. + bearer_header(token_file.write().await.token()) } #[cfg(feature = "oauth")] @@ -279,7 +284,7 @@ impl TryFrom<&AuthInfo> for Auth { // Token file reference. Must be reloaded at least once a minute. if let Some(file) = &auth_info.token_file { return Ok(Self::RefreshableToken(RefreshableToken::File(Arc::new( - Mutex::new(TokenFile::new(file)?), + RwLock::new(TokenFile::new(file)?), )))); }