1use std::cell::OnceCell;2use std::collections::{BTreeMap, HashMap};3use std::sync::{Arc, Mutex, OnceLock};45use anyhow::{Context, bail};6use itertools::Itertools;7use nix_eval::{NativeFn, Value, nix_go, nix_go_json};8use serde::Deserialize;9use tracing::{info, warn};1011use crate::fleetdata::{FleetData, FleetSecrets};12use crate::host::Config;1314#[derive(thiserror::Error, Debug)]15enum Error {}1617struct Parts {18 encrypted: Vec<String>,19 public: Vec<String>,20}2122trait SecretsBackend {23 fn has_shared(&self, name: &str);24 fn has_host(&self, host: &str, name: &str);25 fn shared_parts(&self, name: &str) -> Parts;26 fn host_parts(&self, host: &str, name: &str) -> Parts;27}2829struct FsSecretsBackend {}3031pub static PRIMOPS_DATA: OnceLock<Config> = OnceLock::new();3233#[derive(Deserialize, Debug)]34struct GeneratorPart {35 encrypted: bool,36}3738pub fn init_primops() {39 info!("initializing primops");40 NativeFn::new(41 c"__fleetEnsureHostSecret",42 c"Ensure secret existence for a host, regenerating it in case of some mismatch",43 [c"host", c"secret", c"generator"],44 |es, [host, secret, generator]| {45 info!("get host");46 let host = host.to_string()?;47 info!("get secret");48 let secret = secret.to_string()?;4950 info!("get config");51 let config = PRIMOPS_DATA52 .get()53 .expect("primops data should be set on init");5455 info!("get pkgs");56 let nixpkgs = &config.nixpkgs;57 let default_pkgs = &config.default_pkgs;58 let default_mk_secret_generators = nix_go!(default_pkgs.mkSecretGenerators);59 let generators = nix_go!(default_mk_secret_generators(Obj {60 recipients: <Vec<String>>::new(),61 }));62 let pkgs_and_generators = default_pkgs.clone().attrs_update(generators)?;6364 info!("call package");65 let call_package = nix_go!(nixpkgs.lib.callPackageWith(pkgs_and_generators));66 let default_generator = call_package67 .call(generator.clone())68 .context("calling callPackage with generator")?69 .call(Value::new_attrs(HashMap::new()))70 .context("providing extra callPackage args")?;7172 info!("get parts");73 let mut parts: BTreeMap<String, GeneratorPart> = nix_go_json!(default_generator.parts);74 info!("got parts: {parts:?}");7576 let Some(existing) = config77 .host_secret(&host, &secret) else {78 bail!("missing secret {secret} for host {host}; secret needs regeneration")79 };8081 info!("got existing: {existing:?}");8283 let mut out = HashMap::new();8485 for (part_name, part) in &existing.secret.parts {86 let Some(definition) = parts.remove(part_name) else {87 warn!("secret {secret} part {part_name} is stored, but not defined in nixos config, it will not be passed to nix");88 continue;89 };90 if definition.encrypted != part.raw.encrypted {91 bail!("secret {secret} part {part_name} is supposed to be {}, but it is {}; secret needs regeneration", if definition.encrypted {"encrypted"} else {"unencrypted"}, if part.raw.encrypted {"encrypted"} else {"unencrypted"});92 }93 out.insert(part_name.as_str(), Value::new_attrs(HashMap::from_iter([("raw", Value::new_str(&part.raw.to_string()))])));94 }95 if !parts.is_empty(){96 let defs = parts.keys().collect_vec();97 bail!("secret parts are defined, but not stored: {defs:?}, secret needs regeneration")98 }99100 Ok(Value::new_attrs(out))101 },102 )103 .register();104}