diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/bin/activate.rs (renamed from src/activate.rs) | 21 | ||||
-rw-r--r-- | src/bin/deploy.rs (renamed from src/main.rs) | 203 | ||||
-rw-r--r-- | src/data.rs (renamed from src/utils/data.rs) | 0 | ||||
-rw-r--r-- | src/deploy.rs (renamed from src/utils/deploy.rs) | 111 | ||||
-rw-r--r-- | src/lib.rs (renamed from src/utils/mod.rs) | 22 | ||||
-rw-r--r-- | src/push.rs (renamed from src/utils/push.rs) | 95 |
6 files changed, 228 insertions, 224 deletions
diff --git a/src/activate.rs b/src/bin/activate.rs index 6569bdd..2f13b44 100644 --- a/src/activate.rs +++ b/src/bin/activate.rs @@ -23,12 +23,8 @@ use thiserror::Error; #[macro_use] extern crate log; -#[macro_use] extern crate serde_derive; -#[macro_use] -mod utils; - /// Remote activation utility for deploy-rs #[derive(Clap, Debug)] #[clap(version = "1.0", author = "Serokell <https://serokell.io/>")] @@ -225,7 +221,7 @@ pub async fn activation_confirmation( confirm_timeout: u16, closure: String, ) -> Result<(), ActivationConfirmationError> { - let lock_path = utils::make_lock_path(&temp_path, &closure); + let lock_path = deploy::make_lock_path(&temp_path, &closure); debug!("Ensuring parent directory exists for canary file"); @@ -288,7 +284,7 @@ pub enum WaitError { Waiting(#[from] DangerZoneError), } pub async fn wait(temp_path: String, closure: String) -> Result<(), WaitError> { - let lock_path = utils::make_lock_path(&temp_path, &closure); + let lock_path = deploy::make_lock_path(&temp_path, &closure); let (created, done) = mpsc::channel(1); @@ -429,19 +425,19 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> { // Ensure that this process stays alive after the SSH connection dies let mut signals = Signals::new(&[SIGHUP])?; std::thread::spawn(move || { - for sig in signals.forever() { + for _ in signals.forever() { println!("Received NOHUP - ignoring..."); } }); let opts: Opts = Opts::parse(); - utils::init_logger( + deploy::init_logger( opts.debug_logs, opts.log_dir.as_deref(), match opts.subcmd { - SubCommand::Activate(_) => utils::LoggerType::Activate, - SubCommand::Wait(_) => utils::LoggerType::Wait, + SubCommand::Activate(_) => deploy::LoggerType::Activate, + SubCommand::Wait(_) => deploy::LoggerType::Wait, }, )?; @@ -464,7 +460,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> { match r { Ok(()) => (), - Err(err) => good_panic!("{}", err), + Err(err) => { + error!("{}", err); + std::process::exit(1) + } } Ok(()) diff --git a/src/main.rs b/src/bin/deploy.rs index 1544fed..caf3d4e 100644 --- a/src/main.rs +++ b/src/bin/deploy.rs @@ -18,9 +18,6 @@ extern crate log; #[macro_use] extern crate serde_derive; -#[macro_use] -mod utils; - /// Simple Rust rewrite of a simple Nix Flake deployment tool #[derive(Clap, Debug)] #[clap(version = "1.0", author = "Serokell <https://serokell.io/>")] @@ -103,9 +100,9 @@ async fn test_flake_support() -> Result<bool, std::io::Error> { #[derive(Error, Debug)] enum CheckDeploymentError { #[error("Failed to execute Nix checking command: {0}")] - NixCheckError(#[from] std::io::Error), + NixCheck(#[from] std::io::Error), #[error("Nix checking command resulted in a bad exit code: {0:?}")] - NixCheckExitError(Option<i32>), + NixCheckExit(Option<i32>), } async fn check_deployment( @@ -141,7 +138,7 @@ async fn check_deployment( match check_status.code() { Some(0) => (), - a => return Err(CheckDeploymentError::NixCheckExitError(a)), + a => return Err(CheckDeploymentError::NixCheckExit(a)), }; Ok(()) @@ -150,15 +147,15 @@ async fn check_deployment( #[derive(Error, Debug)] enum GetDeploymentDataError { #[error("Failed to execute nix eval command: {0}")] - NixEvalError(std::io::Error), + NixEval(std::io::Error), #[error("Failed to read output from evaluation: {0}")] - NixEvalOutError(std::io::Error), + NixEvalOut(std::io::Error), #[error("Evaluation resulted in a bad exit code: {0:?}")] - NixEvalExitError(Option<i32>), + NixEvalExit(Option<i32>), #[error("Error converting evaluation output to utf8: {0}")] - DecodeUtf8Error(#[from] std::string::FromUtf8Error), + DecodeUtf8(#[from] std::string::FromUtf8Error), #[error("Error decoding the JSON from evaluation: {0}")] - DecodeJsonError(#[from] serde_json::error::Error), + DecodeJson(#[from] serde_json::error::Error), } /// Evaluates the Nix in the given `repo` and return the processed Data from it @@ -166,7 +163,7 @@ async fn get_deployment_data( supports_flakes: bool, repo: &str, extra_build_args: &[String], -) -> Result<utils::data::Data, GetDeploymentDataError> { +) -> Result<deploy::data::Data, GetDeploymentDataError> { info!("Evaluating flake in {}", repo); let mut c = match supports_flakes { @@ -198,16 +195,16 @@ async fn get_deployment_data( let build_child = build_command .stdout(Stdio::piped()) .spawn() - .map_err(GetDeploymentDataError::NixEvalError)?; + .map_err(GetDeploymentDataError::NixEval)?; let build_output = build_child .wait_with_output() .await - .map_err(GetDeploymentDataError::NixEvalOutError)?; + .map_err(GetDeploymentDataError::NixEvalOut)?; match build_output.status.code() { Some(0) => (), - a => return Err(GetDeploymentDataError::NixEvalExitError(a)), + a => return Err(GetDeploymentDataError::NixEvalExit(a)), }; let data_json = String::from_utf8(build_output.stdout)?; @@ -225,14 +222,14 @@ struct PromptPart<'a> { } fn print_deployment( - parts: &[(utils::DeployData, utils::DeployDefs)], + parts: &[(deploy::DeployData, deploy::DeployDefs)], ) -> Result<(), toml::ser::Error> { let mut part_map: HashMap<String, HashMap<String, PromptPart>> = HashMap::new(); for (data, defs) in parts { part_map .entry(data.node_name.to_string()) - .or_insert(HashMap::new()) + .or_insert_with(HashMap::new) .insert( data.profile_name.to_string(), PromptPart { @@ -264,7 +261,7 @@ enum PromptDeploymentError { } fn prompt_deployment( - parts: &[(utils::DeployData, utils::DeployDefs)], + parts: &[(deploy::DeployData, deploy::DeployDefs)], ) -> Result<(), PromptDeploymentError> { print_deployment(parts)?; @@ -314,9 +311,9 @@ fn prompt_deployment( #[derive(Error, Debug)] enum RunDeployError { #[error("Failed to deploy profile: {0}")] - DeployProfileError(#[from] utils::deploy::DeployProfileError), + DeployProfile(#[from] deploy::deploy::DeployProfileError), #[error("Failed to push profile: {0}")] - PushProfileError(#[from] utils::push::PushProfileError), + PushProfile(#[from] deploy::push::PushProfileError), #[error("No profile named `{0}` was found")] ProfileNotFound(String), #[error("No node named `{0}` was found")] @@ -324,47 +321,78 @@ enum RunDeployError { #[error("Profile was provided without a node name")] ProfileWithoutNode, #[error("Error processing deployment definitions: {0}")] - DeployDataDefsError(#[from] utils::DeployDataDefsError), + DeployDataDefs(#[from] deploy::DeployDataDefsError), #[error("Failed to make printable TOML of deployment: {0}")] TomlFormat(#[from] toml::ser::Error), #[error("{0}")] - PromptDeploymentError(#[from] PromptDeploymentError), + PromptDeployment(#[from] PromptDeploymentError), } +type ToDeploy<'a> = Vec<( + (&'a str, &'a deploy::data::Node), + (&'a str, &'a deploy::data::Profile), +)>; + async fn run_deploy( - deploy_flake: utils::DeployFlake<'_>, - data: utils::data::Data, + deploy_flake: deploy::DeployFlake<'_>, + data: deploy::data::Data, supports_flakes: bool, check_sigs: bool, interactive: bool, - cmd_overrides: utils::CmdOverrides, + cmd_overrides: deploy::CmdOverrides, keep_result: bool, result_path: Option<&str>, extra_build_args: &[String], debug_logs: bool, log_dir: Option<String>, ) -> Result<(), RunDeployError> { - let to_deploy: Vec<((&str, &utils::data::Node), (&str, &utils::data::Profile))> = - match (&deploy_flake.node, &deploy_flake.profile) { - (Some(node_name), Some(profile_name)) => { - let node = match data.nodes.get(node_name) { - Some(x) => x, - None => return Err(RunDeployError::NodeNotFound(node_name.to_owned())), - }; + let to_deploy: ToDeploy = match (&deploy_flake.node, &deploy_flake.profile) { + (Some(node_name), Some(profile_name)) => { + let node = match data.nodes.get(node_name) { + Some(x) => x, + None => return Err(RunDeployError::NodeNotFound(node_name.to_owned())), + }; + let profile = match node.node_settings.profiles.get(profile_name) { + Some(x) => x, + None => return Err(RunDeployError::ProfileNotFound(profile_name.to_owned())), + }; + + vec![((node_name, node), (profile_name, profile))] + } + (Some(node_name), None) => { + let node = match data.nodes.get(node_name) { + Some(x) => x, + None => return Err(RunDeployError::NodeNotFound(node_name.to_owned())), + }; + + let mut profiles_list: Vec<(&str, &deploy::data::Profile)> = Vec::new(); + + for profile_name in [ + node.node_settings.profiles_order.iter().collect(), + node.node_settings.profiles.keys().collect::<Vec<&String>>(), + ] + .concat() + { let profile = match node.node_settings.profiles.get(profile_name) { Some(x) => x, None => return Err(RunDeployError::ProfileNotFound(profile_name.to_owned())), }; - vec![((node_name, node), (profile_name, profile))] + if !profiles_list.iter().any(|(n, _)| n == profile_name) { + profiles_list.push((&profile_name, profile)); + } } - (Some(node_name), None) => { - let node = match data.nodes.get(node_name) { - Some(x) => x, - None => return Err(RunDeployError::NodeNotFound(node_name.to_owned())), - }; - let mut profiles_list: Vec<(&str, &utils::data::Profile)> = Vec::new(); + profiles_list + .into_iter() + .map(|x| ((node_name.as_str(), node), x)) + .collect() + } + (None, None) => { + let mut l = Vec::new(); + + for (node_name, node) in &data.nodes { + let mut profiles_list: Vec<(&str, &deploy::data::Profile)> = Vec::new(); for profile_name in [ node.node_settings.profiles_order.iter().collect(), @@ -384,55 +412,23 @@ async fn run_deploy( } } - profiles_list + let ll: ToDeploy = profiles_list .into_iter() .map(|x| ((node_name.as_str(), node), x)) - .collect() - } - (None, None) => { - let mut l = Vec::new(); - - for (node_name, node) in &data.nodes { - let mut profiles_list: Vec<(&str, &utils::data::Profile)> = Vec::new(); - - for profile_name in [ - node.node_settings.profiles_order.iter().collect(), - node.node_settings.profiles.keys().collect::<Vec<&String>>(), - ] - .concat() - { - let profile = match node.node_settings.profiles.get(profile_name) { - Some(x) => x, - None => { - return Err(RunDeployError::ProfileNotFound( - profile_name.to_owned(), - )) - } - }; - - if !profiles_list.iter().any(|(n, _)| n == profile_name) { - profiles_list.push((&profile_name, profile)); - } - } - - let ll: Vec<((&str, &utils::data::Node), (&str, &utils::data::Profile))> = - profiles_list - .into_iter() - .map(|x| ((node_name.as_str(), node), x)) - .collect(); - - l.extend(ll); - } + .collect(); - l + l.extend(ll); } - (None, Some(_)) => return Err(RunDeployError::ProfileWithoutNode), - }; - let mut parts: Vec<(utils::DeployData, utils::DeployDefs)> = Vec::new(); + l + } + (None, Some(_)) => return Err(RunDeployError::ProfileWithoutNode), + }; + + let mut parts: Vec<(deploy::DeployData, deploy::DeployDefs)> = Vec::new(); for ((node_name, node), (profile_name, profile)) in to_deploy { - let deploy_data = utils::make_deploy_data( + let deploy_data = deploy::make_deploy_data( &data.generic_settings, node, node_name, @@ -455,21 +451,21 @@ async fn run_deploy( } for (deploy_data, deploy_defs) in &parts { - utils::push::push_profile( + deploy::push::push_profile(deploy::push::PushProfileData { supports_flakes, check_sigs, - deploy_flake.repo, - &deploy_data, - &deploy_defs, + repo: deploy_flake.repo, + deploy_data: &deploy_data, + deploy_defs: &deploy_defs, keep_result, result_path, extra_build_args, - ) + }) .await?; } for (deploy_data, deploy_defs) in &parts { - utils::deploy::deploy_profile(&deploy_data, &deploy_defs).await?; + deploy::deploy::deploy_profile(&deploy_data, &deploy_defs).await?; } Ok(()) @@ -478,35 +474,35 @@ async fn run_deploy( #[derive(Error, Debug)] enum RunError { #[error("Failed to deploy profile: {0}")] - DeployProfileError(#[from] utils::deploy::DeployProfileError), + DeployProfile(#[from] deploy::deploy::DeployProfileError), #[error("Failed to push profile: {0}")] - PushProfileError(#[from] utils::push::PushProfileError), + PushProfile(#[from] deploy::push::PushProfileError), #[error("Failed to test for flake support: {0}")] - FlakeTestError(std::io::Error), + FlakeTest(std::io::Error), #[error("Failed to check deployment: {0}")] - CheckDeploymentError(#[from] CheckDeploymentError), + CheckDeployment(#[from] CheckDeploymentError), #[error("Failed to evaluate deployment data: {0}")] - GetDeploymentDataError(#[from] GetDeploymentDataError), + GetDeploymentData(#[from] GetDeploymentDataError), #[error("Error parsing flake: {0}")] - ParseFlakeError(#[from] utils::ParseFlakeError), + ParseFlake(#[from] deploy::ParseFlakeError), #[error("Error initiating logger: {0}")] - LoggerError(#[from] flexi_logger::FlexiLoggerError), + Logger(#[from] flexi_logger::FlexiLoggerError), #[error("{0}")] - RunDeployError(#[from] RunDeployError), + RunDeploy(#[from] RunDeployError), } async fn run() -> Result<(), RunError> { let opts: Opts = Opts::parse(); - utils::init_logger( + deploy::init_logger( opts.debug_logs, opts.log_dir.as_deref(), - utils::LoggerType::Deploy, + deploy::LoggerType::Deploy, )?; - let deploy_flake = utils::parse_flake(opts.flake.as_str())?; + let deploy_flake = deploy::parse_flake(opts.flake.as_str())?; - let cmd_overrides = utils::CmdOverrides { + let cmd_overrides = deploy::CmdOverrides { ssh_user: opts.ssh_user, profile_user: opts.profile_user, ssh_opts: opts.ssh_opts, @@ -518,9 +514,7 @@ async fn run() -> Result<(), RunError> { confirm_timeout: opts.confirm_timeout, }; - let supports_flakes = test_flake_support() - .await - .map_err(RunError::FlakeTestError)?; + let supports_flakes = test_flake_support().await.map_err(RunError::FlakeTest)?; if !supports_flakes { warn!("A Nix version without flakes support was detected, support for this is work in progress"); @@ -557,7 +551,10 @@ async fn run() -> Result<(), RunError> { async fn main() -> Result<(), Box<dyn std::error::Error>> { match run().await { Ok(()) => (), - Err(err) => good_panic!("{}", err), + Err(err) => { + error!("{}", err); + std::process::exit(1); + } } Ok(()) diff --git a/src/utils/data.rs b/src/data.rs index f557e41..f557e41 100644 --- a/src/utils/data.rs +++ b/src/data.rs diff --git a/src/utils/deploy.rs b/src/deploy.rs index 3371160..a33721c 100644 --- a/src/utils/deploy.rs +++ b/src/deploy.rs @@ -8,46 +8,48 @@ use tokio::process::Command; use thiserror::Error; -fn build_activate_command( - sudo: &Option<String>, - profile_path: &str, - closure: &str, +struct ActivateCommandData<'a> { + sudo: &'a Option<String>, + profile_path: &'a str, + closure: &'a str, auto_rollback: bool, - temp_path: &Cow<str>, + temp_path: &'a str, confirm_timeout: u16, magic_rollback: bool, debug_logs: bool, - log_dir: Option<&str>, -) -> String { - let mut self_activate_command = format!("{}/activate-rs", closure); + log_dir: Option<&'a str>, +} + +fn build_activate_command(data: ActivateCommandData) -> String { + let mut self_activate_command = format!("{}/activate-rs", data.closure); - if debug_logs { + if data.debug_logs { self_activate_command = format!("{} --debug-logs", self_activate_command); } - if let Some(log_dir) = log_dir { + if let Some(log_dir) = data.log_dir { self_activate_command = format!("{} --log-dir {}", self_activate_command, log_dir); } self_activate_command = format!( "{} --temp-path '{}' activate '{}' '{}'", - self_activate_command, temp_path, closure, profile_path + self_activate_command, data.temp_path, data.closure, data.profile_path ); self_activate_command = format!( "{} --confirm-timeout {}", - self_activate_command, confirm_timeout + self_activate_command, data.confirm_timeout ); - if magic_rollback { + if data.magic_rollback { self_activate_command = format!("{} --magic-rollback", self_activate_command); } - if auto_rollback { + if data.auto_rollback { self_activate_command = format!("{} --auto-rollback", self_activate_command); } - if let Some(sudo_cmd) = &sudo { + if let Some(sudo_cmd) = &data.sudo { self_activate_command = format!("{} {}", sudo_cmd, self_activate_command); } @@ -60,15 +62,15 @@ fn test_activation_command_builder() { let profile_path = "/blah/profiles/test"; let closure = "/nix/store/blah/etc"; let auto_rollback = true; - let temp_path = &"/tmp".into(); + let temp_path = "/tmp"; let confirm_timeout = 30; let magic_rollback = true; let debug_logs = true; let log_dir = Some("/tmp/something.txt"); assert_eq!( - build_activate_command( - &sudo, + build_activate_command(ActivateCommandData { + sudo: &sudo, profile_path, closure, auto_rollback, @@ -77,35 +79,37 @@ fn test_activation_command_builder() { magic_rollback, debug_logs, log_dir - ), + }), "sudo -u test /nix/store/blah/etc/activate-rs --debug-logs --log-dir /tmp/something.txt --temp-path '/tmp' activate '/nix/store/blah/etc' '/blah/profiles/test' --confirm-timeout 30 --magic-rollback --auto-rollback" .to_string(), ); } -fn build_wait_command( - sudo: &Option<String>, - closure: &str, - temp_path: &Cow<str>, +struct WaitCommandData<'a> { + sudo: &'a Option<String>, + closure: &'a str, + temp_path: &'a str, debug_logs: bool, - log_dir: Option<&str>, -) -> String { - let mut self_activate_command = format!("{}/activate-rs", closure); + log_dir: Option<&'a str>, +} - if debug_logs { +fn build_wait_command(data: WaitCommandData) -> String { + let mut self_activate_command = format!("{}/activate-rs", data.closure); + + if data.debug_logs { self_activate_command = format!("{} --debug-logs", self_activate_command); } - if let Some(log_dir) = log_dir { + if let Some(log_dir) = data.log_dir { self_activate_command = format!("{} --log-dir {}", self_activate_command, log_dir); } self_activate_command = format!( "{} --temp-path '{}' wait '{}'", - self_activate_command, temp_path, closure + self_activate_command, data.temp_path, data.closure ); - if let Some(sudo_cmd) = &sudo { + if let Some(sudo_cmd) = &data.sudo { self_activate_command = format!("{} {}", sudo_cmd, self_activate_command); } @@ -116,18 +120,18 @@ fn build_wait_command( fn test_wait_command_builder() { let sudo = Some("sudo -u test".to_string()); let closure = "/nix/store/blah/etc"; - let temp_path = &"/tmp".into(); + let temp_path = "/tmp"; let debug_logs = true; let log_dir = Some("/tmp/something.txt"); assert_eq!( - build_wait_command( - &sudo, + build_wait_command(WaitCommandData { + sudo: &sudo, closure, temp_path, debug_logs, log_dir - ), + }), "sudo -u test /nix/store/blah/etc/activate-rs --debug-logs --log-dir /tmp/something.txt --temp-path '/tmp' wait '/nix/store/blah/etc'" .to_string(), ); @@ -135,9 +139,6 @@ fn test_wait_command_builder() { #[derive(Error, Debug)] pub enum DeployProfileError { - #[error("Failed to calculate activate bin path from deploy bin path: {0}")] - DeployPathToActivatePathError(#[from] super::DeployPathToActivatePathError), - #[error("Failed to spawn activation command over SSH: {0}")] SSHSpawnActivateError(std::io::Error), @@ -179,30 +180,20 @@ pub async fn deploy_profile( let auto_rollback = deploy_data.merged_settings.auto_rollback.unwrap_or(true); - let self_activate_command = build_activate_command( - &deploy_defs.sudo, - &deploy_defs.profile_path, - &deploy_data.profile.profile_settings.path, + let self_activate_command = build_activate_command(ActivateCommandData { + sudo: &deploy_defs.sudo, + profile_path: &deploy_defs.profile_path, + closure: &deploy_data.profile.profile_settings.path, auto_rollback, - &temp_path, + temp_path: &temp_path, confirm_timeout, magic_rollback, - deploy_data.debug_logs, - deploy_data.log_dir, - ); + debug_logs: deploy_data.debug_logs, + log_dir: deploy_data.log_dir, + }); debug!("Constructed activation command: {}", self_activate_command); - let self_wait_command = build_wait_command( - &deploy_defs.sudo, - &deploy_data.profile.profile_settings.path, - &temp_path, - deploy_data.debug_logs, - deploy_data.log_dir, - ); - - debug!("Constructed wait command: {}", self_wait_command); - let hostname = match deploy_data.cmd_overrides.hostname { Some(ref x) => x, None => &deploy_data.node.node_settings.hostname, @@ -231,6 +222,16 @@ pub async fn deploy_profile( info!("Success activating, done!"); } else { + let self_wait_command = build_wait_command(WaitCommandData { + sudo: &deploy_defs.sudo, + closure: &deploy_data.profile.profile_settings.path, + temp_path: &temp_path, + debug_logs: deploy_data.debug_logs, + log_dir: deploy_data.log_dir, + }); + + debug!("Constructed wait command: {}", self_wait_command); + let ssh_activate = ssh_activate_command .arg(self_activate_command) .spawn() diff --git a/src/utils/mod.rs b/src/lib.rs index bc46f4c..edc0507 100644 --- a/src/utils/mod.rs +++ b/src/lib.rs @@ -11,17 +11,15 @@ use thiserror::Error; use flexi_logger::*; -#[macro_export] -macro_rules! good_panic { - ($($tts:tt)*) => {{ - error!($($tts)*); - std::process::exit(1); - }} -} +#[macro_use] +extern crate log; + +#[macro_use] +extern crate serde_derive; pub fn make_lock_path(temp_path: &str, closure: &str) -> String { let lock_hash = - &closure["/nix/store/".len()..closure.find("-").unwrap_or_else(|| closure.len())]; + &closure["/nix/store/".len()..closure.find('-').unwrap_or_else(|| closure.len())]; format!("{}/deploy-rs-canary-{}", temp_path, lock_hash) } @@ -416,11 +414,3 @@ pub fn make_deploy_data<'a, 's>( log_dir, } } - -#[derive(Error, Debug)] -pub enum DeployPathToActivatePathError { - #[error("Deploy path did not have a parent directory")] - PathTooShort, - #[error("Deploy path was not valid utf8")] - InvalidUtf8, -} diff --git a/src/utils/push.rs b/src/push.rs index 503e062..2f83019 100644 --- a/src/utils/push.rs +++ b/src/push.rs @@ -2,22 +2,22 @@ // // SPDX-License-Identifier: MPL-2.0 +use std::path::Path; use std::process::Stdio; use tokio::process::Command; -use std::path::Path; use thiserror::Error; #[derive(Error, Debug)] pub enum PushProfileError { - #[error("Failed to calculate activate bin path from deploy bin path: {0}")] - DeployPathToActivatePathError(#[from] super::DeployPathToActivatePathError), #[error("Failed to run Nix build command: {0}")] BuildError(std::io::Error), #[error("Nix build command resulted in a bad exit code: {0:?}")] BuildExitError(Option<i32>), - #[error("Activation script deploy-rs-activate does not exist in profile.\n\ - Did you forget to use deploy-rs#lib.<...>.activate.<...> on your profile path?")] + #[error( + "Activation script deploy-rs-activate does not exist in profile.\n\ + Did you forget to use deploy-rs#lib.<...>.activate.<...> on your profile path?" + )] DeployRsActivateDoesntExist, #[error("Activation script activate-rs does not exist in profile.\n\ Is there a mismatch in deploy-rs used in the flake you're deploying and deploy-rs command you're running?")] @@ -32,53 +32,55 @@ pub enum PushProfileError { CopyExitError(Option<i32>), } -pub async fn push_profile( - supports_flakes: bool, - check_sigs: bool, - repo: &str, - deploy_data: &super::DeployData<'_>, - deploy_defs: &super::DeployDefs, - keep_result: bool, - result_path: Option<&str>, - extra_build_args: &[String], -) -> Result<(), PushProfileError> { +pub struct PushProfileData<'a> { + pub supports_flakes: bool, + pub check_sigs: bool, + pub repo: &'a str, + pub deploy_data: &'a super::DeployData<'a>, + pub deploy_defs: &'a super::DeployDefs, + pub keep_result: bool, + pub result_path: Option<&'a str>, + pub extra_build_args: &'a [String], +} + +pub async fn push_profile(data: PushProfileData<'_>) -> Result<(), PushProfileError> { info!( "Building profile `{}` for node `{}`", - deploy_data.profile_name, deploy_data.node_name + data.deploy_data.profile_name, data.deploy_data.node_name ); - let mut build_c = if supports_flakes { + let mut build_c = if data.supports_flakes { Command::new("nix") } else { Command::new("nix-build") }; - let mut build_command = if supports_flakes { + let mut build_command = if data.supports_flakes { build_c.arg("build").arg(format!( "{}#deploy.nodes.\"{}\".profiles.\"{}\".path", - repo, deploy_data.node_name, deploy_data.profile_name + data.repo, data.deploy_data.node_name, data.deploy_data.profile_name )) } else { - build_c.arg(&repo).arg("-A").arg(format!( + build_c.arg(&data.repo).arg("-A").arg(format!( "deploy.nodes.\"{}\".profiles.\"{}\".path", - deploy_data.node_name, deploy_data.profile_name + data.deploy_data.node_name, data.deploy_data.profile_name )) }; - build_command = match (keep_result, supports_flakes) { + build_command = match (data.keep_result, data.supports_flakes) { (true, _) => { - let result_path = result_path.unwrap_or("./.deploy-gc"); + let result_path = data.result_path.unwrap_or("./.deploy-gc"); build_command.arg("--out-link").arg(format!( "{}/{}/{}", - result_path, deploy_data.node_name, deploy_data.profile_name + result_path, data.deploy_data.node_name, data.deploy_data.profile_name )) } (false, false) => build_command.arg("--no-out-link"), (false, true) => build_command.arg("--no-link"), }; - for extra_arg in extra_build_args { + for extra_arg in data.extra_build_args { build_command = build_command.arg(extra_arg); } @@ -94,20 +96,34 @@ pub async fn push_profile( a => return Err(PushProfileError::BuildExitError(a)), }; - if ! Path::new(format!("{}/deploy-rs-activate", deploy_data.profile.profile_settings.path).as_str()).exists() { + if !Path::new( + format!( + "{}/deploy-rs-activate", + data.deploy_data.profile.profile_settings.path + ) + .as_str(), + ) + .exists() + { return Err(PushProfileError::DeployRsActivateDoesntExist); } - if ! Path::new(format!("{}/activate-rs", deploy_data.profile.profile_settings.path).as_str()).exists() { + if !Path::new( + format!( + "{}/activate-rs", + data.deploy_data.profile.profile_settings.path + ) + .as_str(), + ) + .exists() + { return Err(PushProfileError::ActivateRsDoesntExist); } - - if let Ok(local_key) = std::env::var("LOCAL_KEY") { info!( "Signing key present! Signing profile `{}` for node `{}`", - deploy_data.profile_name, deploy_data.node_name + data.deploy_data.profile_name, data.deploy_data.node_name ); let sign_exit_status = Command::new("nix") @@ -115,7 +131,7 @@ pub async fn push_profile( .arg("-r") .arg("-k") .arg(local_key) - .arg(&deploy_data.profile.profile_settings.path) + .arg(&data.deploy_data.profile.profile_settings.path) .status() .await .map_err(PushProfileError::SignError)?; @@ -128,21 +144,22 @@ pub async fn push_profile( debug!( "Copying profile `{}` to node `{}`", - deploy_data.profile_name, deploy_data.node_name + data.deploy_data.profile_name, data.deploy_data.node_name ); let mut copy_command_ = Command::new("nix"); let mut copy_command = copy_command_.arg("copy"); - if deploy_data.merged_settings.fast_connection != Some(true) { + if data.deploy_data.merged_settings.fast_connection != Some(true) { copy_command = copy_command.arg("--substitute-on-destination"); } - if !check_sigs { + if !data.check_sigs { copy_command = copy_command.arg("--no-check-sigs"); } - let ssh_opts_str = deploy_data + let ssh_opts_str = data + .deploy_data .merged_settings .ssh_opts // This should provide some extra safety, but it also breaks for some reason, oh well @@ -151,15 +168,15 @@ pub async fn push_profile( // .collect::<Vec<String>>() .join(" "); - let hostname = match deploy_data.cmd_overrides.hostname { + let hostname = match data.deploy_data.cmd_overrides.hostname { Some(ref x) => x, - None => &deploy_data.node.node_settings.hostname, + None => &data.deploy_data.node.node_settings.hostname, }; let copy_exit_status = copy_command .arg("--to") - .arg(format!("ssh://{}@{}", deploy_defs.ssh_user, hostname)) - .arg(&deploy_data.profile.profile_settings.path) + .arg(format!("ssh://{}@{}", data.deploy_defs.ssh_user, hostname)) + .arg(&data.deploy_data.profile.profile_settings.path) .env("NIX_SSHOPTS", ssh_opts_str) .status() .await |