git.delta.rocks / jrsonnet / refs/commits / 0920f041dcae

difftreelog

source

cmds/fleet/src/fleetdata.rs3.6 KiBsourcehistory
1use age::Recipient;2use anyhow::Result;3use chrono::{DateTime, Utc};4use itertools::Itertools;5use nixlike::format_nix;6use serde::{Deserialize, Deserializer, Serialize, Serializer};7use std::{8	collections::BTreeMap,9	io::{self, Cursor},10};11use tempfile::TempDir;12use tokio::{13	fs::{self, File},14	io::AsyncWriteExt,15	process::Command,16};1718#[derive(Serialize, Deserialize, Default)]19#[serde(rename_all = "camelCase")]20pub struct HostData {21	#[serde(default)]22	#[serde(skip_serializing_if = "String::is_empty")]23	pub encryption_key: String,24}2526#[derive(Serialize, Deserialize)]27#[serde(rename_all = "camelCase")]28pub struct FleetData {29	#[serde(default)]30	pub hosts: BTreeMap<String, HostData>,31	#[serde(default)]32	#[serde(skip_serializing_if = "BTreeMap::is_empty")]33	pub shared_secrets: BTreeMap<String, FleetSharedSecret>,34	#[serde(default)]35	#[serde(skip_serializing_if = "BTreeMap::is_empty")]36	pub host_secrets: BTreeMap<String, BTreeMap<String, FleetSecret>>,37}3839#[derive(Serialize, Deserialize, Clone)]40#[serde(rename_all = "camelCase")]41#[must_use]42pub struct FleetSharedSecret {43	pub owners: Vec<String>,44	#[serde(flatten)]45	pub secret: FleetSecret,46}4748#[derive(Serialize, Deserialize, Clone)]49pub struct SecretData(50	#[serde(51		default,52		skip_serializing_if = "Vec::is_empty",53		serialize_with = "as_z85",54		deserialize_with = "from_z85"55	)]56	pub Vec<u8>,57);58impl SecretData {59	/// Returns None if recipients.is_empty()60	pub fn encrypt(61		recipients: impl IntoIterator<Item = impl Recipient + Send + 'static>,62		data: Vec<u8>,63	) -> Option<Self> {64		let mut encrypted = vec![];65		let recipients = recipients66			.into_iter()67			.map(|v| Box::new(v) as Box<dyn Recipient + Send>)68			.collect_vec();69		let mut encryptor = age::Encryptor::with_recipients(recipients)?70			.wrap_output(&mut encrypted)71			.expect("in memory write");72		io::copy(&mut Cursor::new(data), &mut encryptor).expect("in memory copy");73		encryptor.finish().expect("in memory flush");74		Some(Self(encrypted))75	}76	pub fn encode_z85(&self) -> String {77		z85::encode(&self.0)78	}79	pub fn decode_z85(v: &str) -> Result<Self> {80		let v = z85::decode(v)?;81		Ok(Self(v))82	}83}8485#[derive(Serialize, Deserialize, Clone)]86#[serde(rename_all = "camelCase")]87#[must_use]88pub struct FleetSecret {89	#[serde(default = "Utc::now")]90	pub created_at: DateTime<Utc>,91	#[serde(default)]92	#[serde(skip_serializing_if = "Option::is_none", alias = "expire_at")]93	pub expires_at: Option<DateTime<Utc>>,94	#[serde(skip_serializing_if = "Option::is_none")]95	pub public: Option<String>,96	#[serde(skip_serializing_if = "Option::is_none")]97	pub secret: Option<SecretData>,98}99100fn as_z85<S>(key: &[u8], serializer: S) -> Result<S::Ok, S::Error>101where102	S: Serializer,103{104	serializer.serialize_str(&z85::encode(key))105}106107fn from_z85<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>108where109	D: Deserializer<'de>,110{111	use serde::de::Error;112	String::deserialize(deserializer)113		.and_then(|string| z85::decode(string).map_err(|err| Error::custom(err.to_string())))114}115116/// Isn't used yet117#[allow(dead_code)]118pub async fn dummy_flake() -> Result<TempDir> {119	let data_str = fs::read_to_string("fleet.nix").await?;120121	let mut cmd = Command::new("nix");122	cmd.arg("flake").arg("metadata").arg("--json");123124	let flake_dir = tempfile::tempdir()?;125	let mut flake_nix = flake_dir.path().to_path_buf();126	flake_nix.push("flake.nix");127	// flake_dir128129	File::create(&flake_nix)130		.await?131		.write_all(132			format_nix(&format!(133				"134						{{135							outputs = {{self, ...}}: {{136								data = {data_str};137							}};138						}}139					"140			))141			.as_bytes(),142		)143		.await?;144145	// std::thread::sleep(Duration::MAX);146	// flake_dir.close()147	// FIXME148	dbg!(&flake_nix);149	Ok(flake_dir)150}