Skip to content

Commit

Permalink
feat: retry bitwarden requests (#37)
Browse files Browse the repository at this point in the history
Retry requests to bitwarden up to 4 times using an exponential backoff
strategy & jitter to avoid rate-limits.
  • Loading branch information
nyarthan authored Mar 26, 2024
1 parent 2d0dd2e commit 8bae83a
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 43 deletions.
30 changes: 29 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bwenv"
version = "1.2.3"
version = "1.2.4"
edition = "2021"

[dependencies]
Expand Down
2 changes: 1 addition & 1 deletion bwenv.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
version: 1.2

cache:
path: ../node_modules/.cache/bwenv
path: ./node_modules/.cache/bwenv

global:
overrides:
Expand Down
2 changes: 2 additions & 0 deletions lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ derived-deref = "2.1.0"
dirs = "5.0.1"
reqwest = { version = "0.12.1", features = ["json"] }
once_cell = "1.19.0"
tokio-retry = "0.3.0"
async-mutex = "1.4.0"

[dev-dependencies]
tempfile = "3.10.1"
Expand Down
94 changes: 67 additions & 27 deletions lib/src/bitwarden.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
use bitwarden::secrets_manager::secrets::{SecretIdentifiersByProjectRequest, SecretsGetRequest};
use async_mutex::Mutex;

use bitwarden::secrets_manager::secrets::{
SecretIdentifiersByProjectRequest, SecretIdentifiersResponse, SecretsGetRequest,
};
use bitwarden::{
auth::login::AccessTokenLoginRequest,
client::client_settings::{ClientSettings, DeviceType},
Client,
};
use tokio_retry::strategy::{jitter, ExponentialBackoff};
use tokio_retry::Retry;
use uuid::Uuid;

use crate::config_yaml::Secrets;
use tracing::{error, info};

pub struct BitwardenClient {
_identity_url: String,
_api_url: String,
_user_agent: String,
_device_type: DeviceType,
_access_token: String,
client: Client,
client: Mutex<Client>,
}

impl BitwardenClient {
Expand All @@ -36,10 +43,13 @@ impl BitwardenClient {
access_token: access_token.to_owned(),
})
.await
.unwrap();
.unwrap_or_else(|_| {
error!(message = "Failed to login using access token");
std::process::exit(1);
});

Self {
client,
client: Mutex::new(client),
_access_token: access_token,
_identity_url: identity_url,
_api_url: api_url,
Expand All @@ -51,34 +61,64 @@ impl BitwardenClient {
pub async fn get_secrets_by_project_id<'a, T: AsRef<str>>(
&mut self,
project_id: T,
) -> Secrets<'a> {
let secrets_by_project_request = SecretIdentifiersByProjectRequest {
project_id: Uuid::parse_str(project_id.as_ref()).unwrap(),
};
) -> Result<Secrets<'a>, Box<dyn std::error::Error>> {
let secret_identifiers = async {
let retry_strategy = ExponentialBackoff::from_millis(10).map(jitter).take(4);
let request = || async {
let secrets_by_project_request = SecretIdentifiersByProjectRequest {
project_id: Uuid::parse_str(project_id.as_ref())?,
};

let secret_identifiers = self
.client
.secrets()
.list_by_project(&secrets_by_project_request)
.await
.unwrap();

let secrets_get_request = SecretsGetRequest {
ids: secret_identifiers
.data
.into_iter()
.map(|ident| ident.id)
.collect(),
info!(message = "Fetching secret IDs");

Ok(self
.client
.lock()
.await
.secrets()
.list_by_project(&secrets_by_project_request)
.await?)
};

let result: Result<SecretIdentifiersResponse, Box<dyn std::error::Error>> =
Retry::spawn(retry_strategy, request).await;

result.unwrap()
};

self.client
.secrets()
.get_by_ids(secrets_get_request)
let ids: Vec<Uuid> = secret_identifiers
.await
.unwrap()
.data
.into_iter()
.map(|secret| (secret.key, secret.value))
.collect()
.map(|ident| ident.id)
.collect();

let secrets = async {
let retry_strategy = ExponentialBackoff::from_millis(10).map(jitter).take(4);
let request = || async {
let secrets_get_request = SecretsGetRequest { ids: ids.clone() };

info!(message = "Fetching secrets");

Ok(self
.client
.lock()
.await
.secrets()
.get_by_ids(secrets_get_request)
.await?
.data
.into_iter()
.map(|secret| (secret.key, secret.value))
.collect())
};

let result: Result<Secrets<'a>, Box<dyn std::error::Error>> =
Retry::spawn(retry_strategy, request).await;

result
};

secrets.await
}
}
4 changes: 2 additions & 2 deletions lib/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ mod tests {
#[test]
fn finds_yaml_config_in_current_dir() {
let temp_dir = tempdir().unwrap();
create_config_file(&temp_dir.path().to_path_buf(), "bwenv.yaml");
create_config_file(temp_dir.path(), "bwenv.yaml");

let result = find_local_config(Some(temp_dir.path()));
assert!(result.is_ok());
Expand All @@ -66,7 +66,7 @@ mod tests {
let temp_dir = tempdir().unwrap();
let child_dir = temp_dir.path().join("child");
std::fs::create_dir(&child_dir).unwrap();
create_config_file(&temp_dir.path().to_path_buf(), "bwenv.toml");
create_config_file(temp_dir.path(), "bwenv.toml");

let result = find_local_config(Some(&child_dir));
assert!(result.is_ok());
Expand Down
13 changes: 2 additions & 11 deletions lib/src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,12 @@ impl Default for Data {
}
}

#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, Default)]
pub struct DataContent {
pub last_update_check: u64,
pub last_checked_version: Option<String>,
}

impl Default for DataContent {
fn default() -> Self {
Self {
last_update_check: 0,
last_checked_version: None,
}
}
}

impl Data {
pub fn new() -> Self {
let path = dirs::data_dir()
Expand Down Expand Up @@ -67,7 +58,7 @@ impl Data {
}

pub fn get_content(&self) -> DataContent {
self.read().unwrap_or(DataContent::default())
self.read().unwrap_or_default()
}

pub fn set_content(
Expand Down
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ async fn run_with<'a>(
bitwarden_client
.get_secrets_by_project_id(&project_id)
.await
.unwrap()
})
.await
.unwrap();
Expand Down

0 comments on commit 8bae83a

Please sign in to comment.