key: Make the key source better typed

This commit is contained in:
Zhaofeng Li 2021-02-10 17:34:52 -08:00
parent 62753ea138
commit ce9f639a53
3 changed files with 46 additions and 22 deletions

View file

@ -107,7 +107,7 @@ impl Local {
async fn upload_key(&mut self, name: &str, key: &Key) -> NixResult<()> { async fn upload_key(&mut self, name: &str, key: &Key) -> NixResult<()> {
self.progress_bar.log(&format!("Deploying key {}", name)); self.progress_bar.log(&format!("Deploying key {}", name));
let dest_path = key.dest_dir.join(name); let dest_path = key.dest_dir().join(name);
let temp = NamedTempFile::new()?; let temp = NamedTempFile::new()?;
let (_, temp_path) = temp.keep().map_err(|pe| pe.error)?; let (_, temp_path) = temp.keep().map_err(|pe| pe.error)?;
@ -120,7 +120,7 @@ impl Local {
{ {
let mut command = Command::new("chmod"); let mut command = Command::new("chmod");
command command
.arg(&key.permissions) .arg(&key.permissions())
.arg(&temp_path); .arg(&temp_path);
let mut execution = CommandExecution::new(command); let mut execution = CommandExecution::new(command);
@ -135,7 +135,7 @@ impl Local {
{ {
let mut command = Command::new("chown"); let mut command = Command::new("chown");
command command
.arg(&format!("{}:{}", key.user, key.group)) .arg(&format!("{}:{}", key.user(), key.group()))
.arg(&temp_path); .arg(&temp_path);
let mut execution = CommandExecution::new(command); let mut execution = CommandExecution::new(command);

View file

@ -197,13 +197,13 @@ impl Ssh {
async fn upload_key(&mut self, name: &str, key: &Key) -> NixResult<()> { async fn upload_key(&mut self, name: &str, key: &Key) -> NixResult<()> {
self.progress_bar.log(&format!("Deploying key {}", name)); self.progress_bar.log(&format!("Deploying key {}", name));
let dest_path = key.dest_dir.join(name); let dest_path = key.dest_dir().join(name);
let remote_command = DEPLOY_KEY_TEMPLATE.to_string() let remote_command = DEPLOY_KEY_TEMPLATE.to_string()
.replace("%DESTINATION%", dest_path.to_str().unwrap()) .replace("%DESTINATION%", dest_path.to_str().unwrap())
.replace("%USER%", &key.user) .replace("%USER%", &key.user())
.replace("%GROUP%", &key.group) .replace("%GROUP%", &key.group())
.replace("%PERMISSIONS%", &key.permissions); .replace("%PERMISSIONS%", &key.permissions());
let mut command = self.ssh(&["sh", "-c", &remote_command]); let mut command = self.ssh(&["sh", "-c", &remote_command]);

View file

@ -1,6 +1,6 @@
use std::{ use std::{
io::{self, Cursor}, io::{self, Cursor},
path::PathBuf, path::{Path, PathBuf},
}; };
use regex::Regex; use regex::Regex;
@ -8,31 +8,55 @@ use serde::{Deserialize, Serialize};
use tokio::{fs::File, io::AsyncRead}; use tokio::{fs::File, io::AsyncRead};
use validator::{Validate, ValidationError}; use validator::{Validate, ValidationError};
#[derive(Debug, Clone, Serialize, Deserialize)]
enum KeySource {
#[serde(rename = "text")]
Text(String),
#[serde(rename = "keyCommand")]
Command(Vec<String>),
#[serde(rename = "keyFile")]
File(PathBuf),
}
#[derive(Debug, Clone, Validate, Serialize, Deserialize)] #[derive(Debug, Clone, Validate, Serialize, Deserialize)]
pub struct Key { pub struct Key {
pub(crate) text: Option<String>, #[serde(flatten)]
#[serde(rename = "keyFile")] source: KeySource,
pub(crate) key_file: Option<String>,
#[validate(custom = "validate_dest_dir")] #[validate(custom = "validate_dest_dir")]
#[serde(rename = "destDir")] #[serde(rename = "destDir")]
pub(super) dest_dir: PathBuf, dest_dir: PathBuf,
#[validate(custom = "validate_unix_name")] #[validate(custom = "validate_unix_name")]
pub(super) user: String, user: String,
#[validate(custom = "validate_unix_name")] #[validate(custom = "validate_unix_name")]
pub(super) group: String, group: String,
pub(super) permissions: String,
permissions: String,
} }
impl Key { impl Key {
pub(crate) async fn reader(&'_ self,) -> Result<Box<dyn AsyncRead + Send + Unpin + '_>, io::Error> { pub async fn reader(&'_ self,) -> Result<Box<dyn AsyncRead + Send + Unpin + '_>, io::Error> {
if let Some(ref t) = self.text { match &self.source {
Ok(Box::new(Cursor::new(t))) KeySource::Text(content) => {
} else if let Some(ref p) = self.key_file { Ok(Box::new(Cursor::new(content)))
Ok(Box::new(File::open(p).await?)) }
} else { KeySource::Command(_command) => {
unreachable!("Neither `text` nor `keyFile` set. This should have been validated by Nix assertions."); todo!("Implement keyCommand support")
}
KeySource::File(path) => {
Ok(Box::new(File::open(path).await?))
}
} }
} }
pub fn dest_dir(&self) -> &Path { &self.dest_dir }
pub fn user(&self) -> &str { &self.user }
pub fn group(&self) -> &str { &self.user }
pub fn permissions(&self) -> &str { &self.permissions }
} }
fn validate_unix_name(name: &str) -> Result<(), ValidationError> { fn validate_unix_name(name: &str) -> Result<(), ValidationError> {