1use std::str::FromStr;23use crate::{command::CommandExt, host::Config};4use anyhow::{anyhow, Result};5use log::warn;67impl Config {8 pub fn cached_key(&self, host: &str) -> Option<String> {9 let data = self.data();10 let key = data.hosts.get(host).map(|h| &h.encryption_key);11 if let Some(key) = key {12 if key.is_empty() {13 return None;14 }15 }16 key.cloned()17 }18 pub fn update_key(&self, host: &str, key: String) {19 let mut data = self.data_mut();20 let host = data.hosts.entry(host.to_string()).or_default();21 host.encryption_key = key.trim().to_string();22 }2324 pub fn key(&self, host: &str) -> anyhow::Result<String> {25 if let Some(key) = self.cached_key(host) {26 Ok(key)27 } else {28 warn!("Loading key for {}", host);29 let key = self30 .command_on(&host, "cat", false)31 .arg("/etc/ssh/ssh_host_ed25519_key.pub")32 .run_string()?;33 self.update_key(host, key.clone());34 Ok(key)35 }36 }37 pub fn recipient(&self, host: &str) -> anyhow::Result<age::ssh::Recipient> {38 let key = self.key(host)?;39 age::ssh::Recipient::from_str(&key).map_err(|e| anyhow!("parse recipient error: {:?}", e))40 }4142 pub fn orphaned_data(&self) -> Result<Vec<String>> {43 let mut out = Vec::new();44 let host_names = self.list_hosts()?;45 for hostname in self46 .data()47 .hosts48 .iter()49 .filter(|(_, host)| !host.encryption_key.is_empty())50 .map(|(n, _)| n)51 {52 if !host_names.contains(&hostname.to_owned()) {53 out.push(hostname.to_owned())54 }55 }5657 Ok(out)58 }59}