machines/rust/program/openbao-helper/src/enventry.rs
Kaare Hoff Skovgaard 07360c41da Make some more changes to machine setup
Work being done as an attempt to be able to
create a small monitoring server


Former-commit-id: f7d4bef46c
2025-07-09 15:12:11 +02:00

108 lines
3.1 KiB
Rust

use std::{collections::BTreeMap, marker::PhantomData, vec::IntoIter};
use serde::Deserialize;
pub trait EnvEntryConfig {
const SECRETS: &'static [&'static str];
const BAO_KEY: &'static str;
}
pub struct EnvEntry<T>(Vec<(&'static str, String)>, PhantomData<T>);
impl<T: EnvEntryConfig> EnvEntry<T> {
pub fn try_new_from_env() -> anyhow::Result<Self> {
let mut result = Vec::with_capacity(T::SECRETS.len());
for key in T::SECRETS {
let value = common::env::read_env(key)?;
result.push((*key, value));
}
Ok(Self(result, PhantomData))
}
fn new_from_values(values: Vec<(&'static str, String)>) -> Self {
Self(values, PhantomData)
}
pub fn read_from_bao() -> anyhow::Result<Self> {
read_bao_data()
}
}
impl<T> From<EnvEntry<T>> for Vec<(&'static str, String)> {
fn from(value: EnvEntry<T>) -> Self {
value.0
}
}
impl<T> IntoIterator for EnvEntry<T> {
type Item = (&'static str, String);
type IntoIter = IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<'de, T: EnvEntryConfig> serde::Deserialize<'de> for EnvEntry<T> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_map(EnvEntryVisitor(PhantomData))
}
}
struct EnvEntryVisitor<T>(PhantomData<T>);
impl<'de, T: EnvEntryConfig> serde::de::Visitor<'de> for EnvEntryVisitor<T> {
type Value = EnvEntry<T>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_fmt(format_args!(
"a map with unique keys {} with string values",
T::SECRETS.join(", "),
))
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: serde::de::MapAccess<'de>,
{
let mut values = BTreeMap::<&'static str, String>::new();
while let Some((key, value)) = map.next_entry::<&'de str, String>()? {
let mapped_key = T::SECRETS.iter().find(|n| **n == key).copied();
let Some(key) = mapped_key else {
return Err(serde::de::Error::unknown_field(key, T::SECRETS));
};
if values.contains_key(key) {
return Err(serde::de::Error::duplicate_field(key));
}
values.insert(key, value);
}
for key in T::SECRETS {
if !values.contains_key(key) {
return Err(serde::de::Error::missing_field(key));
}
}
let values = values.into_iter().collect();
let entry = EnvEntry::<T>::new_from_values(values);
Ok(entry)
}
}
#[derive(Debug, Deserialize)]
struct OpenBaoKvEntry<T> {
data: OpenBaoKvEntryData<T>,
}
#[derive(Debug, Deserialize)]
struct OpenBaoKvEntryData<T> {
data: T,
}
fn read_bao_data<T: EnvEntryConfig>() -> anyhow::Result<EnvEntry<T>> {
let mut cmd = common::proc::Command::new("bao");
cmd.args(["kv", "get", "-format=json", "-mount=opentofu", T::BAO_KEY]);
let result: OpenBaoKvEntry<EnvEntry<T>> = cmd.try_spawn_to_json()?;
Ok(result.data.data)
}