--- 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), ); --- a/cmds/fleet/src/command.rs +++ b/cmds/fleet/src/command.rs @@ -150,6 +150,12 @@ info!(target: "nix", "{}", text) } }, + NixLog::Start { text, level: 0, typ: 108, .. } if text == "" => { + // Cache lookup? Coupled with copy log + }, + NixLog::Start { text, level: 4, typ: 101, .. } if text.starts_with("downloading ") => { + // NAR downloading, coupled with copy log + } NixLog::Stop { .. } => {}, NixLog::Result { .. } => {}, _ => warn!("unknown log: {:?}", log) --- 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, @@ -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> { 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(&self, host: &str, attr: &str) -> Result { 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, + + #[structopt(long, default_value = "x86_64-linux")] + pub local_system: String, } impl FleetOpts { pub fn build(mut self) -> Result { + 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, }))) } } --- 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; --- 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"; --- 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); + }; }; } )