Skip to main content

consortium_nix/
copy.rs

1//! Closure copying — transfer built closures to deployment targets.
2
3use std::collections::HashMap;
4use std::process::Command;
5
6use crate::config::DeploymentPlan;
7use crate::error::{NixError, Result};
8
9/// Copy results keyed by hostname.
10pub struct CopyResults {
11    /// Hosts that were successfully copied to.
12    pub succeeded: Vec<String>,
13    /// Map of hostname -> copy error.
14    pub errors: HashMap<String, NixError>,
15}
16
17/// Copy closures to all targets in the deployment plan.
18pub fn copy_closures(plan: &DeploymentPlan) -> Result<CopyResults> {
19    let mut results = CopyResults {
20        succeeded: Vec::new(),
21        errors: HashMap::new(),
22    };
23
24    // TODO: parallelize with consortium's Task/Worker fanout
25    for target in &plan.targets {
26        if !target.needs_copy {
27            results.succeeded.push(target.node.name.clone());
28            continue;
29        }
30
31        let store_uri = format!(
32            "ssh-ng://{}@{}",
33            target.node.target_user, target.node.target_host
34        );
35
36        match copy_closure(&target.toplevel_path, &store_uri) {
37            Ok(()) => {
38                results.succeeded.push(target.node.name.clone());
39            }
40            Err(e) => {
41                results.errors.insert(target.node.name.clone(), e);
42            }
43        }
44    }
45
46    Ok(results)
47}
48
49/// Copy a single closure to a remote store.
50///
51/// Uses `--no-check-sigs` because locally-built closures aren't signed
52/// by a key the remote trusts. We're deploying as root over SSH, so
53/// the trust boundary is the SSH connection itself.
54pub fn copy_closure(store_path: &str, store_uri: &str) -> Result<()> {
55    let output = Command::new("nix")
56        .args(["copy", "--no-check-sigs", "--to", store_uri, store_path])
57        .output()
58        .map_err(|e| NixError::CopyFailed {
59            host: store_uri.to_string(),
60            message: format!("failed to run nix copy: {}", e),
61        })?;
62
63    if !output.status.success() {
64        let stderr = String::from_utf8_lossy(&output.stderr);
65        return Err(NixError::CopyFailed {
66            host: store_uri.to_string(),
67            message: stderr.to_string(),
68        });
69    }
70
71    Ok(())
72}