1use anyhow::{bail, Result};2use chrono::{DateTime, Utc};3use nixlike::format_nix;4use serde::{Deserialize, Deserializer, Serialize, Serializer};5use serde_json::{json, Value};6use std::collections::BTreeMap;7use tempfile::TempDir;8use tokio::{9 fs::{self, File},10 io::AsyncWriteExt,11 process::Command,12};1314use crate::command::CommandExt;1516#[derive(Serialize, Deserialize, Default)]17#[serde(rename_all = "camelCase")]18pub struct HostData {19 #[serde(default)]20 #[serde(skip_serializing_if = "String::is_empty")]21 pub encryption_key: String,22}2324#[derive(Serialize, Deserialize)]25#[serde(rename_all = "camelCase")]26pub struct FleetData {27 #[serde(default)]28 pub hosts: BTreeMap<String, HostData>,29 #[serde(default)]30 #[serde(skip_serializing_if = "BTreeMap::is_empty")]31 pub shared_secrets: BTreeMap<String, FleetSharedSecret>,32 #[serde(default)]33 #[serde(skip_serializing_if = "BTreeMap::is_empty")]34 pub host_secrets: BTreeMap<String, BTreeMap<String, FleetSecret>>,35}3637#[derive(Serialize, Deserialize)]38#[serde(rename_all = "camelCase")]39pub struct FleetSharedSecret {40 pub owners: Vec<String>,41 #[serde(flatten)]42 pub secret: FleetSecret,43}4445#[derive(Serialize, Deserialize)]46#[serde(rename_all = "camelCase")]47pub struct FleetSecret {48 #[serde(default)]49 #[serde(skip_serializing_if = "Option::is_none")]50 pub expire_at: Option<DateTime<Utc>>,51 #[serde(skip_serializing_if = "Option::is_none")]52 pub public: Option<String>,53 #[serde(54 default,55 skip_serializing_if = "Vec::is_empty",56 serialize_with = "as_z85",57 deserialize_with = "from_z85"58 )]59 pub secret: Vec<u8>,60}6162fn as_z85<S>(key: &[u8], serializer: S) -> Result<S::Ok, S::Error>63where64 S: Serializer,65{66 serializer.serialize_str(&z85::encode(key))67}6869fn from_z85<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>70where71 D: Deserializer<'de>,72{73 use serde::de::Error;74 String::deserialize(deserializer)75 .and_then(|string| z85::decode(string).map_err(|err| Error::custom(err.to_string())))76}7778pub async fn dummy_flake() -> Result<TempDir> {79 let data_str = fs::read_to_string("fleet.nix").await?;8081 let mut cmd = Command::new("nix");82 cmd.arg("flake").arg("metadata").arg("--json");8384 let flake_dir = tempfile::tempdir()?;85 let mut flake_nix = flake_dir.path().to_path_buf();86 flake_nix.push("flake.nix");87 8889 File::create(&flake_nix)90 .await?91 .write_all(92 format_nix(&format!(93 "94 {{95 outputs = {{self, ...}}: {{96 data = {data_str};97 }};98 }}99 "100 ))101 .as_bytes(),102 )103 .await?;104105 106 107 108 dbg!(&flake_nix);109 Ok(flake_dir)110}