Compare commits
5 Commits
a3d6980f71
...
35e186f828
Author | SHA1 | Date |
---|---|---|
R Tyler Croy | 35e186f828 | |
R Tyler Croy | e2a5f74c3a | |
R Tyler Croy | c6d62687b4 | |
R Tyler Croy | 4888aa9ecf | |
R Tyler Croy | 50ab1a5b14 |
|
@ -131,6 +131,16 @@ version = "0.1.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00"
|
||||
dependencies = [
|
||||
"matches",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.12.3"
|
||||
|
@ -192,6 +202,17 @@ dependencies = [
|
|||
"quick-error 1.2.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9"
|
||||
dependencies = [
|
||||
"matches",
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.7"
|
||||
|
@ -266,6 +287,12 @@ version = "1.0.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
|
||||
|
||||
[[package]]
|
||||
name = "matches"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.4"
|
||||
|
@ -315,6 +342,12 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||
|
||||
[[package]]
|
||||
name = "pest"
|
||||
version = "2.1.3"
|
||||
|
@ -515,9 +548,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.56"
|
||||
version = "1.0.57"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9802ddde94170d186eeee5005b798d9c159fa970403f1be19976d0cfb939b72"
|
||||
checksum = "4211ce9909eb971f111059df92c45640aad50a619cf55cd76476be803c4c68e6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -542,6 +575,21 @@ dependencies = [
|
|||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec_macros"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.12.0"
|
||||
|
@ -554,12 +602,42 @@ version = "0.1.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
|
||||
dependencies = [
|
||||
"matches",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606"
|
||||
dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"matches",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.11"
|
||||
|
@ -608,26 +686,30 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "zap-cli"
|
||||
version = "0.1.1"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"colored",
|
||||
"gumdrop",
|
||||
"log",
|
||||
"pretty_env_logger",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"ssh2",
|
||||
"zap-model",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zap-model"
|
||||
version = "0.1.1"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"colored",
|
||||
"handlebars",
|
||||
"log",
|
||||
"pest",
|
||||
"pest_derive",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"ssh2",
|
||||
"url",
|
||||
]
|
||||
|
|
|
@ -139,11 +139,11 @@ will be executed in the order that they are defined.
|
|||
.simple.zplan
|
||||
[source]
|
||||
----
|
||||
task 'tasks/echo.ztask' {
|
||||
task 'tasks/echo' {
|
||||
msg = 'Hello from the wonderful world of zplans!'
|
||||
}
|
||||
|
||||
task 'tasks/echo.ztask' {
|
||||
task 'tasks/echo' {
|
||||
msg = 'This is nice'
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "zap-cli"
|
||||
version = "0.1.1"
|
||||
version = "0.2.0"
|
||||
authors = ["R. Tyler Croy <rtyler@brokenco.de>"]
|
||||
edition = "2018"
|
||||
description = "A simple configuration management and orchestration tool"
|
||||
|
@ -13,17 +13,11 @@ keywords = ["sysadmin", "management"]
|
|||
name = "zap"
|
||||
path = "src/main.rs"
|
||||
|
||||
|
||||
[dependencies]
|
||||
colored = "2"
|
||||
gumdrop = "~0.8.0"
|
||||
log = "0.4"
|
||||
pretty_env_logger = "0.4"
|
||||
# Needed for deserializing JSON messages _and_ managing our configuration
|
||||
# effectively
|
||||
serde = { version = "~1.0", features = ["derive", "rc"] }
|
||||
serde_derive = "~1.0"
|
||||
serde_json = "~1.0"
|
||||
serde_yaml = "~0.8"
|
||||
ssh2 = "~0.9.0"
|
||||
zap-model = { version = "~0.1", path = "../model" }
|
||||
zap-model = { version = "~0.2", path = "../model" }
|
||||
|
|
|
@ -5,14 +5,10 @@ use std::collections::HashMap;
|
|||
use std::io::BufReader;
|
||||
use std::path::PathBuf;
|
||||
|
||||
mod inventory;
|
||||
mod transport;
|
||||
|
||||
use crate::inventory::*;
|
||||
use crate::transport::ssh::Ssh;
|
||||
use zap_model::plan::Plan;
|
||||
use zap_model::task::Task;
|
||||
use zap_model::inventory::Inventory;
|
||||
use zap_model::transport::ssh::Ssh;
|
||||
use zap_model::ExecutableTask;
|
||||
use zap_model::{Plan, Task, Transport};
|
||||
|
||||
fn main() {
|
||||
pretty_env_logger::init();
|
||||
|
@ -29,7 +25,7 @@ fn main() {
|
|||
let inventory: Inventory = serde_yaml::from_reader(reader).expect("Failed to read intenvory");
|
||||
|
||||
let mut runner = match &inventory.config.transport {
|
||||
crate::inventory::Transport::Ssh => Ssh::default(),
|
||||
zap_model::inventory::Transport::Ssh => Ssh::default(),
|
||||
};
|
||||
|
||||
match opts.command.unwrap() {
|
||||
|
@ -63,7 +59,7 @@ fn handle_check(opts: CheckOpts) {
|
|||
/**
|
||||
* This function will parse and execute a plan
|
||||
*/
|
||||
fn handle_plan(opts: PlanOpts, runner: &mut dyn crate::transport::Transport, inventory: Inventory) {
|
||||
fn handle_plan(opts: PlanOpts, runner: &mut dyn Transport, inventory: Inventory) {
|
||||
println!("{}", format!("Running plan with: {:?}", opts).green());
|
||||
let mut exit: i32 = -1;
|
||||
|
||||
|
@ -91,7 +87,7 @@ fn handle_plan(opts: PlanOpts, runner: &mut dyn crate::transport::Transport, inv
|
|||
fn execute_task_on(
|
||||
targets: String,
|
||||
task: &ExecutableTask,
|
||||
runner: &mut dyn crate::transport::Transport,
|
||||
runner: &mut dyn Transport,
|
||||
inventory: &Inventory,
|
||||
dry_run: bool,
|
||||
) -> i32 {
|
||||
|
@ -109,7 +105,7 @@ fn execute_task_on(
|
|||
/**
|
||||
* This function will handle a task
|
||||
*/
|
||||
fn handle_task(opts: TaskOpts, runner: &mut dyn crate::transport::Transport, inventory: Inventory) {
|
||||
fn handle_task(opts: TaskOpts, runner: &mut dyn Transport, inventory: Inventory) {
|
||||
println!("{}", format!("Running task with: {:?}", opts).green());
|
||||
|
||||
match Task::from_path(&opts.task) {
|
||||
|
@ -154,7 +150,7 @@ fn handle_task(opts: TaskOpts, runner: &mut dyn crate::transport::Transport, inv
|
|||
* In the case of multiple targets, any non-zero status code will be used to exit
|
||||
* non-zero.
|
||||
*/
|
||||
fn handle_cmd(opts: CmdOpts, runner: &mut dyn crate::transport::Transport, inventory: Inventory) {
|
||||
fn handle_cmd(opts: CmdOpts, runner: &mut dyn 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(
|
||||
|
|
|
@ -4,20 +4,16 @@
|
|||
* It is expected to be run from the root of the project tree.
|
||||
*/
|
||||
|
||||
task 'tasks/echo.ztask' {
|
||||
task 'tasks/echo' {
|
||||
msg = 'Hello from the wonderful world of zplans!'
|
||||
}
|
||||
|
||||
task 'tasks/echo.ztask' {
|
||||
task 'tasks/echo' {
|
||||
msg = 'This is nice'
|
||||
}
|
||||
|
||||
task 'tasks/shell/bash.ztask' {
|
||||
task 'zap://sh' {
|
||||
script = '''
|
||||
ls -lah
|
||||
touch foo
|
||||
pwd
|
||||
'''
|
||||
|
||||
// Don't run again if the foo file is present
|
||||
provides = 'foo'
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "zap-model"
|
||||
version = "0.1.1"
|
||||
version = "0.2.0"
|
||||
authors = ["R. Tyler Croy <rtyler@brokenco.de>"]
|
||||
edition = "2018"
|
||||
description = "Internal models for zap, a simple configuration management tool"
|
||||
|
@ -10,7 +10,16 @@ license = "AGPL-3.0+"
|
|||
keywords = ["sysadmin", "management"]
|
||||
|
||||
[dependencies]
|
||||
colored = "2"
|
||||
handlebars = "~3.5"
|
||||
log = "0.4"
|
||||
pest = "~2.1"
|
||||
pest_derive = "~2.1"
|
||||
# Needed for deserializing JSON messages _and_ managing our configuration
|
||||
# effectively
|
||||
serde = { version = "~1.0", features = ["derive", "rc"] }
|
||||
serde_derive = "~1.0"
|
||||
serde_json = "~1.0"
|
||||
serde_yaml = "~0.8"
|
||||
ssh2 = "~0.9.0"
|
||||
url = "~2.2"
|
||||
|
|
|
@ -4,9 +4,17 @@ extern crate pest;
|
|||
extern crate pest_derive;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub mod inventory;
|
||||
pub mod plan;
|
||||
pub mod task;
|
||||
pub mod tasks;
|
||||
pub mod transport;
|
||||
|
||||
pub use crate::plan::Plan;
|
||||
pub use crate::task::Task;
|
||||
pub use crate::transport::{Transport, TransportError};
|
||||
|
||||
/**
|
||||
* An ExecutableTask is a light container over a Task execpt with user-provided information and is
|
||||
|
@ -14,12 +22,24 @@ pub mod task;
|
|||
*/
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ExecutableTask {
|
||||
pub task: task::Task,
|
||||
pub task: Task,
|
||||
pub parameters: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl ExecutableTask {
|
||||
pub fn new(task: task::Task, parameters: HashMap<String, String>) -> Self {
|
||||
pub fn new(task: Task, parameters: HashMap<String, String>) -> Self {
|
||||
Self { task, parameters }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides will return the files that the ExecutableTask provides
|
||||
*
|
||||
* If these files exist, the task should not be executed
|
||||
*/
|
||||
pub fn provides() -> Vec<PathBuf> {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {}
|
||||
|
|
|
@ -6,7 +6,7 @@ use pest::Parser;
|
|||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::ExecutableTask;
|
||||
use crate::{ExecutableTask, Task};
|
||||
|
||||
#[derive(Parser)]
|
||||
#[grammar = "plan.pest"]
|
||||
|
@ -35,9 +35,16 @@ impl Plan {
|
|||
for pair in parsed.into_inner() {
|
||||
match pair.as_rule() {
|
||||
Rule::string => {
|
||||
let path = PathBuf::from(parse_str(&mut pair.into_inner())?);
|
||||
let name = parse_str(&mut pair.into_inner())?;
|
||||
let task = match name.starts_with("zap://") {
|
||||
true => Task::from_url(&name),
|
||||
false => {
|
||||
let path = PathBuf::from(format!("{}.ztask", name));
|
||||
Task::from_path(&path)
|
||||
}
|
||||
};
|
||||
|
||||
match crate::task::Task::from_path(&path) {
|
||||
match task {
|
||||
Ok(task) => raw_task = Some(task),
|
||||
Err(err) => {
|
||||
error!("Failed to parse task: {:?}", err);
|
||||
|
@ -164,11 +171,11 @@ mod tests {
|
|||
* It is expected to be run from the root of the project tree.
|
||||
*/
|
||||
|
||||
task '../tasks/echo.ztask' {
|
||||
task '../tasks/echo' {
|
||||
msg = 'Hello from the wonderful world of zplans!'
|
||||
}
|
||||
|
||||
task '../tasks/echo.ztask' {
|
||||
task '../tasks/echo' {
|
||||
msg = 'This can actually take inline shells too: $(date)'
|
||||
}"#;
|
||||
let _plan = PlanParser::parse(Rule::planfile, buf)
|
||||
|
@ -179,11 +186,11 @@ task '../tasks/echo.ztask' {
|
|||
|
||||
#[test]
|
||||
fn parse_plan_fn() {
|
||||
let buf = r#"task '../tasks/echo.ztask' {
|
||||
let buf = r#"task '../tasks/echo' {
|
||||
msg = 'Hello from the wonderful world of zplans!'
|
||||
}
|
||||
|
||||
task '../tasks/echo.ztask' {
|
||||
task '../tasks/echo' {
|
||||
msg = 'This can actually take inline shells too: $(date)'
|
||||
}"#;
|
||||
let plan = Plan::from_str(buf).expect("Failed to parse the plan");
|
|
@ -7,6 +7,7 @@ use std::collections::HashMap;
|
|||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::path::PathBuf;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[grammar = "task.pest"]
|
||||
|
@ -180,6 +181,28 @@ impl Task {
|
|||
))
|
||||
}
|
||||
|
||||
pub fn from_url(url: &str) -> Result<Self, PestError<Rule>> {
|
||||
if let Ok(url) = Url::parse(url) {
|
||||
println!("UR: {:?}", url);
|
||||
if let Some(name) = url.host_str() {
|
||||
// XXX: Temporary hard-coding see #5
|
||||
let mut task = Task::new(name);
|
||||
assert_eq!(name, "sh");
|
||||
// This is a hacky temporary workaround for now too
|
||||
// a real builtin shouldn't need to bother with a handlebars template
|
||||
task.script.inline = Some("#!/bin/sh\n{{script}}".into());
|
||||
return Ok(task);
|
||||
}
|
||||
}
|
||||
|
||||
Err(PestError::new_from_pos(
|
||||
ErrorVariant::CustomError {
|
||||
message: "Could not find a valid task definition".to_string(),
|
||||
},
|
||||
pest::Position::from_start(url),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn from_path(path: &PathBuf) -> Result<Self, PestError<Rule>> {
|
||||
match File::open(path) {
|
||||
Ok(mut file) => {
|
||||
|
@ -317,4 +340,10 @@ mod tests {
|
|||
let script = task.script;
|
||||
assert_eq!(script.as_bytes(None).unwrap(), "env".as_bytes());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn task_from_url() {
|
||||
let task = Task::from_url("zap://sh").expect("Failed to load task from URL");
|
||||
assert_eq!(task.name, "sh");
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -1,9 +1,14 @@
|
|||
use crate::inventory::{Group, Inventory, Target};
|
||||
use crate::ExecutableTask;
|
||||
|
||||
use std::path::Path;
|
||||
use zap_model::ExecutableTask;
|
||||
|
||||
pub mod ssh;
|
||||
|
||||
pub enum TransportError {
|
||||
GeneralError(String),
|
||||
}
|
||||
|
||||
/**
|
||||
* The Transport trait allows for multiple transports to be implemented for
|
||||
* connecting to targets
|
||||
|
@ -11,6 +16,9 @@ pub mod ssh;
|
|||
pub trait Transport {
|
||||
fn connect(&mut self, target: &Target) -> bool;
|
||||
fn disconnect(&mut self);
|
||||
fn file_exists(&self, path: &Path) -> Result<bool, TransportError>;
|
||||
fn run(&mut self, command: &ExecutableTask, target: &Target, dry_run: bool) -> i32;
|
||||
fn run_script(&mut self, script: &str) -> i32;
|
||||
fn run_group(
|
||||
&mut self,
|
||||
cmd: &ExecutableTask,
|
||||
|
@ -18,6 +26,5 @@ pub trait Transport {
|
|||
inv: &Inventory,
|
||||
dry_run: bool,
|
||||
) -> i32;
|
||||
fn run(&mut self, command: &ExecutableTask, target: &Target, dry_run: bool) -> i32;
|
||||
fn send_bytes(&self, remote_path: &Path, bytes: &Vec<u8>, mode: i32) -> bool;
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
use crate::inventory::{Group, Inventory, Target};
|
||||
use crate::transport::Transport;
|
||||
use crate::{ExecutableTask, TransportError};
|
||||
|
||||
use colored::*;
|
||||
|
||||
use log::*;
|
||||
|
@ -10,7 +12,7 @@ use std::io::BufReader;
|
|||
use std::net::TcpStream;
|
||||
use std::path::Path;
|
||||
|
||||
use zap_model::ExecutableTask;
|
||||
const REMOTE_SCRIPT: &str = "._zap_command";
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Ssh {
|
||||
|
@ -90,6 +92,45 @@ impl Transport for Ssh {
|
|||
true
|
||||
}
|
||||
|
||||
fn file_exists(&self, path: &Path) -> Result<bool, TransportError> {
|
||||
if let Err(error) = self.session.scp_recv(path) {
|
||||
if error.code() == ssh2::ErrorCode::Session(-28) {
|
||||
debug!("The file ({}) does not exist", path.display());
|
||||
} else {
|
||||
error!(
|
||||
"A failure occurred while trying to check a file exists: {:?}",
|
||||
error
|
||||
);
|
||||
return Err(TransportError::GeneralError(
|
||||
"Failed to check that file exists".into(),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
// If we successfully fetched the provided file, then we should
|
||||
// return 0 and skip the function
|
||||
trace!("The file exists: {}", path.display());
|
||||
return Ok(true);
|
||||
}
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* run_script will copy the given string over and execute it
|
||||
*/
|
||||
fn run_script(&mut self, script: &str) -> i32 {
|
||||
if self.send_bytes(Path::new(REMOTE_SCRIPT), &script.as_bytes().to_vec(), 0o700) {
|
||||
let mut channel = self.session.channel_session().unwrap();
|
||||
channel.exec(&format!("./{}", REMOTE_SCRIPT));
|
||||
|
||||
let mut s = String::new();
|
||||
channel.read_to_string(&mut s).unwrap();
|
||||
print!("{}", s);
|
||||
channel.wait_close().expect("Failed to close the channel");
|
||||
return channel.exit_status().unwrap();
|
||||
}
|
||||
return -10;
|
||||
}
|
||||
|
||||
fn run(&mut self, command: &ExecutableTask, target: &Target, dry_run: bool) -> i32 {
|
||||
if !self.connect(target) {
|
||||
error!("Failed to connect to {:?}", target);
|
||||
|
@ -102,47 +143,21 @@ impl Transport for Ssh {
|
|||
provides
|
||||
);
|
||||
|
||||
if let Err(error) = self.session.scp_recv(&Path::new(&provides)) {
|
||||
if error.code() == ssh2::ErrorCode::Session(-28) {
|
||||
debug!(
|
||||
"The provided file ({}) does not exist, the command should be run",
|
||||
provides
|
||||
);
|
||||
} else {
|
||||
error!(
|
||||
"A failure occurred while trying to check the provided file: {:?}",
|
||||
error
|
||||
);
|
||||
return -1;
|
||||
if let Ok(found) = self.file_exists(Path::new(provides)) {
|
||||
if found {
|
||||
debug!("File {} exists, skipping task", provides);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
// If we successfully fetched the provided file, then we should
|
||||
// return 0 and skip the function
|
||||
debug!(
|
||||
"The provided file ({}) was found, avoiding re-running",
|
||||
provides
|
||||
);
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
let remote_script = "._zap_command";
|
||||
let args_file = "._zap_args.json";
|
||||
|
||||
if let Some(unless) = &command.parameters.get("unless") {
|
||||
debug!("An `unless` parameter was given, running {}", unless);
|
||||
if self.send_bytes(Path::new(remote_script), &unless.as_bytes().to_vec(), 0o700) {
|
||||
let mut channel = self.session.channel_session().unwrap();
|
||||
channel.exec(&format!("./{}", remote_script));
|
||||
let mut s = String::new();
|
||||
channel.read_to_string(&mut s).unwrap();
|
||||
print!("{}", s);
|
||||
channel.wait_close().expect("Failed to close the channel");
|
||||
let exit = channel.exit_status().unwrap();
|
||||
if exit == 0 {
|
||||
debug!("Unless script returned success, so bailing out early");
|
||||
return 0;
|
||||
}
|
||||
if 0 == self.run_script(unless) {
|
||||
debug!("`unless` script returned 0, skipping the task");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,27 +171,28 @@ impl Transport for Ssh {
|
|||
return 0;
|
||||
}
|
||||
|
||||
if !self.send_bytes(Path::new(remote_script), &script, 0o700) {
|
||||
if !self.send_bytes(Path::new(REMOTE_SCRIPT), &script, 0o700) {
|
||||
error!("Failed to upload script file for execution");
|
||||
return -1;
|
||||
}
|
||||
|
||||
let mut channel = self.session.channel_session().unwrap();
|
||||
let stderr = channel.stderr();
|
||||
let args_file = "._zap_args.json";
|
||||
|
||||
if command.task.script.has_file() {
|
||||
let args = serde_json::to_string(&command.parameters)
|
||||
.expect("Failed to serialize parameters for task");
|
||||
if self.send_bytes(Path::new(args_file), &args.into_bytes(), 0o400) {
|
||||
channel
|
||||
.exec(&format!("./{} {}", remote_script, args_file))
|
||||
.exec(&format!("./{} {}", REMOTE_SCRIPT, args_file))
|
||||
.unwrap();
|
||||
} else {
|
||||
error!("Failed to upload the arguments file");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
channel.exec(&format!("./{}", remote_script)).unwrap();
|
||||
channel.exec(&format!("./{}", REMOTE_SCRIPT)).unwrap();
|
||||
}
|
||||
|
||||
let reader = BufReader::new(stderr);
|
||||
|
@ -197,7 +213,7 @@ impl Transport for Ssh {
|
|||
*/
|
||||
let mut channel = self.session.channel_session().unwrap();
|
||||
channel
|
||||
.exec(&format!("rm -f {} {}", remote_script, args_file))
|
||||
.exec(&format!("rm -f {} {}", REMOTE_SCRIPT, args_file))
|
||||
.unwrap();
|
||||
return exit;
|
||||
} else {
|
|
@ -1,19 +0,0 @@
|
|||
/*
|
||||
* The sh task is a simple passthrough to /bin/sh on the target machine
|
||||
*/
|
||||
|
||||
task Sh {
|
||||
parameters {
|
||||
script {
|
||||
required = true
|
||||
help = 'A script to run via the /bin/sh'
|
||||
type = string
|
||||
}
|
||||
}
|
||||
|
||||
script {
|
||||
inline = '''#!/bin/sh
|
||||
{{script}}
|
||||
'''
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue