1use crate::{2 command::CommandExt,3 host::{FleetConfig, Host},4};5use anyhow::Result;6use log::warn;7use std::{8 fs::{create_dir_all, metadata, read, read_dir, write},9 path::PathBuf,10};1112impl FleetConfig {13 fn host_keys_dir(&self) -> Result<PathBuf> {14 let mut out = self.data_dir().clone();15 out.push("host_keys");16 create_dir_all(&out)?;17 Ok(out)18 }1920 fn host_key_file(&self, host: &str) -> Result<PathBuf> {21 let mut dir = self.host_keys_dir()?;22 dir.push(format!("{}.asc", host));23 Ok(dir)24 }2526 pub fn list_orphaned_keys(&self) -> Result<Vec<(String, PathBuf)>> {27 let mut out = Vec::new();28 let host_names = self.list_host_names()?;29 for file in read_dir(&self.host_keys_dir()?)? {30 let file = file?;31 anyhow::ensure!(32 file.file_type()?.is_file(),33 "host_keys dir should contain only files"34 );35 let name = file.file_name();36 let name = name.to_str().unwrap();37 if let Some(hostname) = name.strip_suffix(".asc") {38 if !host_names.contains(&hostname.to_owned()) {39 out.push((hostname.to_owned(), file.path()))40 }41 } else {42 out.push(("<unknown>".to_owned(), file.path()))43 }44 }4546 Ok(out)47 }48}4950impl Host {51 pub fn key(&self) -> anyhow::Result<String> {52 let key_path = self.fleet_config.host_key_file(&self.hostname)?;53 if metadata(&key_path).map(|m| m.is_file()).unwrap_or(false) {54 Ok(String::from_utf8(read(key_path)?)?)55 } else {56 warn!("Loading key for {}", self.hostname);57 let key = self58 .command_on("cat", false)59 .arg("/etc/ssh/ssh_host_ed25519_key.pub")60 .run_string()?;61 write(key_path, key.clone())?;62 Ok(key)63 }64 }65}