git.delta.rocks / jrsonnet / refs/commits / 590ae3fadf8f

difftreelog

source

cmds/fleet/src/host.rs3.8 KiBsourcehistory
1use std::{2	cell::{Ref, RefCell, RefMut},3	env::current_dir,4	ffi::{OsStr, OsString},5	ops::Deref,6	path::PathBuf,7	sync::Arc,8};910use anyhow::Result;11use clap::{ArgGroup, Parser};12use serde::de::DeserializeOwned;13use tokio::process::Command;1415use crate::{command::CommandExt, fleetdata::FleetData};1617pub struct FleetConfigInternals {18	pub local_system: String,19	pub directory: PathBuf,20	pub opts: FleetOpts,21	pub data: RefCell<FleetData>,22}2324#[derive(Clone)]25pub struct Config(Arc<FleetConfigInternals>);2627impl Deref for Config {28	type Target = FleetConfigInternals;2930	fn deref(&self) -> &Self::Target {31		&self.032	}33}3435impl Config {36	pub fn should_skip(&self, host: &str) -> bool {37		if !self.opts.skip.is_empty() {38			self.opts.skip.iter().any(|h| h as &str == host)39		} else if !self.opts.only.is_empty() {40			!self.opts.only.iter().any(|h| h as &str == host)41		} else {42			false43		}44	}45	pub fn is_local(&self, host: &str) -> bool {46		self.opts.localhost.as_ref().map(|s| s as &str) == Some(host)47	}4849	pub fn command_on(&self, host: &str, program: impl AsRef<OsStr>, sudo: bool) -> Command {50		if self.is_local(host) {51			if sudo {52				let mut cmd = Command::new("sudo");53				cmd.arg(program);54				cmd55			} else {56				Command::new(program)57			}58		} else {59			let mut cmd = Command::new("ssh");60			cmd.arg(host).arg("--");61			if sudo {62				cmd.arg("sudo");63			}64			cmd.arg(program);65			cmd66		}67	}6869	pub fn configuration_attr_name(&self, name: &str) -> OsString {70		let mut str = self.directory.as_os_str().to_owned();71		str.push("#");72		str.push(&format!(73			"fleetConfigurations.default.{}.{}",74			self.local_system, name75		));76		str77	}7879	pub async fn list_hosts(&self) -> Result<Vec<String>> {80		Command::new("nix")81			.arg("eval")82			.arg(self.configuration_attr_name("configuredHosts"))83			.args(&["--apply", "builtins.attrNames", "--json", "--show-trace"])84			.run_nix_json()85			.await86	}87	pub async fn config_attr<T: DeserializeOwned>(&self, host: &str, attr: &str) -> Result<T> {88		Command::new("nix")89			.arg("eval")90			.arg(91				self.configuration_attr_name(&format!(92					"configuredSystems.{}.config.{}",93					host, attr94				)),95			)96			.args(&["--json", "--show-trace"])97			.run_nix_json()98			.await99	}100101	pub fn data(&self) -> Ref<FleetData> {102		self.data.borrow()103	}104	pub fn data_mut(&self) -> RefMut<FleetData> {105		self.data.borrow_mut()106	}107108	pub fn save(&self) -> Result<()> {109		let mut fleet_data_path = self.directory.clone();110		fleet_data_path.push("fleet.nix");111		let data = nixlike::serialize(&self.data() as &FleetData)?;112		std::fs::write(113			fleet_data_path,114			format!(115				"# This file contains fleet state and shouldn't be edited by hand\n\n{}\n",116				data117			),118		)?;119		Ok(())120	}121}122123#[derive(Parser, Clone)]124#[clap(group = ArgGroup::new("target_hosts"))]125pub struct FleetOpts {126	/// All hosts except those would be skipped127	#[clap(long, number_of_values = 1, group = "target_hosts")]128	only: Vec<String>,129130	/// Hosts to skip131	#[clap(long, number_of_values = 1, group = "target_hosts")]132	skip: Vec<String>,133134	/// Host, which should be threaten as current machine135	#[clap(long)]136	pub localhost: Option<String>,137138	// TODO: unhardcode x86_64-linux139	/// Override detected system for host, to perform builds via140	/// binfmt-declared qemu instead of trying to crosscompile141	#[clap(long, default_value = "x86_64-linux")]142	pub local_system: String,143}144145impl FleetOpts {146	pub fn build(mut self) -> Result<Config> {147		let local_system = self.local_system.clone();148		if self.localhost.is_none() {149			self.localhost150				.replace(hostname::get().unwrap().to_str().unwrap().to_owned());151		}152		let directory = current_dir()?;153154		let mut fleet_data_path = directory.clone();155		fleet_data_path.push("fleet.nix");156		let bytes = std::fs::read_to_string(fleet_data_path)?;157		let data = nixlike::parse_str(&bytes)?;158159		Ok(Config(Arc::new(FleetConfigInternals {160			opts: self,161			directory,162			data,163			local_system,164		})))165	}166}