difftreelog
feat parallel host builds
in: trunk
6 files changed
cmds/fleet/src/cmds/build_systems.rsdiffbeforeafterboth1use std::{env::current_dir, time::Duration};1use std::{env::current_dir, time::Duration};223use crate::{command::CommandExt, host::Config, nix::SYSTEMS_ATTRIBUTE};3use crate::{command::CommandExt, host::Config};4use anyhow::Result;4use anyhow::Result;5use structopt::StructOpt;5use structopt::StructOpt;6use tokio::{process::Command, task::LocalSet, time::sleep};6use tokio::{process::Command, task::LocalSet, time::sleep};21 privileged_build: bool,21 privileged_build: bool,22 #[structopt(subcommand)]22 #[structopt(subcommand)]23 subcommand: Subcommand,23 subcommand: Subcommand,24 #[structopt(long)]25 show_trace: bool,24}26}252726enum UploadAction {28enum UploadAction {79}81}808281impl BuildSystems {83impl BuildSystems {82 pub async fn run(self, config: &Config) -> Result<()> {84 async fn build_task(self, config: Config, host: String) -> Result<()> {83 let hosts = config.list_hosts().await?;84 let set = LocalSet::new();85 let this = &self;86 for host in hosts.iter() {87 if config.should_skip(host) {88 continue;89 }90 let config = config.clone();91 let host = host.clone();92 let this = this.clone();93 let span = info_span!("deployment", host = field::display(&host));94 set.spawn_local(95 (async move {96 let res: Result<()> = try {97 info!("building");85 info!("building");98 let built = {86 let built = {99 let dir = tempfile::tempdir()?;87 let dir = tempfile::tempdir()?;100 dir.path().to_owned()88 dir.path().to_owned()101 };89 };10290103 let mut nix_build = if this.privileged_build {91 let mut nix_build = if self.privileged_build {104 let mut out = Command::new("sudo");92 let mut out = Command::new("sudo");105 out.arg("nix");93 out.arg("nix");106 out94 out117 "--out-link",105 "--out-link",118 ])106 ])119 .arg(&built)107 .arg(&built)120 .arg(format!(108 .arg(config.configuration_attr_name(&format!(121 "{}.{}.config.system.build.toplevel",109 "configuredSystems.{}.config.system.build.toplevel",122 SYSTEMS_ATTRIBUTE, host,110 host123 ));111 )));124112113 if self.show_trace {114 nix_build.arg("--show-trace");115 }125 if let Some(builders) = &this.builders {116 if let Some(builders) = &self.builders {126 nix_build.arg("--builders").arg(builders);117 nix_build.arg("--builders").arg(builders);127 }118 }128 if let Some(jobs) = &this.jobs {119 if let Some(jobs) = &self.jobs {129 nix_build.arg("--max-jobs");120 nix_build.arg("--max-jobs");130 nix_build.arg(format!("{}", jobs));121 nix_build.arg(format!("{}", jobs));131 }122 }132 if !this.fail_fast {123 if !self.fail_fast {133 nix_build.arg("--keep-going");124 nix_build.arg("--keep-going");134 }125 }135126136 nix_build.run_nix().await?;127 nix_build.run_nix().await?;137 let built = std::fs::canonicalize(built)?;128 let built = std::fs::canonicalize(built)?;138129139 let action = Action::from(this.subcommand.clone());130 let action = Action::from(self.subcommand.clone());140131141 match action {132 match action {142 Action::Upload(action) => {133 Action::Upload(action) => {190 out.push(format!("sd-image-{}", host));181 out.push(format!("sd-image-{}", host));191182192 info!("building sd image to {:?}", out);183 info!("building sd image to {:?}", out);193 let mut nix_build = if this.privileged_build {184 let mut nix_build = if self.privileged_build {194 let mut out = Command::new("sudo");185 let mut out = Command::new("sudo");195 out.arg("nix");186 out.arg("nix");196 out187 out200 nix_build191 nix_build201 .args(&["build", "--impure", "--no-link", "--out-link"])192 .args(&["build", "--impure", "--no-link", "--out-link"])202 .arg(&out)193 .arg(&out)203 .arg(format!(194 .arg(config.configuration_attr_name(&format!(204 "{}.{}.config.system.build.sdImage",195 "configuredSystems.{}.config.system.build.sdImage",205 SYSTEMS_ATTRIBUTE, host,196 host,206 ));197 )));207 if let Some(builders) = &this.builders {198 if let Some(builders) = &self.builders {208 nix_build.arg("--builders").arg(builders);199 nix_build.arg("--builders").arg(builders);209 }200 }210 if let Some(jobs) = &this.jobs {201 if let Some(jobs) = &self.jobs {211 nix_build.arg("--max-jobs");202 nix_build.arg("--max-jobs");212 nix_build.arg(format!("{}", jobs));203 nix_build.arg(format!("{}", jobs));213 }204 }214 if !this.fail_fast {205 if !self.fail_fast {215 nix_build.arg("--keep-going");206 nix_build.arg("--keep-going");216 }207 }217208218 nix_build.inherit_stdio().run_nix().await?;209 nix_build.inherit_stdio().run_nix().await?;219 }210 }220 };211 };221 };222 match res {223 Ok(_) => {}224 Err(e) => {225 error!("failed to deploy host: {}", e)226 }227 }228 Ok(())229 })230 .instrument(span),231 );232 }233 set.await;234 Ok(())212 Ok(())235 }213 }214215 pub async fn run(self, config: &Config) -> Result<()> {216 let hosts = config.list_hosts().await?;217 let set = LocalSet::new();218 let this = &self;219 for host in hosts.iter() {220 if config.should_skip(host) {221 continue;222 }223 let config = config.clone();224 let host = host.clone();225 let this = this.clone();226 let span = info_span!("deployment", host = field::display(&host));227 set.spawn_local(228 (async move {229 match this.build_task(config, host).await {230 Ok(_) => {}231 Err(e) => {232 error!("failed to deploy host: {}", e)233 }234 }235 })236 .instrument(span),237 );238 }239 set.await;240 Ok(())241 }236}242}237243cmds/fleet/src/command.rsdiffbeforeafterboth150 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 log155 },156 NixLog::Start { text, level: 4, typ: 101, .. } if text.starts_with("downloading ") => {157 // NAR downloading, coupled with copy log158 }153 NixLog::Stop { .. } => {},159 NixLog::Stop { .. } => {},154 NixLog::Result { .. } => {},160 NixLog::Result { .. } => {},155 _ => warn!("unknown log: {:?}", log)161 _ => warn!("unknown log: {:?}", log)cmds/fleet/src/host.rsdiffbeforeafterboth16use crate::{command::CommandExt, fleetdata::FleetData};16use crate::{command::CommandExt, fleetdata::FleetData};171718pub struct FleetConfigInternals {18pub struct FleetConfigInternals {19 pub local_system: String,19 pub directory: PathBuf,20 pub directory: PathBuf,20 pub opts: FleetOpts,21 pub opts: FleetOpts,21 pub data: RefCell<FleetData>,22 pub data: RefCell<FleetData>,66 }67 }67 }68 }686969 pub fn full_attr_name(&self, attr_name: &str) -> OsString {70 pub fn configuration_attr_name(&self, name: &str) -> OsString {70 let mut str = self.directory.as_os_str().to_owned();71 let mut str = self.directory.as_os_str().to_owned();71 str.push("#");72 str.push("#");72 str.push(attr_name);73 str.push(&format!(74 "fleetConfigurations.default.{}.{}",75 self.local_system,76 name77 ));73 str78 str74 }79 }758076 pub async fn list_hosts(&self) -> Result<Vec<String>> {81 pub async fn list_hosts(&self) -> Result<Vec<String>> {77 Command::new("nix")82 Command::new("nix")78 .arg("eval")83 .arg("eval")79 .arg(self.full_attr_name("fleetConfigurations.default.configuredHosts"))84 .arg(self.configuration_attr_name("configuredHosts"))80 .args(&["--apply", "builtins.attrNames", "--json", "--show-trace"])85 .args(&["--apply", "builtins.attrNames", "--json", "--show-trace"])81 .run_nix_json()86 .run_nix_json()82 .await87 .await83 }88 }84 pub async fn config_attr<T: DeserializeOwned>(&self, host: &str, attr: &str) -> Result<T> {89 pub async fn config_attr<T: DeserializeOwned>(&self, host: &str, attr: &str) -> Result<T> {85 Command::new("nix")90 Command::new("nix")86 .arg("eval")91 .arg("eval")87 .arg(self.full_attr_name(&format!(92 .arg(self.configuration_attr_name(&format!("configuredSystems.{}.config.{}", host, attr)))88 "fleetConfigurations.default.configuredSystems.{}.config.{}",89 host, attr90 )))91 .args(&["--json", "--show-trace"])93 .args(&["--json", "--show-trace"])130 #[structopt(long)]132 #[structopt(long)]131 pub localhost: Option<String>,133 pub localhost: Option<String>,134135 #[structopt(long, default_value = "x86_64-linux")]136 pub local_system: String,132}137}133138134impl FleetOpts {139impl FleetOpts {135 pub fn build(mut self) -> Result<Config> {140 pub fn build(mut self) -> Result<Config> {141 let local_system = self.local_system.clone();136 if self.localhost.is_none() {142 if self.localhost.is_none() {137 self.localhost143 self.localhost138 .replace(hostname::get().unwrap().to_str().unwrap().to_owned());144 .replace(hostname::get().unwrap().to_str().unwrap().to_owned());148 opts: self,154 opts: self,149 directory,155 directory,150 data,156 data,157 local_system,151 })))158 })))152 }159 }153}160}cmds/fleet/src/main.rsdiffbeforeafterboth1#![feature(try_blocks)]23pub mod command;1pub mod command;4pub mod host;2pub mod host;5pub mod keys;3pub mod keys;67pub mod cmds;4pub mod cmds;8pub mod nix;9510mod fleetdata;6mod fleetdata;117cmds/fleet/src/nix.rsdiffbeforeafterbothno changes
lib/default.nixdiffbeforeafterboth2 fleetConfiguration = { data, nixpkgs, hosts, ... }@allConfig:2 fleetConfiguration = { data, nixpkgs, hosts, ... }@allConfig:3 let3 let4 config = builtins.removeAttrs allConfig [ "nixpkgs" "data" ];4 config = builtins.removeAttrs allConfig [ "nixpkgs" "data" ];5 fleetLib = import ./fleetLib.nix {6 inherit nixpkgs hosts;7 };5 in8 in6 flake-utils.lib.eachDefaultSystem (system: rec {9 nixpkgs.lib.genAttrs flake-utils.lib.defaultSystems (system: rec {7 root = nixpkgs.lib.evalModules {10 root = nixpkgs.lib.evalModules {8 modules = (import ../modules/fleet/_modules.nix) ++ [ config data ];11 modules = (import ../modules/fleet/_modules.nix) ++ [ config data ];9 specialArgs = {12 specialArgs = {10 inherit nixpkgs;13 inherit nixpkgs;11 fleet = import ./fleetLib.nix {14 fleet = fleetLib;12 inherit nixpkgs hosts;13 };14 };15 };15 };16 };16 configuredHosts = root.config.hosts;17 configuredHosts = root.config.hosts;27 else [ ]28 else [ ]28 ) ++ [29 ) ++ [29 ({ ... }: {30 ({ ... }: {31 nixpkgs.system = system;30 nixpkgs.localSystem.system = system;32 nixpkgs.localSystem.system = system;31 nixpkgs.crossSystem = if system == configuredHosts.${name}.system then null else {33 nixpkgs.crossSystem = if system == configuredHosts.${name}.system then null else {32 system = configuredHosts.${name}.system;34 system = configuredHosts.${name}.system;33 };35 };34 })36 })35 ];37 ];38 specialArgs = {39 fleet = fleetLib.hostsToAttrs (host: configuredSystems.${host}.config);40 };36 };41 };37 }42 }38 )43 )