consortium_nix/
activate.rs1use std::collections::HashMap;
4use std::process::Command;
5
6use crate::config::{DeployAction, DeploymentPlan, ProfileType};
7use crate::error::{NixError, Result};
8
9pub struct ActivationResults {
11 pub succeeded: Vec<String>,
13 pub errors: HashMap<String, NixError>,
15}
16
17pub fn activate_all(plan: &DeploymentPlan) -> Result<ActivationResults> {
19 let mut results = ActivationResults {
20 succeeded: Vec::new(),
21 errors: HashMap::new(),
22 };
23
24 if plan.action == DeployAction::Build {
25 results.succeeded = plan.targets.iter().map(|t| t.node.name.clone()).collect();
27 return Ok(results);
28 }
29
30 for target in &plan.targets {
33 match activate_host(
34 &target.node.target_host,
35 &target.node.target_user,
36 &target.toplevel_path,
37 &target.node.profile_type,
38 plan.action,
39 ) {
40 Ok(()) => {
41 results.succeeded.push(target.node.name.clone());
42 }
43 Err(e) => {
44 results.errors.insert(target.node.name.clone(), e);
45 }
46 }
47 }
48
49 Ok(results)
50}
51
52pub fn activate_host(
54 host: &str,
55 user: &str,
56 toplevel_path: &str,
57 profile_type: &ProfileType,
58 action: DeployAction,
59) -> Result<()> {
60 match action {
63 DeployAction::Switch | DeployAction::Boot => {
64 set_profile(host, user, toplevel_path)?;
65 }
66 DeployAction::Test | DeployAction::DryActivate | DeployAction::Build => {}
67 }
68
69 let activation_cmd = match profile_type {
71 ProfileType::Nixos => {
72 format!("{}/bin/switch-to-configuration {}", toplevel_path, action)
73 }
74 ProfileType::NixDarwin => {
75 format!(
76 "{}/activate-user && sudo {}/activate",
77 toplevel_path, toplevel_path
78 )
79 }
80 };
81
82 let output = Command::new("ssh")
83 .args([
84 "-oStrictHostKeyChecking=no",
85 "-oPasswordAuthentication=no",
86 "-oConnectTimeout=30",
87 "-l",
88 user,
89 host,
90 &activation_cmd,
91 ])
92 .output()
93 .map_err(|e| NixError::ActivationFailed {
94 host: host.to_string(),
95 message: format!("failed to run activation: {}", e),
96 })?;
97
98 if !output.status.success() {
99 let stderr = String::from_utf8_lossy(&output.stderr);
100 return Err(NixError::ActivationFailed {
101 host: host.to_string(),
102 message: stderr.to_string(),
103 });
104 }
105
106 Ok(())
107}
108
109fn set_profile(host: &str, user: &str, toplevel_path: &str) -> Result<()> {
111 let cmd = format!(
112 "nix-env -p /nix/var/nix/profiles/system --set {}",
113 toplevel_path
114 );
115
116 let output = Command::new("ssh")
117 .args([
118 "-oStrictHostKeyChecking=no",
119 "-oPasswordAuthentication=no",
120 "-oConnectTimeout=30",
121 "-l",
122 user,
123 host,
124 &cmd,
125 ])
126 .output()
127 .map_err(|e| NixError::ActivationFailed {
128 host: host.to_string(),
129 message: format!("failed to set profile: {}", e),
130 })?;
131
132 if !output.status.success() {
133 let stderr = String::from_utf8_lossy(&output.stderr);
134 return Err(NixError::ActivationFailed {
135 host: host.to_string(),
136 message: format!("profile set failed: {}", stderr),
137 });
138 }
139
140 Ok(())
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146
147 #[test]
148 fn test_profile_set_only_for_switch_and_boot() {
149 let no_profile_actions = vec![
151 DeployAction::Test,
152 DeployAction::DryActivate,
153 DeployAction::Build,
154 ];
155 let profile_actions = vec![DeployAction::Switch, DeployAction::Boot];
156 assert_eq!(no_profile_actions.len() + profile_actions.len(), 5);
157 }
158
159 #[test]
160 fn test_activation_command_nixos() {
161 let toplevel = "/nix/store/abc-nixos-system";
162 let cmd = format!(
163 "{}/bin/switch-to-configuration {}",
164 toplevel,
165 DeployAction::Switch
166 );
167 assert_eq!(
168 cmd,
169 "/nix/store/abc-nixos-system/bin/switch-to-configuration switch"
170 );
171
172 let cmd = format!(
173 "{}/bin/switch-to-configuration {}",
174 toplevel,
175 DeployAction::DryActivate
176 );
177 assert_eq!(
178 cmd,
179 "/nix/store/abc-nixos-system/bin/switch-to-configuration dry-activate"
180 );
181 }
182
183 #[test]
184 fn test_activation_command_darwin() {
185 let toplevel = "/nix/store/abc-darwin-system";
186 let cmd = format!("{}/activate-user && sudo {}/activate", toplevel, toplevel);
187 assert_eq!(
188 cmd,
189 "/nix/store/abc-darwin-system/activate-user && sudo /nix/store/abc-darwin-system/activate"
190 );
191 }
192}