git.delta.rocks / fleet / refs/commits / 32d9799cb7a1

difftreelog

fix remote copy

oqlpxzwuYaroslav Bolyukin2026-06-15parent: #12bc0d4.patch.diff

2 files changed

modifiedcmds/fleet/src/cmds/build_systems.rsdiffbeforeafterboth
after · cmds/fleet/src/cmds/build_systems.rs
1use std::{env::current_dir, os::unix::fs::symlink};23use anyhow::Result;4use camino::Utf8PathBuf;5use clap::Parser;6use fleet_base::{7	deploy::{DeployAction, deploy_task, upload_task},8	host::{Config, DeployKind, GenerationStorage},9	opts::FleetOpts,10};11use futures::{StreamExt as _, stream::FuturesUnordered};12use nix_eval::nix_go;13use tokio::task::spawn_blocking;14use tracing::{Instrument, error, field, info, info_span, warn};1516#[derive(Parser)]17pub struct Deploy {18	/// Disable automatic rollback19	#[clap(long)]20	disable_rollback: bool,21	/// Action to execute after system is built22	action: DeployAction,23}2425#[derive(Parser, Clone)]26pub struct BuildSystems {27	/// Attribute to build. Systems are deployed from "toplevel-fleet" attr, well-known used attributes28	/// are "sdImage"/"isoImage", and your configuration may include any other build attributes.29	#[clap(long, default_value = "toplevel-fleet")]30	build_attr: String,31}3233async fn build_task(config: Config, hostname: String, build_attr: &str) -> Result<Utf8PathBuf> {34	info!("building");35	let host = config.host(&hostname)?;36	// let action = Action::from(self.subcommand.clone());37	let nixos = host.nixos_config()?;38	let drv = nix_go!(nixos.system.build[{ build_attr }]);39	let out_output = spawn_blocking(move || drv.build("out"))40		.await41		.expect("system derivation build should not panic")?;4243	// We already have system profiles for backups.44	if !host.local {45		info!("adding gc root");46		let local = config.local_host();47		let plugin_id = local.ensure_nix_plugin().await?;48		let nix = local49			.remowt()50			.await?51			.plugin_endpoints::<remowt_fleet::NixClient<_>>(plugin_id);52		let profile = format!(53			"/nix/var/nix/profiles/{}-{hostname}",54			config.data.gc_root_prefix55		);56		nix.switch_profile(profile, out_output.clone())57			.await58			.map_err(|e| anyhow::anyhow!("{e:?}"))?59			.map_err(|e| anyhow::anyhow!("{e}"))?;60	}6162	Ok(out_output)63}6465impl BuildSystems {66	pub async fn run(self, config: &Config, opts: &FleetOpts) -> Result<()> {67		let hosts = opts.filter_skipped(config.list_hosts()?)?;68		let tasks = FuturesUnordered::new();69		let build_attr = self.build_attr.clone();70		for host in hosts {71			let config = config.clone();72			let span = info_span!("build", host = field::display(&host.name));73			let hostname = host.name;74			let build_attr = build_attr.clone();75			tasks.push(76				(async move {77					let built = match build_task(config, hostname.clone(), &build_attr).await {78						Ok(path) => path,79						Err(e) => {80							error!("failed to deploy host: {:?}", e);81							return;82						}83					};84					// TODO: Handle error85					let mut out = current_dir().expect("cwd exists");86					out.push(format!("built-{hostname}"));8788					info!("linking iso image to {:?}", out);89					if let Err(e) = symlink(built, out) {90						error!("failed to symlink: {e}")91					}92				})93				.instrument(span),94			);95		}96		tasks.collect::<Vec<()>>().await;97		Ok(())98	}99}100101impl Deploy {102	pub async fn run(self, config: &Config, opts: &FleetOpts) -> Result<()> {103		let hosts = opts.filter_skipped(config.list_hosts()?)?;104		let tasks = FuturesUnordered::new();105		for host in hosts.into_iter() {106			let config = config.clone();107			let span = info_span!("deploy", host = field::display(&host.name));108			let hostname = host.name.clone();109			let opts = opts.clone();110			if let Some(deploy_kind) = opts.action_attr::<DeployKind>(&host, "deploy_kind")? {111				host.set_deploy_kind(deploy_kind);112			};113			if let Some(destination) = opts.action_attr::<String>(&host, "dest")? {114				host.set_session_destination(destination);115			};116			if let Some(legacy) = opts.action_attr::<bool>(&host, "legacy_ssh_store")? {117				host.set_legacy_ssh_store(legacy);118			};119120			tasks.push(121				(async move {122					let built = match build_task(config.clone(), hostname.clone(), "toplevel-fleet")123						.await124					{125						Ok(path) => path,126						Err(e) => {127							error!("failed to build host system closure: {:?}", e);128							return;129						}130					};131132					let deploy_kind = match host.deploy_kind().await {133						Ok(v) => v,134						Err(e) => {135							error!("failed to query target deploy kind: {e}");136							return;137						}138					};139140					// TODO: Make disable_rollback a host attribute instead141					let mut disable_rollback = self.disable_rollback;142					if !disable_rollback && deploy_kind != DeployKind::Fleet {143						warn!("disabling rollback, as not supported by non-fleet deployment kinds");144						disable_rollback = true;145					}146147					let remote_path =148						match upload_task(&config, &host, GenerationStorage::Deployer, built).await149						{150							Ok(v) => v,151							Err(e) => {152								error!("upload failed: {e}");153								return;154							}155						};156157					if let Err(e) = deploy_task(158						self.action,159						&host,160						remote_path,161						match opts.action_attr(&host, "specialisation") {162							Ok(v) => v,163							_ => {164								error!("unreachable? failed to get specialization");165								return;166							}167						},168						disable_rollback,169					)170					.await171					{172						error!("activation failed: {e}");173					}174				})175				.instrument(span),176			);177		}178		tasks.collect::<Vec<()>>().await;179		Ok(())180	}181}
modifiedcrates/fleet-base/src/host.rsdiffbeforeafterboth
--- a/crates/fleet-base/src/host.rs
+++ b/crates/fleet-base/src/host.rs
@@ -24,7 +24,7 @@
 use tempfile::NamedTempFile;
 use time::UtcDateTime;
 use tokio::task::spawn_blocking;
-use tracing::{info, warn};
+use tracing::warn;
 
 use crate::fleetdata::{
 	FleetData, FleetSecretData, FleetSecretDistribution, FleetSecretPart, SecretOwner,
@@ -282,7 +282,7 @@
 
 	/// Client for this host's unprivileged agent.
 	pub async fn remowt(&self) -> Result<Remowt> {
-		Ok(self.connection().await?)
+		self.connection().await
 	}
 
 	pub fn ensure_nix_plugin(&self) -> Pin<Box<dyn Future<Output = Result<u16>> + Send + '_>> {
@@ -310,7 +310,7 @@
 						.rpc()
 						.wait_for_connection_to(Address::Plugin(NIX_PLUGIN_ID))
 						.await
-						.map_err(|e| anyhow!("failed to wait for plugin"))?;
+						.map_err(|_| anyhow!("failed to wait for plugin"))?;
 					anyhow::Ok(())
 				})
 				.await?;
@@ -427,8 +427,8 @@
 		let store = self.nix_store().await?;
 		{
 			let path = path.clone();
-			let store = eval_store();
-			spawn_blocking(move || store.copy_to(&store, path.as_ref()))
+			let eval_store = eval_store();
+			spawn_blocking(move || eval_store.copy_to(&store, path.as_ref()))
 				.await
 				.expect("copy_to panicked")
 				.context("copying closure to remote store")?;