1use std::str::FromStr;23use crate::{command::CommandExt, host::Config};4use anyhow::{anyhow, Result};5use tracing::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 async 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 .await?;34 self.update_key(host, key.clone());35 Ok(key)36 }37 }38 pub async fn recipient(&self, host: &str) -> anyhow::Result<age::ssh::Recipient> {39 let key = self.key(host).await?;40 age::ssh::Recipient::from_str(&key).map_err(|e| anyhow!("parse recipient error: {:?}", e))41 }4243 pub async fn orphaned_data(&self) -> Result<Vec<String>> {44 let mut out = Vec::new();45 let host_names = self.list_hosts().await?;46 for hostname in self47 .data()48 .hosts49 .iter()50 .filter(|(_, host)| !host.encryption_key.is_empty())51 .map(|(n, _)| n)52 {53 if !host_names.contains(&hostname.to_owned()) {54 out.push(hostname.to_owned())55 }56 }5758 Ok(out)59 }60}