Refactor NixOptions

This commit is contained in:
Zhaofeng Li 2022-01-08 01:20:36 -08:00
parent 31fd1e49ac
commit deca292b53
6 changed files with 96 additions and 50 deletions

View file

@ -105,7 +105,7 @@ pub async fn run(_global_args: &ArgMatches, local_args: &ArgMatches) -> Result<(
let target = { let target = {
if let Some(info) = hive.deployment_info_single(&hostname).await.unwrap() { if let Some(info) = hive.deployment_info_single(&hostname).await.unwrap() {
let nix_options = hive.nix_options().await.unwrap(); let nix_options = hive.nix_options_with_builders().await.unwrap();
if !info.allows_local_deployment() { if !info.allows_local_deployment() {
log::error!("Local deployment is not enabled for host {}.", hostname.as_str()); log::error!("Local deployment is not enabled for host {}.", hostname.as_str());
log::error!("Hint: Set deployment.allowLocalDeployment to true."); log::error!("Hint: Set deployment.allowLocalDeployment to true.");

View file

@ -20,6 +20,7 @@ use itertools::Itertools;
use crate::progress::Sender as ProgressSender; use crate::progress::Sender as ProgressSender;
use crate::job::{JobMonitor, JobHandle, JobType, JobState}; use crate::job::{JobMonitor, JobHandle, JobType, JobState};
use crate::util; use crate::util;
use super::NixOptions;
use super::{ use super::{
Hive, Hive,
@ -55,7 +56,7 @@ pub struct Deployment {
options: Options, options: Options,
/// Options passed to Nix invocations. /// Options passed to Nix invocations.
nix_options: Vec<String>, nix_options: NixOptions,
/// Handle to send messages to the ProgressOutput. /// Handle to send messages to the ProgressOutput.
progress: Option<ProgressSender>, progress: Option<ProgressSender>,
@ -106,7 +107,7 @@ impl Deployment {
hive, hive,
goal, goal,
options: Options::default(), options: Options::default(),
nix_options: Vec::new(), nix_options: NixOptions::default(),
progress, progress,
nodes: targets.keys().cloned().collect(), nodes: targets.keys().cloned().collect(),
targets, targets,
@ -133,7 +134,7 @@ impl Deployment {
monitor.set_label_width(width); monitor.set_label_width(width);
} }
let nix_options = self.hive.nix_options().await?; let nix_options = self.hive.nix_options_with_builders().await?;
self.nix_options = nix_options; self.nix_options = nix_options;
if self.goal == Goal::UploadKeys { if self.goal == Goal::UploadKeys {

View file

@ -11,7 +11,7 @@ use validator::Validate;
use super::{ use super::{
Flake, Flake,
ColmenaResult, NixOptions,
NodeName, NodeName,
NodeConfig, NodeConfig,
NodeFilter, NodeFilter,
@ -19,6 +19,7 @@ use super::{
StorePath, StorePath,
}; };
use super::deployment::TargetNode; use super::deployment::TargetNode;
use crate::error::ColmenaResult;
use crate::util::{CommandExecution, CommandExt}; use crate::util::{CommandExecution, CommandExt};
use crate::job::JobHandle; use crate::job::JobHandle;
@ -82,8 +83,8 @@ pub struct Hive {
/// Whether to pass --show-trace in Nix commands. /// Whether to pass --show-trace in Nix commands.
show_trace: bool, show_trace: bool,
/// The cached --builders expression. /// The cached machines_file expression.
builders: RwLock<Option<Option<String>>>, machines_file: RwLock<Option<Option<String>>>,
} }
impl Hive { impl Hive {
@ -98,7 +99,7 @@ impl Hive {
context_dir, context_dir,
eval_nix: eval_nix.into_temp_path(), eval_nix: eval_nix.into_temp_path(),
show_trace: false, show_trace: false,
builders: RwLock::new(None), machines_file: RwLock::new(None),
}) })
} }
@ -110,11 +111,20 @@ impl Hive {
self.show_trace = value; self.show_trace = value;
} }
pub async fn nix_options(&self) -> ColmenaResult<Vec<String>> { /// Returns Nix options to set for this Hive.
let mut options = self.builder_args().await?; pub fn nix_options(&self) -> NixOptions {
let mut options = NixOptions::default();
options.set_show_trace(self.show_trace);
options
}
if self.show_trace { /// Returns Nix options to set for this Hive, with configured remote builders.
options.push("--show-trace".to_owned()); pub async fn nix_options_with_builders(&self) -> ColmenaResult<NixOptions> {
let mut options = NixOptions::default();
options.set_show_trace(self.show_trace);
if let Some(machines_file) = self.machines_file().await? {
options.set_builders(Some(format!("@{}", machines_file)));
} }
Ok(options) Ok(options)
@ -266,7 +276,7 @@ impl Hive {
.collect() .collect()
} }
/// Evaluates an expression using values from the configuration /// Evaluates an expression using values from the configuration.
pub async fn introspect(&self, expression: String, instantiate: bool) -> ColmenaResult<String> { pub async fn introspect(&self, expression: String, instantiate: bool) -> ColmenaResult<String> {
if instantiate { if instantiate {
let expression = format!("hive.introspect ({})", expression); let expression = format!("hive.introspect ({})", expression);
@ -279,10 +289,10 @@ impl Hive {
} }
} }
/// Retrieve machinesFile setting for the hive. /// Retrieve the machinesFile setting for the Hive.
async fn machines_file(&self) -> ColmenaResult<Option<String>> { async fn machines_file(&self) -> ColmenaResult<Option<String>> {
if let Some(builders_opt) = &*self.builders.read().await { if let Some(machines_file) = &*self.machines_file.read().await {
return Ok(builders_opt.clone()); return Ok(machines_file.clone());
} }
let expr = "toJSON (hive.meta.machinesFile or null)"; let expr = "toJSON (hive.meta.machinesFile or null)";
@ -290,26 +300,11 @@ impl Hive {
.capture_json().await?; .capture_json().await?;
let parsed: Option<String> = serde_json::from_str(&s).unwrap(); let parsed: Option<String> = serde_json::from_str(&s).unwrap();
self.builders.write().await.replace(parsed.clone()); self.machines_file.write().await.replace(parsed.clone());
Ok(parsed) Ok(parsed)
} }
/// Returns Nix arguments to set builders.
async fn builder_args(&self) -> ColmenaResult<Vec<String>> {
let mut options = Vec::new();
if let Some(machines_file) = self.machines_file().await? {
options.append(&mut vec![
"--option".to_owned(),
"builders".to_owned(),
format!("@{}", machines_file),
]);
}
Ok(options)
}
fn nix_instantiate(&self, expression: &str) -> NixInstantiate { fn nix_instantiate(&self, expression: &str) -> NixInstantiate {
NixInstantiate::new(self, expression.to_owned()) NixInstantiate::new(self, expression.to_owned())
} }
@ -332,7 +327,7 @@ impl<'hive> NixInstantiate<'hive> {
} }
} }
fn instantiate(self) -> Command { fn instantiate(&self) -> Command {
// FIXME: unwrap // FIXME: unwrap
// Technically filenames can be arbitrary byte strings (OsStr), // Technically filenames can be arbitrary byte strings (OsStr),
// but Nix may not like it... // but Nix may not like it...
@ -365,38 +360,34 @@ impl<'hive> NixInstantiate<'hive> {
} }
} }
if self.hive.show_trace {
command.arg("--show-trace");
}
command command
} }
fn eval(self) -> Command { fn eval(self) -> Command {
let mut command = self.instantiate(); let mut command = self.instantiate();
let options = self.hive.nix_options();
command.arg("--eval").arg("--json").arg("--strict") command.arg("--eval").arg("--json").arg("--strict")
// Ensures the derivations are instantiated // Ensures the derivations are instantiated
// Required for system profile evaluation and IFD // Required for system profile evaluation and IFD
.arg("--read-write-mode"); .arg("--read-write-mode")
.args(options.to_args());
command command
} }
async fn instantiate_with_builders(self) -> ColmenaResult<Command> { async fn instantiate_with_builders(self) -> ColmenaResult<Command> {
let hive = self.hive; let options = self.hive.nix_options_with_builders().await?;
let mut command = self.instantiate(); let mut command = self.instantiate();
let builder_args = hive.builder_args().await?; command.args(options.to_args());
command.args(&builder_args);
Ok(command) Ok(command)
} }
async fn eval_with_builders(self) -> ColmenaResult<Command> { async fn eval_with_builders(self) -> ColmenaResult<Command> {
let hive = self.hive; let options = self.hive.nix_options_with_builders().await?;
let mut command = self.eval(); let mut command = self.eval();
let builder_args = hive.builder_args().await?; command.args(options.to_args());
command.args(&builder_args);
Ok(command) Ok(command)
} }

View file

@ -6,7 +6,7 @@ use async_trait::async_trait;
use tokio::process::Command; use tokio::process::Command;
use crate::error::{ColmenaResult, ColmenaError}; use crate::error::{ColmenaResult, ColmenaError};
use crate::nix::{StorePath, Profile, Goal, Key, SYSTEM_PROFILE, CURRENT_PROFILE}; use crate::nix::{StorePath, Profile, Goal, Key, NixOptions, SYSTEM_PROFILE, CURRENT_PROFILE};
use crate::util::{CommandExecution, CommandExt}; use crate::util::{CommandExecution, CommandExt};
use crate::job::JobHandle; use crate::job::JobHandle;
use super::{CopyDirection, CopyOptions, Host, key_uploader}; use super::{CopyDirection, CopyOptions, Host, key_uploader};
@ -18,11 +18,11 @@ use super::{CopyDirection, CopyOptions, Host, key_uploader};
#[derive(Debug)] #[derive(Debug)]
pub struct Local { pub struct Local {
job: Option<JobHandle>, job: Option<JobHandle>,
nix_options: Vec<String>, nix_options: NixOptions,
} }
impl Local { impl Local {
pub fn new(nix_options: Vec<String>) -> Self { pub fn new(nix_options: NixOptions) -> Self {
Self { Self {
job: None, job: None,
nix_options, nix_options,
@ -39,7 +39,7 @@ impl Host for Local {
async fn realize_remote(&mut self, derivation: &StorePath) -> ColmenaResult<Vec<StorePath>> { async fn realize_remote(&mut self, derivation: &StorePath) -> ColmenaResult<Vec<StorePath>> {
let mut command = Command::new("nix-store"); let mut command = Command::new("nix-store");
command.args(self.nix_options.clone()); command.args(self.nix_options.to_args());
command command
.arg("--no-gc-warning") .arg("--no-gc-warning")
.arg("--realise") .arg("--realise")

View file

@ -2,8 +2,9 @@ use std::collections::HashMap;
use async_trait::async_trait; use async_trait::async_trait;
use super::{StorePath, Profile, Goal, ColmenaResult, ColmenaError, Key}; use crate::error::{ColmenaError, ColmenaResult};
use crate::job::JobHandle; use crate::job::JobHandle;
use super::{StorePath, Profile, Goal, Key, NixOptions};
mod ssh; mod ssh;
pub use ssh::Ssh; pub use ssh::Ssh;
@ -13,7 +14,7 @@ pub use local::Local;
mod key_uploader; mod key_uploader;
pub(crate) fn local(nix_options: Vec<String>) -> Box<dyn Host + 'static> { pub(crate) fn local(nix_options: NixOptions) -> Box<dyn Host + 'static> {
Box::new(Local::new(nix_options)) Box::new(Local::new(nix_options))
} }

View file

@ -81,6 +81,22 @@ pub struct NodeConfig {
keys: HashMap<String, Key>, keys: HashMap<String, Key>,
} }
/// Nix options.
#[derive(Debug, Clone)]
pub struct NixOptions {
/// Whether to pass --show-trace.
show_trace: bool,
/// Designated builders.
///
/// See <https://nixos.org/manual/nix/stable/advanced-topics/distributed-builds.html>.
///
/// Valid examples:
/// - `@/path/to/machines`
/// - `builder@host.tld riscv64-linux /home/nix/.ssh/keys/builder.key 8 1 kvm`
builders: Option<String>,
}
impl NodeName { impl NodeName {
/// Returns the string. /// Returns the string.
pub fn as_str(&self) -> &str { pub fn as_str(&self) -> &str {
@ -150,6 +166,43 @@ impl NodeConfig {
} }
} }
impl Default for NixOptions {
fn default() -> Self {
Self {
show_trace: false,
builders: None,
}
}
}
impl NixOptions {
pub fn set_show_trace(&mut self, show_trace: bool) {
self.show_trace = show_trace;
}
pub fn set_builders(&mut self, builders: Option<String>) {
self.builders = builders;
}
pub fn to_args(&self) -> Vec<String> {
let mut options = Vec::new();
if let Some(builders) = &self.builders {
options.append(&mut vec![
"--option".to_string(),
"builders".to_string(),
builders.clone(),
]);
}
if self.show_trace {
options.push("--show-trace".to_string());
}
options
}
}
fn validate_keys(keys: &HashMap<String, Key>) -> Result<(), ValidationErrorType> { fn validate_keys(keys: &HashMap<String, Key>) -> Result<(), ValidationErrorType> {
// Bad secret names: // Bad secret names:
// - /etc/passwd // - /etc/passwd