Skip to content

Commit

Permalink
Token reloading with RwLock (#835)
Browse files Browse the repository at this point in the history
  • Loading branch information
kazk authored Mar 1, 2022
1 parent e4c0108 commit 0b5e803
Showing 1 changed file with 13 additions and 8 deletions.
21 changes: 13 additions & 8 deletions kube-client/src/client/auth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -148,7 +150,7 @@ impl TokenFile {
#[derive(Debug, Clone)]
pub enum RefreshableToken {
Exec(Arc<Mutex<(SecretString, DateTime<Utc>, AuthInfo)>>),
File(Arc<Mutex<TokenFile>>),
File(Arc<RwLock<TokenFile>>),
#[cfg(feature = "oauth")]
GcpOauth(Arc<Mutex<oauth::Gcp>>),
}
Expand Down Expand Up @@ -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")]
Expand Down Expand Up @@ -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)?),
))));
}

Expand Down

0 comments on commit 0b5e803

Please sign in to comment.