use std::path::PathBuf;
use std::sync::Arc;
use anyhow::Context;
use async_trait::async_trait;
use mz_repr::CatalogItemId;
use mz_secrets::{SecretsController, SecretsReader};
use tokio::fs::{self, OpenOptions};
use tokio::io::AsyncWriteExt;
use crate::ProcessOrchestrator;
#[async_trait]
impl SecretsController for ProcessOrchestrator {
async fn ensure(&self, id: CatalogItemId, contents: &[u8]) -> Result<(), anyhow::Error> {
let file_path = self.secrets_dir.join(id.to_string());
let mut file = OpenOptions::new()
.mode(0o600)
.create(true)
.write(true)
.truncate(true)
.open(file_path)
.await
.with_context(|| format!("writing secret {id}"))?;
file.write_all(contents)
.await
.with_context(|| format!("writing secret {id}"))?;
file.sync_all()
.await
.with_context(|| format!("writing secret {id}"))?;
Ok(())
}
async fn delete(&self, id: CatalogItemId) -> Result<(), anyhow::Error> {
fs::remove_file(self.secrets_dir.join(id.to_string()))
.await
.with_context(|| format!("deleting secret {id}"))?;
Ok(())
}
async fn list(&self) -> Result<Vec<CatalogItemId>, anyhow::Error> {
let mut ids = Vec::new();
let mut entries = fs::read_dir(&self.secrets_dir)
.await
.context("listing secrets")?;
while let Some(dir) = entries.next_entry().await? {
let id: CatalogItemId = dir.file_name().to_string_lossy().parse()?;
ids.push(id);
}
Ok(ids)
}
fn reader(&self) -> Arc<dyn SecretsReader> {
Arc::new(ProcessSecretsReader {
secrets_dir: self.secrets_dir.clone(),
})
}
}
#[derive(Debug)]
pub struct ProcessSecretsReader {
secrets_dir: PathBuf,
}
impl ProcessSecretsReader {
pub fn new(secrets_dir: PathBuf) -> ProcessSecretsReader {
ProcessSecretsReader { secrets_dir }
}
}
#[async_trait]
impl SecretsReader for ProcessSecretsReader {
async fn read(&self, id: CatalogItemId) -> Result<Vec<u8>, anyhow::Error> {
let contents = fs::read(self.secrets_dir.join(id.to_string()))
.await
.with_context(|| format!("reading secret {id}"))?;
Ok(contents)
}
}