keyCommand: on error, do not upload key, report

This commit is contained in:
Justinas Stankevicius 2021-02-11 21:01:31 +02:00
parent e49e9367c0
commit 4c7f8eb838
5 changed files with 50 additions and 11 deletions

View file

@ -281,7 +281,7 @@ impl Deployment {
task.log("Uploading keys..."); task.log("Uploading keys...");
if let Err(e) = target.host.upload_keys(&target.config.keys).await { if let Err(e) = target.host.upload_keys(&target.config.keys).await {
task.failure(&format!("Failed to upload keys: {}", e)); task.failure_err(&e);
let mut results = arc_self.results.lock().await; let mut results = arc_self.results.lock().await;
let stage = Stage::Apply(node.to_string()); let stage = Stage::Apply(node.to_string());
@ -481,7 +481,7 @@ impl Deployment {
bar.log("Uploading keys..."); bar.log("Uploading keys...");
if let Err(e) = target.host.upload_keys(&target.config.keys).await { if let Err(e) = target.host.upload_keys(&target.config.keys).await {
bar.failure(&format!("Failed to upload keys: {}", e)); bar.failure_err(&e);
let mut results = self.results.lock().await; let mut results = self.results.lock().await;
let stage = Stage::Apply(name.to_string()); let stage = Stage::Apply(name.to_string());

View file

@ -211,10 +211,10 @@ impl Ssh {
command.stderr(Stdio::piped()); command.stderr(Stdio::piped());
command.stdout(Stdio::piped()); command.stdout(Stdio::piped());
let mut child = command.spawn()?;
let mut stdin = child.stdin.take().unwrap();
let mut reader = key.reader().await?; let mut reader = key.reader().await?;
let mut child = command.spawn()?;
let mut stdin = child.stdin.take().unwrap();
tokio::io::copy(reader.as_mut(), &mut stdin).await?; tokio::io::copy(reader.as_mut(), &mut stdin).await?;
stdin.flush().await?; stdin.flush().await?;
drop(stdin); drop(stdin);

View file

@ -2,11 +2,12 @@ use std::{
convert::TryFrom, convert::TryFrom,
io::{self, Cursor}, io::{self, Cursor},
path::{Path, PathBuf}, path::{Path, PathBuf},
process::Stdio, process::{ExitStatus, Stdio},
}; };
use regex::Regex; use regex::Regex;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use snafu::Snafu;
use tokio::{ use tokio::{
fs::File, fs::File,
io::AsyncRead, io::AsyncRead,
@ -14,6 +15,21 @@ use tokio::{
}; };
use validator::{Validate, ValidationError}; use validator::{Validate, ValidationError};
#[non_exhaustive]
#[derive(Debug, Snafu)]
pub enum KeyError {
#[snafu(display("I/O Error: {}", error))]
IoError { error: io::Error },
#[snafu(display("Key command failed: {}, stderr: {}", status, stderr))]
KeyCommandStatus { status: ExitStatus, stderr: String },
}
impl From<std::io::Error> for KeyError {
fn from(error: std::io::Error) -> Self {
Self::IoError { error }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(try_from = "KeySources")] #[serde(try_from = "KeySources")]
enum KeySource { enum KeySource {
@ -78,7 +94,7 @@ pub struct Key {
} }
impl Key { impl Key {
pub async fn reader(&'_ self,) -> Result<Box<dyn AsyncRead + Send + Unpin + '_>, io::Error> { pub async fn reader(&'_ self) -> Result<Box<dyn AsyncRead + Send + Unpin + '_>, KeyError> {
match &self.source { match &self.source {
KeySource::Text(content) => { KeySource::Text(content) => {
Ok(Box::new(Cursor::new(content))) Ok(Box::new(Cursor::new(content)))
@ -87,15 +103,25 @@ impl Key {
let pathname = &command[0]; let pathname = &command[0];
let argv = &command[1..]; let argv = &command[1..];
let stdout = Command::new(pathname) let output = Command::new(pathname)
.args(argv) .args(argv)
.stdin(Stdio::null()) .stdin(Stdio::null())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.stderr(Stdio::null()) .stderr(Stdio::piped())
.spawn()? .spawn()?
.stdout.take().unwrap(); .wait_with_output().await?;
Ok(Box::new(stdout)) if output.status.success() {
Ok(Box::new(Cursor::new(output.stdout)))
} else {
Err(KeyError::KeyCommandStatus {
status: output.status,
stderr: std::str::from_utf8(&output.stderr)
.unwrap_or_default()
.trim_end()
.into(),
})
}
} }
KeySource::File(path) => { KeySource::File(path) => {
Ok(Box::new(File::open(path).await?)) Ok(Box::new(File::open(path).await?))

View file

@ -59,6 +59,9 @@ pub enum NixError {
#[snafu(display("Validation error"))] #[snafu(display("Validation error"))]
ValidationError { errors: ValidationErrors }, ValidationError { errors: ValidationErrors },
#[snafu(display("Failed to upload keys: {}", error))]
KeyError { error: key::KeyError },
#[snafu(display("Invalid NixOS system profile"))] #[snafu(display("Invalid NixOS system profile"))]
InvalidProfile, InvalidProfile,
@ -72,6 +75,12 @@ impl From<std::io::Error> for NixError {
} }
} }
impl From<key::KeyError> for NixError {
fn from(error: key::KeyError) -> Self {
Self::KeyError { error }
}
}
impl From<ValidationErrors> for NixError { impl From<ValidationErrors> for NixError {
fn from(errors: ValidationErrors) -> Self { fn from(errors: ValidationErrors) -> Self {
Self::ValidationError { errors } Self::ValidationError { errors }

View file

@ -224,6 +224,10 @@ impl TaskProgress {
} }
} }
pub fn failure_err<E: std::error::Error>(self, error: &E) {
self.failure(&error.to_string())
}
fn plain_print(&self, style: Style, line: &str) { fn plain_print(&self, style: Style, line: &str) {
eprintln!("{:>width$} | {}", style.apply_to(&self.label), line, width = self.label_width); eprintln!("{:>width$} | {}", style.apply_to(&self.label), line, width = self.label_width);
} }