1use std::{2 collections::{BTreeMap, HashMap},3 path::PathBuf,4};56use anyhow::{bail, Context, Result};7use clap::Parser;8use fleet_base::host::Config;9use nix_eval::nix_go;10use serde::Deserialize;11use serde_json::Value;12use tokio::{fs::copy, process::Command};1314#[derive(Deserialize)]15pub struct TfData {16 17 #[allow(dead_code)]18 managed: bool,19 20 #[serde(default)]21 #[serde(skip_serializing_if = "BTreeMap::is_empty")]22 pub hosts: BTreeMap<String, Value>,23}2425#[derive(Parser)]26pub enum Tf {27 28 Generate,29 30 Refresh,31}32impl Tf {33 pub async fn run(&self, config: &Config) -> Result<()> {34 match self {35 Tf::Generate => {36 let system = &config.local_system;37 let config = &config.config_field;38 let data: HashMap<String, PathBuf> = nix_go!(config.tf({ system })).build().await?;39 let data = &data["out"];4041 copy(data, "fleet.tf.json").await?;42 }43 Tf::Refresh => {44 let cmd = Command::new("terraform").arg("refresh").status().await?;45 if !cmd.success() {46 bail!("terraform refresh failed")47 }4849 let data = Command::new("terraform")50 .arg("output")51 .arg("-json")52 .arg("fleet")53 .output()54 .await?;55 let tf_data: TfData = serde_json::from_slice(&data.stdout)56 .context("failed to parse terraform fleet output")?;5758 let mut data = config.data();59 data.extra.insert(60 "terraformHosts".to_owned(),61 serde_json::to_value(tf_data.hosts).expect("should be valid extra"),62 );63 }64 }6566 Ok(())67 }68}