1use anyhow::Result;2use chrono::{DateTime, Utc};3use nixlike::format_nix;4use serde::{Deserialize, Deserializer, Serialize, Serializer};5use std::collections::BTreeMap;6use tempfile::TempDir;7use tokio::{8 fs::{self, File},9 io::AsyncWriteExt,10 process::Command,11};1213#[derive(Serialize, Deserialize, Default)]14#[serde(rename_all = "camelCase")]15pub struct HostData {16 #[serde(default)]17 #[serde(skip_serializing_if = "String::is_empty")]18 pub encryption_key: String,19}2021#[derive(Serialize, Deserialize)]22#[serde(rename_all = "camelCase")]23pub struct FleetData {24 #[serde(default)]25 pub hosts: BTreeMap<String, HostData>,26 #[serde(default)]27 #[serde(skip_serializing_if = "BTreeMap::is_empty")]28 pub shared_secrets: BTreeMap<String, FleetSharedSecret>,29 #[serde(default)]30 #[serde(skip_serializing_if = "BTreeMap::is_empty")]31 pub host_secrets: BTreeMap<String, BTreeMap<String, FleetSecret>>,32}3334#[derive(Serialize, Deserialize, Clone)]35#[serde(rename_all = "camelCase")]36#[must_use]37pub struct FleetSharedSecret {38 pub owners: Vec<String>,39 #[serde(flatten)]40 pub secret: FleetSecret,41}4243#[derive(Serialize, Deserialize, Clone)]44#[serde(rename_all = "camelCase")]45#[must_use]46pub struct FleetSecret {47 #[serde(default = "Utc::now")]48 pub created_at: DateTime<Utc>,49 #[serde(default)]50 #[serde(skip_serializing_if = "Option::is_none", alias = "expire_at")]51 pub expires_at: Option<DateTime<Utc>>,52 #[serde(skip_serializing_if = "Option::is_none")]53 pub public: Option<String>,54 #[serde(55 default,56 skip_serializing_if = "Vec::is_empty",57 serialize_with = "as_z85",58 deserialize_with = "from_z85"59 )]60 pub secret: Vec<u8>,61}6263fn as_z85<S>(key: &[u8], serializer: S) -> Result<S::Ok, S::Error>64where65 S: Serializer,66{67 serializer.serialize_str(&z85::encode(key))68}6970fn from_z85<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>71where72 D: Deserializer<'de>,73{74 use serde::de::Error;75 String::deserialize(deserializer)76 .and_then(|string| z85::decode(string).map_err(|err| Error::custom(err.to_string())))77}787980#[allow(dead_code)]81pub async fn dummy_flake() -> Result<TempDir> {82 let data_str = fs::read_to_string("fleet.nix").await?;8384 let mut cmd = Command::new("nix");85 cmd.arg("flake").arg("metadata").arg("--json");8687 let flake_dir = tempfile::tempdir()?;88 let mut flake_nix = flake_dir.path().to_path_buf();89 flake_nix.push("flake.nix");90 9192 File::create(&flake_nix)93 .await?94 .write_all(95 format_nix(&format!(96 "97 {{98 outputs = {{self, ...}}: {{99 data = {data_str};100 }};101 }}102 "103 ))104 .as_bytes(),105 )106 .await?;107108 109 110 111 dbg!(&flake_nix);112 Ok(flake_dir)113}