diff --git a/cli/src/main.rs b/cli/src/main.rs index 39140e1..70bc577 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -71,7 +71,13 @@ fn handle_plan(opts: PlanOpts, runner: &dyn crate::transport::Transport, invento info!("Plan located, preparing to execute"); for task in plan.tasks { info!("Running executable task: {:?}", task); - exit = execute_task_on(opts.targets.clone(), &task, runner, &inventory); + exit = execute_task_on( + opts.targets.clone(), + &task, + runner, + &inventory, + opts.dry_run, + ); } } Err(err) => { @@ -86,13 +92,14 @@ fn execute_task_on( task: &ExecutableTask, runner: &dyn crate::transport::Transport, inventory: &Inventory, + dry_run: bool, ) -> i32 { if let Some(group) = inventory.groups.iter().find(|g| g.name == targets) { - return runner.run_group(task, &group, &inventory); + return runner.run_group(task, &group, &inventory, dry_run); } if let Some(target) = inventory.targets.iter().find(|t| t.name == targets) { - return runner.run(task, &target); + return runner.run(task, &target, dry_run); } error!("Failed to locate a script to execute for the task!"); return -1; @@ -122,7 +129,13 @@ fn handle_task(opts: TaskOpts, runner: &dyn crate::transport::Transport, invento let task = ExecutableTask::new(task, parameters); - std::process::exit(execute_task_on(opts.targets, &task, runner, &inventory)); + std::process::exit(execute_task_on( + opts.targets, + &task, + runner, + &inventory, + opts.dry_run, + )); } Err(err) => { println!("Failed to load task: {:?}", err); @@ -143,7 +156,13 @@ fn handle_task(opts: TaskOpts, runner: &dyn crate::transport::Transport, invento fn handle_cmd(opts: CmdOpts, runner: &dyn crate::transport::Transport, inventory: Inventory) { let mut task = ExecutableTask::new(Task::new("Dynamic"), HashMap::new()); task.task.script.inline = Some(opts.command); - std::process::exit(execute_task_on(opts.targets, &task, runner, &inventory)); + std::process::exit(execute_task_on( + opts.targets, + &task, + runner, + &inventory, + false, + )); } #[derive(Debug, Options)] @@ -182,7 +201,7 @@ enum Command { #[options(help = "Execute a plan on a target(s)")] Plan(PlanOpts), #[options(help = "Check that the specified .ztask or .zplan file is valid")] - Check(CheckOpts) + Check(CheckOpts), } #[derive(Debug, Options)] @@ -206,6 +225,8 @@ struct TaskOpts { parameter: Vec, #[options(help = "Name of a target or group")] targets: String, + #[options(help = "Run the task in dry-run mode")] + dry_run: bool, } #[derive(Debug, Options)] @@ -214,6 +235,8 @@ struct PlanOpts { plan: PathBuf, #[options(help = "Name of a target or group")] targets: String, + #[options(help = "Run the task in dry-run mode")] + dry_run: bool, } #[derive(Debug, Options)] diff --git a/cli/src/transport/mod.rs b/cli/src/transport/mod.rs index f674c1b..1514bd0 100644 --- a/cli/src/transport/mod.rs +++ b/cli/src/transport/mod.rs @@ -8,6 +8,7 @@ pub mod ssh; * connecting to targets */ pub trait Transport { - fn run_group(&self, cmd: &ExecutableTask, group: &Group, inv: &Inventory) -> i32; - fn run(&self, command: &ExecutableTask, target: &Target) -> i32; + fn run_group(&self, cmd: &ExecutableTask, group: &Group, inv: &Inventory, dry_run: bool) + -> i32; + fn run(&self, command: &ExecutableTask, target: &Target, dry_run: bool) -> i32; } diff --git a/cli/src/transport/ssh.rs b/cli/src/transport/ssh.rs index 71d5d0f..411d1b3 100644 --- a/cli/src/transport/ssh.rs +++ b/cli/src/transport/ssh.rs @@ -1,5 +1,6 @@ use crate::inventory::{Group, Inventory, Target}; use crate::transport::Transport; +use colored::*; use log::*; use serde::{Deserialize, Serialize}; @@ -22,21 +23,27 @@ impl Default for Ssh { } impl Transport for Ssh { - fn run_group(&self, command: &ExecutableTask, group: &Group, inventory: &Inventory) -> i32 { + fn run_group( + &self, + command: &ExecutableTask, + group: &Group, + inventory: &Inventory, + dry_run: bool, + ) -> i32 { let mut status = 1; for target_name in group.targets.iter() { // XXX: This is inefficient for target in inventory.targets.iter() { if &target.name == target_name { println!("Running on `{}`", target.name); - status = self.run(command, &target); + status = self.run(command, &target, dry_run); } } } status } - fn run(&self, command: &ExecutableTask, target: &Target) -> i32 { + fn run(&self, command: &ExecutableTask, target: &Target, dry_run: bool) -> i32 { // Connect to the local SSH server let tcp = TcpStream::connect(format!("{}:22", target.uri)).unwrap(); let mut sess = Session::new().unwrap(); @@ -66,6 +73,7 @@ impl Transport for Ssh { "A `provides` parameter was given, checking to see if {} exists on the remote", provides ); + if let Err(error) = sess.scp_recv(&Path::new(&provides)) { if error.code() == ssh2::ErrorCode::Session(-28) { debug!( @@ -91,6 +99,15 @@ impl Transport for Ssh { } if let Some(script) = command.task.script.as_bytes(Some(&command.parameters)) { + if dry_run { + println!("{}", "Dry-run\n----".yellow()); + let mut out = std::io::stdout(); + out.write(&script) + .expect("Somehow failed to write to stdout"); + println!("{}", "\n----".yellow()); + return 0; + } + let mut remote_file = sess .scp_send( Path::new(remote_script),