forked from DGNum/colmena
Move nix::{NixResult, NixError} to error::{ColmenaResult, ColmenaError}
This commit is contained in:
parent
16ed9d8c66
commit
31fd1e49ac
26 changed files with 295 additions and 275 deletions
|
@ -3,6 +3,7 @@ use std::path::PathBuf;
|
||||||
|
|
||||||
use clap::{Arg, App, ArgMatches, ArgSettings};
|
use clap::{Arg, App, ArgMatches, ArgSettings};
|
||||||
|
|
||||||
|
use crate::error::ColmenaError;
|
||||||
use crate::nix::deployment::{
|
use crate::nix::deployment::{
|
||||||
Deployment,
|
Deployment,
|
||||||
Goal,
|
Goal,
|
||||||
|
@ -11,7 +12,7 @@ use crate::nix::deployment::{
|
||||||
ParallelismLimit,
|
ParallelismLimit,
|
||||||
};
|
};
|
||||||
use crate::progress::SimpleProgressOutput;
|
use crate::progress::SimpleProgressOutput;
|
||||||
use crate::nix::{NixError, NodeFilter};
|
use crate::nix::NodeFilter;
|
||||||
use crate::util;
|
use crate::util;
|
||||||
|
|
||||||
pub fn register_deploy_args(command: App) -> App {
|
pub fn register_deploy_args(command: App) -> App {
|
||||||
|
@ -126,7 +127,7 @@ pub fn subcommand() -> App<'static> {
|
||||||
util::register_selector_args(command)
|
util::register_selector_args(command)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(_global_args: &ArgMatches, local_args: &ArgMatches) -> Result<(), NixError> {
|
pub async fn run(_global_args: &ArgMatches, local_args: &ArgMatches) -> Result<(), ColmenaError> {
|
||||||
let hive = util::hive_from_args(local_args).await?;
|
let hive = util::hive_from_args(local_args).await?;
|
||||||
|
|
||||||
let ssh_config = env::var("SSH_CONFIG_FILE")
|
let ssh_config = env::var("SSH_CONFIG_FILE")
|
||||||
|
|
|
@ -5,13 +5,14 @@ use clap::{Arg, App, ArgMatches};
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
|
use crate::error::ColmenaError;
|
||||||
use crate::nix::deployment::{
|
use crate::nix::deployment::{
|
||||||
Deployment,
|
Deployment,
|
||||||
Goal,
|
Goal,
|
||||||
TargetNode,
|
TargetNode,
|
||||||
Options,
|
Options,
|
||||||
};
|
};
|
||||||
use crate::nix::{NixError, NodeName, host};
|
use crate::nix::{NodeName, host};
|
||||||
use crate::progress::SimpleProgressOutput;
|
use crate::progress::SimpleProgressOutput;
|
||||||
use crate::util;
|
use crate::util;
|
||||||
|
|
||||||
|
@ -57,7 +58,7 @@ By default, Colmena will deploy keys set in `deployment.keys` before activating
|
||||||
.takes_value(false))
|
.takes_value(false))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(_global_args: &ArgMatches, local_args: &ArgMatches) -> Result<(), NixError> {
|
pub async fn run(_global_args: &ArgMatches, local_args: &ArgMatches) -> Result<(), ColmenaError> {
|
||||||
// Sanity check: Are we running NixOS?
|
// Sanity check: Are we running NixOS?
|
||||||
if let Ok(os_release) = fs::read_to_string("/etc/os-release").await {
|
if let Ok(os_release) = fs::read_to_string("/etc/os-release").await {
|
||||||
if !os_release.contains("ID=nixos\n") {
|
if !os_release.contains("ID=nixos\n") {
|
||||||
|
|
|
@ -2,8 +2,8 @@ use std::path::PathBuf;
|
||||||
|
|
||||||
use clap::{Arg, App, AppSettings, ArgMatches};
|
use clap::{Arg, App, AppSettings, ArgMatches};
|
||||||
|
|
||||||
|
use crate::error::ColmenaError;
|
||||||
use crate::util;
|
use crate::util;
|
||||||
use crate::nix::NixError;
|
|
||||||
|
|
||||||
pub fn subcommand() -> App<'static> {
|
pub fn subcommand() -> App<'static> {
|
||||||
subcommand_gen("eval")
|
subcommand_gen("eval")
|
||||||
|
@ -41,7 +41,7 @@ For example, to retrieve the configuration of one node, you may write something
|
||||||
.takes_value(false))
|
.takes_value(false))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(global_args: &ArgMatches, local_args: &ArgMatches) -> Result<(), NixError> {
|
pub async fn run(global_args: &ArgMatches, local_args: &ArgMatches) -> Result<(), ColmenaError> {
|
||||||
if let Some("introspect") = global_args.subcommand_name() {
|
if let Some("introspect") = global_args.subcommand_name() {
|
||||||
log::warn!("`colmena introspect` has been renamed to `colmena eval`. Please update your scripts.");
|
log::warn!("`colmena introspect` has been renamed to `colmena eval`. Please update your scripts.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,9 @@ use clap::{Arg, App, AppSettings, ArgMatches};
|
||||||
use futures::future::join_all;
|
use futures::future::join_all;
|
||||||
use tokio::sync::Semaphore;
|
use tokio::sync::Semaphore;
|
||||||
|
|
||||||
use crate::nix::{NixError, NodeFilter};
|
use crate::error::ColmenaError;
|
||||||
use crate::job::{JobMonitor, JobState, JobType};
|
use crate::job::{JobMonitor, JobState, JobType};
|
||||||
|
use crate::nix::NodeFilter;
|
||||||
use crate::progress::SimpleProgressOutput;
|
use crate::progress::SimpleProgressOutput;
|
||||||
use crate::util;
|
use crate::util;
|
||||||
|
|
||||||
|
@ -54,7 +55,7 @@ It's recommended to use -- to separate Colmena options from the command to run.
|
||||||
util::register_selector_args(command)
|
util::register_selector_args(command)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(_global_args: &ArgMatches, local_args: &ArgMatches) -> Result<(), NixError> {
|
pub async fn run(_global_args: &ArgMatches, local_args: &ArgMatches) -> Result<(), ColmenaError> {
|
||||||
let hive = util::hive_from_args(local_args).await?;
|
let hive = util::hive_from_args(local_args).await?;
|
||||||
let ssh_config = env::var("SSH_CONFIG_FILE")
|
let ssh_config = env::var("SSH_CONFIG_FILE")
|
||||||
.ok().map(PathBuf::from);
|
.ok().map(PathBuf::from);
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
use clap::{App, ArgMatches};
|
use clap::{App, ArgMatches};
|
||||||
|
|
||||||
use crate::nix::{NixCheck, NixError};
|
use crate::error::ColmenaError;
|
||||||
|
use crate::nix::NixCheck;
|
||||||
|
|
||||||
pub fn subcommand() -> App<'static> {
|
pub fn subcommand() -> App<'static> {
|
||||||
App::new("nix-info")
|
App::new("nix-info")
|
||||||
.about("Show information about the current Nix installation")
|
.about("Show information about the current Nix installation")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(_global_args: &ArgMatches, _local_args: &ArgMatches) -> Result<(), NixError> {
|
pub async fn run(_global_args: &ArgMatches, _local_args: &ArgMatches) -> Result<(), ColmenaError> {
|
||||||
let check = NixCheck::detect().await;
|
let check = NixCheck::detect().await;
|
||||||
check.print_version_info();
|
check.print_version_info();
|
||||||
check.print_flakes_info(false);
|
check.print_flakes_info(false);
|
||||||
|
|
|
@ -3,8 +3,9 @@ use std::time::Duration;
|
||||||
use clap::{App, AppSettings, ArgMatches};
|
use clap::{App, AppSettings, ArgMatches};
|
||||||
use tokio::time;
|
use tokio::time;
|
||||||
|
|
||||||
|
use crate::error::{ColmenaError, ColmenaResult};
|
||||||
use crate::job::{JobMonitor, JobType};
|
use crate::job::{JobMonitor, JobType};
|
||||||
use crate::nix::{NixError, NixResult, NodeName};
|
use crate::nix::NodeName;
|
||||||
use crate::progress::{ProgressOutput, spinner::SpinnerOutput};
|
use crate::progress::{ProgressOutput, spinner::SpinnerOutput};
|
||||||
|
|
||||||
macro_rules! node {
|
macro_rules! node {
|
||||||
|
@ -19,7 +20,7 @@ pub fn subcommand() -> App<'static> {
|
||||||
.setting(AppSettings::Hidden)
|
.setting(AppSettings::Hidden)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(_global_args: &ArgMatches, _local_args: &ArgMatches) -> Result<(), NixError> {
|
pub async fn run(_global_args: &ArgMatches, _local_args: &ArgMatches) -> Result<(), ColmenaError> {
|
||||||
let mut output = SpinnerOutput::new();
|
let mut output = SpinnerOutput::new();
|
||||||
let (monitor, meta) = JobMonitor::new(output.get_sender());
|
let (monitor, meta) = JobMonitor::new(output.get_sender());
|
||||||
|
|
||||||
|
@ -52,7 +53,7 @@ pub async fn run(_global_args: &ArgMatches, _local_args: &ArgMatches) -> Result<
|
||||||
|
|
||||||
let (_, _) = tokio::join!(eval, build);
|
let (_, _) = tokio::join!(eval, build);
|
||||||
|
|
||||||
Err(NixError::Unsupported) as NixResult<()>
|
Err(ColmenaError::Unsupported) as ColmenaResult<()>
|
||||||
});
|
});
|
||||||
|
|
||||||
let (monitor, output, ret) = tokio::join!(
|
let (monitor, output, ret) = tokio::join!(
|
||||||
|
|
103
src/error.rs
Normal file
103
src/error.rs
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
//! Custom error types.
|
||||||
|
|
||||||
|
use std::os::unix::process::ExitStatusExt;
|
||||||
|
use std::process::ExitStatus;
|
||||||
|
|
||||||
|
use snafu::Snafu;
|
||||||
|
use validator::ValidationErrors;
|
||||||
|
|
||||||
|
use crate::nix::{key, StorePath};
|
||||||
|
|
||||||
|
pub type ColmenaResult<T> = Result<T, ColmenaError>;
|
||||||
|
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[derive(Debug, Snafu)]
|
||||||
|
pub enum ColmenaError {
|
||||||
|
#[snafu(display("I/O Error: {}", error))]
|
||||||
|
IoError { error: std::io::Error },
|
||||||
|
|
||||||
|
#[snafu(display("Nix returned invalid response: {}", output))]
|
||||||
|
BadOutput { output: String },
|
||||||
|
|
||||||
|
#[snafu(display("Nix exited with error code: {}", exit_code))]
|
||||||
|
NixFailure { exit_code: i32 },
|
||||||
|
|
||||||
|
#[snafu(display("Nix was killed by signal {}", signal))]
|
||||||
|
NixKilled { signal: i32 },
|
||||||
|
|
||||||
|
#[snafu(display("This operation is not supported"))]
|
||||||
|
Unsupported,
|
||||||
|
|
||||||
|
#[snafu(display("Invalid Nix store path"))]
|
||||||
|
InvalidStorePath,
|
||||||
|
|
||||||
|
#[snafu(display("Validation error"))]
|
||||||
|
ValidationError { errors: ValidationErrors },
|
||||||
|
|
||||||
|
#[snafu(display("Failed to upload keys: {}", error))]
|
||||||
|
KeyError { error: key::KeyError },
|
||||||
|
|
||||||
|
#[snafu(display("Store path {:?} is not a derivation", store_path))]
|
||||||
|
NotADerivation { store_path: StorePath },
|
||||||
|
|
||||||
|
#[snafu(display("Invalid NixOS system profile"))]
|
||||||
|
InvalidProfile,
|
||||||
|
|
||||||
|
#[snafu(display("Unknown active profile: {:?}", store_path))]
|
||||||
|
ActiveProfileUnknown { store_path: StorePath },
|
||||||
|
|
||||||
|
#[snafu(display("Could not determine current profile"))]
|
||||||
|
FailedToGetCurrentProfile,
|
||||||
|
|
||||||
|
#[snafu(display("Current Nix version does not support Flakes"))]
|
||||||
|
NoFlakesSupport,
|
||||||
|
|
||||||
|
#[snafu(display("Don't know how to connect to the node"))]
|
||||||
|
NoTargetHost,
|
||||||
|
|
||||||
|
#[snafu(display("Node name cannot be empty"))]
|
||||||
|
EmptyNodeName,
|
||||||
|
|
||||||
|
#[snafu(display("Filter rule cannot be empty"))]
|
||||||
|
EmptyFilterRule,
|
||||||
|
|
||||||
|
#[snafu(display("Deployment already executed"))]
|
||||||
|
DeploymentAlreadyExecuted,
|
||||||
|
|
||||||
|
#[snafu(display("Unknown error: {}", message))]
|
||||||
|
Unknown { message: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::io::Error> for ColmenaError {
|
||||||
|
fn from(error: std::io::Error) -> Self {
|
||||||
|
Self::IoError { error }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<key::KeyError> for ColmenaError {
|
||||||
|
fn from(error: key::KeyError) -> Self {
|
||||||
|
Self::KeyError { error }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ValidationErrors> for ColmenaError {
|
||||||
|
fn from(errors: ValidationErrors) -> Self {
|
||||||
|
Self::ValidationError { errors }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ExitStatus> for ColmenaError {
|
||||||
|
fn from(status: ExitStatus) -> Self {
|
||||||
|
match status.code() {
|
||||||
|
Some(exit_code) => Self::NixFailure { exit_code },
|
||||||
|
None => Self::NixKilled { signal: status.signal().unwrap() },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ColmenaError {
|
||||||
|
pub fn unknown(error: Box<dyn std::error::Error>) -> Self {
|
||||||
|
let message = error.to_string();
|
||||||
|
Self::Unknown { message }
|
||||||
|
}
|
||||||
|
}
|
55
src/job.rs
55
src/job.rs
|
@ -13,7 +13,8 @@ use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
|
||||||
use tokio::time;
|
use tokio::time;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::nix::{NixResult, NixError, NodeName};
|
use crate::error::{ColmenaResult, ColmenaError};
|
||||||
|
use crate::nix::NodeName;
|
||||||
use crate::progress::{Sender as ProgressSender, Message as ProgressMessage, Line, LineStyle};
|
use crate::progress::{Sender as ProgressSender, Message as ProgressMessage, Line, LineStyle};
|
||||||
|
|
||||||
pub type Sender = UnboundedSender<Event>;
|
pub type Sender = UnboundedSender<Event>;
|
||||||
|
@ -192,7 +193,7 @@ pub enum EventPayload {
|
||||||
|
|
||||||
/// The job failed.
|
/// The job failed.
|
||||||
///
|
///
|
||||||
/// We can't pass the NixError because the wrapper needs to
|
/// We can't pass the ColmenaError because the wrapper needs to
|
||||||
/// be able to return it as-is.
|
/// be able to return it as-is.
|
||||||
Failure(String),
|
Failure(String),
|
||||||
|
|
||||||
|
@ -277,7 +278,7 @@ impl JobMonitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Starts the monitor.
|
/// Starts the monitor.
|
||||||
pub async fn run_until_completion(mut self) -> NixResult<Self> {
|
pub async fn run_until_completion(mut self) -> ColmenaResult<Self> {
|
||||||
if let Some(width) = self.label_width {
|
if let Some(width) = self.label_width {
|
||||||
if let Some(sender) = &self.progress {
|
if let Some(sender) = &self.progress {
|
||||||
sender.send(ProgressMessage::HintLabelWidth(width)).unwrap();
|
sender.send(ProgressMessage::HintLabelWidth(width)).unwrap();
|
||||||
|
@ -465,7 +466,7 @@ impl JobMonitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shows human-readable summary and performs cleanup.
|
/// Shows human-readable summary and performs cleanup.
|
||||||
async fn finish(mut self) -> NixResult<Self> {
|
async fn finish(mut self) -> ColmenaResult<Self> {
|
||||||
if let Some(sender) = self.progress.take() {
|
if let Some(sender) = self.progress.take() {
|
||||||
sender.send(ProgressMessage::Complete).unwrap();
|
sender.send(ProgressMessage::Complete).unwrap();
|
||||||
}
|
}
|
||||||
|
@ -500,7 +501,7 @@ impl JobHandleInner {
|
||||||
/// Creates a new job with a distinct ID.
|
/// Creates a new job with a distinct ID.
|
||||||
///
|
///
|
||||||
/// This sends out a Creation message with the metadata.
|
/// This sends out a Creation message with the metadata.
|
||||||
pub fn create_job(&self, job_type: JobType, nodes: Vec<NodeName>) -> NixResult<JobHandle> {
|
pub fn create_job(&self, job_type: JobType, nodes: Vec<NodeName>) -> ColmenaResult<JobHandle> {
|
||||||
let job_id = JobId::new();
|
let job_id = JobId::new();
|
||||||
let creation = JobCreation {
|
let creation = JobCreation {
|
||||||
friendly_name: None,
|
friendly_name: None,
|
||||||
|
@ -509,7 +510,7 @@ impl JobHandleInner {
|
||||||
};
|
};
|
||||||
|
|
||||||
if job_type == JobType::Meta {
|
if job_type == JobType::Meta {
|
||||||
return Err(NixError::Unknown { message: "Cannot create a meta job!".to_string() });
|
return Err(ColmenaError::Unknown { message: "Cannot create a meta job!".to_string() });
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_handle = Arc::new(Self {
|
let new_handle = Arc::new(Self {
|
||||||
|
@ -525,9 +526,9 @@ impl JobHandleInner {
|
||||||
/// Runs a closure, automatically updating the job monitor based on the result.
|
/// Runs a closure, automatically updating the job monitor based on the result.
|
||||||
///
|
///
|
||||||
/// This immediately transitions the state to Running.
|
/// This immediately transitions the state to Running.
|
||||||
pub async fn run<F, U, T>(self: Arc<Self>, f: U) -> NixResult<T>
|
pub async fn run<F, U, T>(self: Arc<Self>, f: U) -> ColmenaResult<T>
|
||||||
where U: FnOnce(Arc<Self>) -> F,
|
where U: FnOnce(Arc<Self>) -> F,
|
||||||
F: Future<Output = NixResult<T>>,
|
F: Future<Output = ColmenaResult<T>>,
|
||||||
{
|
{
|
||||||
self.run_internal(f, true).await
|
self.run_internal(f, true).await
|
||||||
}
|
}
|
||||||
|
@ -535,52 +536,52 @@ impl JobHandleInner {
|
||||||
/// Runs a closure, automatically updating the job monitor based on the result.
|
/// Runs a closure, automatically updating the job monitor based on the result.
|
||||||
///
|
///
|
||||||
/// This does not immediately transition the state to Running.
|
/// This does not immediately transition the state to Running.
|
||||||
pub async fn run_waiting<F, U, T>(self: Arc<Self>, f: U) -> NixResult<T>
|
pub async fn run_waiting<F, U, T>(self: Arc<Self>, f: U) -> ColmenaResult<T>
|
||||||
where U: FnOnce(Arc<Self>) -> F,
|
where U: FnOnce(Arc<Self>) -> F,
|
||||||
F: Future<Output = NixResult<T>>,
|
F: Future<Output = ColmenaResult<T>>,
|
||||||
{
|
{
|
||||||
self.run_internal(f, false).await
|
self.run_internal(f, false).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends a line of child stdout to the job monitor.
|
/// Sends a line of child stdout to the job monitor.
|
||||||
pub fn stdout(&self, output: String) -> NixResult<()> {
|
pub fn stdout(&self, output: String) -> ColmenaResult<()> {
|
||||||
self.send_payload(EventPayload::ChildStdout(output))
|
self.send_payload(EventPayload::ChildStdout(output))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends a line of child stderr to the job monitor.
|
/// Sends a line of child stderr to the job monitor.
|
||||||
pub fn stderr(&self, output: String) -> NixResult<()> {
|
pub fn stderr(&self, output: String) -> ColmenaResult<()> {
|
||||||
self.send_payload(EventPayload::ChildStderr(output))
|
self.send_payload(EventPayload::ChildStderr(output))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends a human-readable message to the job monitor.
|
/// Sends a human-readable message to the job monitor.
|
||||||
pub fn message(&self, message: String) -> NixResult<()> {
|
pub fn message(&self, message: String) -> ColmenaResult<()> {
|
||||||
self.send_payload(EventPayload::Message(message))
|
self.send_payload(EventPayload::Message(message))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transitions to a new job state.
|
/// Transitions to a new job state.
|
||||||
pub fn state(&self, new_state: JobState) -> NixResult<()> {
|
pub fn state(&self, new_state: JobState) -> ColmenaResult<()> {
|
||||||
self.send_payload(EventPayload::NewState(new_state))
|
self.send_payload(EventPayload::NewState(new_state))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Marks the job as successful, with a custom message.
|
/// Marks the job as successful, with a custom message.
|
||||||
pub fn success_with_message(&self, message: String) -> NixResult<()> {
|
pub fn success_with_message(&self, message: String) -> ColmenaResult<()> {
|
||||||
self.send_payload(EventPayload::SuccessWithMessage(message))
|
self.send_payload(EventPayload::SuccessWithMessage(message))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Marks the job as noop.
|
/// Marks the job as noop.
|
||||||
pub fn noop(&self, message: String) -> NixResult<()> {
|
pub fn noop(&self, message: String) -> ColmenaResult<()> {
|
||||||
self.send_payload(EventPayload::Noop(message))
|
self.send_payload(EventPayload::Noop(message))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Marks the job as failed.
|
/// Marks the job as failed.
|
||||||
pub fn failure(&self, error: &NixError) -> NixResult<()> {
|
pub fn failure(&self, error: &ColmenaError) -> ColmenaResult<()> {
|
||||||
self.send_payload(EventPayload::Failure(error.to_string()))
|
self.send_payload(EventPayload::Failure(error.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs a closure, automatically updating the job monitor based on the result.
|
/// Runs a closure, automatically updating the job monitor based on the result.
|
||||||
async fn run_internal<F, U, T>(self: Arc<Self>, f: U, report_running: bool) -> NixResult<T>
|
async fn run_internal<F, U, T>(self: Arc<Self>, f: U, report_running: bool) -> ColmenaResult<T>
|
||||||
where U: FnOnce(Arc<Self>) -> F,
|
where U: FnOnce(Arc<Self>) -> F,
|
||||||
F: Future<Output = NixResult<T>>,
|
F: Future<Output = ColmenaResult<T>>,
|
||||||
{
|
{
|
||||||
if report_running {
|
if report_running {
|
||||||
// Tell monitor we are starting
|
// Tell monitor we are starting
|
||||||
|
@ -603,7 +604,7 @@ impl JobHandleInner {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends an event to the job monitor.
|
/// Sends an event to the job monitor.
|
||||||
fn send_payload(&self, payload: EventPayload) -> NixResult<()> {
|
fn send_payload(&self, payload: EventPayload) -> ColmenaResult<()> {
|
||||||
if payload.privileged() {
|
if payload.privileged() {
|
||||||
panic!("Tried to send privileged payload with JobHandle");
|
panic!("Tried to send privileged payload with JobHandle");
|
||||||
}
|
}
|
||||||
|
@ -611,7 +612,7 @@ impl JobHandleInner {
|
||||||
let event = Event::new(self.job_id, payload);
|
let event = Event::new(self.job_id, payload);
|
||||||
|
|
||||||
self.sender.send(event)
|
self.sender.send(event)
|
||||||
.map_err(|e| NixError::unknown(Box::new(e)))?;
|
.map_err(|e| ColmenaError::unknown(Box::new(e)))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -619,9 +620,9 @@ impl JobHandleInner {
|
||||||
|
|
||||||
impl MetaJobHandle {
|
impl MetaJobHandle {
|
||||||
/// Runs a closure, automatically updating the job monitor based on the result.
|
/// Runs a closure, automatically updating the job monitor based on the result.
|
||||||
pub async fn run<F, U, T>(self, f: U) -> NixResult<T>
|
pub async fn run<F, U, T>(self, f: U) -> ColmenaResult<T>
|
||||||
where U: FnOnce(JobHandle) -> F,
|
where U: FnOnce(JobHandle) -> F,
|
||||||
F: Future<Output = NixResult<T>>,
|
F: Future<Output = ColmenaResult<T>>,
|
||||||
{
|
{
|
||||||
let normal_handle = Arc::new(JobHandleInner {
|
let normal_handle = Arc::new(JobHandleInner {
|
||||||
job_id: self.job_id,
|
job_id: self.job_id,
|
||||||
|
@ -645,11 +646,11 @@ impl MetaJobHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends an event to the job monitor.
|
/// Sends an event to the job monitor.
|
||||||
fn send_payload(&self, payload: EventPayload) -> NixResult<()> {
|
fn send_payload(&self, payload: EventPayload) -> ColmenaResult<()> {
|
||||||
let event = Event::new(self.job_id, payload);
|
let event = Event::new(self.job_id, payload);
|
||||||
|
|
||||||
self.sender.send(event)
|
self.sender.send(event)
|
||||||
.map_err(|e| NixError::unknown(Box::new(e)))?;
|
.map_err(|e| ColmenaError::unknown(Box::new(e)))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -874,7 +875,7 @@ mod tests {
|
||||||
Ok(())
|
Ok(())
|
||||||
}).await?;
|
}).await?;
|
||||||
|
|
||||||
Err(NixError::Unsupported) as NixResult<()>
|
Err(ColmenaError::Unsupported) as ColmenaResult<()>
|
||||||
});
|
});
|
||||||
|
|
||||||
// Run until completion
|
// Run until completion
|
||||||
|
@ -884,7 +885,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
match ret {
|
match ret {
|
||||||
Err(NixError::Unsupported) => (),
|
Err(ColmenaError::Unsupported) => (),
|
||||||
_ => {
|
_ => {
|
||||||
panic!("Wrapper must return error as-is");
|
panic!("Wrapper must return error as-is");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#![deny(unused_must_use)]
|
#![deny(unused_must_use)]
|
||||||
|
|
||||||
|
mod error;
|
||||||
mod nix;
|
mod nix;
|
||||||
mod cli;
|
mod cli;
|
||||||
mod command;
|
mod command;
|
||||||
|
|
|
@ -26,8 +26,8 @@ use super::{
|
||||||
Host,
|
Host,
|
||||||
NodeName,
|
NodeName,
|
||||||
NodeConfig,
|
NodeConfig,
|
||||||
NixError,
|
ColmenaError,
|
||||||
NixResult,
|
ColmenaResult,
|
||||||
Profile,
|
Profile,
|
||||||
ProfileDerivation,
|
ProfileDerivation,
|
||||||
CopyDirection,
|
CopyDirection,
|
||||||
|
@ -120,9 +120,9 @@ impl Deployment {
|
||||||
///
|
///
|
||||||
/// If a ProgressSender is supplied, then this should be run in parallel
|
/// If a ProgressSender is supplied, then this should be run in parallel
|
||||||
/// with its `run_until_completion()` future.
|
/// with its `run_until_completion()` future.
|
||||||
pub async fn execute(mut self) -> NixResult<()> {
|
pub async fn execute(mut self) -> ColmenaResult<()> {
|
||||||
if self.executed {
|
if self.executed {
|
||||||
return Err(NixError::DeploymentAlreadyExecuted);
|
return Err(ColmenaError::DeploymentAlreadyExecuted);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.executed = true;
|
self.executed = true;
|
||||||
|
@ -148,7 +148,7 @@ impl Deployment {
|
||||||
}
|
}
|
||||||
|
|
||||||
join_all(futures).await
|
join_all(futures).await
|
||||||
.into_iter().collect::<NixResult<Vec<()>>>()?;
|
.into_iter().collect::<ColmenaResult<Vec<()>>>()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
@ -173,7 +173,7 @@ impl Deployment {
|
||||||
}
|
}
|
||||||
|
|
||||||
join_all(futures).await
|
join_all(futures).await
|
||||||
.into_iter().collect::<NixResult<Vec<()>>>()?;
|
.into_iter().collect::<ColmenaResult<Vec<()>>>()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
@ -219,7 +219,7 @@ impl Deployment {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Executes the deployment against a portion of nodes.
|
/// Executes the deployment against a portion of nodes.
|
||||||
async fn execute_chunk(self: &DeploymentHandle, parent: JobHandle, mut chunk: TargetNodeMap) -> NixResult<()> {
|
async fn execute_chunk(self: &DeploymentHandle, parent: JobHandle, mut chunk: TargetNodeMap) -> ColmenaResult<()> {
|
||||||
if self.goal == Goal::UploadKeys {
|
if self.goal == Goal::UploadKeys {
|
||||||
unreachable!(); // some logic is screwed up
|
unreachable!(); // some logic is screwed up
|
||||||
}
|
}
|
||||||
|
@ -256,14 +256,14 @@ impl Deployment {
|
||||||
}
|
}
|
||||||
|
|
||||||
join_all(futures).await
|
join_all(futures).await
|
||||||
.into_iter().collect::<NixResult<Vec<()>>>()?;
|
.into_iter().collect::<ColmenaResult<Vec<()>>>()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluates a set of nodes, returning their corresponding store derivations.
|
/// Evaluates a set of nodes, returning their corresponding store derivations.
|
||||||
async fn evaluate_nodes(self: &DeploymentHandle, parent: JobHandle, nodes: Vec<NodeName>)
|
async fn evaluate_nodes(self: &DeploymentHandle, parent: JobHandle, nodes: Vec<NodeName>)
|
||||||
-> NixResult<HashMap<NodeName, ProfileDerivation>>
|
-> ColmenaResult<HashMap<NodeName, ProfileDerivation>>
|
||||||
{
|
{
|
||||||
let job = parent.create_job(JobType::Evaluate, nodes.clone())?;
|
let job = parent.create_job(JobType::Evaluate, nodes.clone())?;
|
||||||
|
|
||||||
|
@ -280,12 +280,12 @@ impl Deployment {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Only uploads keys to a node.
|
/// Only uploads keys to a node.
|
||||||
async fn upload_keys_to_node(self: &DeploymentHandle, parent: JobHandle, mut target: TargetNode) -> NixResult<()> {
|
async fn upload_keys_to_node(self: &DeploymentHandle, parent: JobHandle, mut target: TargetNode) -> ColmenaResult<()> {
|
||||||
let nodes = vec![target.name.clone()];
|
let nodes = vec![target.name.clone()];
|
||||||
let job = parent.create_job(JobType::UploadKeys, nodes)?;
|
let job = parent.create_job(JobType::UploadKeys, nodes)?;
|
||||||
job.run(|_| async move {
|
job.run(|_| async move {
|
||||||
if target.host.is_none() {
|
if target.host.is_none() {
|
||||||
return Err(NixError::Unsupported);
|
return Err(ColmenaError::Unsupported);
|
||||||
}
|
}
|
||||||
|
|
||||||
let host = target.host.as_mut().unwrap();
|
let host = target.host.as_mut().unwrap();
|
||||||
|
@ -297,7 +297,7 @@ impl Deployment {
|
||||||
|
|
||||||
/// Builds a system profile directly on the node itself.
|
/// Builds a system profile directly on the node itself.
|
||||||
async fn build_on_node(self: &DeploymentHandle, parent: JobHandle, mut target: TargetNode, profile_drv: ProfileDerivation)
|
async fn build_on_node(self: &DeploymentHandle, parent: JobHandle, mut target: TargetNode, profile_drv: ProfileDerivation)
|
||||||
-> NixResult<(TargetNode, Profile)>
|
-> ColmenaResult<(TargetNode, Profile)>
|
||||||
{
|
{
|
||||||
let nodes = vec![target.name.clone()];
|
let nodes = vec![target.name.clone()];
|
||||||
|
|
||||||
|
@ -306,7 +306,7 @@ impl Deployment {
|
||||||
let build_job = parent.create_job(JobType::Build, nodes.clone())?;
|
let build_job = parent.create_job(JobType::Build, nodes.clone())?;
|
||||||
let (target, profile) = build_job.run(|job| async move {
|
let (target, profile) = build_job.run(|job| async move {
|
||||||
if target.host.is_none() {
|
if target.host.is_none() {
|
||||||
return Err(NixError::Unsupported);
|
return Err(ColmenaError::Unsupported);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut host = target.host.as_mut().unwrap();
|
let mut host = target.host.as_mut().unwrap();
|
||||||
|
@ -331,7 +331,7 @@ impl Deployment {
|
||||||
|
|
||||||
/// Builds and pushes a system profile on a node.
|
/// Builds and pushes a system profile on a node.
|
||||||
async fn build_and_push_node(self: &DeploymentHandle, parent: JobHandle, mut target: TargetNode, profile_drv: ProfileDerivation)
|
async fn build_and_push_node(self: &DeploymentHandle, parent: JobHandle, mut target: TargetNode, profile_drv: ProfileDerivation)
|
||||||
-> NixResult<(TargetNode, Profile)>
|
-> ColmenaResult<(TargetNode, Profile)>
|
||||||
{
|
{
|
||||||
let nodes = vec![target.name.clone()];
|
let nodes = vec![target.name.clone()];
|
||||||
|
|
||||||
|
@ -361,7 +361,7 @@ impl Deployment {
|
||||||
let arc_self = self.clone();
|
let arc_self = self.clone();
|
||||||
let target = push_job.run(|job| async move {
|
let target = push_job.run(|job| async move {
|
||||||
if target.host.is_none() {
|
if target.host.is_none() {
|
||||||
return Err(NixError::Unsupported);
|
return Err(ColmenaError::Unsupported);
|
||||||
}
|
}
|
||||||
|
|
||||||
let host = target.host.as_mut().unwrap();
|
let host = target.host.as_mut().unwrap();
|
||||||
|
@ -383,7 +383,7 @@ impl Deployment {
|
||||||
///
|
///
|
||||||
/// This will also upload keys to the node.
|
/// This will also upload keys to the node.
|
||||||
async fn activate_node(self: DeploymentHandle, parent: JobHandle, mut target: TargetNode, profile: Profile)
|
async fn activate_node(self: DeploymentHandle, parent: JobHandle, mut target: TargetNode, profile: Profile)
|
||||||
-> NixResult<()>
|
-> ColmenaResult<()>
|
||||||
{
|
{
|
||||||
let nodes = vec![target.name.clone()];
|
let nodes = vec![target.name.clone()];
|
||||||
let target_name = target.name.clone();
|
let target_name = target.name.clone();
|
||||||
|
@ -437,7 +437,7 @@ impl Deployment {
|
||||||
if arc_self.options.force_replace_unknown_profiles {
|
if arc_self.options.force_replace_unknown_profiles {
|
||||||
job.message("Warning: Remote profile is unknown, but unknown profiles are being ignored".to_string())?;
|
job.message("Warning: Remote profile is unknown, but unknown profiles are being ignored".to_string())?;
|
||||||
} else {
|
} else {
|
||||||
return Err(NixError::ActiveProfileUnknown {
|
return Err(ColmenaError::ActiveProfileUnknown {
|
||||||
store_path: profile,
|
store_path: profile,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ use std::process::Stdio;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
use super::{NixCheck, NixError, NixResult};
|
use super::{NixCheck, ColmenaError, ColmenaResult};
|
||||||
|
|
||||||
/// A Nix Flake.
|
/// A Nix Flake.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -24,7 +24,7 @@ impl Flake {
|
||||||
///
|
///
|
||||||
/// This will try to retrieve the resolved URL of the local flake
|
/// This will try to retrieve the resolved URL of the local flake
|
||||||
/// in the specified directory.
|
/// in the specified directory.
|
||||||
pub async fn from_dir<P: AsRef<Path>>(dir: P) -> NixResult<Self> {
|
pub async fn from_dir<P: AsRef<Path>>(dir: P) -> ColmenaResult<Self> {
|
||||||
NixCheck::require_flake_support().await?;
|
NixCheck::require_flake_support().await?;
|
||||||
|
|
||||||
let flake = dir.as_ref().as_os_str().to_str()
|
let flake = dir.as_ref().as_os_str().to_str()
|
||||||
|
@ -39,7 +39,7 @@ impl Flake {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a flake from a Flake URI.
|
/// Creates a flake from a Flake URI.
|
||||||
pub async fn from_uri(uri: String) -> NixResult<Self> {
|
pub async fn from_uri(uri: String) -> ColmenaResult<Self> {
|
||||||
NixCheck::require_flake_support().await?;
|
NixCheck::require_flake_support().await?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
@ -69,7 +69,7 @@ struct FlakeMetadata {
|
||||||
|
|
||||||
impl FlakeMetadata {
|
impl FlakeMetadata {
|
||||||
/// Resolves a flake.
|
/// Resolves a flake.
|
||||||
async fn resolve(flake: &str) -> NixResult<Self> {
|
async fn resolve(flake: &str) -> ColmenaResult<Self> {
|
||||||
let child = Command::new("nix")
|
let child = Command::new("nix")
|
||||||
.args(&["flake", "metadata", "--json"])
|
.args(&["flake", "metadata", "--json"])
|
||||||
.args(&["--experimental-features", "nix-command flakes"])
|
.args(&["--experimental-features", "nix-command flakes"])
|
||||||
|
@ -86,7 +86,7 @@ impl FlakeMetadata {
|
||||||
serde_json::from_slice::<FlakeMetadata>(&output.stdout)
|
serde_json::from_slice::<FlakeMetadata>(&output.stdout)
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
let output = String::from_utf8_lossy(&output.stdout).to_string();
|
let output = String::from_utf8_lossy(&output.stdout).to_string();
|
||||||
NixError::BadOutput { output }
|
ColmenaError::BadOutput { output }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ use validator::Validate;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
Flake,
|
Flake,
|
||||||
NixResult,
|
ColmenaResult,
|
||||||
NodeName,
|
NodeName,
|
||||||
NodeConfig,
|
NodeConfig,
|
||||||
NodeFilter,
|
NodeFilter,
|
||||||
|
@ -39,7 +39,7 @@ pub enum HivePath {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HivePath {
|
impl HivePath {
|
||||||
pub async fn from_path<P: AsRef<Path>>(path: P) -> NixResult<Self> {
|
pub async fn from_path<P: AsRef<Path>>(path: P) -> ColmenaResult<Self> {
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
|
|
||||||
if let Some(osstr) = path.file_name() {
|
if let Some(osstr) = path.file_name() {
|
||||||
|
@ -87,7 +87,7 @@ pub struct Hive {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hive {
|
impl Hive {
|
||||||
pub fn new(path: HivePath) -> NixResult<Self> {
|
pub fn new(path: HivePath) -> ColmenaResult<Self> {
|
||||||
let mut eval_nix = NamedTempFile::new()?;
|
let mut eval_nix = NamedTempFile::new()?;
|
||||||
eval_nix.write_all(HIVE_EVAL).unwrap();
|
eval_nix.write_all(HIVE_EVAL).unwrap();
|
||||||
|
|
||||||
|
@ -110,7 +110,7 @@ impl Hive {
|
||||||
self.show_trace = value;
|
self.show_trace = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn nix_options(&self) -> NixResult<Vec<String>> {
|
pub async fn nix_options(&self) -> ColmenaResult<Vec<String>> {
|
||||||
let mut options = self.builder_args().await?;
|
let mut options = self.builder_args().await?;
|
||||||
|
|
||||||
if self.show_trace {
|
if self.show_trace {
|
||||||
|
@ -121,7 +121,7 @@ impl Hive {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convenience wrapper to filter nodes for CLI actions.
|
/// Convenience wrapper to filter nodes for CLI actions.
|
||||||
pub async fn select_nodes(&self, filter: Option<NodeFilter>, ssh_config: Option<PathBuf>, ssh_only: bool) -> NixResult<HashMap<NodeName, TargetNode>> {
|
pub async fn select_nodes(&self, filter: Option<NodeFilter>, ssh_config: Option<PathBuf>, ssh_only: bool) -> ColmenaResult<HashMap<NodeName, TargetNode>> {
|
||||||
let mut node_configs = None;
|
let mut node_configs = None;
|
||||||
|
|
||||||
log::info!("Enumerating nodes...");
|
log::info!("Enumerating nodes...");
|
||||||
|
@ -197,13 +197,13 @@ impl Hive {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a list of all node names.
|
/// Returns a list of all node names.
|
||||||
pub async fn node_names(&self) -> NixResult<Vec<NodeName>> {
|
pub async fn node_names(&self) -> ColmenaResult<Vec<NodeName>> {
|
||||||
self.nix_instantiate("attrNames hive.nodes").eval()
|
self.nix_instantiate("attrNames hive.nodes").eval()
|
||||||
.capture_json().await
|
.capture_json().await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve deployment info for all nodes.
|
/// Retrieve deployment info for all nodes.
|
||||||
pub async fn deployment_info(&self) -> NixResult<HashMap<NodeName, NodeConfig>> {
|
pub async fn deployment_info(&self) -> ColmenaResult<HashMap<NodeName, NodeConfig>> {
|
||||||
let configs: HashMap<NodeName, NodeConfig> = self.nix_instantiate("hive.deploymentConfig").eval_with_builders().await?
|
let configs: HashMap<NodeName, NodeConfig> = self.nix_instantiate("hive.deploymentConfig").eval_with_builders().await?
|
||||||
.capture_json().await?;
|
.capture_json().await?;
|
||||||
|
|
||||||
|
@ -217,14 +217,14 @@ impl Hive {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve deployment info for a single node.
|
/// Retrieve deployment info for a single node.
|
||||||
pub async fn deployment_info_single(&self, node: &NodeName) -> NixResult<Option<NodeConfig>> {
|
pub async fn deployment_info_single(&self, node: &NodeName) -> ColmenaResult<Option<NodeConfig>> {
|
||||||
let expr = format!("hive.nodes.\"{}\".config.deployment or null", node.as_str());
|
let expr = format!("hive.nodes.\"{}\".config.deployment or null", node.as_str());
|
||||||
self.nix_instantiate(&expr).eval_with_builders().await?
|
self.nix_instantiate(&expr).eval_with_builders().await?
|
||||||
.capture_json().await
|
.capture_json().await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve deployment info for a list of nodes.
|
/// Retrieve deployment info for a list of nodes.
|
||||||
pub async fn deployment_info_selected(&self, nodes: &[NodeName]) -> NixResult<HashMap<NodeName, NodeConfig>> {
|
pub async fn deployment_info_selected(&self, nodes: &[NodeName]) -> ColmenaResult<HashMap<NodeName, NodeConfig>> {
|
||||||
let nodes_expr = SerializedNixExpresssion::new(nodes)?;
|
let nodes_expr = SerializedNixExpresssion::new(nodes)?;
|
||||||
|
|
||||||
let configs: HashMap<NodeName, NodeConfig> = self.nix_instantiate(&format!("hive.deploymentConfigSelected {}", nodes_expr.expression()))
|
let configs: HashMap<NodeName, NodeConfig> = self.nix_instantiate(&format!("hive.deploymentConfigSelected {}", nodes_expr.expression()))
|
||||||
|
@ -246,7 +246,7 @@ impl Hive {
|
||||||
/// Evaluation may take up a lot of memory, so we make it possible
|
/// Evaluation may take up a lot of memory, so we make it possible
|
||||||
/// to split up the evaluation process into chunks and run them
|
/// to split up the evaluation process into chunks and run them
|
||||||
/// concurrently with other processes (e.g., build and apply).
|
/// concurrently with other processes (e.g., build and apply).
|
||||||
pub async fn eval_selected(&self, nodes: &[NodeName], job: Option<JobHandle>) -> NixResult<HashMap<NodeName, ProfileDerivation>> {
|
pub async fn eval_selected(&self, nodes: &[NodeName], job: Option<JobHandle>) -> ColmenaResult<HashMap<NodeName, ProfileDerivation>> {
|
||||||
let nodes_expr = SerializedNixExpresssion::new(nodes)?;
|
let nodes_expr = SerializedNixExpresssion::new(nodes)?;
|
||||||
|
|
||||||
let expr = format!("hive.evalSelected {}", nodes_expr.expression());
|
let expr = format!("hive.evalSelected {}", nodes_expr.expression());
|
||||||
|
@ -267,7 +267,7 @@ impl Hive {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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) -> NixResult<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);
|
||||||
self.nix_instantiate(&expression).instantiate_with_builders().await?
|
self.nix_instantiate(&expression).instantiate_with_builders().await?
|
||||||
|
@ -280,7 +280,7 @@ impl Hive {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve machinesFile setting for the hive.
|
/// Retrieve machinesFile setting for the hive.
|
||||||
async fn machines_file(&self) -> NixResult<Option<String>> {
|
async fn machines_file(&self) -> ColmenaResult<Option<String>> {
|
||||||
if let Some(builders_opt) = &*self.builders.read().await {
|
if let Some(builders_opt) = &*self.builders.read().await {
|
||||||
return Ok(builders_opt.clone());
|
return Ok(builders_opt.clone());
|
||||||
}
|
}
|
||||||
|
@ -296,7 +296,7 @@ impl Hive {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns Nix arguments to set builders.
|
/// Returns Nix arguments to set builders.
|
||||||
async fn builder_args(&self) -> NixResult<Vec<String>> {
|
async fn builder_args(&self) -> ColmenaResult<Vec<String>> {
|
||||||
let mut options = Vec::new();
|
let mut options = Vec::new();
|
||||||
|
|
||||||
if let Some(machines_file) = self.machines_file().await? {
|
if let Some(machines_file) = self.machines_file().await? {
|
||||||
|
@ -381,7 +381,7 @@ impl<'hive> NixInstantiate<'hive> {
|
||||||
command
|
command
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn instantiate_with_builders(self) -> NixResult<Command> {
|
async fn instantiate_with_builders(self) -> ColmenaResult<Command> {
|
||||||
let hive = self.hive;
|
let hive = self.hive;
|
||||||
let mut command = self.instantiate();
|
let mut command = self.instantiate();
|
||||||
|
|
||||||
|
@ -391,7 +391,7 @@ impl<'hive> NixInstantiate<'hive> {
|
||||||
Ok(command)
|
Ok(command)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn eval_with_builders(self) -> NixResult<Command> {
|
async fn eval_with_builders(self) -> ColmenaResult<Command> {
|
||||||
let hive = self.hive;
|
let hive = self.hive;
|
||||||
let mut command = self.eval();
|
let mut command = self.eval();
|
||||||
|
|
||||||
|
@ -413,7 +413,7 @@ struct SerializedNixExpresssion {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SerializedNixExpresssion {
|
impl SerializedNixExpresssion {
|
||||||
pub fn new<T>(data: T) -> NixResult<Self> where T: Serialize {
|
pub fn new<T>(data: T) -> ColmenaResult<Self> where T: Serialize {
|
||||||
let mut tmp = NamedTempFile::new()?;
|
let mut tmp = NamedTempFile::new()?;
|
||||||
let json = serde_json::to_vec(&data).expect("Could not serialize data");
|
let json = serde_json::to_vec(&data).expect("Could not serialize data");
|
||||||
tmp.write_all(&json)?;
|
tmp.write_all(&json)?;
|
||||||
|
|
|
@ -12,8 +12,9 @@ use shell_escape::unix::escape;
|
||||||
use tokio::io::{AsyncWriteExt, BufReader};
|
use tokio::io::{AsyncWriteExt, BufReader};
|
||||||
use tokio::process::Child;
|
use tokio::process::Child;
|
||||||
|
|
||||||
|
use crate::error::ColmenaResult;
|
||||||
use crate::job::JobHandle;
|
use crate::job::JobHandle;
|
||||||
use crate::nix::{Key, NixResult};
|
use crate::nix::Key;
|
||||||
use crate::util::capture_stream;
|
use crate::util::capture_stream;
|
||||||
|
|
||||||
const SCRIPT_TEMPLATE: &str = include_str!("./key_uploader.template.sh");
|
const SCRIPT_TEMPLATE: &str = include_str!("./key_uploader.template.sh");
|
||||||
|
@ -30,7 +31,7 @@ pub fn generate_script<'a>(key: &'a Key, destination: &'a Path, require_ownershi
|
||||||
escape(key_script.into())
|
escape(key_script.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn feed_uploader(mut uploader: Child, key: &Key, job: Option<JobHandle>) -> NixResult<()> {
|
pub async fn feed_uploader(mut uploader: Child, key: &Key, job: Option<JobHandle>) -> ColmenaResult<()> {
|
||||||
let mut reader = key.reader().await?;
|
let mut reader = key.reader().await?;
|
||||||
let mut stdin = uploader.stdin.take().unwrap();
|
let mut stdin = uploader.stdin.take().unwrap();
|
||||||
|
|
||||||
|
|
|
@ -5,10 +5,11 @@ use std::process::Stdio;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
use super::{CopyDirection, CopyOptions, Host, key_uploader};
|
use crate::error::{ColmenaResult, ColmenaError};
|
||||||
use crate::nix::{StorePath, Profile, Goal, NixError, NixResult, Key, SYSTEM_PROFILE, CURRENT_PROFILE};
|
use crate::nix::{StorePath, Profile, Goal, Key, 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};
|
||||||
|
|
||||||
/// The local machine running Colmena.
|
/// The local machine running Colmena.
|
||||||
///
|
///
|
||||||
|
@ -31,11 +32,11 @@ impl Local {
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Host for Local {
|
impl Host for Local {
|
||||||
async fn copy_closure(&mut self, _closure: &StorePath, _direction: CopyDirection, _options: CopyOptions) -> NixResult<()> {
|
async fn copy_closure(&mut self, _closure: &StorePath, _direction: CopyDirection, _options: CopyOptions) -> ColmenaResult<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn realize_remote(&mut self, derivation: &StorePath) -> NixResult<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.clone());
|
||||||
|
@ -55,7 +56,7 @@ impl Host for Local {
|
||||||
.map(|p| p.to_string().try_into()).collect()
|
.map(|p| p.to_string().try_into()).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn upload_keys(&mut self, keys: &HashMap<String, Key>, require_ownership: bool) -> NixResult<()> {
|
async fn upload_keys(&mut self, keys: &HashMap<String, Key>, require_ownership: bool) -> ColmenaResult<()> {
|
||||||
for (name, key) in keys {
|
for (name, key) in keys {
|
||||||
self.upload_key(name, key, require_ownership).await?;
|
self.upload_key(name, key, require_ownership).await?;
|
||||||
}
|
}
|
||||||
|
@ -63,9 +64,9 @@ impl Host for Local {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn activate(&mut self, profile: &Profile, goal: Goal) -> NixResult<()> {
|
async fn activate(&mut self, profile: &Profile, goal: Goal) -> ColmenaResult<()> {
|
||||||
if !goal.requires_activation() {
|
if !goal.requires_activation() {
|
||||||
return Err(NixError::Unsupported);
|
return Err(ColmenaError::Unsupported);
|
||||||
}
|
}
|
||||||
|
|
||||||
if goal.should_switch_profile() {
|
if goal.should_switch_profile() {
|
||||||
|
@ -91,14 +92,14 @@ impl Host for Local {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_main_system_profile(&mut self) -> NixResult<StorePath> {
|
async fn get_main_system_profile(&mut self) -> ColmenaResult<StorePath> {
|
||||||
let paths = Command::new("sh")
|
let paths = Command::new("sh")
|
||||||
.args(&["-c", &format!("readlink -e {} || readlink -e {}", SYSTEM_PROFILE, CURRENT_PROFILE)])
|
.args(&["-c", &format!("readlink -e {} || readlink -e {}", SYSTEM_PROFILE, CURRENT_PROFILE)])
|
||||||
.capture_output()
|
.capture_output()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let path = paths.lines().into_iter().next()
|
let path = paths.lines().into_iter().next()
|
||||||
.ok_or(NixError::FailedToGetCurrentProfile)?
|
.ok_or(ColmenaError::FailedToGetCurrentProfile)?
|
||||||
.to_string()
|
.to_string()
|
||||||
.try_into()?;
|
.try_into()?;
|
||||||
|
|
||||||
|
@ -112,7 +113,7 @@ impl Host for Local {
|
||||||
|
|
||||||
impl Local {
|
impl Local {
|
||||||
/// "Uploads" a single key.
|
/// "Uploads" a single key.
|
||||||
async fn upload_key(&mut self, name: &str, key: &Key, require_ownership: bool) -> NixResult<()> {
|
async fn upload_key(&mut self, name: &str, key: &Key, require_ownership: bool) -> ColmenaResult<()> {
|
||||||
if let Some(job) = &self.job {
|
if let Some(job) = &self.job {
|
||||||
job.message(format!("Deploying key {}", name))?;
|
job.message(format!("Deploying key {}", name))?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
|
||||||
use super::{StorePath, Profile, Goal, NixResult, NixError, Key};
|
use super::{StorePath, Profile, Goal, ColmenaResult, ColmenaError, Key};
|
||||||
use crate::job::JobHandle;
|
use crate::job::JobHandle;
|
||||||
|
|
||||||
mod ssh;
|
mod ssh;
|
||||||
|
@ -65,20 +65,20 @@ pub trait Host: Send + Sync + std::fmt::Debug {
|
||||||
/// Sends or receives the specified closure to the host
|
/// Sends or receives the specified closure to the host
|
||||||
///
|
///
|
||||||
/// The StorePath and its dependent paths will then exist on this host.
|
/// The StorePath and its dependent paths will then exist on this host.
|
||||||
async fn copy_closure(&mut self, closure: &StorePath, direction: CopyDirection, options: CopyOptions) -> NixResult<()>;
|
async fn copy_closure(&mut self, closure: &StorePath, direction: CopyDirection, options: CopyOptions) -> ColmenaResult<()>;
|
||||||
|
|
||||||
/// Realizes the specified derivation on the host
|
/// Realizes the specified derivation on the host
|
||||||
///
|
///
|
||||||
/// The derivation must already exist on the host.
|
/// The derivation must already exist on the host.
|
||||||
/// After realization, paths in the Vec<StorePath> will then
|
/// After realization, paths in the Vec<StorePath> will then
|
||||||
/// exist on the host.
|
/// exist on the host.
|
||||||
async fn realize_remote(&mut self, derivation: &StorePath) -> NixResult<Vec<StorePath>>;
|
async fn realize_remote(&mut self, derivation: &StorePath) -> ColmenaResult<Vec<StorePath>>;
|
||||||
|
|
||||||
/// Provides a JobHandle to use during operations.
|
/// Provides a JobHandle to use during operations.
|
||||||
fn set_job(&mut self, bar: Option<JobHandle>);
|
fn set_job(&mut self, bar: Option<JobHandle>);
|
||||||
|
|
||||||
/// Realizes the specified local derivation on the host then retrieves the outputs.
|
/// Realizes the specified local derivation on the host then retrieves the outputs.
|
||||||
async fn realize(&mut self, derivation: &StorePath) -> NixResult<Vec<StorePath>> {
|
async fn realize(&mut self, derivation: &StorePath) -> ColmenaResult<Vec<StorePath>> {
|
||||||
let options = CopyOptions::default()
|
let options = CopyOptions::default()
|
||||||
.include_outputs(true);
|
.include_outputs(true);
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ pub trait Host: Send + Sync + std::fmt::Debug {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pushes and optionally activates a profile to the host.
|
/// Pushes and optionally activates a profile to the host.
|
||||||
async fn deploy(&mut self, profile: &Profile, goal: Goal, copy_options: CopyOptions) -> NixResult<()> {
|
async fn deploy(&mut self, profile: &Profile, goal: Goal, copy_options: CopyOptions) -> ColmenaResult<()> {
|
||||||
self.copy_closure(profile.as_store_path(), CopyDirection::ToRemote, copy_options).await?;
|
self.copy_closure(profile.as_store_path(), CopyDirection::ToRemote, copy_options).await?;
|
||||||
|
|
||||||
if goal.requires_activation() {
|
if goal.requires_activation() {
|
||||||
|
@ -106,8 +106,8 @@ pub trait Host: Send + Sync + std::fmt::Debug {
|
||||||
/// will not be applied if the specified user/group does not
|
/// will not be applied if the specified user/group does not
|
||||||
/// exist.
|
/// exist.
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
async fn upload_keys(&mut self, keys: &HashMap<String, Key>, require_ownership: bool) -> NixResult<()> {
|
async fn upload_keys(&mut self, keys: &HashMap<String, Key>, require_ownership: bool) -> ColmenaResult<()> {
|
||||||
Err(NixError::Unsupported)
|
Err(ColmenaError::Unsupported)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the main system profile on the host.
|
/// Returns the main system profile on the host.
|
||||||
|
@ -115,19 +115,19 @@ pub trait Host: Send + Sync + std::fmt::Debug {
|
||||||
/// This may _not_ be the system profile that's currently activated!
|
/// This may _not_ be the system profile that's currently activated!
|
||||||
/// It will first try `/nix/var/nix/profiles/system`, falling back
|
/// It will first try `/nix/var/nix/profiles/system`, falling back
|
||||||
/// to `/run/current-system` if it doesn't exist.
|
/// to `/run/current-system` if it doesn't exist.
|
||||||
async fn get_main_system_profile(&mut self) -> NixResult<StorePath>;
|
async fn get_main_system_profile(&mut self) -> ColmenaResult<StorePath>;
|
||||||
|
|
||||||
/// Activates a system profile on the host, if it runs NixOS.
|
/// Activates a system profile on the host, if it runs NixOS.
|
||||||
///
|
///
|
||||||
/// The profile must already exist on the host. You should probably use deploy instead.
|
/// The profile must already exist on the host. You should probably use deploy instead.
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
async fn activate(&mut self, profile: &Profile, goal: Goal) -> NixResult<()> {
|
async fn activate(&mut self, profile: &Profile, goal: Goal) -> ColmenaResult<()> {
|
||||||
Err(NixError::Unsupported)
|
Err(ColmenaError::Unsupported)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs an arbitrary command on the host.
|
/// Runs an arbitrary command on the host.
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
async fn run_command(&mut self, command: &[&str]) -> NixResult<()> {
|
async fn run_command(&mut self, command: &[&str]) -> ColmenaResult<()> {
|
||||||
Err(NixError::Unsupported)
|
Err(ColmenaError::Unsupported)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,10 +6,11 @@ use std::process::Stdio;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
use super::{CopyDirection, CopyOptions, Host, key_uploader};
|
use crate::error::{ColmenaResult, ColmenaError};
|
||||||
use crate::nix::{StorePath, Profile, Goal, NixResult, NixError, Key, SYSTEM_PROFILE, CURRENT_PROFILE};
|
use crate::nix::{StorePath, Profile, Goal, Key, 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};
|
||||||
|
|
||||||
/// A remote machine connected over SSH.
|
/// A remote machine connected over SSH.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -35,11 +36,11 @@ pub struct Ssh {
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Host for Ssh {
|
impl Host for Ssh {
|
||||||
async fn copy_closure(&mut self, closure: &StorePath, direction: CopyDirection, options: CopyOptions) -> NixResult<()> {
|
async fn copy_closure(&mut self, closure: &StorePath, direction: CopyDirection, options: CopyOptions) -> ColmenaResult<()> {
|
||||||
let command = self.nix_copy_closure(closure, direction, options);
|
let command = self.nix_copy_closure(closure, direction, options);
|
||||||
self.run_command(command).await
|
self.run_command(command).await
|
||||||
}
|
}
|
||||||
async fn realize_remote(&mut self, derivation: &StorePath) -> NixResult<Vec<StorePath>> {
|
async fn realize_remote(&mut self, derivation: &StorePath) -> ColmenaResult<Vec<StorePath>> {
|
||||||
let command = self.ssh(&["nix-store", "--no-gc-warning", "--realise", derivation.as_path().to_str().unwrap()]);
|
let command = self.ssh(&["nix-store", "--no-gc-warning", "--realise", derivation.as_path().to_str().unwrap()]);
|
||||||
|
|
||||||
let mut execution = CommandExecution::new(command);
|
let mut execution = CommandExecution::new(command);
|
||||||
|
@ -51,16 +52,16 @@ impl Host for Ssh {
|
||||||
|
|
||||||
paths.lines().map(|p| p.to_string().try_into()).collect()
|
paths.lines().map(|p| p.to_string().try_into()).collect()
|
||||||
}
|
}
|
||||||
async fn upload_keys(&mut self, keys: &HashMap<String, Key>, require_ownership: bool) -> NixResult<()> {
|
async fn upload_keys(&mut self, keys: &HashMap<String, Key>, require_ownership: bool) -> ColmenaResult<()> {
|
||||||
for (name, key) in keys {
|
for (name, key) in keys {
|
||||||
self.upload_key(name, key, require_ownership).await?;
|
self.upload_key(name, key, require_ownership).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
async fn activate(&mut self, profile: &Profile, goal: Goal) -> NixResult<()> {
|
async fn activate(&mut self, profile: &Profile, goal: Goal) -> ColmenaResult<()> {
|
||||||
if !goal.requires_activation() {
|
if !goal.requires_activation() {
|
||||||
return Err(NixError::Unsupported);
|
return Err(ColmenaError::Unsupported);
|
||||||
}
|
}
|
||||||
|
|
||||||
if goal.should_switch_profile() {
|
if goal.should_switch_profile() {
|
||||||
|
@ -74,11 +75,11 @@ impl Host for Ssh {
|
||||||
let command = self.ssh(&v);
|
let command = self.ssh(&v);
|
||||||
self.run_command(command).await
|
self.run_command(command).await
|
||||||
}
|
}
|
||||||
async fn run_command(&mut self, command: &[&str]) -> NixResult<()> {
|
async fn run_command(&mut self, command: &[&str]) -> ColmenaResult<()> {
|
||||||
let command = self.ssh(command);
|
let command = self.ssh(command);
|
||||||
self.run_command(command).await
|
self.run_command(command).await
|
||||||
}
|
}
|
||||||
async fn get_main_system_profile(&mut self) -> NixResult<StorePath> {
|
async fn get_main_system_profile(&mut self) -> ColmenaResult<StorePath> {
|
||||||
let command = format!("\"readlink -e {} || readlink -e {}\"", SYSTEM_PROFILE, CURRENT_PROFILE);
|
let command = format!("\"readlink -e {} || readlink -e {}\"", SYSTEM_PROFILE, CURRENT_PROFILE);
|
||||||
|
|
||||||
let paths = self.ssh(&["sh", "-c", &command])
|
let paths = self.ssh(&["sh", "-c", &command])
|
||||||
|
@ -86,7 +87,7 @@ impl Host for Ssh {
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let path = paths.lines().into_iter().next()
|
let path = paths.lines().into_iter().next()
|
||||||
.ok_or(NixError::FailedToGetCurrentProfile)?
|
.ok_or(ColmenaError::FailedToGetCurrentProfile)?
|
||||||
.to_string()
|
.to_string()
|
||||||
.try_into()?;
|
.try_into()?;
|
||||||
|
|
||||||
|
@ -150,7 +151,7 @@ impl Ssh {
|
||||||
cmd
|
cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run_command(&mut self, command: Command) -> NixResult<()> {
|
async fn run_command(&mut self, command: Command) -> ColmenaResult<()> {
|
||||||
let mut execution = CommandExecution::new(command);
|
let mut execution = CommandExecution::new(command);
|
||||||
execution.set_job(self.job.clone());
|
execution.set_job(self.job.clone());
|
||||||
|
|
||||||
|
@ -216,7 +217,7 @@ impl Ssh {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Uploads a single key.
|
/// Uploads a single key.
|
||||||
async fn upload_key(&mut self, name: &str, key: &Key, require_ownership: bool) -> NixResult<()> {
|
async fn upload_key(&mut self, name: &str, key: &Key, require_ownership: bool) -> ColmenaResult<()> {
|
||||||
if let Some(job) = &self.job {
|
if let Some(job) = &self.job {
|
||||||
job.message(format!("Uploading key {}", name))?;
|
job.message(format!("Uploading key {}", name))?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ use log::Level;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
use super::{NixError, NixResult};
|
use super::{ColmenaError, ColmenaResult};
|
||||||
|
|
||||||
struct NixVersion {
|
struct NixVersion {
|
||||||
major: usize,
|
major: usize,
|
||||||
|
@ -89,12 +89,12 @@ impl NixCheck {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn require_flake_support() -> NixResult<()> {
|
pub async fn require_flake_support() -> ColmenaResult<()> {
|
||||||
let check = Self::detect().await;
|
let check = Self::detect().await;
|
||||||
|
|
||||||
if !check.flakes_supported() {
|
if !check.flakes_supported() {
|
||||||
check.print_flakes_info(true);
|
check.print_flakes_info(true);
|
||||||
Err(NixError::NoFlakesSupport)
|
Err(ColmenaError::NoFlakesSupport)
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
107
src/nix/mod.rs
107
src/nix/mod.rs
|
@ -1,15 +1,14 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::os::unix::process::ExitStatusExt;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::process::ExitStatus;
|
|
||||||
|
|
||||||
use serde::de;
|
use serde::de;
|
||||||
use serde::{Deserialize, Deserializer, Serialize};
|
use serde::{Deserialize, Deserializer, Serialize};
|
||||||
use snafu::Snafu;
|
|
||||||
use users::get_current_username;
|
use users::get_current_username;
|
||||||
use validator::{Validate, ValidationErrors, ValidationError as ValidationErrorType};
|
use validator::{Validate, ValidationError as ValidationErrorType};
|
||||||
|
|
||||||
|
use crate::error::{ColmenaResult, ColmenaError};
|
||||||
|
|
||||||
pub mod host;
|
pub mod host;
|
||||||
pub use host::{Host, CopyDirection, CopyOptions};
|
pub use host::{Host, CopyDirection, CopyOptions};
|
||||||
|
@ -45,100 +44,6 @@ pub const SYSTEM_PROFILE: &str = "/nix/var/nix/profiles/system";
|
||||||
/// Path to the system profile that's currently active.
|
/// Path to the system profile that's currently active.
|
||||||
pub const CURRENT_PROFILE: &str = "/run/current-system";
|
pub const CURRENT_PROFILE: &str = "/run/current-system";
|
||||||
|
|
||||||
pub type NixResult<T> = Result<T, NixError>;
|
|
||||||
|
|
||||||
#[non_exhaustive]
|
|
||||||
#[derive(Debug, Snafu)]
|
|
||||||
pub enum NixError {
|
|
||||||
#[snafu(display("I/O Error: {}", error))]
|
|
||||||
IoError { error: std::io::Error },
|
|
||||||
|
|
||||||
#[snafu(display("Nix returned invalid response: {}", output))]
|
|
||||||
BadOutput { output: String },
|
|
||||||
|
|
||||||
#[snafu(display("Nix exited with error code: {}", exit_code))]
|
|
||||||
NixFailure { exit_code: i32 },
|
|
||||||
|
|
||||||
#[snafu(display("Nix was killed by signal {}", signal))]
|
|
||||||
NixKilled { signal: i32 },
|
|
||||||
|
|
||||||
#[snafu(display("This operation is not supported"))]
|
|
||||||
Unsupported,
|
|
||||||
|
|
||||||
#[snafu(display("Invalid Nix store path"))]
|
|
||||||
InvalidStorePath,
|
|
||||||
|
|
||||||
#[snafu(display("Validation error"))]
|
|
||||||
ValidationError { errors: ValidationErrors },
|
|
||||||
|
|
||||||
#[snafu(display("Failed to upload keys: {}", error))]
|
|
||||||
KeyError { error: key::KeyError },
|
|
||||||
|
|
||||||
#[snafu(display("Store path {:?} is not a derivation", store_path))]
|
|
||||||
NotADerivation { store_path: StorePath },
|
|
||||||
|
|
||||||
#[snafu(display("Invalid NixOS system profile"))]
|
|
||||||
InvalidProfile,
|
|
||||||
|
|
||||||
#[snafu(display("Unknown active profile: {:?}", store_path))]
|
|
||||||
ActiveProfileUnknown { store_path: StorePath },
|
|
||||||
|
|
||||||
#[snafu(display("Could not determine current profile"))]
|
|
||||||
FailedToGetCurrentProfile,
|
|
||||||
|
|
||||||
#[snafu(display("Current Nix version does not support Flakes"))]
|
|
||||||
NoFlakesSupport,
|
|
||||||
|
|
||||||
#[snafu(display("Don't know how to connect to the node"))]
|
|
||||||
NoTargetHost,
|
|
||||||
|
|
||||||
#[snafu(display("Node name cannot be empty"))]
|
|
||||||
EmptyNodeName,
|
|
||||||
|
|
||||||
#[snafu(display("Filter rule cannot be empty"))]
|
|
||||||
EmptyFilterRule,
|
|
||||||
|
|
||||||
#[snafu(display("Deployment already executed"))]
|
|
||||||
DeploymentAlreadyExecuted,
|
|
||||||
|
|
||||||
#[snafu(display("Unknown error: {}", message))]
|
|
||||||
Unknown { message: String },
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<std::io::Error> for NixError {
|
|
||||||
fn from(error: std::io::Error) -> Self {
|
|
||||||
Self::IoError { error }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<key::KeyError> for NixError {
|
|
||||||
fn from(error: key::KeyError) -> Self {
|
|
||||||
Self::KeyError { error }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ValidationErrors> for NixError {
|
|
||||||
fn from(errors: ValidationErrors) -> Self {
|
|
||||||
Self::ValidationError { errors }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ExitStatus> for NixError {
|
|
||||||
fn from(status: ExitStatus) -> Self {
|
|
||||||
match status.code() {
|
|
||||||
Some(exit_code) => Self::NixFailure { exit_code },
|
|
||||||
None => Self::NixKilled { signal: status.signal().unwrap() },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NixError {
|
|
||||||
pub fn unknown(error: Box<dyn std::error::Error>) -> Self {
|
|
||||||
let message = error.to_string();
|
|
||||||
Self::Unknown { message }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A node's attribute name.
|
/// A node's attribute name.
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, Hash, Eq, PartialEq)]
|
#[derive(Serialize, Deserialize, Clone, Debug, Hash, Eq, PartialEq)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
|
@ -183,7 +88,7 @@ impl NodeName {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a NodeName from a String.
|
/// Creates a NodeName from a String.
|
||||||
pub fn new(name: String) -> NixResult<Self> {
|
pub fn new(name: String) -> ColmenaResult<Self> {
|
||||||
let validated = Self::validate(name)?;
|
let validated = Self::validate(name)?;
|
||||||
Ok(Self(validated))
|
Ok(Self(validated))
|
||||||
}
|
}
|
||||||
|
@ -199,10 +104,10 @@ impl NodeName {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate(s: String) -> NixResult<String> {
|
fn validate(s: String) -> ColmenaResult<String> {
|
||||||
// FIXME: Elaborate
|
// FIXME: Elaborate
|
||||||
if s.is_empty() {
|
if s.is_empty() {
|
||||||
return Err(NixError::EmptyNodeName);
|
return Err(ColmenaError::EmptyNodeName);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(s)
|
Ok(s)
|
||||||
|
|
|
@ -6,7 +6,7 @@ use std::iter::{Iterator, FromIterator};
|
||||||
|
|
||||||
use glob::Pattern as GlobPattern;
|
use glob::Pattern as GlobPattern;
|
||||||
|
|
||||||
use super::{NixError, NixResult, NodeName, NodeConfig};
|
use super::{ColmenaError, ColmenaResult, NodeName, NodeConfig};
|
||||||
|
|
||||||
/// A node filter containing a list of rules.
|
/// A node filter containing a list of rules.
|
||||||
pub struct NodeFilter {
|
pub struct NodeFilter {
|
||||||
|
@ -27,7 +27,7 @@ enum Rule {
|
||||||
|
|
||||||
impl NodeFilter {
|
impl NodeFilter {
|
||||||
/// Creates a new filter using an expression passed using `--on`.
|
/// Creates a new filter using an expression passed using `--on`.
|
||||||
pub fn new<S: AsRef<str>>(filter: S) -> NixResult<Self> {
|
pub fn new<S: AsRef<str>>(filter: S) -> ColmenaResult<Self> {
|
||||||
let filter = filter.as_ref();
|
let filter = filter.as_ref();
|
||||||
let trimmed = filter.trim();
|
let trimmed = filter.trim();
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ impl NodeFilter {
|
||||||
let pattern = pattern.trim();
|
let pattern = pattern.trim();
|
||||||
|
|
||||||
if pattern.is_empty() {
|
if pattern.is_empty() {
|
||||||
return Err(NixError::EmptyFilterRule);
|
return Err(ColmenaError::EmptyFilterRule);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(tag_pattern) = pattern.strip_prefix('@') {
|
if let Some(tag_pattern) = pattern.strip_prefix('@') {
|
||||||
|
@ -51,7 +51,7 @@ impl NodeFilter {
|
||||||
} else {
|
} else {
|
||||||
Ok(Rule::MatchName(GlobPattern::new(pattern).unwrap()))
|
Ok(Rule::MatchName(GlobPattern::new(pattern).unwrap()))
|
||||||
}
|
}
|
||||||
}).collect::<Vec<NixResult<Rule>>>();
|
}).collect::<Vec<ColmenaResult<Rule>>>();
|
||||||
|
|
||||||
let rules = Result::from_iter(rules)?;
|
let rules = Result::from_iter(rules)?;
|
||||||
|
|
||||||
|
@ -100,8 +100,8 @@ impl NodeFilter {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs the filter against a set of node names and returns the matched ones.
|
/// Runs the filter against a set of node names and returns the matched ones.
|
||||||
pub fn filter_node_names(&self, nodes: &[NodeName]) -> NixResult<HashSet<NodeName>> {
|
pub fn filter_node_names(&self, nodes: &[NodeName]) -> ColmenaResult<HashSet<NodeName>> {
|
||||||
nodes.iter().filter_map(|name| -> Option<NixResult<NodeName>> {
|
nodes.iter().filter_map(|name| -> Option<ColmenaResult<NodeName>> {
|
||||||
for rule in self.rules.iter() {
|
for rule in self.rules.iter() {
|
||||||
match rule {
|
match rule {
|
||||||
Rule::MatchName(pat) => {
|
Rule::MatchName(pat) => {
|
||||||
|
@ -110,7 +110,7 @@ impl NodeFilter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Some(Err(NixError::Unknown {
|
return Some(Err(ColmenaError::Unknown {
|
||||||
message: format!("Not enough information to run rule {:?} - We only have node names", rule),
|
message: format!("Not enough information to run rule {:?} - We only have node names", rule),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,8 @@ use tokio::process::Command;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
Goal,
|
Goal,
|
||||||
NixResult,
|
ColmenaResult,
|
||||||
NixError,
|
ColmenaError,
|
||||||
StorePath,
|
StorePath,
|
||||||
StoreDerivation,
|
StoreDerivation,
|
||||||
BuildResult,
|
BuildResult,
|
||||||
|
@ -20,16 +20,16 @@ pub type ProfileDerivation = StoreDerivation<Profile>;
|
||||||
pub struct Profile(StorePath);
|
pub struct Profile(StorePath);
|
||||||
|
|
||||||
impl Profile {
|
impl Profile {
|
||||||
pub fn from_store_path(path: StorePath) -> NixResult<Self> {
|
pub fn from_store_path(path: StorePath) -> ColmenaResult<Self> {
|
||||||
if
|
if
|
||||||
!path.is_dir() ||
|
!path.is_dir() ||
|
||||||
!path.join("bin/switch-to-configuration").exists()
|
!path.join("bin/switch-to-configuration").exists()
|
||||||
{
|
{
|
||||||
return Err(NixError::InvalidProfile);
|
return Err(ColmenaError::InvalidProfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
if path.to_str().is_none() {
|
if path.to_str().is_none() {
|
||||||
Err(NixError::InvalidProfile)
|
Err(ColmenaError::InvalidProfile)
|
||||||
} else {
|
} else {
|
||||||
Ok(Self(path))
|
Ok(Self(path))
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ impl Profile {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a GC root for this profile.
|
/// Create a GC root for this profile.
|
||||||
pub async fn create_gc_root(&self, path: &Path) -> NixResult<()> {
|
pub async fn create_gc_root(&self, path: &Path) -> ColmenaResult<()> {
|
||||||
let mut command = Command::new("nix-store");
|
let mut command = Command::new("nix-store");
|
||||||
command.args(&["--no-build-output", "--indirect", "--add-root", path.to_str().unwrap()]);
|
command.args(&["--no-build-output", "--indirect", "--add-root", path.to_str().unwrap()]);
|
||||||
command.args(&["--realise", self.as_path().to_str().unwrap()]);
|
command.args(&["--realise", self.as_path().to_str().unwrap()]);
|
||||||
|
@ -83,19 +83,19 @@ impl Profile {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<BuildResult<Profile>> for Profile {
|
impl TryFrom<BuildResult<Profile>> for Profile {
|
||||||
type Error = NixError;
|
type Error = ColmenaError;
|
||||||
|
|
||||||
fn try_from(result: BuildResult<Self>) -> NixResult<Self> {
|
fn try_from(result: BuildResult<Self>) -> ColmenaResult<Self> {
|
||||||
let paths = result.paths();
|
let paths = result.paths();
|
||||||
|
|
||||||
if paths.is_empty() {
|
if paths.is_empty() {
|
||||||
return Err(NixError::BadOutput {
|
return Err(ColmenaError::BadOutput {
|
||||||
output: String::from("There is no store path"),
|
output: String::from("There is no store path"),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if paths.len() > 1 {
|
if paths.len() > 1 {
|
||||||
return Err(NixError::BadOutput {
|
return Err(ColmenaError::BadOutput {
|
||||||
output: String::from("Build resulted in more than 1 store path"),
|
output: String::from("Build resulted in more than 1 store path"),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,9 @@ use std::fmt;
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
|
use crate::error::{ColmenaError, ColmenaResult};
|
||||||
use crate::util::CommandExt;
|
use crate::util::CommandExt;
|
||||||
use super::{Host, NixResult, NixError};
|
use super::Host;
|
||||||
|
|
||||||
/// A Nix store path.
|
/// A Nix store path.
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
@ -43,7 +44,7 @@ impl StorePath {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the immediate dependencies of the store path.
|
/// Returns the immediate dependencies of the store path.
|
||||||
pub async fn references(&self) -> NixResult<Vec<StorePath>> {
|
pub async fn references(&self) -> ColmenaResult<Vec<StorePath>> {
|
||||||
let references = Command::new("nix-store")
|
let references = Command::new("nix-store")
|
||||||
.args(&["--query", "--references"])
|
.args(&["--query", "--references"])
|
||||||
.arg(&self.0)
|
.arg(&self.0)
|
||||||
|
@ -55,11 +56,11 @@ impl StorePath {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts the store path into a store derivation.
|
/// Converts the store path into a store derivation.
|
||||||
pub fn into_derivation<T: TryFrom<BuildResult<T>>>(self) -> NixResult<StoreDerivation<T>> {
|
pub fn into_derivation<T: TryFrom<BuildResult<T>>>(self) -> ColmenaResult<StoreDerivation<T>> {
|
||||||
if self.is_derivation() {
|
if self.is_derivation() {
|
||||||
Ok(StoreDerivation::<T>::from_store_path_unchecked(self))
|
Ok(StoreDerivation::<T>::from_store_path_unchecked(self))
|
||||||
} else {
|
} else {
|
||||||
Err(NixError::NotADerivation { store_path: self })
|
Err(ColmenaError::NotADerivation { store_path: self })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,13 +74,13 @@ impl Deref for StorePath {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<String> for StorePath {
|
impl TryFrom<String> for StorePath {
|
||||||
type Error = NixError;
|
type Error = ColmenaError;
|
||||||
|
|
||||||
fn try_from(s: String) -> NixResult<Self> {
|
fn try_from(s: String) -> ColmenaResult<Self> {
|
||||||
if s.starts_with("/nix/store/") {
|
if s.starts_with("/nix/store/") {
|
||||||
Ok(Self(s.into()))
|
Ok(Self(s.into()))
|
||||||
} else {
|
} else {
|
||||||
Err(NixError::InvalidStorePath)
|
Err(ColmenaError::InvalidStorePath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,9 +114,9 @@ impl<T: TryFrom<BuildResult<T>>> StoreDerivation<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: TryFrom<BuildResult<T>, Error=NixError>> StoreDerivation<T> {
|
impl<T: TryFrom<BuildResult<T>, Error=ColmenaError>> StoreDerivation<T> {
|
||||||
/// Builds the store derivation on a host, resulting in a T.
|
/// Builds the store derivation on a host, resulting in a T.
|
||||||
pub async fn realize(&self, host: &mut Box<dyn Host>) -> NixResult<T> {
|
pub async fn realize(&self, host: &mut Box<dyn Host>) -> ColmenaResult<T> {
|
||||||
let paths: Vec<StorePath> = host.realize(&self.path).await?;
|
let paths: Vec<StorePath> = host.realize(&self.path).await?;
|
||||||
|
|
||||||
let result = BuildResult {
|
let result = BuildResult {
|
||||||
|
@ -126,7 +127,7 @@ impl<T: TryFrom<BuildResult<T>, Error=NixError>> StoreDerivation<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds the store derivation on a host without copying the results back.
|
/// Builds the store derivation on a host without copying the results back.
|
||||||
pub async fn realize_remote(&self, host: &mut Box<dyn Host>) -> NixResult<T> {
|
pub async fn realize_remote(&self, host: &mut Box<dyn Host>) -> ColmenaResult<T> {
|
||||||
let paths: Vec<StorePath> = host.realize_remote(&self.path).await?;
|
let paths: Vec<StorePath> = host.realize_remote(&self.path).await?;
|
||||||
|
|
||||||
let result = BuildResult {
|
let result = BuildResult {
|
||||||
|
@ -143,7 +144,7 @@ impl<T: TryFrom<BuildResult<T>>> fmt::Display for StoreDerivation<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: TryFrom<BuildResult<T>, Error=NixError>> BuildResult<T> {
|
impl<T: TryFrom<BuildResult<T>, Error=ColmenaError>> BuildResult<T> {
|
||||||
pub fn paths(&self) -> &[StorePath] {
|
pub fn paths(&self) -> &[StorePath] {
|
||||||
self.results.as_slice()
|
self.results.as_slice()
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,8 @@ use tokio::sync::mpsc::{self,
|
||||||
UnboundedSender as TokioSender,
|
UnboundedSender as TokioSender,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::error::ColmenaResult;
|
||||||
use crate::job::JobId;
|
use crate::job::JobId;
|
||||||
use crate::nix::NixResult;
|
|
||||||
|
|
||||||
pub use plain::PlainOutput;
|
pub use plain::PlainOutput;
|
||||||
pub use spinner::SpinnerOutput;
|
pub use spinner::SpinnerOutput;
|
||||||
|
@ -33,7 +33,7 @@ pub enum SimpleProgressOutput {
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait ProgressOutput : Sized {
|
pub trait ProgressOutput : Sized {
|
||||||
/// Runs until a Message::Complete is received.
|
/// Runs until a Message::Complete is received.
|
||||||
async fn run_until_completion(self) -> NixResult<Self>;
|
async fn run_until_completion(self) -> ColmenaResult<Self>;
|
||||||
|
|
||||||
/// Returns a sender.
|
/// Returns a sender.
|
||||||
///
|
///
|
||||||
|
@ -109,7 +109,7 @@ impl SimpleProgressOutput {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run_until_completion(self) -> NixResult<Self> {
|
pub async fn run_until_completion(self) -> ColmenaResult<Self> {
|
||||||
match self {
|
match self {
|
||||||
Self::Plain(o) => {
|
Self::Plain(o) => {
|
||||||
o.run_until_completion().await
|
o.run_until_completion().await
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use console::Style as ConsoleStyle;
|
use console::Style as ConsoleStyle;
|
||||||
|
|
||||||
use crate::nix::NixResult;
|
use crate::error::ColmenaResult;
|
||||||
use super::{
|
use super::{
|
||||||
DEFAULT_LABEL_WIDTH,
|
DEFAULT_LABEL_WIDTH,
|
||||||
ProgressOutput,
|
ProgressOutput,
|
||||||
|
@ -81,7 +81,7 @@ impl PlainOutput {
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl ProgressOutput for PlainOutput {
|
impl ProgressOutput for PlainOutput {
|
||||||
async fn run_until_completion(mut self) -> NixResult<Self> {
|
async fn run_until_completion(mut self) -> ColmenaResult<Self> {
|
||||||
loop {
|
loop {
|
||||||
let message = self.receiver.recv().await;
|
let message = self.receiver.recv().await;
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,8 @@ use std::time::Instant;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use indicatif::{MultiProgress, ProgressStyle, ProgressBar};
|
use indicatif::{MultiProgress, ProgressStyle, ProgressBar};
|
||||||
|
|
||||||
|
use crate::error::ColmenaResult;
|
||||||
use crate::job::JobId;
|
use crate::job::JobId;
|
||||||
use crate::nix::NixResult;
|
|
||||||
use super::{
|
use super::{
|
||||||
DEFAULT_LABEL_WIDTH,
|
DEFAULT_LABEL_WIDTH,
|
||||||
ProgressOutput,
|
ProgressOutput,
|
||||||
|
@ -171,7 +171,7 @@ impl SpinnerOutput {
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl ProgressOutput for SpinnerOutput {
|
impl ProgressOutput for SpinnerOutput {
|
||||||
async fn run_until_completion(mut self) -> NixResult<Self> {
|
async fn run_until_completion(mut self) -> ColmenaResult<Self> {
|
||||||
let meta_bar = self.multi.add(self.meta_bar.clone());
|
let meta_bar = self.multi.add(self.meta_bar.clone());
|
||||||
meta_bar.enable_steady_tick(100);
|
meta_bar.enable_steady_tick(100);
|
||||||
|
|
||||||
|
|
|
@ -7,12 +7,12 @@ use std::future::Future;
|
||||||
|
|
||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
|
|
||||||
use crate::nix::NixError;
|
use crate::error::ColmenaError;
|
||||||
|
|
||||||
/// Runs a closure and tries to troubleshoot if it returns an error.
|
/// Runs a closure and tries to troubleshoot if it returns an error.
|
||||||
pub async fn run_wrapped<'a, F, U, T>(global_args: &'a ArgMatches, local_args: &'a ArgMatches, f: U) -> T
|
pub async fn run_wrapped<'a, F, U, T>(global_args: &'a ArgMatches, local_args: &'a ArgMatches, f: U) -> T
|
||||||
where U: FnOnce(&'a ArgMatches, &'a ArgMatches) -> F,
|
where U: FnOnce(&'a ArgMatches, &'a ArgMatches) -> F,
|
||||||
F: Future<Output = Result<T, NixError>>,
|
F: Future<Output = Result<T, ColmenaError>>,
|
||||||
{
|
{
|
||||||
match f(global_args, local_args).await {
|
match f(global_args, local_args).await {
|
||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
|
@ -30,8 +30,8 @@ pub async fn run_wrapped<'a, F, U, T>(global_args: &'a ArgMatches, local_args: &
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn troubleshoot(global_args: &ArgMatches, _local_args: &ArgMatches, error: &NixError) -> Result<(), NixError> {
|
fn troubleshoot(global_args: &ArgMatches, _local_args: &ArgMatches, error: &ColmenaError) -> Result<(), ColmenaError> {
|
||||||
if let NixError::NoFlakesSupport = error {
|
if let ColmenaError::NoFlakesSupport = error {
|
||||||
// People following the tutorial might put hive.nix directly
|
// People following the tutorial might put hive.nix directly
|
||||||
// in their Colmena checkout, and encounter NoFlakesSupport
|
// in their Colmena checkout, and encounter NoFlakesSupport
|
||||||
// because Colmena always prefers flake.nix when it exists.
|
// because Colmena always prefers flake.nix when it exists.
|
||||||
|
|
37
src/util.rs
37
src/util.rs
|
@ -9,7 +9,8 @@ use serde::de::DeserializeOwned;
|
||||||
use tokio::io::{AsyncRead, AsyncBufReadExt, BufReader};
|
use tokio::io::{AsyncRead, AsyncBufReadExt, BufReader};
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
use super::nix::{Flake, Hive, HivePath, NixResult, NixError, StorePath};
|
use super::error::{ColmenaResult, ColmenaError};
|
||||||
|
use super::nix::{Flake, Hive, HivePath, StorePath};
|
||||||
use super::nix::deployment::TargetNodeMap;
|
use super::nix::deployment::TargetNodeMap;
|
||||||
use super::job::JobHandle;
|
use super::job::JobHandle;
|
||||||
|
|
||||||
|
@ -26,16 +27,16 @@ pub struct CommandExecution {
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait CommandExt {
|
pub trait CommandExt {
|
||||||
/// Runs the command with stdout and stderr passed through to the user.
|
/// Runs the command with stdout and stderr passed through to the user.
|
||||||
async fn passthrough(&mut self) -> NixResult<()>;
|
async fn passthrough(&mut self) -> ColmenaResult<()>;
|
||||||
|
|
||||||
/// Runs the command, capturing the output as a String.
|
/// Runs the command, capturing the output as a String.
|
||||||
async fn capture_output(&mut self) -> NixResult<String>;
|
async fn capture_output(&mut self) -> ColmenaResult<String>;
|
||||||
|
|
||||||
/// Runs the command, capturing deserialized output from JSON.
|
/// Runs the command, capturing deserialized output from JSON.
|
||||||
async fn capture_json<T>(&mut self) -> NixResult<T> where T: DeserializeOwned;
|
async fn capture_json<T>(&mut self) -> ColmenaResult<T> where T: DeserializeOwned;
|
||||||
|
|
||||||
/// Runs the command, capturing a single store path.
|
/// Runs the command, capturing a single store path.
|
||||||
async fn capture_store_path(&mut self) -> NixResult<StorePath>;
|
async fn capture_store_path(&mut self) -> ColmenaResult<StorePath>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CommandExecution {
|
impl CommandExecution {
|
||||||
|
@ -65,7 +66,7 @@ impl CommandExecution {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs the command.
|
/// Runs the command.
|
||||||
pub async fn run(&mut self) -> NixResult<()> {
|
pub async fn run(&mut self) -> ColmenaResult<()> {
|
||||||
self.command.stdin(Stdio::null());
|
self.command.stdin(Stdio::null());
|
||||||
self.command.stdout(Stdio::piped());
|
self.command.stdout(Stdio::piped());
|
||||||
self.command.stderr(Stdio::piped());
|
self.command.stderr(Stdio::piped());
|
||||||
|
@ -103,7 +104,7 @@ impl CommandExecution {
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl CommandExt for Command {
|
impl CommandExt for Command {
|
||||||
/// Runs the command with stdout and stderr passed through to the user.
|
/// Runs the command with stdout and stderr passed through to the user.
|
||||||
async fn passthrough(&mut self) -> NixResult<()> {
|
async fn passthrough(&mut self) -> ColmenaResult<()> {
|
||||||
let exit = self
|
let exit = self
|
||||||
.spawn()?
|
.spawn()?
|
||||||
.wait()
|
.wait()
|
||||||
|
@ -117,7 +118,7 @@ impl CommandExt for Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Captures output as a String.
|
/// Captures output as a String.
|
||||||
async fn capture_output(&mut self) -> NixResult<String> {
|
async fn capture_output(&mut self) -> ColmenaResult<String> {
|
||||||
// We want the user to see the raw errors
|
// We want the user to see the raw errors
|
||||||
let output = self
|
let output = self
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
|
@ -135,15 +136,15 @@ impl CommandExt for Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Captures deserialized output from JSON.
|
/// Captures deserialized output from JSON.
|
||||||
async fn capture_json<T>(&mut self) -> NixResult<T> where T: DeserializeOwned {
|
async fn capture_json<T>(&mut self) -> ColmenaResult<T> where T: DeserializeOwned {
|
||||||
let output = self.capture_output().await?;
|
let output = self.capture_output().await?;
|
||||||
serde_json::from_str(&output).map_err(|_| NixError::BadOutput {
|
serde_json::from_str(&output).map_err(|_| ColmenaError::BadOutput {
|
||||||
output: output.clone()
|
output: output.clone()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Captures a single store path.
|
/// Captures a single store path.
|
||||||
async fn capture_store_path(&mut self) -> NixResult<StorePath> {
|
async fn capture_store_path(&mut self) -> ColmenaResult<StorePath> {
|
||||||
let output = self.capture_output().await?;
|
let output = self.capture_output().await?;
|
||||||
let path = output.trim_end().to_owned();
|
let path = output.trim_end().to_owned();
|
||||||
StorePath::try_from(path)
|
StorePath::try_from(path)
|
||||||
|
@ -152,12 +153,12 @@ impl CommandExt for Command {
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl CommandExt for CommandExecution {
|
impl CommandExt for CommandExecution {
|
||||||
async fn passthrough(&mut self) -> NixResult<()> {
|
async fn passthrough(&mut self) -> ColmenaResult<()> {
|
||||||
self.run().await
|
self.run().await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Captures output as a String.
|
/// Captures output as a String.
|
||||||
async fn capture_output(&mut self) -> NixResult<String> {
|
async fn capture_output(&mut self) -> ColmenaResult<String> {
|
||||||
self.run().await?;
|
self.run().await?;
|
||||||
let (stdout, _) = self.get_logs();
|
let (stdout, _) = self.get_logs();
|
||||||
|
|
||||||
|
@ -165,22 +166,22 @@ impl CommandExt for CommandExecution {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Captures deserialized output from JSON.
|
/// Captures deserialized output from JSON.
|
||||||
async fn capture_json<T>(&mut self) -> NixResult<T> where T: DeserializeOwned {
|
async fn capture_json<T>(&mut self) -> ColmenaResult<T> where T: DeserializeOwned {
|
||||||
let output = self.capture_output().await?;
|
let output = self.capture_output().await?;
|
||||||
serde_json::from_str(&output).map_err(|_| NixError::BadOutput {
|
serde_json::from_str(&output).map_err(|_| ColmenaError::BadOutput {
|
||||||
output: output.clone()
|
output: output.clone()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Captures a single store path.
|
/// Captures a single store path.
|
||||||
async fn capture_store_path(&mut self) -> NixResult<StorePath> {
|
async fn capture_store_path(&mut self) -> ColmenaResult<StorePath> {
|
||||||
let output = self.capture_output().await?;
|
let output = self.capture_output().await?;
|
||||||
let path = output.trim_end().to_owned();
|
let path = output.trim_end().to_owned();
|
||||||
StorePath::try_from(path)
|
StorePath::try_from(path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn hive_from_args(args: &ArgMatches) -> NixResult<Hive> {
|
pub async fn hive_from_args(args: &ArgMatches) -> ColmenaResult<Hive> {
|
||||||
let path = match args.occurrences_of("config") {
|
let path = match args.occurrences_of("config") {
|
||||||
0 => {
|
0 => {
|
||||||
// traverse upwards until we find hive.nix
|
// traverse upwards until we find hive.nix
|
||||||
|
@ -281,7 +282,7 @@ fn canonicalize_cli_path(path: &str) -> PathBuf {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn capture_stream<R>(mut stream: BufReader<R>, job: Option<JobHandle>, stderr: bool) -> NixResult<String>
|
pub async fn capture_stream<R>(mut stream: BufReader<R>, job: Option<JobHandle>, stderr: bool) -> ColmenaResult<String>
|
||||||
where R: AsyncRead + Unpin
|
where R: AsyncRead + Unpin
|
||||||
{
|
{
|
||||||
let mut log = String::new();
|
let mut log = String::new();
|
||||||
|
|
Loading…
Reference in a new issue