use anyhow::anyhow;
use env_file_reader::read_file;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::path::PathBuf;
use vaultrs::client::{VaultClient, VaultClientSettingsBuilder};
use vaultrs::kv2;
use crate::entities::custom_command::CustomCommand;
use crate::entities::environment::RunEnvironment;
pub const VAULT_ADDR_ENV: &str = "DEPLOYER_VAULT_ADDR";
pub const VAULT_ADDR_TOKEN: &str = "DEPLOYER_VAULT_TOKEN";
#[derive(Deserialize, Serialize, PartialEq, Eq, Hash, Clone)]
pub struct Variable {
#[serde(default, skip_serializing_if = "crate::utils::is_false")]
pub is_secret: bool,
pub value: VarValue,
}
#[derive(Deserialize, Serialize, PartialEq, Eq, Hash, Clone)]
#[serde(rename_all = "snake_case", tag = "type")]
#[allow(missing_docs)]
pub enum VarValue {
Plain { value: String },
FromEnvFile(FromEnvFile),
FromEnvVar { var_name: String },
FromCmd { cmd: String },
FromHcVaultKv2(Kv2Paths),
}
impl Variable {
pub fn new_plain(value: &str) -> anyhow::Result<Self> {
Ok(Self {
is_secret: false,
value: VarValue::Plain {
value: value.to_string(),
},
})
}
pub async fn get_value(&self, env: &RunEnvironment<'_>) -> anyhow::Result<String> {
match &self.value {
VarValue::Plain { value } => Ok(value.to_owned()),
VarValue::FromEnvFile(info) => {
let env_variables = read_file(&info.env_file_path)?;
let val = env_variables
.get(&info.key)
.ok_or(anyhow!("There is no such key in your ENV file."))?;
Ok(val.to_owned())
}
VarValue::FromEnvVar { var_name } => Ok(std::env::var(var_name)?),
VarValue::FromCmd { cmd } => CustomCommand::run_simple(env, cmd)
.await
.map(|res| res.join("\n").trim().to_string()),
VarValue::FromHcVaultKv2(info) => {
let vault_addr = std::env::var(VAULT_ADDR_ENV)?;
let vault_token = std::env::var(VAULT_ADDR_TOKEN)?;
let client = VaultClient::new(
VaultClientSettingsBuilder::default()
.address(vault_addr)
.token(vault_token)
.build()?,
)?;
kv2::read(&client, &info.mount_path, &info.secret_path)
.await
.map_err(|e| anyhow!(e.to_string()))
}
}
}
}
#[derive(Deserialize, Serialize, PartialEq, Eq, Hash, Clone)]
pub struct FromEnvFile {
pub env_file_path: PathBuf,
pub key: String,
}
#[derive(Deserialize, Serialize, PartialEq, Eq, Hash, Clone)]
pub struct Kv2Paths {
pub mount_path: String,
pub secret_path: String,
}
#[allow(missing_docs)]
pub trait VarTraits {
fn is_secret(&self, title: &str) -> bool;
fn titles(&self) -> Vec<String>;
fn find(&self, title: &str) -> Option<Variable>;
}
impl VarTraits for BTreeMap<String, Variable> {
fn is_secret(&self, title: &str) -> bool {
self
.iter()
.find(|(t, _)| t.as_str().eq(title))
.is_some_and(|(_, v)| v.is_secret)
}
fn titles(&self) -> Vec<String> {
self.keys().map(|t| t.as_str().to_string()).collect::<Vec<_>>()
}
fn find(&self, title: &str) -> Option<Variable> {
self.iter().find(|(t, _)| t.as_str().eq(title)).map(|(_, v)| v.clone())
}
}