tatara_engine/secrets/
sops.rs1use super::{SecretFetcher, SecretValue};
5use anyhow::{Context, Result};
6use async_trait::async_trait;
7use tokio::process::Command;
8
9pub struct SopsSecretFetcher;
10
11#[async_trait]
12impl SecretFetcher for SopsSecretFetcher {
13 async fn fetch(&self, key: &str) -> Result<SecretValue> {
18 let (file_path, json_path) = key
19 .split_once('#')
20 .context("SOPS key must be 'file_path#json.path'")?;
21
22 let extract_path = json_path
24 .split('.')
25 .map(|part| format!("[\"{part}\"]"))
26 .collect::<String>();
27
28 let output = Command::new("sops")
29 .args(["--decrypt", "--extract", &extract_path, file_path])
30 .output()
31 .await
32 .context("failed to run sops")?;
33
34 if !output.status.success() {
35 let stderr = String::from_utf8_lossy(&output.stderr);
36 anyhow::bail!("sops decrypt failed: {stderr}");
37 }
38
39 let value = String::from_utf8(output.stdout)
40 .context("sops output not valid UTF-8")?
41 .trim()
42 .to_string();
43
44 Ok(SecretValue {
45 value,
46 version: "sops".to_string(),
47 })
48 }
49}