From a0328dbcf76b7c551e92fd25060cfc7d7e4d9ebe Mon Sep 17 00:00:00 2001
From: notgne2
Date: Tue, 29 Sep 2020 21:27:49 -0700
Subject: More separation and component testing

---
 src/main.rs         |  57 +++++++++++++++-----------
 src/utils/deploy.rs | 116 +++++++++++++++++++++++++++++++++++++++-------------
 2 files changed, 122 insertions(+), 51 deletions(-)

(limited to 'src')

diff --git a/src/main.rs b/src/main.rs
index 668b697..f8b03a3 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -143,7 +143,7 @@ async fn test_flake_support() -> Result<bool, Box<dyn std::error::Error>> {
 async fn get_deployment_data(
     supports_flakes: bool,
     repo: &str,
-    extra_build_args: Vec<String>,
+    extra_build_args: &[String],
 ) -> Result<utils::data::Data, Box<dyn std::error::Error>> {
     let mut c = match supports_flakes {
         true => Command::new("nix"),
@@ -156,12 +156,12 @@ async fn get_deployment_data(
         }
         false => {
             c
-                        .arg("--strict")
-                        .arg("--read-write-mode")
-                        .arg("--json")
-                        .arg("--eval")
-                        .arg("--E")
-                        .arg(format!("let r = import {}/.; in if builtins.isFunction r then (r {{}}).deploy else r.deploy", repo))
+                .arg("--strict")
+                .arg("--read-write-mode")
+                .arg("--json")
+                .arg("--eval")
+                .arg("--E")
+                .arg(format!("let r = import {}/.; in if builtins.isFunction r then (r {{}}).deploy else r.deploy", repo))
         }
     };
 
@@ -186,23 +186,13 @@ async fn get_deployment_data(
 
     Ok(serde_json::from_str(&data_json)?)
 }
-#[tokio::main]
-async fn main() -> Result<(), Box<dyn std::error::Error>> {
-    if std::env::var("DEPLOY_LOG").is_err() {
-        std::env::set_var("DEPLOY_LOG", "info");
-    }
-
-    pretty_env_logger::init_custom_env("DEPLOY_LOG");
-
-    let opts: Opts = Opts::parse();
-
-    let deploy_flake = utils::parse_flake(opts.flake.as_str());
-
-    let supports_flakes = test_flake_support().await?;
-
-    let data =
-        get_deployment_data(supports_flakes, deploy_flake.repo, opts.extra_build_args).await?;
 
+async fn run_deploy(
+    deploy_flake: utils::DeployFlake<'_>,
+    data: utils::data::Data,
+    supports_flakes: bool,
+    opts: &Opts,
+) -> Result<(), Box<dyn std::error::Error>> {
     match (deploy_flake.node, deploy_flake.profile) {
         (Some(node_name), Some(profile_name)) => {
             let node = match data.nodes.get(node_name) {
@@ -289,3 +279,24 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
 
     Ok(())
 }
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn std::error::Error>> {
+    if std::env::var("DEPLOY_LOG").is_err() {
+        std::env::set_var("DEPLOY_LOG", "info");
+    }
+
+    pretty_env_logger::init_custom_env("DEPLOY_LOG");
+
+    let opts: Opts = Opts::parse();
+
+    let deploy_flake = utils::parse_flake(opts.flake.as_str());
+
+    let supports_flakes = test_flake_support().await?;
+
+    let data =
+        get_deployment_data(supports_flakes, deploy_flake.repo, &opts.extra_build_args).await?;
+
+    run_deploy(deploy_flake, data, supports_flakes, &opts).await?;
+
+    Ok(())
+}
diff --git a/src/utils/deploy.rs b/src/utils/deploy.rs
index 42bd0b4..c44c0d3 100644
--- a/src/utils/deploy.rs
+++ b/src/utils/deploy.rs
@@ -2,47 +2,54 @@ use super::data;
 
 use tokio::process::Command;
 
-pub async fn deploy_profile(
-    profile: &data::Profile,
-    profile_name: &str,
-    node: &data::Node,
-    node_name: &str,
-    merged_settings: &data::GenericSettings,
-    deploy_data: &super::DeployData<'_>,
-    auto_rollback: bool,
-) -> Result<(), Box<dyn std::error::Error>> {
-    info!(
-        "Activating profile `{}` for node `{}`",
-        profile_name, node_name
-    );
-
-    let mut self_activate_command = format!(
-        "{} '{}' '{}'",
-        deploy_data
-            .current_exe
-            .as_path()
+fn deploy_path_to_activate_path_str(
+    deploy_path: &std::path::Path,
+) -> Result<String, Box<dyn std::error::Error>> {
+    Ok(format!(
+        "{}/activate",
+        deploy_path
             .parent()
-            .unwrap()
+            .ok_or("Deploy path too short")?
             .to_str()
-            .unwrap()
+            .ok_or("Deploy path is not valid utf8")?
             .to_owned()
-            + "/activate",
-        deploy_data.profile_path,
-        profile.profile_settings.path,
-    );
+    ))
+}
 
-    if let Some(sudo_cmd) = &deploy_data.sudo {
+#[test]
+fn test_activate_path_generation() {
+    match deploy_path_to_activate_path_str(&std::path::PathBuf::from(
+        "/blah/blah/deploy-rs/bin/deploy",
+    )) {
+        Err(_) => panic!(""),
+        Ok(x) => assert_eq!(x, "/blah/blah/deploy-rs/bin/activate".to_string()),
+    }
+}
+
+fn build_activate_command(
+    activate_path_str: String,
+    sudo: &Option<String>,
+    profile_path: &str,
+    closure: &str,
+    activate_cmd: &Option<String>,
+    bootstrap_cmd: &Option<String>,
+    auto_rollback: bool,
+) -> Result<String, Box<dyn std::error::Error>> {
+    let mut self_activate_command =
+        format!("{} '{}' '{}'", activate_path_str, profile_path, closure);
+
+    if let Some(sudo_cmd) = &sudo {
         self_activate_command = format!("{} {}", sudo_cmd, self_activate_command);
     }
 
-    if let Some(ref bootstrap_cmd) = profile.profile_settings.bootstrap {
+    if let Some(ref bootstrap_cmd) = bootstrap_cmd {
         self_activate_command = format!(
             "{} --bootstrap-cmd '{}'",
             self_activate_command, bootstrap_cmd
         );
     }
 
-    if let Some(ref activate_cmd) = profile.profile_settings.activate {
+    if let Some(ref activate_cmd) = activate_cmd {
         self_activate_command = format!(
             "{} --activate-cmd '{}'",
             self_activate_command, activate_cmd
@@ -53,6 +60,59 @@ pub async fn deploy_profile(
         self_activate_command = format!("{} --auto-rollback", self_activate_command);
     }
 
+    Ok(self_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 = "/blah/etc";
+    let activate_cmd = Some("$THING/bin/aaaaaaa".to_string());
+    let bootstrap_cmd = None;
+    let auto_rollback = true;
+
+    match build_activate_command(
+        activate_path_str,
+        &sudo,
+        profile_path,
+        closure,
+        &activate_cmd,
+        &bootstrap_cmd,
+        auto_rollback,
+    ) {
+        Err(_) => panic!(""),
+        Ok(x) => assert_eq!(x, "sudo -u test /blah/bin/activate '/blah/profiles/test' '/blah/etc' --activate-cmd '$THING/bin/aaaaaaa' --auto-rollback".to_string()),
+    }
+}
+
+pub async fn deploy_profile(
+    profile: &data::Profile,
+    profile_name: &str,
+    node: &data::Node,
+    node_name: &str,
+    merged_settings: &data::GenericSettings,
+    deploy_data: &super::DeployData<'_>,
+    auto_rollback: bool,
+) -> Result<(), Box<dyn std::error::Error>> {
+    info!(
+        "Activating profile `{}` for node `{}`",
+        profile_name, node_name
+    );
+
+    let activate_path_str = deploy_path_to_activate_path_str(&deploy_data.current_exe)?;
+
+    let self_activate_command = build_activate_command(
+        activate_path_str,
+        &deploy_data.sudo,
+        &deploy_data.profile_path,
+        &profile.profile_settings.path,
+        &profile.profile_settings.activate,
+        &profile.profile_settings.bootstrap,
+        auto_rollback,
+    )?;
+
     let mut c = Command::new("ssh");
     let mut ssh_command = c.arg(format!(
         "ssh://{}@{}",
-- 
cgit v1.2.3