use std::collections::BTreeMap;
use std::fmt::Debug;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use anyhow::Context;
use async_trait::async_trait;
use mz_repr::CatalogItemId;
pub mod cache;
#[async_trait]
pub trait SecretsController: Debug + Send + Sync {
async fn ensure(&self, id: CatalogItemId, contents: &[u8]) -> Result<(), anyhow::Error>;
async fn delete(&self, id: CatalogItemId) -> Result<(), anyhow::Error>;
async fn list(&self) -> Result<Vec<CatalogItemId>, anyhow::Error>;
fn reader(&self) -> Arc<dyn SecretsReader>;
}
#[derive(Debug)]
pub struct CachingPolicy {
pub enabled: bool,
pub ttl: Duration,
}
#[async_trait]
pub trait SecretsReader: Debug + Send + Sync {
async fn read(&self, id: CatalogItemId) -> Result<Vec<u8>, anyhow::Error>;
async fn read_string(&self, id: CatalogItemId) -> Result<String, anyhow::Error> {
let contents = self.read(id).await?;
String::from_utf8(contents).context("converting secret value to string")
}
}
#[derive(Debug)]
pub struct InMemorySecretsController {
data: Arc<Mutex<BTreeMap<CatalogItemId, Vec<u8>>>>,
}
impl InMemorySecretsController {
pub fn new() -> Self {
Self {
data: Arc::new(Mutex::new(BTreeMap::new())),
}
}
}
#[async_trait]
impl SecretsController for InMemorySecretsController {
async fn ensure(&self, id: CatalogItemId, contents: &[u8]) -> Result<(), anyhow::Error> {
self.data.lock().unwrap().insert(id, contents.to_vec());
Ok(())
}
async fn delete(&self, id: CatalogItemId) -> Result<(), anyhow::Error> {
self.data.lock().unwrap().remove(&id);
Ok(())
}
async fn list(&self) -> Result<Vec<CatalogItemId>, anyhow::Error> {
Ok(self.data.lock().unwrap().keys().cloned().collect())
}
fn reader(&self) -> Arc<dyn SecretsReader> {
Arc::new(InMemorySecretsController {
data: Arc::clone(&self.data),
})
}
}
#[async_trait]
impl SecretsReader for InMemorySecretsController {
async fn read(&self, id: CatalogItemId) -> Result<Vec<u8>, anyhow::Error> {
let contents = self.data.lock().unwrap().get(&id).cloned();
contents.ok_or_else(|| anyhow::anyhow!("secret does not exist"))
}
}