1use std::{2 collections::{BTreeMap, HashMap},3 ffi::OsString,4 path::PathBuf,5};67use anyhow::{Context, Result};8use clap::Parser;9use fleet_base::host::Config;10use nix_eval::nix_go;11use serde::Deserialize;12use serde_json::Value;13use tempfile::NamedTempFile;14use tokio::{15 fs::{self, create_dir_all},16 process::Command,17};18use tracing::debug;1920#[derive(Deserialize, Debug)]21pub struct TfData {22 23 #[allow(dead_code)]24 managed: bool,25 26 #[serde(default)]27 #[serde(skip_serializing_if = "BTreeMap::is_empty")]28 pub hosts: BTreeMap<String, Value>,29}3031#[derive(Parser)]32pub struct Tf {33 args: Vec<OsString>,34}35impl Tf {36 pub async fn run(&self, config: &Config) -> Result<()> {37 let dir = config.directory.join(".fleet/tf/default");38 39 40 41 {42 debug!("generating terraform configs");43 let system = &config.local_system;44 let config = &config.config_field;45 let data: HashMap<String, PathBuf> = nix_go!(config.tf({ system })).build().await?;46 let data = &data["out"];47 let data = fs::read(&data).await?;4849 create_dir_all(&dir).await?;5051 let tmp = NamedTempFile::new_in(&dir)?;52 fs::write(tmp.path(), data).await?;53 tmp.persist(dir.join("fleet.tf.json"))?;54 }5556 {57 debug!("running terraform command");58 Command::new("terraform")59 .current_dir(&dir)60 .args(&self.args)61 .status()62 .await?;63 }64 {65 debug!("syncing terraform data");66 let data = Command::new("terraform")67 .current_dir(dir)68 .arg("output")69 .arg("-json")70 .arg("fleet")71 .output()72 .await?;73 let tf_data: TfData = serde_json::from_slice(&data.stdout)74 .context("failed to parse terraform fleet output")?;7576 let mut data = config.data();77 debug!("synchronized done = {tf_data:?}");78 data.extra.insert(79 "terraformHosts".to_owned(),80 serde_json::to_value(tf_data.hosts).expect("should be valid extra"),81 );82 }8384 Ok(())85 }86}