pub mod env;
pub mod sops;
use anyhow::Result;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use tatara_core::domain::secret::{SecretProvider, SecretRef};
use tracing::{debug, info};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SecretValue {
pub value: String,
pub version: String,
}
#[async_trait]
pub trait SecretFetcher: Send + Sync {
async fn fetch(&self, key: &str) -> Result<SecretValue>;
async fn needs_rotation(&self, key: &str, current_version: &str) -> Result<bool> {
let current = self.fetch(key).await?;
Ok(current.version != current_version)
}
}
pub struct SecretResolver {
env_fetcher: env::EnvSecretFetcher,
sops_fetcher: sops::SopsSecretFetcher,
}
impl SecretResolver {
pub fn new() -> Self {
Self {
env_fetcher: env::EnvSecretFetcher,
sops_fetcher: sops::SopsSecretFetcher,
}
}
pub async fn resolve(&self, refs: &[SecretRef]) -> Result<ResolvedSecrets> {
let mut env_vars = std::collections::HashMap::new();
let mut files = Vec::new();
for secret_ref in refs {
let fetcher: &dyn SecretFetcher = match secret_ref.provider {
SecretProvider::Env => &self.env_fetcher,
SecretProvider::Sops => &self.sops_fetcher,
SecretProvider::Akeyless => {
anyhow::bail!("Akeyless provider not yet configured");
}
};
let value = fetcher.fetch(&secret_ref.key).await?;
debug!(name = %secret_ref.name, provider = ?secret_ref.provider, "resolved secret");
if let Some(env_var) = &secret_ref.env_var {
env_vars.insert(env_var.clone(), value.value.clone());
}
if let Some(mount_path) = &secret_ref.mount_path {
files.push(SecretFile {
path: mount_path.clone(),
content: value.value.clone(),
});
}
}
info!(
env_count = env_vars.len(),
file_count = files.len(),
"secrets resolved"
);
Ok(ResolvedSecrets { env_vars, files })
}
}
impl Default for SecretResolver {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Default)]
pub struct ResolvedSecrets {
pub env_vars: std::collections::HashMap<String, String>,
pub files: Vec<SecretFile>,
}
#[derive(Debug)]
pub struct SecretFile {
pub path: String,
pub content: String,
}