git.delta.rocks / jrsonnet / refs/commits / 81e3c77f96ff

difftreelog

feat parallel host builds

Yaroslav Bolyukin2021-12-26parent: #6a5196a.patch.diff
in: trunk

6 files changed

modifiedcmds/fleet/src/cmds/build_systems.rsdiffbeforeafterboth
--- a/cmds/fleet/src/cmds/build_systems.rs
+++ b/cmds/fleet/src/cmds/build_systems.rs
@@ -1,6 +1,6 @@
 use std::{env::current_dir, time::Duration};
 
-use crate::{command::CommandExt, host::Config, nix::SYSTEMS_ATTRIBUTE};
+use crate::{command::CommandExt, host::Config};
 use anyhow::Result;
 use structopt::StructOpt;
 use tokio::{process::Command, task::LocalSet, time::sleep};
@@ -21,6 +21,8 @@
 	privileged_build: bool,
 	#[structopt(subcommand)]
 	subcommand: Subcommand,
+	#[structopt(long)]
+	show_trace: bool,
 }
 
 enum UploadAction {
@@ -79,6 +81,137 @@
 }
 
 impl BuildSystems {
+	async fn build_task(self, config: Config, host: String) -> Result<()> {
+		info!("building");
+		let built = {
+			let dir = tempfile::tempdir()?;
+			dir.path().to_owned()
+		};
+
+		let mut nix_build = if self.privileged_build {
+			let mut out = Command::new("sudo");
+			out.arg("nix");
+			out
+		} else {
+			Command::new("nix")
+		};
+		nix_build
+			.args(&[
+				"build",
+				"--impure",
+				"--json",
+				// "--show-trace",
+				"--no-link",
+				"--out-link",
+			])
+			.arg(&built)
+			.arg(config.configuration_attr_name(&format!(
+				"configuredSystems.{}.config.system.build.toplevel",
+				host
+			)));
+
+		if self.show_trace {
+			nix_build.arg("--show-trace");
+		}
+		if let Some(builders) = &self.builders {
+			nix_build.arg("--builders").arg(builders);
+		}
+		if let Some(jobs) = &self.jobs {
+			nix_build.arg("--max-jobs");
+			nix_build.arg(format!("{}", jobs));
+		}
+		if !self.fail_fast {
+			nix_build.arg("--keep-going");
+		}
+
+		nix_build.run_nix().await?;
+		let built = std::fs::canonicalize(built)?;
+
+		let action = Action::from(self.subcommand.clone());
+
+		match action {
+			Action::Upload(action) => {
+				if !config.is_local(&host) {
+					info!("uploading system closure");
+					let mut tries = 0;
+					loop {
+						match Command::new("nix")
+							.args(&["copy", "--to"])
+							.arg(format!("ssh://root@{}", host))
+							.arg(&built)
+							.inherit_stdio()
+							.run_nix()
+							.await
+						{
+							Ok(()) => break,
+							Err(e) if tries < 3 => {
+								tries += 1;
+								warn!("Copy failure ({}/3): {}", tries, e);
+								sleep(Duration::from_millis(5000)).await;
+							}
+							Err(e) => return Err(e),
+						}
+					}
+				}
+				if let Some(action) = action {
+					if action.should_switch_profile() {
+						info!("switching generation");
+						config
+							.command_on(&host, "nix-env", true)
+							.args(&["-p", "/nix/var/nix/profiles/system", "--set"])
+							.arg(&built)
+							.inherit_stdio()
+							.run()
+							.await?;
+					}
+					info!("executing activation script");
+					let mut switch_script = built.clone();
+					switch_script.push("bin");
+					switch_script.push("switch-to-configuration");
+					config
+						.command_on(&host, switch_script, true)
+						.arg(action.name())
+						.inherit_stdio()
+						.run()
+						.await?;
+				}
+			}
+			Action::Package(PackageAction::SdImage) => {
+				let mut out = current_dir()?;
+				out.push(format!("sd-image-{}", host));
+
+				info!("building sd image to {:?}", out);
+				let mut nix_build = if self.privileged_build {
+					let mut out = Command::new("sudo");
+					out.arg("nix");
+					out
+				} else {
+					Command::new("nix")
+				};
+				nix_build
+					.args(&["build", "--impure", "--no-link", "--out-link"])
+					.arg(&out)
+					.arg(config.configuration_attr_name(&format!(
+						"configuredSystems.{}.config.system.build.sdImage",
+						host,
+					)));
+				if let Some(builders) = &self.builders {
+					nix_build.arg("--builders").arg(builders);
+				}
+				if let Some(jobs) = &self.jobs {
+					nix_build.arg("--max-jobs");
+					nix_build.arg(format!("{}", jobs));
+				}
+				if !self.fail_fast {
+					nix_build.arg("--keep-going");
+				}
+
+				nix_build.inherit_stdio().run_nix().await?;
+			}
+		};
+		Ok(())
+	}
+
 	pub async fn run(self, config: &Config) -> Result<()> {
 		let hosts = config.list_hosts().await?;
 		let set = LocalSet::new();
@@ -93,139 +226,12 @@
 			let span = info_span!("deployment", host = field::display(&host));
 			set.spawn_local(
 				(async move {
-					let res: Result<()> = try {
-						info!("building");
-						let built = {
-							let dir = tempfile::tempdir()?;
-							dir.path().to_owned()
-						};
-
-						let mut nix_build = if this.privileged_build {
-							let mut out = Command::new("sudo");
-							out.arg("nix");
-							out
-						} else {
-							Command::new("nix")
-						};
-						nix_build
-							.args(&[
-								"build",
-								"--impure",
-								"--json",
-								// "--show-trace",
-								"--no-link",
-								"--out-link",
-							])
-							.arg(&built)
-							.arg(format!(
-								"{}.{}.config.system.build.toplevel",
-								SYSTEMS_ATTRIBUTE, host,
-							));
-
-						if let Some(builders) = &this.builders {
-							nix_build.arg("--builders").arg(builders);
-						}
-						if let Some(jobs) = &this.jobs {
-							nix_build.arg("--max-jobs");
-							nix_build.arg(format!("{}", jobs));
-						}
-						if !this.fail_fast {
-							nix_build.arg("--keep-going");
-						}
-
-						nix_build.run_nix().await?;
-						let built = std::fs::canonicalize(built)?;
-
-						let action = Action::from(this.subcommand.clone());
-
-						match action {
-							Action::Upload(action) => {
-								if !config.is_local(&host) {
-									info!("uploading system closure");
-									let mut tries = 0;
-									loop {
-										match Command::new("nix")
-											.args(&["copy", "--to"])
-											.arg(format!("ssh://root@{}", host))
-											.arg(&built)
-											.inherit_stdio()
-											.run_nix()
-											.await
-										{
-											Ok(()) => break,
-											Err(e) if tries < 3 => {
-												tries += 1;
-												warn!("Copy failure ({}/3): {}", tries, e);
-												sleep(Duration::from_millis(5000)).await;
-											}
-											Err(e) => return Err(e),
-										}
-									}
-								}
-								if let Some(action) = action {
-									if action.should_switch_profile() {
-										info!("switching generation");
-										config
-											.command_on(&host, "nix-env", true)
-											.args(&["-p", "/nix/var/nix/profiles/system", "--set"])
-											.arg(&built)
-											.inherit_stdio()
-											.run()
-											.await?;
-									}
-									info!("executing activation script");
-									let mut switch_script = built.clone();
-									switch_script.push("bin");
-									switch_script.push("switch-to-configuration");
-									config
-										.command_on(&host, switch_script, true)
-										.arg(action.name())
-										.inherit_stdio()
-										.run()
-										.await?;
-								}
-							}
-							Action::Package(PackageAction::SdImage) => {
-								let mut out = current_dir()?;
-								out.push(format!("sd-image-{}", host));
-
-								info!("building sd image to {:?}", out);
-								let mut nix_build = if this.privileged_build {
-									let mut out = Command::new("sudo");
-									out.arg("nix");
-									out
-								} else {
-									Command::new("nix")
-								};
-								nix_build
-									.args(&["build", "--impure", "--no-link", "--out-link"])
-									.arg(&out)
-									.arg(format!(
-										"{}.{}.config.system.build.sdImage",
-										SYSTEMS_ATTRIBUTE, host,
-									));
-								if let Some(builders) = &this.builders {
-									nix_build.arg("--builders").arg(builders);
-								}
-								if let Some(jobs) = &this.jobs {
-									nix_build.arg("--max-jobs");
-									nix_build.arg(format!("{}", jobs));
-								}
-								if !this.fail_fast {
-									nix_build.arg("--keep-going");
-								}
-
-								nix_build.inherit_stdio().run_nix().await?;
-							}
-						};
-					};
-					match res {
+					match this.build_task(config, host).await {
 						Ok(_) => {}
 						Err(e) => {
 							error!("failed to deploy host: {}", e)
 						}
 					}
-					Ok(())
 				})
 				.instrument(span),
 			);
modifiedcmds/fleet/src/command.rsdiffbeforeafterboth
150 info!(target: "nix", "{}", text)150 info!(target: "nix", "{}", text)
151 }151 }
152 },152 },
153 NixLog::Start { text, level: 0, typ: 108, .. } if text == "" => {
154 // Cache lookup? Coupled with copy log
155 },
156 NixLog::Start { text, level: 4, typ: 101, .. } if text.starts_with("downloading ") => {
157 // NAR downloading, coupled with copy log
158 }
153 NixLog::Stop { .. } => {},159 NixLog::Stop { .. } => {},
154 NixLog::Result { .. } => {},160 NixLog::Result { .. } => {},
155 _ => warn!("unknown log: {:?}", log)161 _ => warn!("unknown log: {:?}", log)
modifiedcmds/fleet/src/host.rsdiffbeforeafterboth
--- a/cmds/fleet/src/host.rs
+++ b/cmds/fleet/src/host.rs
@@ -16,6 +16,7 @@
 use crate::{command::CommandExt, fleetdata::FleetData};
 
 pub struct FleetConfigInternals {
+	pub local_system: String,
 	pub directory: PathBuf,
 	pub opts: FleetOpts,
 	pub data: RefCell<FleetData>,
@@ -66,17 +67,21 @@
 		}
 	}
 
-	pub fn full_attr_name(&self, attr_name: &str) -> OsString {
+	pub fn configuration_attr_name(&self, name: &str) -> OsString {
 		let mut str = self.directory.as_os_str().to_owned();
 		str.push("#");
-		str.push(attr_name);
+		str.push(&format!(
+			"fleetConfigurations.default.{}.{}",
+			self.local_system,
+			name
+		));
 		str
 	}
 
 	pub async fn list_hosts(&self) -> Result<Vec<String>> {
 		Command::new("nix")
 			.arg("eval")
-			.arg(self.full_attr_name("fleetConfigurations.default.configuredHosts"))
+			.arg(self.configuration_attr_name("configuredHosts"))
 			.args(&["--apply", "builtins.attrNames", "--json", "--show-trace"])
 			.run_nix_json()
 			.await
@@ -84,10 +89,7 @@
 	pub async fn config_attr<T: DeserializeOwned>(&self, host: &str, attr: &str) -> Result<T> {
 		Command::new("nix")
 			.arg("eval")
-			.arg(self.full_attr_name(&format!(
-				"fleetConfigurations.default.configuredSystems.{}.config.{}",
-				host, attr
-			)))
+			.arg(self.configuration_attr_name(&format!("configuredSystems.{}.config.{}", host, attr)))
 			.args(&["--json", "--show-trace"])
 			.run_nix_json()
 			.await
@@ -129,10 +131,14 @@
 	/// Host, which should be threaten as current machine
 	#[structopt(long)]
 	pub localhost: Option<String>,
+
+	#[structopt(long, default_value = "x86_64-linux")]
+	pub local_system: String,
 }
 
 impl FleetOpts {
 	pub fn build(mut self) -> Result<Config> {
+		let local_system = self.local_system.clone();
 		if self.localhost.is_none() {
 			self.localhost
 				.replace(hostname::get().unwrap().to_str().unwrap().to_owned());
@@ -148,6 +154,7 @@
 			opts: self,
 			directory,
 			data,
+			local_system,
 		})))
 	}
 }
modifiedcmds/fleet/src/main.rsdiffbeforeafterboth
--- a/cmds/fleet/src/main.rs
+++ b/cmds/fleet/src/main.rs
@@ -1,11 +1,7 @@
-#![feature(try_blocks)]
-
 pub mod command;
 pub mod host;
 pub mod keys;
-
 pub mod cmds;
-pub mod nix;
 
 mod fleetdata;
 
deletedcmds/fleet/src/nix.rsdiffbeforeafterboth
--- a/cmds/fleet/src/nix.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-pub const HOSTS_ATTRIBUTE: &str = ".#fleetConfigurations.default.configuredHosts";
-pub const SECRETS_ATTRIBUTE: &str = ".#fleetConfigurations.default.configuredSecrets";
-pub const SYSTEMS_ATTRIBUTE: &str = ".#fleetConfigurations.default.configuredSystems";
modifiedlib/default.nixdiffbeforeafterboth
--- a/lib/default.nix
+++ b/lib/default.nix
@@ -2,15 +2,16 @@
   fleetConfiguration = { data, nixpkgs, hosts, ... }@allConfig:
     let
       config = builtins.removeAttrs allConfig [ "nixpkgs" "data" ];
+      fleetLib = import ./fleetLib.nix {
+        inherit nixpkgs hosts;
+      };
     in
-    flake-utils.lib.eachDefaultSystem (system: rec {
+    nixpkgs.lib.genAttrs flake-utils.lib.defaultSystems (system: rec {
       root = nixpkgs.lib.evalModules {
         modules = (import ../modules/fleet/_modules.nix) ++ [ config data ];
         specialArgs = {
           inherit nixpkgs;
-          fleet = import ./fleetLib.nix {
-            inherit nixpkgs hosts;
-          };
+          fleet = fleetLib;
         };
       };
       configuredHosts = root.config.hosts;
@@ -27,12 +28,16 @@
                   else [ ]
                 ) ++ [
                   ({ ... }: {
+                    nixpkgs.system = system;
                     nixpkgs.localSystem.system = system;
                     nixpkgs.crossSystem = if system == configuredHosts.${name}.system then null else {
                       system = configuredHosts.${name}.system;
                     };
                   })
                 ];
+                specialArgs = {
+                  fleet = fleetLib.hostsToAttrs (host: configuredSystems.${host}.config);
+                };
               };
             }
           )