From 4461d1ea3f494778f07041164f6b8a252bd26ea2 Mon Sep 17 00:00:00 2001 From: notgne2 Date: Sun, 27 Dec 2020 10:48:18 -0700 Subject: Replace logger, add more logging options including files --- src/utils/deploy.rs | 20 +++++++++-- src/utils/mod.rs | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 110 insertions(+), 5 deletions(-) (limited to 'src/utils') diff --git a/src/utils/deploy.rs b/src/utils/deploy.rs index 14a44a0..3aced6c 100644 --- a/src/utils/deploy.rs +++ b/src/utils/deploy.rs @@ -16,6 +16,8 @@ fn build_activate_command( temp_path: &Cow, confirm_timeout: u16, magic_rollback: bool, + debug_logs: bool, + log_dir: Option<&str>, ) -> String { let mut self_activate_command = format!( "{}/activate-rs '{}' '{}' --temp-path {} --confirm-timeout {}", @@ -30,6 +32,14 @@ fn build_activate_command( self_activate_command = format!("{} --auto-rollback", self_activate_command); } + if debug_logs { + self_activate_command = format!("{} --debug-logs", self_activate_command); + } + + if let Some(log_dir) = log_dir { + self_activate_command = format!("{} --log-file {}", self_activate_command, log_dir); + } + if let Some(sudo_cmd) = &sudo { self_activate_command = format!("{} {}", sudo_cmd, self_activate_command); } @@ -47,6 +57,8 @@ fn test_activation_command_builder() { let temp_path = &"/tmp".into(); let confirm_timeout = 30; let magic_rollback = true; + let debug_logs = true; + let log_dir = Some("/tmp/something.txt"); assert_eq!( build_activate_command( @@ -56,9 +68,11 @@ fn test_activation_command_builder() { auto_rollback, temp_path, confirm_timeout, - magic_rollback + magic_rollback, + debug_logs, + log_dir ), - "sudo -u test /nix/store/blah/etc/activate-rs '/blah/profiles/test' '/nix/store/blah/etc' --temp-path /tmp --confirm-timeout 30 --magic-rollback --auto-rollback" + "sudo -u test /nix/store/blah/etc/activate-rs '/blah/profiles/test' '/nix/store/blah/etc' --temp-path /tmp --confirm-timeout 30 --magic-rollback --auto-rollback --debug-logs --log-file /tmp/something.txt" .to_string(), ); } @@ -107,6 +121,8 @@ pub async fn deploy_profile( &temp_path, confirm_timeout, magic_rollback, + deploy_data.debug_logs, + deploy_data.log_dir, ); debug!("Constructed activation command: {}", self_activate_command); diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 5770f98..42c81f7 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -3,14 +3,14 @@ // // SPDX-License-Identifier: MPL-2.0 -use rnix::{types::*, NodeOrToken, SyntaxKind::*, SyntaxNode}; - -use std::path::PathBuf; +use rnix::{types::*, SyntaxKind::*}; use merge::Merge; use thiserror::Error; +use flexi_logger::*; + #[macro_export] macro_rules! good_panic { ($($tts:tt)*) => {{ @@ -19,6 +19,87 @@ macro_rules! good_panic { }} } +pub fn logger_formatter_activate( + w: &mut dyn std::io::Write, + _now: &mut DeferredNow, + record: &Record, +) -> Result<(), std::io::Error> { + let level = record.level(); + + write!( + w, + "⭐ REMOTE ⭐ {0} {1} {0} {2}", + match level { + log::Level::Error => "❌", + log::Level::Warn => "⚠️", + log::Level::Info => "ℹ️", + log::Level::Debug => "❓", + log::Level::Trace => "🖊️", + }, + style(level, level.to_string()), + record.args() + ) +} + +pub fn logger_formatter_deploy( + w: &mut dyn std::io::Write, + _now: &mut DeferredNow, + record: &Record, +) -> Result<(), std::io::Error> { + let level = record.level(); + + write!( + w, + "🚀 {0} {1} {0} {2}", + match level { + log::Level::Error => "❌", + log::Level::Warn => "⚠️", + log::Level::Info => "ℹ️", + log::Level::Debug => "❓", + log::Level::Trace => "🖊️", + }, + style(level, level.to_string()), + record.args() + ) +} + +pub fn init_logger( + debug_logs: bool, + log_dir: Option<&str>, + activate: bool, +) -> Result<(), FlexiLoggerError> { + let logger_formatter = match activate { + true => logger_formatter_activate, + false => logger_formatter_deploy, + }; + + if let Some(log_dir) = log_dir { + Logger::with_env_or_str("debug") + .log_to_file() + .format_for_stderr(logger_formatter) + .set_palette("196;208;51;7;8".to_string()) + .directory(log_dir) + .discriminant(if activate { "activate" } else { "deploy" }) + .duplicate_to_stderr(match debug_logs { + true => Duplicate::Debug, + false => Duplicate::Info, + }) + .print_message() + .start()?; + } else { + Logger::with_env_or_str(match debug_logs { + true => "debug", + false => "info", + }) + .log_target(LogTarget::StdErr) + .format(logger_formatter_deploy) + .set_palette("196;208;51;7;8".to_string()) + .start()?; + } + + Ok(()) +} + pub mod data; pub mod deploy; pub mod push; @@ -191,6 +272,9 @@ pub struct DeployData<'a> { pub cmd_overrides: &'a CmdOverrides, pub merged_settings: data::GenericSettings, + + pub debug_logs: bool, + pub log_dir: Option<&'a str>, } #[derive(Debug)] @@ -259,6 +343,8 @@ pub fn make_deploy_data<'a, 's>( profile: &'a data::Profile, profile_name: &'a str, cmd_overrides: &'a CmdOverrides, + debug_logs: bool, + log_dir: Option<&'a str>, ) -> DeployData<'a> { let mut merged_settings = top_settings.clone(); merged_settings.merge(node.generic_settings.clone()); @@ -292,6 +378,9 @@ pub fn make_deploy_data<'a, 's>( cmd_overrides, merged_settings, + + debug_logs, + log_dir, } } -- cgit v1.2.3 From 72b04b0ae84f68a3314de32da87381097a54fbe5 Mon Sep 17 00:00:00 2001 From: notgne2 Date: Sun, 27 Dec 2020 10:59:32 -0700 Subject: Fix log dir/file mismatch --- src/utils/deploy.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/utils') diff --git a/src/utils/deploy.rs b/src/utils/deploy.rs index 3aced6c..93cebee 100644 --- a/src/utils/deploy.rs +++ b/src/utils/deploy.rs @@ -37,7 +37,7 @@ fn build_activate_command( } if let Some(log_dir) = log_dir { - self_activate_command = format!("{} --log-file {}", self_activate_command, log_dir); + self_activate_command = format!("{} --log-dir {}", self_activate_command, log_dir); } if let Some(sudo_cmd) = &sudo { @@ -72,7 +72,7 @@ fn test_activation_command_builder() { debug_logs, log_dir ), - "sudo -u test /nix/store/blah/etc/activate-rs '/blah/profiles/test' '/nix/store/blah/etc' --temp-path /tmp --confirm-timeout 30 --magic-rollback --auto-rollback --debug-logs --log-file /tmp/something.txt" + "sudo -u test /nix/store/blah/etc/activate-rs '/blah/profiles/test' '/nix/store/blah/etc' --temp-path /tmp --confirm-timeout 30 --magic-rollback --auto-rollback --debug-logs --log-dir /tmp/something.txt" .to_string(), ); } -- cgit v1.2.3 From 01ea759cf653750694201522a1b7e0d86f89a177 Mon Sep 17 00:00:00 2001 From: notgne2 Date: Sun, 27 Dec 2020 11:05:37 -0700 Subject: Deduplicate level to emoji pattern into make_emoji() --- src/utils/mod.rs | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) (limited to 'src/utils') diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 42c81f7..00277c8 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -19,6 +19,16 @@ macro_rules! good_panic { }} } +fn make_emoji(level: log::Level) -> &'static str { + match level { + log::Level::Error => "❌", + log::Level::Warn => "⚠️", + log::Level::Info => "ℹ️", + log::Level::Debug => "❓", + log::Level::Trace => "🖊️", + } +} + pub fn logger_formatter_activate( w: &mut dyn std::io::Write, _now: &mut DeferredNow, @@ -29,13 +39,7 @@ pub fn logger_formatter_activate( write!( w, "⭐ REMOTE ⭐ {0} {1} {0} {2}", - match level { - log::Level::Error => "❌", - log::Level::Warn => "⚠️", - log::Level::Info => "ℹ️", - log::Level::Debug => "❓", - log::Level::Trace => "🖊️", - }, + make_emoji(level), style(level, level.to_string()), record.args() ) @@ -51,13 +55,7 @@ pub fn logger_formatter_deploy( write!( w, "🚀 {0} {1} {0} {2}", - match level { - log::Level::Error => "❌", - log::Level::Warn => "⚠️", - log::Level::Info => "ℹ️", - log::Level::Debug => "❓", - log::Level::Trace => "🖊️", - }, + make_emoji(level), style(level, level.to_string()), record.args() ) -- cgit v1.2.3 From 48548d1b1564861a63603814d62bb008e4e3edae Mon Sep 17 00:00:00 2001 From: notgne2 Date: Sun, 27 Dec 2020 11:33:55 -0700 Subject: Remove discriminant (they are already differenciated by binary name) --- src/utils/mod.rs | 1 - 1 file changed, 1 deletion(-) (limited to 'src/utils') diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 00277c8..9557aab 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -77,7 +77,6 @@ pub fn init_logger( .format_for_stderr(logger_formatter) .set_palette("196;208;51;7;8".to_string()) .directory(log_dir) - .discriminant(if activate { "activate" } else { "deploy" }) .duplicate_to_stderr(match debug_logs { true => Duplicate::Debug, false => Duplicate::Info, -- cgit v1.2.3 From b6a6abe104d348ba696054c930a3dafe0edf8d00 Mon Sep 17 00:00:00 2001 From: notgne2 Date: Wed, 30 Dec 2020 20:25:59 -0700 Subject: Modify activation logic to keep SSH connection alive for magic-rollback and avoid forking or disconnecting std{out,err} --- src/utils/deploy.rs | 167 +++++++++++++++++++++++++++++++++++++++++----------- src/utils/mod.rs | 54 ++++++++++++++--- 2 files changed, 179 insertions(+), 42 deletions(-) (limited to 'src/utils') diff --git a/src/utils/deploy.rs b/src/utils/deploy.rs index 93cebee..3371160 100644 --- a/src/utils/deploy.rs +++ b/src/utils/deploy.rs @@ -19,9 +19,24 @@ fn build_activate_command( debug_logs: bool, log_dir: Option<&str>, ) -> String { - let mut self_activate_command = format!( - "{}/activate-rs '{}' '{}' --temp-path {} --confirm-timeout {}", - closure, profile_path, closure, temp_path, confirm_timeout + let mut self_activate_command = format!("{}/activate-rs", closure); + + if debug_logs { + self_activate_command = format!("{} --debug-logs", self_activate_command); + } + + if let Some(log_dir) = 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 = format!( + "{} --confirm-timeout {}", + self_activate_command, confirm_timeout ); if magic_rollback { @@ -32,14 +47,6 @@ fn build_activate_command( self_activate_command = format!("{} --auto-rollback", self_activate_command); } - if debug_logs { - self_activate_command = format!("{} --debug-logs", self_activate_command); - } - - if let Some(log_dir) = log_dir { - self_activate_command = format!("{} --log-dir {}", self_activate_command, log_dir); - } - if let Some(sudo_cmd) = &sudo { self_activate_command = format!("{} {}", sudo_cmd, self_activate_command); } @@ -49,7 +56,6 @@ fn build_activate_command( #[test] fn test_activation_command_builder() { - let activate_path_str = "/blah/bin/activate".to_string(); let sudo = Some("sudo -u test".to_string()); let profile_path = "/blah/profiles/test"; let closure = "/nix/store/blah/etc"; @@ -72,7 +78,57 @@ fn test_activation_command_builder() { debug_logs, log_dir ), - "sudo -u test /nix/store/blah/etc/activate-rs '/blah/profiles/test' '/nix/store/blah/etc' --temp-path /tmp --confirm-timeout 30 --magic-rollback --auto-rollback --debug-logs --log-dir /tmp/something.txt" + "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, + closure: &str, + temp_path: &Cow, + debug_logs: bool, + log_dir: Option<&str>, +) -> String { + let mut self_activate_command = format!("{}/activate-rs", closure); + + if debug_logs { + self_activate_command = format!("{} --debug-logs", self_activate_command); + } + + if let Some(log_dir) = 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 + ); + + if let Some(sudo_cmd) = &sudo { + self_activate_command = format!("{} {}", sudo_cmd, self_activate_command); + } + + self_activate_command +} + +#[test] +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 debug_logs = true; + let log_dir = Some("/tmp/something.txt"); + + assert_eq!( + build_wait_command( + &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(), ); } @@ -81,10 +137,20 @@ fn test_activation_command_builder() { 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), + #[error("Failed to run activation command over SSH: {0}")] SSHActivateError(std::io::Error), - #[error("Activation over SSH resulted in a bad exit code: {0:?}")] + #[error("Activating over SSH resulted in a bad exit code: {0:?}")] SSHActivateExitError(Option), + + #[error("Failed to run wait command over SSH: {0}")] + SSHWaitError(std::io::Error), + #[error("Waiting over SSH resulted in a bad exit code: {0:?}")] + SSHWaitExitError(Option), + #[error("Failed to run confirmation command over SSH (the server should roll back): {0}")] SSHConfirmError(std::io::Error), #[error( @@ -127,35 +193,70 @@ pub async fn deploy_profile( 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, }; - let mut c = Command::new("ssh"); - let mut ssh_command = c - .arg("-t") - .arg(format!("ssh://{}@{}", deploy_defs.ssh_user, hostname)); + let ssh_addr = format!("ssh://{}@{}", deploy_defs.ssh_user, hostname); + + let mut ssh_activate_command_ = Command::new("ssh"); + let ssh_activate_command = ssh_activate_command_.arg(&ssh_addr); for ssh_opt in &deploy_data.merged_settings.ssh_opts { - ssh_command = ssh_command.arg(ssh_opt); + ssh_activate_command.arg(&ssh_opt); } - let ssh_exit_status = ssh_command - .arg(self_activate_command) - .status() - .await - .map_err(DeployProfileError::SSHActivateError)?; + if !magic_rollback { + let ssh_activate_exit_status = ssh_activate_command + .arg(self_activate_command) + .status() + .await + .map_err(DeployProfileError::SSHActivateError)?; - match ssh_exit_status.code() { - Some(0) => (), - a => return Err(DeployProfileError::SSHActivateExitError(a)), - }; + match ssh_activate_exit_status.code() { + Some(0) => (), + a => return Err(DeployProfileError::SSHActivateExitError(a)), + }; - info!("Success activating!"); + info!("Success activating, done!"); + } else { + let ssh_activate = ssh_activate_command + .arg(self_activate_command) + .spawn() + .map_err(DeployProfileError::SSHSpawnActivateError)?; - if magic_rollback { - info!("Attempting to confirm activation"); + info!("Creating activation waiter"); + + let mut ssh_wait_command_ = Command::new("ssh"); + let ssh_wait_command = ssh_wait_command_.arg(&ssh_addr); + + for ssh_opt in &deploy_data.merged_settings.ssh_opts { + ssh_wait_command.arg(ssh_opt); + } + + let ssh_wait_exit_status = ssh_wait_command + .arg(self_wait_command) + .status() + .await + .map_err(DeployProfileError::SSHWaitError)?; + + match ssh_wait_exit_status.code() { + Some(0) => (), + a => return Err(DeployProfileError::SSHWaitExitError(a)), + }; + + info!("Success activating, attempting to confirm activation"); let mut c = Command::new("ssh"); let mut ssh_confirm_command = c.arg(format!("ssh://{}@{}", deploy_defs.ssh_user, hostname)); @@ -164,8 +265,8 @@ pub async fn deploy_profile( ssh_confirm_command = ssh_confirm_command.arg(ssh_opt); } - let lock_hash = &deploy_data.profile.profile_settings.path["/nix/store/".len()..]; - let lock_path = format!("{}/deploy-rs-canary-{}", temp_path, lock_hash); + let lock_path = + super::make_lock_path(&temp_path, &deploy_data.profile.profile_settings.path); let mut confirm_command = format!("rm {}", lock_path); if let Some(sudo_cmd) = &deploy_defs.sudo { diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 9557aab..b1aa3e6 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -19,6 +19,12 @@ macro_rules! good_panic { }} } +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())]; + format!("{}/deploy-rs-canary-{}", temp_path, lock_hash) +} + fn make_emoji(level: log::Level) -> &'static str { match level { log::Level::Error => "❌", @@ -38,7 +44,23 @@ pub fn logger_formatter_activate( write!( w, - "⭐ REMOTE ⭐ {0} {1} {0} {2}", + "⭐ {0} {1} {0} {2}", + make_emoji(level), + style(level, level.to_string()), + record.args() + ) +} + +pub fn logger_formatter_wait( + w: &mut dyn std::io::Write, + _now: &mut DeferredNow, + record: &Record, +) -> Result<(), std::io::Error> { + let level = record.level(); + + write!( + w, + "👀 {0} {1} {0} {2}", make_emoji(level), style(level, level.to_string()), record.args() @@ -61,18 +83,25 @@ pub fn logger_formatter_deploy( ) } +pub enum LoggerType { + Deploy, + Activate, + Wait, +} + pub fn init_logger( debug_logs: bool, log_dir: Option<&str>, - activate: bool, + logger_type: LoggerType, ) -> Result<(), FlexiLoggerError> { - let logger_formatter = match activate { - true => logger_formatter_activate, - false => logger_formatter_deploy, + let logger_formatter = match logger_type { + LoggerType::Deploy => logger_formatter_deploy, + LoggerType::Activate => logger_formatter_activate, + LoggerType::Wait => logger_formatter_wait, }; if let Some(log_dir) = log_dir { - Logger::with_env_or_str("debug") + let mut logger = Logger::with_env_or_str("debug") .log_to_file() .format_for_stderr(logger_formatter) .set_palette("196;208;51;7;8".to_string()) @@ -81,15 +110,22 @@ pub fn init_logger( true => Duplicate::Debug, false => Duplicate::Info, }) - .print_message() - .start()?; + .print_message(); + + match logger_type { + LoggerType::Activate => logger = logger.discriminant("activate"), + LoggerType::Wait => logger = logger.discriminant("wait"), + LoggerType::Deploy => (), + } + + logger.start()?; } else { Logger::with_env_or_str(match debug_logs { true => "debug", false => "info", }) .log_target(LogTarget::StdErr) - .format(logger_formatter_deploy) + .format(logger_formatter) .set_palette("196;208;51;7;8".to_string()) .start()?; } -- cgit v1.2.3 From 12fee773d5a3a142a8cb9b57e57ff2be98346584 Mon Sep 17 00:00:00 2001 From: notgne2 Date: Fri, 1 Jan 2021 19:30:33 -0700 Subject: Format logs differently --- src/utils/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/utils') diff --git a/src/utils/mod.rs b/src/utils/mod.rs index b1aa3e6..89b124f 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -44,7 +44,7 @@ pub fn logger_formatter_activate( write!( w, - "⭐ {0} {1} {0} {2}", + "⭐ {} [activate] [{}] {}", make_emoji(level), style(level, level.to_string()), record.args() @@ -60,7 +60,7 @@ pub fn logger_formatter_wait( write!( w, - "👀 {0} {1} {0} {2}", + "👀 {} [wait] [{}] {}", make_emoji(level), style(level, level.to_string()), record.args() @@ -76,7 +76,7 @@ pub fn logger_formatter_deploy( write!( w, - "🚀 {0} {1} {0} {2}", + "🚀 {} [deploy] [{}] {}", make_emoji(level), style(level, level.to_string()), record.args() -- cgit v1.2.3 From 70c55363a91572790ba5d49b70c58040f112e55c Mon Sep 17 00:00:00 2001 From: notgne2 Date: Fri, 8 Jan 2021 18:24:04 -0700 Subject: Restructure project --- src/utils/data.rs | 73 --------- src/utils/deploy.rs | 296 ------------------------------------ src/utils/mod.rs | 426 ---------------------------------------------------- src/utils/push.rs | 174 --------------------- 4 files changed, 969 deletions(-) delete mode 100644 src/utils/data.rs delete mode 100644 src/utils/deploy.rs delete mode 100644 src/utils/mod.rs delete mode 100644 src/utils/push.rs (limited to 'src/utils') diff --git a/src/utils/data.rs b/src/utils/data.rs deleted file mode 100644 index f557e41..0000000 --- a/src/utils/data.rs +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-FileCopyrightText: 2020 Serokell -// -// SPDX-License-Identifier: MPL-2.0 - -use merge::Merge; - -use std::collections::HashMap; - -#[derive(Deserialize, Debug, Clone, Merge)] -pub struct GenericSettings { - #[serde(rename(deserialize = "sshUser"))] - pub ssh_user: Option, - pub user: Option, - #[serde( - skip_serializing_if = "Vec::is_empty", - default, - rename(deserialize = "sshOpts") - )] - #[merge(strategy = merge::vec::append)] - pub ssh_opts: Vec, - #[serde(rename(deserialize = "fastConnection"))] - pub fast_connection: Option, - #[serde(rename(deserialize = "autoRollback"))] - pub auto_rollback: Option, - #[serde(rename(deserialize = "confirmTimeout"))] - pub confirm_timeout: Option, - #[serde(rename(deserialize = "tempPath"))] - pub temp_path: Option, - #[serde(rename(deserialize = "magicRollback"))] - pub magic_rollback: Option, -} - -#[derive(Deserialize, Debug, Clone)] -pub struct NodeSettings { - pub hostname: String, - pub profiles: HashMap, - #[serde( - skip_serializing_if = "Vec::is_empty", - default, - rename(deserialize = "profilesOrder") - )] - pub profiles_order: Vec, -} - -#[derive(Deserialize, Debug, Clone)] -pub struct ProfileSettings { - pub path: String, - #[serde(rename(deserialize = "profilePath"))] - pub profile_path: Option, -} - -#[derive(Deserialize, Debug, Clone)] -pub struct Profile { - #[serde(flatten)] - pub profile_settings: ProfileSettings, - #[serde(flatten)] - pub generic_settings: GenericSettings, -} - -#[derive(Deserialize, Debug, Clone)] -pub struct Node { - #[serde(flatten)] - pub generic_settings: GenericSettings, - #[serde(flatten)] - pub node_settings: NodeSettings, -} - -#[derive(Deserialize, Debug, Clone)] -pub struct Data { - #[serde(flatten)] - pub generic_settings: GenericSettings, - pub nodes: HashMap, -} diff --git a/src/utils/deploy.rs b/src/utils/deploy.rs deleted file mode 100644 index 3371160..0000000 --- a/src/utils/deploy.rs +++ /dev/null @@ -1,296 +0,0 @@ -// SPDX-FileCopyrightText: 2020 Serokell -// SPDX-FileCopyrightText: 2020 Andreas Fuchs -// -// SPDX-License-Identifier: MPL-2.0 - -use std::borrow::Cow; -use tokio::process::Command; - -use thiserror::Error; - -fn build_activate_command( - sudo: &Option, - profile_path: &str, - closure: &str, - auto_rollback: bool, - temp_path: &Cow, - confirm_timeout: u16, - magic_rollback: bool, - debug_logs: bool, - log_dir: Option<&str>, -) -> String { - let mut self_activate_command = format!("{}/activate-rs", closure); - - if debug_logs { - self_activate_command = format!("{} --debug-logs", self_activate_command); - } - - if let Some(log_dir) = 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 = format!( - "{} --confirm-timeout {}", - self_activate_command, confirm_timeout - ); - - if magic_rollback { - self_activate_command = format!("{} --magic-rollback", self_activate_command); - } - - if auto_rollback { - self_activate_command = format!("{} --auto-rollback", self_activate_command); - } - - if let Some(sudo_cmd) = &sudo { - self_activate_command = format!("{} {}", sudo_cmd, self_activate_command); - } - - self_activate_command -} - -#[test] -fn test_activation_command_builder() { - let sudo = Some("sudo -u test".to_string()); - let profile_path = "/blah/profiles/test"; - let closure = "/nix/store/blah/etc"; - let auto_rollback = true; - let temp_path = &"/tmp".into(); - 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, - profile_path, - closure, - auto_rollback, - temp_path, - confirm_timeout, - 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, - closure: &str, - temp_path: &Cow, - debug_logs: bool, - log_dir: Option<&str>, -) -> String { - let mut self_activate_command = format!("{}/activate-rs", closure); - - if debug_logs { - self_activate_command = format!("{} --debug-logs", self_activate_command); - } - - if let Some(log_dir) = 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 - ); - - if let Some(sudo_cmd) = &sudo { - self_activate_command = format!("{} {}", sudo_cmd, self_activate_command); - } - - self_activate_command -} - -#[test] -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 debug_logs = true; - let log_dir = Some("/tmp/something.txt"); - - assert_eq!( - build_wait_command( - &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(), - ); -} - -#[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), - - #[error("Failed to run activation command over SSH: {0}")] - SSHActivateError(std::io::Error), - #[error("Activating over SSH resulted in a bad exit code: {0:?}")] - SSHActivateExitError(Option), - - #[error("Failed to run wait command over SSH: {0}")] - SSHWaitError(std::io::Error), - #[error("Waiting over SSH resulted in a bad exit code: {0:?}")] - SSHWaitExitError(Option), - - #[error("Failed to run confirmation command over SSH (the server should roll back): {0}")] - SSHConfirmError(std::io::Error), - #[error( - "Confirming activation over SSH resulted in a bad exit code (the server should roll back): {0:?}" - )] - SSHConfirmExitError(Option), -} - -pub async fn deploy_profile( - deploy_data: &super::DeployData<'_>, - deploy_defs: &super::DeployDefs, -) -> Result<(), DeployProfileError> { - info!( - "Activating profile `{}` for node `{}`", - deploy_data.profile_name, deploy_data.node_name - ); - - let temp_path: Cow = match &deploy_data.merged_settings.temp_path { - Some(x) => x.into(), - None => "/tmp".into(), - }; - - let confirm_timeout = deploy_data.merged_settings.confirm_timeout.unwrap_or(30); - - let magic_rollback = deploy_data.merged_settings.magic_rollback.unwrap_or(true); - - 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, - auto_rollback, - &temp_path, - confirm_timeout, - magic_rollback, - deploy_data.debug_logs, - 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, - }; - - let ssh_addr = format!("ssh://{}@{}", deploy_defs.ssh_user, hostname); - - let mut ssh_activate_command_ = Command::new("ssh"); - let ssh_activate_command = ssh_activate_command_.arg(&ssh_addr); - - for ssh_opt in &deploy_data.merged_settings.ssh_opts { - ssh_activate_command.arg(&ssh_opt); - } - - if !magic_rollback { - let ssh_activate_exit_status = ssh_activate_command - .arg(self_activate_command) - .status() - .await - .map_err(DeployProfileError::SSHActivateError)?; - - match ssh_activate_exit_status.code() { - Some(0) => (), - a => return Err(DeployProfileError::SSHActivateExitError(a)), - }; - - info!("Success activating, done!"); - } else { - let ssh_activate = ssh_activate_command - .arg(self_activate_command) - .spawn() - .map_err(DeployProfileError::SSHSpawnActivateError)?; - - info!("Creating activation waiter"); - - let mut ssh_wait_command_ = Command::new("ssh"); - let ssh_wait_command = ssh_wait_command_.arg(&ssh_addr); - - for ssh_opt in &deploy_data.merged_settings.ssh_opts { - ssh_wait_command.arg(ssh_opt); - } - - let ssh_wait_exit_status = ssh_wait_command - .arg(self_wait_command) - .status() - .await - .map_err(DeployProfileError::SSHWaitError)?; - - match ssh_wait_exit_status.code() { - Some(0) => (), - a => return Err(DeployProfileError::SSHWaitExitError(a)), - }; - - info!("Success activating, attempting to confirm activation"); - - let mut c = Command::new("ssh"); - let mut ssh_confirm_command = c.arg(format!("ssh://{}@{}", deploy_defs.ssh_user, hostname)); - - for ssh_opt in &deploy_data.merged_settings.ssh_opts { - ssh_confirm_command = ssh_confirm_command.arg(ssh_opt); - } - - let lock_path = - super::make_lock_path(&temp_path, &deploy_data.profile.profile_settings.path); - - let mut confirm_command = format!("rm {}", lock_path); - if let Some(sudo_cmd) = &deploy_defs.sudo { - confirm_command = format!("{} {}", sudo_cmd, confirm_command); - } - - debug!( - "Attempting to run command to confirm deployment: {}", - confirm_command - ); - - let ssh_exit_status = ssh_confirm_command - .arg(confirm_command) - .status() - .await - .map_err(DeployProfileError::SSHConfirmError)?; - - match ssh_exit_status.code() { - Some(0) => (), - a => return Err(DeployProfileError::SSHConfirmExitError(a)), - }; - - info!("Deployment confirmed."); - } - - Ok(()) -} diff --git a/src/utils/mod.rs b/src/utils/mod.rs deleted file mode 100644 index bc46f4c..0000000 --- a/src/utils/mod.rs +++ /dev/null @@ -1,426 +0,0 @@ -// SPDX-FileCopyrightText: 2020 Serokell -// SPDX-FileCopyrightText: 2020 Andreas Fuchs -// -// SPDX-License-Identifier: MPL-2.0 - -use rnix::{types::*, SyntaxKind::*}; - -use merge::Merge; - -use thiserror::Error; - -use flexi_logger::*; - -#[macro_export] -macro_rules! good_panic { - ($($tts:tt)*) => {{ - error!($($tts)*); - std::process::exit(1); - }} -} - -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())]; - format!("{}/deploy-rs-canary-{}", temp_path, lock_hash) -} - -fn make_emoji(level: log::Level) -> &'static str { - match level { - log::Level::Error => "❌", - log::Level::Warn => "⚠️", - log::Level::Info => "ℹ️", - log::Level::Debug => "❓", - log::Level::Trace => "🖊️", - } -} - -pub fn logger_formatter_activate( - w: &mut dyn std::io::Write, - _now: &mut DeferredNow, - record: &Record, -) -> Result<(), std::io::Error> { - let level = record.level(); - - write!( - w, - "⭐ {} [activate] [{}] {}", - make_emoji(level), - style(level, level.to_string()), - record.args() - ) -} - -pub fn logger_formatter_wait( - w: &mut dyn std::io::Write, - _now: &mut DeferredNow, - record: &Record, -) -> Result<(), std::io::Error> { - let level = record.level(); - - write!( - w, - "👀 {} [wait] [{}] {}", - make_emoji(level), - style(level, level.to_string()), - record.args() - ) -} - -pub fn logger_formatter_deploy( - w: &mut dyn std::io::Write, - _now: &mut DeferredNow, - record: &Record, -) -> Result<(), std::io::Error> { - let level = record.level(); - - write!( - w, - "🚀 {} [deploy] [{}] {}", - make_emoji(level), - style(level, level.to_string()), - record.args() - ) -} - -pub enum LoggerType { - Deploy, - Activate, - Wait, -} - -pub fn init_logger( - debug_logs: bool, - log_dir: Option<&str>, - logger_type: LoggerType, -) -> Result<(), FlexiLoggerError> { - let logger_formatter = match logger_type { - LoggerType::Deploy => logger_formatter_deploy, - LoggerType::Activate => logger_formatter_activate, - LoggerType::Wait => logger_formatter_wait, - }; - - if let Some(log_dir) = log_dir { - let mut logger = Logger::with_env_or_str("debug") - .log_to_file() - .format_for_stderr(logger_formatter) - .set_palette("196;208;51;7;8".to_string()) - .directory(log_dir) - .duplicate_to_stderr(match debug_logs { - true => Duplicate::Debug, - false => Duplicate::Info, - }) - .print_message(); - - match logger_type { - LoggerType::Activate => logger = logger.discriminant("activate"), - LoggerType::Wait => logger = logger.discriminant("wait"), - LoggerType::Deploy => (), - } - - logger.start()?; - } else { - Logger::with_env_or_str(match debug_logs { - true => "debug", - false => "info", - }) - .log_target(LogTarget::StdErr) - .format(logger_formatter) - .set_palette("196;208;51;7;8".to_string()) - .start()?; - } - - Ok(()) -} - -pub mod data; -pub mod deploy; -pub mod push; - -#[derive(Debug)] -pub struct CmdOverrides { - pub ssh_user: Option, - pub profile_user: Option, - pub ssh_opts: Option, - pub fast_connection: Option, - pub auto_rollback: Option, - pub hostname: Option, - pub magic_rollback: Option, - pub temp_path: Option, - pub confirm_timeout: Option, -} - -#[derive(PartialEq, Debug)] -pub struct DeployFlake<'a> { - pub repo: &'a str, - pub node: Option, - pub profile: Option, -} - -#[derive(Error, Debug)] -pub enum ParseFlakeError { - #[error("The given path was too long, did you mean to put something in quotes?")] - PathTooLong, - #[error("Unrecognized node or token encountered")] - Unrecognized, -} -pub fn parse_flake(flake: &str) -> Result { - let flake_fragment_start = flake.find('#'); - let (repo, maybe_fragment) = match flake_fragment_start { - Some(s) => (&flake[..s], Some(&flake[s + 1..])), - None => (flake, None), - }; - - let mut node: Option = None; - let mut profile: Option = None; - - if let Some(fragment) = maybe_fragment { - let ast = rnix::parse(fragment); - - let first_child = match ast.root().node().first_child() { - Some(x) => x, - None => { - return Ok(DeployFlake { - repo, - node: None, - profile: None, - }) - } - }; - - let mut node_over = false; - - for entry in first_child.children_with_tokens() { - let x: Option = match (entry.kind(), node_over) { - (TOKEN_DOT, false) => { - node_over = true; - None - } - (TOKEN_DOT, true) => { - return Err(ParseFlakeError::PathTooLong); - } - (NODE_IDENT, _) => Some(entry.into_node().unwrap().text().to_string()), - (TOKEN_IDENT, _) => Some(entry.into_token().unwrap().text().to_string()), - (NODE_STRING, _) => { - let c = entry - .into_node() - .unwrap() - .children_with_tokens() - .nth(1) - .unwrap(); - - Some(c.into_token().unwrap().text().to_string()) - } - _ => return Err(ParseFlakeError::Unrecognized), - }; - - if !node_over { - node = x; - } else { - profile = x; - } - } - } - - Ok(DeployFlake { - repo, - node, - profile, - }) -} - -#[test] -fn test_parse_flake() { - assert_eq!( - parse_flake("../deploy/examples/system").unwrap(), - DeployFlake { - repo: "../deploy/examples/system", - node: None, - profile: None, - } - ); - - assert_eq!( - parse_flake("../deploy/examples/system#").unwrap(), - DeployFlake { - repo: "../deploy/examples/system", - node: None, - profile: None, - } - ); - - assert_eq!( - parse_flake("../deploy/examples/system#computer.\"something.nix\"").unwrap(), - DeployFlake { - repo: "../deploy/examples/system", - node: Some("computer".to_string()), - profile: Some("something.nix".to_string()), - } - ); - - assert_eq!( - parse_flake("../deploy/examples/system#\"example.com\".system").unwrap(), - DeployFlake { - repo: "../deploy/examples/system", - node: Some("example.com".to_string()), - profile: Some("system".to_string()), - } - ); - - assert_eq!( - parse_flake("../deploy/examples/system#example").unwrap(), - DeployFlake { - repo: "../deploy/examples/system", - node: Some("example".to_string()), - profile: None - } - ); - - assert_eq!( - parse_flake("../deploy/examples/system#example.system").unwrap(), - DeployFlake { - repo: "../deploy/examples/system", - node: Some("example".to_string()), - profile: Some("system".to_string()) - } - ); - - assert_eq!( - parse_flake("../deploy/examples/system").unwrap(), - DeployFlake { - repo: "../deploy/examples/system", - node: None, - profile: None, - } - ); -} - -#[derive(Debug, Clone)] -pub struct DeployData<'a> { - pub node_name: &'a str, - pub node: &'a data::Node, - pub profile_name: &'a str, - pub profile: &'a data::Profile, - - pub cmd_overrides: &'a CmdOverrides, - - pub merged_settings: data::GenericSettings, - - pub debug_logs: bool, - pub log_dir: Option<&'a str>, -} - -#[derive(Debug)] -pub struct DeployDefs { - pub ssh_user: String, - pub profile_user: String, - pub profile_path: String, - pub sudo: Option, -} - -#[derive(Error, Debug)] -pub enum DeployDataDefsError { - #[error("Neither `user` nor `sshUser` are set for profile {0} of node {1}")] - NoProfileUser(String, String), -} - -impl<'a> DeployData<'a> { - pub fn defs(&'a self) -> Result { - let ssh_user = match self.merged_settings.ssh_user { - Some(ref u) => u.clone(), - None => whoami::username(), - }; - - let profile_user = match self.merged_settings.user { - Some(ref x) => x.clone(), - None => match self.merged_settings.ssh_user { - Some(ref x) => x.clone(), - None => { - return Err(DeployDataDefsError::NoProfileUser( - self.profile_name.to_owned(), - self.node_name.to_owned(), - )) - } - }, - }; - - let profile_path = match self.profile.profile_settings.profile_path { - None => match &profile_user[..] { - "root" => format!("/nix/var/nix/profiles/{}", self.profile_name), - _ => format!( - "/nix/var/nix/profiles/per-user/{}/{}", - profile_user, self.profile_name - ), - }, - Some(ref x) => x.clone(), - }; - - let sudo: Option = match self.merged_settings.user { - Some(ref user) if user != &ssh_user => Some(format!("sudo -u {}", user)), - _ => None, - }; - - Ok(DeployDefs { - ssh_user, - profile_user, - profile_path, - sudo, - }) - } -} - -pub fn make_deploy_data<'a, 's>( - top_settings: &'s data::GenericSettings, - node: &'a data::Node, - node_name: &'a str, - profile: &'a data::Profile, - profile_name: &'a str, - cmd_overrides: &'a CmdOverrides, - debug_logs: bool, - log_dir: Option<&'a str>, -) -> DeployData<'a> { - let mut merged_settings = profile.generic_settings.clone(); - merged_settings.merge(node.generic_settings.clone()); - merged_settings.merge(top_settings.clone()); - - if cmd_overrides.ssh_user.is_some() { - merged_settings.ssh_user = cmd_overrides.ssh_user.clone(); - } - if cmd_overrides.profile_user.is_some() { - merged_settings.user = cmd_overrides.profile_user.clone(); - } - if let Some(ref ssh_opts) = cmd_overrides.ssh_opts { - merged_settings.ssh_opts = ssh_opts.split(' ').map(|x| x.to_owned()).collect(); - } - if let Some(fast_connection) = cmd_overrides.fast_connection { - merged_settings.fast_connection = Some(fast_connection); - } - if let Some(auto_rollback) = cmd_overrides.auto_rollback { - merged_settings.auto_rollback = Some(auto_rollback); - } - if let Some(magic_rollback) = cmd_overrides.magic_rollback { - merged_settings.magic_rollback = Some(magic_rollback); - } - - DeployData { - profile, - profile_name, - node, - node_name, - - cmd_overrides, - - merged_settings, - - debug_logs, - 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/utils/push.rs deleted file mode 100644 index 503e062..0000000 --- a/src/utils/push.rs +++ /dev/null @@ -1,174 +0,0 @@ -// SPDX-FileCopyrightText: 2020 Serokell -// -// SPDX-License-Identifier: MPL-2.0 - -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), - #[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?")] - ActivateRsDoesntExist, - #[error("Failed to run Nix sign command: {0}")] - SignError(std::io::Error), - #[error("Nix sign command resulted in a bad exit code: {0:?}")] - SignExitError(Option), - #[error("Failed to run Nix copy command: {0}")] - CopyError(std::io::Error), - #[error("Nix copy command resulted in a bad exit code: {0:?}")] - CopyExitError(Option), -} - -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> { - info!( - "Building profile `{}` for node `{}`", - deploy_data.profile_name, deploy_data.node_name - ); - - let mut build_c = if supports_flakes { - Command::new("nix") - } else { - Command::new("nix-build") - }; - - let mut build_command = if supports_flakes { - build_c.arg("build").arg(format!( - "{}#deploy.nodes.\"{}\".profiles.\"{}\".path", - repo, deploy_data.node_name, deploy_data.profile_name - )) - } else { - build_c.arg(&repo).arg("-A").arg(format!( - "deploy.nodes.\"{}\".profiles.\"{}\".path", - deploy_data.node_name, deploy_data.profile_name - )) - }; - - build_command = match (keep_result, supports_flakes) { - (true, _) => { - let result_path = result_path.unwrap_or("./.deploy-gc"); - - build_command.arg("--out-link").arg(format!( - "{}/{}/{}", - result_path, deploy_data.node_name, 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 { - build_command = build_command.arg(extra_arg); - } - - let build_exit_status = build_command - // Logging should be in stderr, this just stops the store path from printing for no reason - .stdout(Stdio::null()) - .status() - .await - .map_err(PushProfileError::BuildError)?; - - match build_exit_status.code() { - Some(0) => (), - a => return Err(PushProfileError::BuildExitError(a)), - }; - - if ! Path::new(format!("{}/deploy-rs-activate", 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() { - 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 - ); - - let sign_exit_status = Command::new("nix") - .arg("sign-paths") - .arg("-r") - .arg("-k") - .arg(local_key) - .arg(&deploy_data.profile.profile_settings.path) - .status() - .await - .map_err(PushProfileError::SignError)?; - - match sign_exit_status.code() { - Some(0) => (), - a => return Err(PushProfileError::SignExitError(a)), - }; - } - - debug!( - "Copying profile `{}` to node `{}`", - deploy_data.profile_name, 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) { - copy_command = copy_command.arg("--substitute-on-destination"); - } - - if !check_sigs { - copy_command = copy_command.arg("--no-check-sigs"); - } - - let ssh_opts_str = deploy_data - .merged_settings - .ssh_opts - // This should provide some extra safety, but it also breaks for some reason, oh well - // .iter() - // .map(|x| format!("'{}'", x)) - // .collect::>() - .join(" "); - - let hostname = match deploy_data.cmd_overrides.hostname { - Some(ref x) => x, - None => &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) - .env("NIX_SSHOPTS", ssh_opts_str) - .status() - .await - .map_err(PushProfileError::CopyError)?; - - match copy_exit_status.code() { - Some(0) => (), - a => return Err(PushProfileError::CopyExitError(a)), - }; - - Ok(()) -} -- cgit v1.2.3