forked from DGNum/colmena
Move nix::NixCommand to util::CommandExt
This commit is contained in:
parent
2aeab62880
commit
16ed9d8c66
6 changed files with 109 additions and 103 deletions
|
@ -19,8 +19,7 @@ use super::{
|
||||||
StorePath,
|
StorePath,
|
||||||
};
|
};
|
||||||
use super::deployment::TargetNode;
|
use super::deployment::TargetNode;
|
||||||
use super::NixCommand;
|
use crate::util::{CommandExecution, CommandExt};
|
||||||
use crate::util::CommandExecution;
|
|
||||||
use crate::job::JobHandle;
|
use crate::job::JobHandle;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -6,8 +6,8 @@ use async_trait::async_trait;
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
use super::{CopyDirection, CopyOptions, Host, key_uploader};
|
use super::{CopyDirection, CopyOptions, Host, key_uploader};
|
||||||
use crate::nix::{StorePath, Profile, Goal, NixError, NixResult, NixCommand, Key, SYSTEM_PROFILE, CURRENT_PROFILE};
|
use crate::nix::{StorePath, Profile, Goal, NixError, NixResult, Key, SYSTEM_PROFILE, CURRENT_PROFILE};
|
||||||
use crate::util::CommandExecution;
|
use crate::util::{CommandExecution, CommandExt};
|
||||||
use crate::job::JobHandle;
|
use crate::job::JobHandle;
|
||||||
|
|
||||||
/// The local machine running Colmena.
|
/// The local machine running Colmena.
|
||||||
|
|
|
@ -7,8 +7,8 @@ use async_trait::async_trait;
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
use super::{CopyDirection, CopyOptions, Host, key_uploader};
|
use super::{CopyDirection, CopyOptions, Host, key_uploader};
|
||||||
use crate::nix::{StorePath, Profile, Goal, NixResult, NixCommand, NixError, Key, SYSTEM_PROFILE, CURRENT_PROFILE};
|
use crate::nix::{StorePath, Profile, Goal, NixResult, NixError, Key, SYSTEM_PROFILE, CURRENT_PROFILE};
|
||||||
use crate::util::CommandExecution;
|
use crate::util::{CommandExecution, CommandExt};
|
||||||
use crate::job::JobHandle;
|
use crate::job::JobHandle;
|
||||||
|
|
||||||
/// A remote machine connected over SSH.
|
/// A remote machine connected over SSH.
|
||||||
|
|
|
@ -1,21 +1,16 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::convert::TryFrom;
|
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::os::unix::process::ExitStatusExt;
|
use std::os::unix::process::ExitStatusExt;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::process::{ExitStatus, Stdio};
|
use std::process::ExitStatus;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use serde::de;
|
||||||
use serde::de::{self, DeserializeOwned};
|
|
||||||
use serde::{Deserialize, Deserializer, Serialize};
|
use serde::{Deserialize, Deserializer, Serialize};
|
||||||
use snafu::Snafu;
|
use snafu::Snafu;
|
||||||
use tokio::process::Command;
|
|
||||||
use users::get_current_username;
|
use users::get_current_username;
|
||||||
use validator::{Validate, ValidationErrors, ValidationError as ValidationErrorType};
|
use validator::{Validate, ValidationErrors, ValidationError as ValidationErrorType};
|
||||||
|
|
||||||
use crate::util::CommandExecution;
|
|
||||||
|
|
||||||
pub mod host;
|
pub mod host;
|
||||||
pub use host::{Host, CopyDirection, CopyOptions};
|
pub use host::{Host, CopyDirection, CopyOptions};
|
||||||
use host::Ssh;
|
use host::Ssh;
|
||||||
|
@ -181,14 +176,6 @@ pub struct NodeConfig {
|
||||||
keys: HashMap<String, Key>,
|
keys: HashMap<String, Key>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
trait NixCommand {
|
|
||||||
async fn passthrough(&mut self) -> NixResult<()>;
|
|
||||||
async fn capture_output(&mut self) -> NixResult<String>;
|
|
||||||
async fn capture_json<T>(&mut self) -> NixResult<T> where T: DeserializeOwned;
|
|
||||||
async fn capture_store_path(&mut self) -> NixResult<StorePath>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NodeName {
|
impl NodeName {
|
||||||
/// Returns the string.
|
/// Returns the string.
|
||||||
pub fn as_str(&self) -> &str {
|
pub fn as_str(&self) -> &str {
|
||||||
|
@ -258,86 +245,6 @@ impl NodeConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl NixCommand for Command {
|
|
||||||
/// Runs the command with stdout and stderr passed through to the user.
|
|
||||||
async fn passthrough(&mut self) -> NixResult<()> {
|
|
||||||
let exit = self
|
|
||||||
.spawn()?
|
|
||||||
.wait()
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
if exit.success() {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(exit.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Captures output as a String.
|
|
||||||
async fn capture_output(&mut self) -> NixResult<String> {
|
|
||||||
// We want the user to see the raw errors
|
|
||||||
let output = self
|
|
||||||
.stdout(Stdio::piped())
|
|
||||||
.stderr(Stdio::inherit())
|
|
||||||
.spawn()?
|
|
||||||
.wait_with_output()
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
if output.status.success() {
|
|
||||||
// FIXME: unwrap
|
|
||||||
Ok(String::from_utf8(output.stdout).unwrap())
|
|
||||||
} else {
|
|
||||||
Err(output.status.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Captures deserialized output from JSON.
|
|
||||||
async fn capture_json<T>(&mut self) -> NixResult<T> where T: DeserializeOwned {
|
|
||||||
let output = self.capture_output().await?;
|
|
||||||
serde_json::from_str(&output).map_err(|_| NixError::BadOutput {
|
|
||||||
output: output.clone()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Captures a single store path.
|
|
||||||
async fn capture_store_path(&mut self) -> NixResult<StorePath> {
|
|
||||||
let output = self.capture_output().await?;
|
|
||||||
let path = output.trim_end().to_owned();
|
|
||||||
StorePath::try_from(path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl NixCommand for CommandExecution {
|
|
||||||
async fn passthrough(&mut self) -> NixResult<()> {
|
|
||||||
self.run().await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Captures output as a String.
|
|
||||||
async fn capture_output(&mut self) -> NixResult<String> {
|
|
||||||
self.run().await?;
|
|
||||||
let (stdout, _) = self.get_logs();
|
|
||||||
|
|
||||||
Ok(stdout.unwrap().to_owned())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Captures deserialized output from JSON.
|
|
||||||
async fn capture_json<T>(&mut self) -> NixResult<T> where T: DeserializeOwned {
|
|
||||||
let output = self.capture_output().await?;
|
|
||||||
serde_json::from_str(&output).map_err(|_| NixError::BadOutput {
|
|
||||||
output: output.clone()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Captures a single store path.
|
|
||||||
async fn capture_store_path(&mut self) -> NixResult<StorePath> {
|
|
||||||
let output = self.capture_output().await?;
|
|
||||||
let path = output.trim_end().to_owned();
|
|
||||||
StorePath::try_from(path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn validate_keys(keys: &HashMap<String, Key>) -> Result<(), ValidationErrorType> {
|
fn validate_keys(keys: &HashMap<String, Key>) -> Result<(), ValidationErrorType> {
|
||||||
// Bad secret names:
|
// Bad secret names:
|
||||||
// - /etc/passwd
|
// - /etc/passwd
|
||||||
|
|
|
@ -7,7 +7,8 @@ use std::fmt;
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
use super::{Host, NixCommand, NixResult, NixError};
|
use crate::util::CommandExt;
|
||||||
|
use super::{Host, NixResult, NixError};
|
||||||
|
|
||||||
/// A Nix store path.
|
/// A Nix store path.
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
|
101
src/util.rs
101
src/util.rs
|
@ -1,12 +1,15 @@
|
||||||
|
use std::convert::TryFrom;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
use clap::{App, Arg, ArgMatches};
|
use clap::{App, Arg, ArgMatches};
|
||||||
use futures::future::join3;
|
use futures::future::join3;
|
||||||
|
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};
|
use super::nix::{Flake, Hive, HivePath, NixResult, NixError, StorePath};
|
||||||
use super::nix::deployment::TargetNodeMap;
|
use super::nix::deployment::TargetNodeMap;
|
||||||
use super::job::JobHandle;
|
use super::job::JobHandle;
|
||||||
|
|
||||||
|
@ -19,6 +22,22 @@ pub struct CommandExecution {
|
||||||
stderr: Option<String>,
|
stderr: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Helper extensions for Commands.
|
||||||
|
#[async_trait]
|
||||||
|
pub trait CommandExt {
|
||||||
|
/// Runs the command with stdout and stderr passed through to the user.
|
||||||
|
async fn passthrough(&mut self) -> NixResult<()>;
|
||||||
|
|
||||||
|
/// Runs the command, capturing the output as a String.
|
||||||
|
async fn capture_output(&mut self) -> NixResult<String>;
|
||||||
|
|
||||||
|
/// Runs the command, capturing deserialized output from JSON.
|
||||||
|
async fn capture_json<T>(&mut self) -> NixResult<T> where T: DeserializeOwned;
|
||||||
|
|
||||||
|
/// Runs the command, capturing a single store path.
|
||||||
|
async fn capture_store_path(&mut self) -> NixResult<StorePath>;
|
||||||
|
}
|
||||||
|
|
||||||
impl CommandExecution {
|
impl CommandExecution {
|
||||||
pub fn new(command: Command) -> Self {
|
pub fn new(command: Command) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -81,6 +100,86 @@ 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<()> {
|
||||||
|
let exit = self
|
||||||
|
.spawn()?
|
||||||
|
.wait()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if exit.success() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(exit.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Captures output as a String.
|
||||||
|
async fn capture_output(&mut self) -> NixResult<String> {
|
||||||
|
// We want the user to see the raw errors
|
||||||
|
let output = self
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.stderr(Stdio::inherit())
|
||||||
|
.spawn()?
|
||||||
|
.wait_with_output()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if output.status.success() {
|
||||||
|
// FIXME: unwrap
|
||||||
|
Ok(String::from_utf8(output.stdout).unwrap())
|
||||||
|
} else {
|
||||||
|
Err(output.status.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Captures deserialized output from JSON.
|
||||||
|
async fn capture_json<T>(&mut self) -> NixResult<T> where T: DeserializeOwned {
|
||||||
|
let output = self.capture_output().await?;
|
||||||
|
serde_json::from_str(&output).map_err(|_| NixError::BadOutput {
|
||||||
|
output: output.clone()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Captures a single store path.
|
||||||
|
async fn capture_store_path(&mut self) -> NixResult<StorePath> {
|
||||||
|
let output = self.capture_output().await?;
|
||||||
|
let path = output.trim_end().to_owned();
|
||||||
|
StorePath::try_from(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl CommandExt for CommandExecution {
|
||||||
|
async fn passthrough(&mut self) -> NixResult<()> {
|
||||||
|
self.run().await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Captures output as a String.
|
||||||
|
async fn capture_output(&mut self) -> NixResult<String> {
|
||||||
|
self.run().await?;
|
||||||
|
let (stdout, _) = self.get_logs();
|
||||||
|
|
||||||
|
Ok(stdout.unwrap().to_owned())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Captures deserialized output from JSON.
|
||||||
|
async fn capture_json<T>(&mut self) -> NixResult<T> where T: DeserializeOwned {
|
||||||
|
let output = self.capture_output().await?;
|
||||||
|
serde_json::from_str(&output).map_err(|_| NixError::BadOutput {
|
||||||
|
output: output.clone()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Captures a single store path.
|
||||||
|
async fn capture_store_path(&mut self) -> NixResult<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) -> NixResult<Hive> {
|
||||||
let path = match args.occurrences_of("config") {
|
let path = match args.occurrences_of("config") {
|
||||||
0 => {
|
0 => {
|
||||||
|
|
Loading…
Add table
Reference in a new issue