Move nix::{NixResult, NixError} to error::{ColmenaResult, ColmenaError}

This commit is contained in:
Zhaofeng Li 2022-01-08 01:20:36 -08:00
parent 16ed9d8c66
commit 31fd1e49ac
26 changed files with 295 additions and 275 deletions

View file

@ -3,6 +3,7 @@ use std::path::PathBuf;
use clap::{Arg, App, ArgMatches, ArgSettings};
use crate::error::ColmenaError;
use crate::nix::deployment::{
Deployment,
Goal,
@ -11,7 +12,7 @@ use crate::nix::deployment::{
ParallelismLimit,
};
use crate::progress::SimpleProgressOutput;
use crate::nix::{NixError, NodeFilter};
use crate::nix::NodeFilter;
use crate::util;
pub fn register_deploy_args(command: App) -> App {
@ -126,7 +127,7 @@ pub fn subcommand() -> App<'static> {
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 ssh_config = env::var("SSH_CONFIG_FILE")

View file

@ -5,13 +5,14 @@ use clap::{Arg, App, ArgMatches};
use tokio::fs;
use tokio::process::Command;
use crate::error::ColmenaError;
use crate::nix::deployment::{
Deployment,
Goal,
TargetNode,
Options,
};
use crate::nix::{NixError, NodeName, host};
use crate::nix::{NodeName, host};
use crate::progress::SimpleProgressOutput;
use crate::util;
@ -57,7 +58,7 @@ By default, Colmena will deploy keys set in `deployment.keys` before activating
.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?
if let Ok(os_release) = fs::read_to_string("/etc/os-release").await {
if !os_release.contains("ID=nixos\n") {

View file

@ -2,8 +2,8 @@ use std::path::PathBuf;
use clap::{Arg, App, AppSettings, ArgMatches};
use crate::error::ColmenaError;
use crate::util;
use crate::nix::NixError;
pub fn subcommand() -> App<'static> {
subcommand_gen("eval")
@ -41,7 +41,7 @@ For example, to retrieve the configuration of one node, you may write something
.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() {
log::warn!("`colmena introspect` has been renamed to `colmena eval`. Please update your scripts.");
}

View file

@ -6,8 +6,9 @@ use clap::{Arg, App, AppSettings, ArgMatches};
use futures::future::join_all;
use tokio::sync::Semaphore;
use crate::nix::{NixError, NodeFilter};
use crate::error::ColmenaError;
use crate::job::{JobMonitor, JobState, JobType};
use crate::nix::NodeFilter;
use crate::progress::SimpleProgressOutput;
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)
}
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 ssh_config = env::var("SSH_CONFIG_FILE")
.ok().map(PathBuf::from);

View file

@ -1,13 +1,14 @@
use clap::{App, ArgMatches};
use crate::nix::{NixCheck, NixError};
use crate::error::ColmenaError;
use crate::nix::NixCheck;
pub fn subcommand() -> App<'static> {
App::new("nix-info")
.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;
check.print_version_info();
check.print_flakes_info(false);

View file

@ -3,8 +3,9 @@ use std::time::Duration;
use clap::{App, AppSettings, ArgMatches};
use tokio::time;
use crate::error::{ColmenaError, ColmenaResult};
use crate::job::{JobMonitor, JobType};
use crate::nix::{NixError, NixResult, NodeName};
use crate::nix::NodeName;
use crate::progress::{ProgressOutput, spinner::SpinnerOutput};
macro_rules! node {
@ -19,7 +20,7 @@ pub fn subcommand() -> App<'static> {
.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 (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);
Err(NixError::Unsupported) as NixResult<()>
Err(ColmenaError::Unsupported) as ColmenaResult<()>
});
let (monitor, output, ret) = tokio::join!(

103
src/error.rs Normal file
View 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 }
}
}

View file

@ -13,7 +13,8 @@ use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
use tokio::time;
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};
pub type Sender = UnboundedSender<Event>;
@ -192,7 +193,7 @@ pub enum EventPayload {
/// 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.
Failure(String),
@ -277,7 +278,7 @@ impl JobMonitor {
}
/// 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(sender) = &self.progress {
sender.send(ProgressMessage::HintLabelWidth(width)).unwrap();
@ -465,7 +466,7 @@ impl JobMonitor {
}
/// 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() {
sender.send(ProgressMessage::Complete).unwrap();
}
@ -500,7 +501,7 @@ impl JobHandleInner {
/// Creates a new job with a distinct ID.
///
/// 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 creation = JobCreation {
friendly_name: None,
@ -509,7 +510,7 @@ impl JobHandleInner {
};
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 {
@ -525,9 +526,9 @@ impl JobHandleInner {
/// Runs a closure, automatically updating the job monitor based on the result.
///
/// 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,
F: Future<Output = NixResult<T>>,
F: Future<Output = ColmenaResult<T>>,
{
self.run_internal(f, true).await
}
@ -535,52 +536,52 @@ impl JobHandleInner {
/// Runs a closure, automatically updating the job monitor based on the result.
///
/// 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,
F: Future<Output = NixResult<T>>,
F: Future<Output = ColmenaResult<T>>,
{
self.run_internal(f, false).await
}
/// 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))
}
/// 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))
}
/// 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))
}
/// 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))
}
/// 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))
}
/// 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))
}
/// 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()))
}
/// 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,
F: Future<Output = NixResult<T>>,
F: Future<Output = ColmenaResult<T>>,
{
if report_running {
// Tell monitor we are starting
@ -603,7 +604,7 @@ impl JobHandleInner {
}
/// Sends an event to the job monitor.
fn send_payload(&self, payload: EventPayload) -> NixResult<()> {
fn send_payload(&self, payload: EventPayload) -> ColmenaResult<()> {
if payload.privileged() {
panic!("Tried to send privileged payload with JobHandle");
}
@ -611,7 +612,7 @@ impl JobHandleInner {
let event = Event::new(self.job_id, payload);
self.sender.send(event)
.map_err(|e| NixError::unknown(Box::new(e)))?;
.map_err(|e| ColmenaError::unknown(Box::new(e)))?;
Ok(())
}
@ -619,9 +620,9 @@ impl JobHandleInner {
impl MetaJobHandle {
/// 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,
F: Future<Output = NixResult<T>>,
F: Future<Output = ColmenaResult<T>>,
{
let normal_handle = Arc::new(JobHandleInner {
job_id: self.job_id,
@ -645,11 +646,11 @@ impl MetaJobHandle {
}
/// 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);
self.sender.send(event)
.map_err(|e| NixError::unknown(Box::new(e)))?;
.map_err(|e| ColmenaError::unknown(Box::new(e)))?;
Ok(())
}
@ -874,7 +875,7 @@ mod tests {
Ok(())
}).await?;
Err(NixError::Unsupported) as NixResult<()>
Err(ColmenaError::Unsupported) as ColmenaResult<()>
});
// Run until completion
@ -884,7 +885,7 @@ mod tests {
);
match ret {
Err(NixError::Unsupported) => (),
Err(ColmenaError::Unsupported) => (),
_ => {
panic!("Wrapper must return error as-is");
}

View file

@ -1,5 +1,6 @@
#![deny(unused_must_use)]
mod error;
mod nix;
mod cli;
mod command;

View file

@ -26,8 +26,8 @@ use super::{
Host,
NodeName,
NodeConfig,
NixError,
NixResult,
ColmenaError,
ColmenaResult,
Profile,
ProfileDerivation,
CopyDirection,
@ -120,9 +120,9 @@ impl Deployment {
///
/// If a ProgressSender is supplied, then this should be run in parallel
/// with its `run_until_completion()` future.
pub async fn execute(mut self) -> NixResult<()> {
pub async fn execute(mut self) -> ColmenaResult<()> {
if self.executed {
return Err(NixError::DeploymentAlreadyExecuted);
return Err(ColmenaError::DeploymentAlreadyExecuted);
}
self.executed = true;
@ -148,7 +148,7 @@ impl Deployment {
}
join_all(futures).await
.into_iter().collect::<NixResult<Vec<()>>>()?;
.into_iter().collect::<ColmenaResult<Vec<()>>>()?;
Ok(())
});
@ -173,7 +173,7 @@ impl Deployment {
}
join_all(futures).await
.into_iter().collect::<NixResult<Vec<()>>>()?;
.into_iter().collect::<ColmenaResult<Vec<()>>>()?;
Ok(())
});
@ -219,7 +219,7 @@ impl Deployment {
}
/// 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 {
unreachable!(); // some logic is screwed up
}
@ -256,14 +256,14 @@ impl Deployment {
}
join_all(futures).await
.into_iter().collect::<NixResult<Vec<()>>>()?;
.into_iter().collect::<ColmenaResult<Vec<()>>>()?;
Ok(())
}
/// Evaluates a set of nodes, returning their corresponding store derivations.
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())?;
@ -280,12 +280,12 @@ impl Deployment {
}
/// 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 job = parent.create_job(JobType::UploadKeys, nodes)?;
job.run(|_| async move {
if target.host.is_none() {
return Err(NixError::Unsupported);
return Err(ColmenaError::Unsupported);
}
let host = target.host.as_mut().unwrap();
@ -297,7 +297,7 @@ impl Deployment {
/// Builds a system profile directly on the node itself.
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()];
@ -306,7 +306,7 @@ impl Deployment {
let build_job = parent.create_job(JobType::Build, nodes.clone())?;
let (target, profile) = build_job.run(|job| async move {
if target.host.is_none() {
return Err(NixError::Unsupported);
return Err(ColmenaError::Unsupported);
}
let mut host = target.host.as_mut().unwrap();
@ -331,7 +331,7 @@ impl Deployment {
/// 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)
-> NixResult<(TargetNode, Profile)>
-> ColmenaResult<(TargetNode, Profile)>
{
let nodes = vec![target.name.clone()];
@ -361,7 +361,7 @@ impl Deployment {
let arc_self = self.clone();
let target = push_job.run(|job| async move {
if target.host.is_none() {
return Err(NixError::Unsupported);
return Err(ColmenaError::Unsupported);
}
let host = target.host.as_mut().unwrap();
@ -383,7 +383,7 @@ impl Deployment {
///
/// This will also upload keys to the node.
async fn activate_node(self: DeploymentHandle, parent: JobHandle, mut target: TargetNode, profile: Profile)
-> NixResult<()>
-> ColmenaResult<()>
{
let nodes = vec![target.name.clone()];
let target_name = target.name.clone();
@ -437,7 +437,7 @@ impl Deployment {
if arc_self.options.force_replace_unknown_profiles {
job.message("Warning: Remote profile is unknown, but unknown profiles are being ignored".to_string())?;
} else {
return Err(NixError::ActiveProfileUnknown {
return Err(ColmenaError::ActiveProfileUnknown {
store_path: profile,
});
}

View file

@ -7,7 +7,7 @@ use std::process::Stdio;
use serde::Deserialize;
use tokio::process::Command;
use super::{NixCheck, NixError, NixResult};
use super::{NixCheck, ColmenaError, ColmenaResult};
/// A Nix Flake.
#[derive(Debug)]
@ -24,7 +24,7 @@ impl Flake {
///
/// This will try to retrieve the resolved URL of the local flake
/// 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?;
let flake = dir.as_ref().as_os_str().to_str()
@ -39,7 +39,7 @@ impl Flake {
}
/// 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?;
Ok(Self {
@ -69,7 +69,7 @@ struct FlakeMetadata {
impl FlakeMetadata {
/// Resolves a flake.
async fn resolve(flake: &str) -> NixResult<Self> {
async fn resolve(flake: &str) -> ColmenaResult<Self> {
let child = Command::new("nix")
.args(&["flake", "metadata", "--json"])
.args(&["--experimental-features", "nix-command flakes"])
@ -86,7 +86,7 @@ impl FlakeMetadata {
serde_json::from_slice::<FlakeMetadata>(&output.stdout)
.map_err(|_| {
let output = String::from_utf8_lossy(&output.stdout).to_string();
NixError::BadOutput { output }
ColmenaError::BadOutput { output }
})
}
}

View file

@ -11,7 +11,7 @@ use validator::Validate;
use super::{
Flake,
NixResult,
ColmenaResult,
NodeName,
NodeConfig,
NodeFilter,
@ -39,7 +39,7 @@ pub enum 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();
if let Some(osstr) = path.file_name() {
@ -87,7 +87,7 @@ pub struct Hive {
}
impl Hive {
pub fn new(path: HivePath) -> NixResult<Self> {
pub fn new(path: HivePath) -> ColmenaResult<Self> {
let mut eval_nix = NamedTempFile::new()?;
eval_nix.write_all(HIVE_EVAL).unwrap();
@ -110,7 +110,7 @@ impl Hive {
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?;
if self.show_trace {
@ -121,7 +121,7 @@ impl Hive {
}
/// 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;
log::info!("Enumerating nodes...");
@ -197,13 +197,13 @@ impl Hive {
}
/// 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()
.capture_json().await
}
/// 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?
.capture_json().await?;
@ -217,14 +217,14 @@ impl Hive {
}
/// 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());
self.nix_instantiate(&expr).eval_with_builders().await?
.capture_json().await
}
/// 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 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
/// to split up the evaluation process into chunks and run them
/// 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 expr = format!("hive.evalSelected {}", nodes_expr.expression());
@ -267,7 +267,7 @@ impl Hive {
}
/// 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 {
let expression = format!("hive.introspect ({})", expression);
self.nix_instantiate(&expression).instantiate_with_builders().await?
@ -280,7 +280,7 @@ impl 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 {
return Ok(builders_opt.clone());
}
@ -296,7 +296,7 @@ impl Hive {
}
/// 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();
if let Some(machines_file) = self.machines_file().await? {
@ -381,7 +381,7 @@ impl<'hive> NixInstantiate<'hive> {
command
}
async fn instantiate_with_builders(self) -> NixResult<Command> {
async fn instantiate_with_builders(self) -> ColmenaResult<Command> {
let hive = self.hive;
let mut command = self.instantiate();
@ -391,7 +391,7 @@ impl<'hive> NixInstantiate<'hive> {
Ok(command)
}
async fn eval_with_builders(self) -> NixResult<Command> {
async fn eval_with_builders(self) -> ColmenaResult<Command> {
let hive = self.hive;
let mut command = self.eval();
@ -413,7 +413,7 @@ struct 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 json = serde_json::to_vec(&data).expect("Could not serialize data");
tmp.write_all(&json)?;

View file

@ -12,8 +12,9 @@ use shell_escape::unix::escape;
use tokio::io::{AsyncWriteExt, BufReader};
use tokio::process::Child;
use crate::error::ColmenaResult;
use crate::job::JobHandle;
use crate::nix::{Key, NixResult};
use crate::nix::Key;
use crate::util::capture_stream;
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())
}
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 stdin = uploader.stdin.take().unwrap();

View file

@ -5,10 +5,11 @@ use std::process::Stdio;
use async_trait::async_trait;
use tokio::process::Command;
use super::{CopyDirection, CopyOptions, Host, key_uploader};
use crate::nix::{StorePath, Profile, Goal, NixError, NixResult, Key, SYSTEM_PROFILE, CURRENT_PROFILE};
use crate::error::{ColmenaResult, ColmenaError};
use crate::nix::{StorePath, Profile, Goal, Key, SYSTEM_PROFILE, CURRENT_PROFILE};
use crate::util::{CommandExecution, CommandExt};
use crate::job::JobHandle;
use super::{CopyDirection, CopyOptions, Host, key_uploader};
/// The local machine running Colmena.
///
@ -31,11 +32,11 @@ impl Local {
#[async_trait]
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(())
}
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");
command.args(self.nix_options.clone());
@ -55,7 +56,7 @@ impl Host for Local {
.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 {
self.upload_key(name, key, require_ownership).await?;
}
@ -63,9 +64,9 @@ impl Host for Local {
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() {
return Err(NixError::Unsupported);
return Err(ColmenaError::Unsupported);
}
if goal.should_switch_profile() {
@ -91,14 +92,14 @@ impl Host for Local {
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")
.args(&["-c", &format!("readlink -e {} || readlink -e {}", SYSTEM_PROFILE, CURRENT_PROFILE)])
.capture_output()
.await?;
let path = paths.lines().into_iter().next()
.ok_or(NixError::FailedToGetCurrentProfile)?
.ok_or(ColmenaError::FailedToGetCurrentProfile)?
.to_string()
.try_into()?;
@ -112,7 +113,7 @@ impl Host for Local {
impl Local {
/// "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 {
job.message(format!("Deploying key {}", name))?;
}

View file

@ -2,7 +2,7 @@ use std::collections::HashMap;
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;
mod ssh;
@ -65,20 +65,20 @@ pub trait Host: Send + Sync + std::fmt::Debug {
/// Sends or receives the specified closure to the 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
///
/// The derivation must already exist on the host.
/// After realization, paths in the Vec<StorePath> will then
/// 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.
fn set_job(&mut self, bar: Option<JobHandle>);
/// 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()
.include_outputs(true);
@ -90,7 +90,7 @@ pub trait Host: Send + Sync + std::fmt::Debug {
}
/// 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?;
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
/// exist.
#[allow(unused_variables)]
async fn upload_keys(&mut self, keys: &HashMap<String, Key>, require_ownership: bool) -> NixResult<()> {
Err(NixError::Unsupported)
async fn upload_keys(&mut self, keys: &HashMap<String, Key>, require_ownership: bool) -> ColmenaResult<()> {
Err(ColmenaError::Unsupported)
}
/// 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!
/// It will first try `/nix/var/nix/profiles/system`, falling back
/// 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.
///
/// The profile must already exist on the host. You should probably use deploy instead.
#[allow(unused_variables)]
async fn activate(&mut self, profile: &Profile, goal: Goal) -> NixResult<()> {
Err(NixError::Unsupported)
async fn activate(&mut self, profile: &Profile, goal: Goal) -> ColmenaResult<()> {
Err(ColmenaError::Unsupported)
}
/// Runs an arbitrary command on the host.
#[allow(unused_variables)]
async fn run_command(&mut self, command: &[&str]) -> NixResult<()> {
Err(NixError::Unsupported)
async fn run_command(&mut self, command: &[&str]) -> ColmenaResult<()> {
Err(ColmenaError::Unsupported)
}
}

View file

@ -6,10 +6,11 @@ use std::process::Stdio;
use async_trait::async_trait;
use tokio::process::Command;
use super::{CopyDirection, CopyOptions, Host, key_uploader};
use crate::nix::{StorePath, Profile, Goal, NixResult, NixError, Key, SYSTEM_PROFILE, CURRENT_PROFILE};
use crate::error::{ColmenaResult, ColmenaError};
use crate::nix::{StorePath, Profile, Goal, Key, SYSTEM_PROFILE, CURRENT_PROFILE};
use crate::util::{CommandExecution, CommandExt};
use crate::job::JobHandle;
use super::{CopyDirection, CopyOptions, Host, key_uploader};
/// A remote machine connected over SSH.
#[derive(Debug)]
@ -35,11 +36,11 @@ pub struct Ssh {
#[async_trait]
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);
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 mut execution = CommandExecution::new(command);
@ -51,16 +52,16 @@ impl Host for Ssh {
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 {
self.upload_key(name, key, require_ownership).await?;
}
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() {
return Err(NixError::Unsupported);
return Err(ColmenaError::Unsupported);
}
if goal.should_switch_profile() {
@ -74,11 +75,11 @@ impl Host for Ssh {
let command = self.ssh(&v);
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);
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 paths = self.ssh(&["sh", "-c", &command])
@ -86,7 +87,7 @@ impl Host for Ssh {
.await?;
let path = paths.lines().into_iter().next()
.ok_or(NixError::FailedToGetCurrentProfile)?
.ok_or(ColmenaError::FailedToGetCurrentProfile)?
.to_string()
.try_into()?;
@ -150,7 +151,7 @@ impl Ssh {
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);
execution.set_job(self.job.clone());
@ -216,7 +217,7 @@ impl Ssh {
}
/// 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 {
job.message(format!("Uploading key {}", name))?;
}

View file

@ -5,7 +5,7 @@ use log::Level;
use regex::Regex;
use tokio::process::Command;
use super::{NixError, NixResult};
use super::{ColmenaError, ColmenaResult};
struct NixVersion {
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;
if !check.flakes_supported() {
check.print_flakes_info(true);
Err(NixError::NoFlakesSupport)
Err(ColmenaError::NoFlakesSupport)
} else {
Ok(())
}

View file

@ -1,15 +1,14 @@
use std::collections::HashMap;
use std::hash::Hash;
use std::ops::Deref;
use std::os::unix::process::ExitStatusExt;
use std::path::Path;
use std::process::ExitStatus;
use serde::de;
use serde::{Deserialize, Deserializer, Serialize};
use snafu::Snafu;
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 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.
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.
#[derive(Serialize, Deserialize, Clone, Debug, Hash, Eq, PartialEq)]
#[serde(transparent)]
@ -183,7 +88,7 @@ impl NodeName {
}
/// 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)?;
Ok(Self(validated))
}
@ -199,10 +104,10 @@ impl NodeName {
})
}
fn validate(s: String) -> NixResult<String> {
fn validate(s: String) -> ColmenaResult<String> {
// FIXME: Elaborate
if s.is_empty() {
return Err(NixError::EmptyNodeName);
return Err(ColmenaError::EmptyNodeName);
}
Ok(s)

View file

@ -6,7 +6,7 @@ use std::iter::{Iterator, FromIterator};
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.
pub struct NodeFilter {
@ -27,7 +27,7 @@ enum Rule {
impl NodeFilter {
/// 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 trimmed = filter.trim();
@ -43,7 +43,7 @@ impl NodeFilter {
let pattern = pattern.trim();
if pattern.is_empty() {
return Err(NixError::EmptyFilterRule);
return Err(ColmenaError::EmptyFilterRule);
}
if let Some(tag_pattern) = pattern.strip_prefix('@') {
@ -51,7 +51,7 @@ impl NodeFilter {
} else {
Ok(Rule::MatchName(GlobPattern::new(pattern).unwrap()))
}
}).collect::<Vec<NixResult<Rule>>>();
}).collect::<Vec<ColmenaResult<Rule>>>();
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.
pub fn filter_node_names(&self, nodes: &[NodeName]) -> NixResult<HashSet<NodeName>> {
nodes.iter().filter_map(|name| -> Option<NixResult<NodeName>> {
pub fn filter_node_names(&self, nodes: &[NodeName]) -> ColmenaResult<HashSet<NodeName>> {
nodes.iter().filter_map(|name| -> Option<ColmenaResult<NodeName>> {
for rule in self.rules.iter() {
match rule {
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),
}));
}

View file

@ -6,8 +6,8 @@ use tokio::process::Command;
use super::{
Goal,
NixResult,
NixError,
ColmenaResult,
ColmenaError,
StorePath,
StoreDerivation,
BuildResult,
@ -20,16 +20,16 @@ pub type ProfileDerivation = StoreDerivation<Profile>;
pub struct Profile(StorePath);
impl Profile {
pub fn from_store_path(path: StorePath) -> NixResult<Self> {
pub fn from_store_path(path: StorePath) -> ColmenaResult<Self> {
if
!path.is_dir() ||
!path.join("bin/switch-to-configuration").exists()
{
return Err(NixError::InvalidProfile);
return Err(ColmenaError::InvalidProfile);
}
if path.to_str().is_none() {
Err(NixError::InvalidProfile)
Err(ColmenaError::InvalidProfile)
} else {
Ok(Self(path))
}
@ -63,7 +63,7 @@ impl 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");
command.args(&["--no-build-output", "--indirect", "--add-root", 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 {
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();
if paths.is_empty() {
return Err(NixError::BadOutput {
return Err(ColmenaError::BadOutput {
output: String::from("There is no store path"),
});
}
if paths.len() > 1 {
return Err(NixError::BadOutput {
return Err(ColmenaError::BadOutput {
output: String::from("Build resulted in more than 1 store path"),
});
}

View file

@ -7,8 +7,9 @@ use std::fmt;
use serde::{Serialize, Deserialize};
use tokio::process::Command;
use crate::error::{ColmenaError, ColmenaResult};
use crate::util::CommandExt;
use super::{Host, NixResult, NixError};
use super::Host;
/// A Nix store path.
#[derive(Debug, Clone, Serialize, Deserialize)]
@ -43,7 +44,7 @@ impl StorePath {
}
/// 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")
.args(&["--query", "--references"])
.arg(&self.0)
@ -55,11 +56,11 @@ impl StorePath {
}
/// 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() {
Ok(StoreDerivation::<T>::from_store_path_unchecked(self))
} 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 {
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/") {
Ok(Self(s.into()))
} 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.
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 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.
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 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] {
self.results.as_slice()
}

View file

@ -13,8 +13,8 @@ use tokio::sync::mpsc::{self,
UnboundedSender as TokioSender,
};
use crate::error::ColmenaResult;
use crate::job::JobId;
use crate::nix::NixResult;
pub use plain::PlainOutput;
pub use spinner::SpinnerOutput;
@ -33,7 +33,7 @@ pub enum SimpleProgressOutput {
#[async_trait]
pub trait ProgressOutput : Sized {
/// 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.
///
@ -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 {
Self::Plain(o) => {
o.run_until_completion().await

View file

@ -3,7 +3,7 @@
use async_trait::async_trait;
use console::Style as ConsoleStyle;
use crate::nix::NixResult;
use crate::error::ColmenaResult;
use super::{
DEFAULT_LABEL_WIDTH,
ProgressOutput,
@ -81,7 +81,7 @@ impl PlainOutput {
#[async_trait]
impl ProgressOutput for PlainOutput {
async fn run_until_completion(mut self) -> NixResult<Self> {
async fn run_until_completion(mut self) -> ColmenaResult<Self> {
loop {
let message = self.receiver.recv().await;

View file

@ -6,8 +6,8 @@ use std::time::Instant;
use async_trait::async_trait;
use indicatif::{MultiProgress, ProgressStyle, ProgressBar};
use crate::error::ColmenaResult;
use crate::job::JobId;
use crate::nix::NixResult;
use super::{
DEFAULT_LABEL_WIDTH,
ProgressOutput,
@ -171,7 +171,7 @@ impl SpinnerOutput {
#[async_trait]
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());
meta_bar.enable_steady_tick(100);

View file

@ -7,12 +7,12 @@ use std::future::Future;
use clap::ArgMatches;
use crate::nix::NixError;
use crate::error::ColmenaError;
/// 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
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 {
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> {
if let NixError::NoFlakesSupport = error {
fn troubleshoot(global_args: &ArgMatches, _local_args: &ArgMatches, error: &ColmenaError) -> Result<(), ColmenaError> {
if let ColmenaError::NoFlakesSupport = error {
// People following the tutorial might put hive.nix directly
// in their Colmena checkout, and encounter NoFlakesSupport
// because Colmena always prefers flake.nix when it exists.

View file

@ -9,7 +9,8 @@ use serde::de::DeserializeOwned;
use tokio::io::{AsyncRead, AsyncBufReadExt, BufReader};
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::job::JobHandle;
@ -26,16 +27,16 @@ pub struct CommandExecution {
#[async_trait]
pub trait CommandExt {
/// 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.
async fn capture_output(&mut self) -> NixResult<String>;
async fn capture_output(&mut self) -> ColmenaResult<String>;
/// 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.
async fn capture_store_path(&mut self) -> NixResult<StorePath>;
async fn capture_store_path(&mut self) -> ColmenaResult<StorePath>;
}
impl CommandExecution {
@ -65,7 +66,7 @@ impl CommandExecution {
}
/// Runs the command.
pub async fn run(&mut self) -> NixResult<()> {
pub async fn run(&mut self) -> ColmenaResult<()> {
self.command.stdin(Stdio::null());
self.command.stdout(Stdio::piped());
self.command.stderr(Stdio::piped());
@ -103,7 +104,7 @@ impl CommandExecution {
#[async_trait]
impl CommandExt for Command {
/// 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
.spawn()?
.wait()
@ -117,7 +118,7 @@ impl CommandExt for Command {
}
/// 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
let output = self
.stdout(Stdio::piped())
@ -135,15 +136,15 @@ impl CommandExt for Command {
}
/// 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?;
serde_json::from_str(&output).map_err(|_| NixError::BadOutput {
serde_json::from_str(&output).map_err(|_| ColmenaError::BadOutput {
output: output.clone()
})
}
/// 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 path = output.trim_end().to_owned();
StorePath::try_from(path)
@ -152,12 +153,12 @@ impl CommandExt for Command {
#[async_trait]
impl CommandExt for CommandExecution {
async fn passthrough(&mut self) -> NixResult<()> {
async fn passthrough(&mut self) -> ColmenaResult<()> {
self.run().await
}
/// Captures output as a String.
async fn capture_output(&mut self) -> NixResult<String> {
async fn capture_output(&mut self) -> ColmenaResult<String> {
self.run().await?;
let (stdout, _) = self.get_logs();
@ -165,22 +166,22 @@ impl CommandExt for CommandExecution {
}
/// 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?;
serde_json::from_str(&output).map_err(|_| NixError::BadOutput {
serde_json::from_str(&output).map_err(|_| ColmenaError::BadOutput {
output: output.clone()
})
}
/// 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 path = output.trim_end().to_owned();
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") {
0 => {
// 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
{
let mut log = String::new();