Compare commits
5 Commits
97ae0d52a6
...
ff3f3c5263
Author | SHA1 | Date |
---|---|---|
R Tyler Croy | ff3f3c5263 | |
R Tyler Croy | 754fd428f8 | |
R Tyler Croy | 7b9066d096 | |
R Tyler Croy | b00e9835e8 | |
R Tyler Croy | 904bf007ce |
|
@ -1,5 +1,25 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
|
@ -12,6 +32,39 @@ version = "1.2.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
|
||||
dependencies = [
|
||||
"block-padding",
|
||||
"byte-tools",
|
||||
"byteorder",
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-padding"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
|
||||
dependencies = [
|
||||
"byte-tools",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "byte-tools"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.66"
|
||||
|
@ -24,18 +77,6 @@ version = "0.1.10"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "cli"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"gumdrop",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"ssh2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cloudabi"
|
||||
version = "0.0.3"
|
||||
|
@ -45,12 +86,66 @@ dependencies = [
|
|||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"lazy_static",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dtoa"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88d7ed2934d741c6b37e33e3832298e8850b53fd2d2bea03873375596c7cea4e"
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"humantime",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fake-simd"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||
|
||||
[[package]]
|
||||
name = "gumdrop"
|
||||
version = "0.8.0"
|
||||
|
@ -71,12 +166,50 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "handlebars"
|
||||
version = "3.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "964d0e99a61fe9b1b347389b77ebf8b7e1587b70293676aaca7d27e59b9073b2"
|
||||
dependencies = [
|
||||
"log",
|
||||
"pest",
|
||||
"pest_derive",
|
||||
"quick-error 2.0.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
|
||||
dependencies = [
|
||||
"quick-error 1.2.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.81"
|
||||
|
@ -124,6 +257,33 @@ dependencies = [
|
|||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "maplit"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.60"
|
||||
|
@ -161,12 +321,65 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pest"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
|
||||
dependencies = [
|
||||
"ucd-trie",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pest_derive"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_generator",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pest_generator"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_meta",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pest_meta"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d"
|
||||
dependencies = [
|
||||
"maplit",
|
||||
"pest",
|
||||
"sha-1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
|
||||
|
||||
[[package]]
|
||||
name = "pretty_env_logger"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d"
|
||||
dependencies = [
|
||||
"env_logger",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.24"
|
||||
|
@ -176,6 +389,18 @@ dependencies = [
|
|||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "1.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ac73b1112776fc109b2e61909bc46c7e1bf0d7f690ffb1676553acce16d5cda"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.8"
|
||||
|
@ -191,6 +416,24 @@ version = "0.1.57"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
"thread_local",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.5"
|
||||
|
@ -246,6 +489,18 @@ dependencies = [
|
|||
"yaml-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha-1"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"digest",
|
||||
"fake-simd",
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.5.1"
|
||||
|
@ -275,6 +530,36 @@ dependencies = [
|
|||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
|
||||
|
||||
[[package]]
|
||||
name = "ucd-trie"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.1"
|
||||
|
@ -303,6 +588,15 @@ version = "0.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
|
@ -317,3 +611,30 @@ checksum = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d"
|
|||
dependencies = [
|
||||
"linked-hash-map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zap"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"colored",
|
||||
"glob",
|
||||
"gumdrop",
|
||||
"handlebars",
|
||||
"log",
|
||||
"pretty_env_logger",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"ssh2",
|
||||
"zap-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zap-parser"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"pest",
|
||||
"pest_derive",
|
||||
]
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[workspace]
|
||||
members = [
|
||||
'cli',
|
||||
'parser',
|
||||
]
|
||||
|
|
|
@ -64,10 +64,12 @@ task Install {
|
|||
required = true
|
||||
}
|
||||
|
||||
// Unless should be implied on every task
|
||||
unless {
|
||||
type = string
|
||||
help = "Script which when returns zero if the package has been installed, i.e. `test -f /usr/bin/nginx`"
|
||||
}
|
||||
// provides should be implied on every task
|
||||
}
|
||||
|
||||
// Parameters exposed as environment variables
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
[package]
|
||||
name = "cli"
|
||||
name = "zap"
|
||||
version = "0.1.0"
|
||||
authors = ["R. Tyler Croy <rtyler@brokenco.de>"]
|
||||
edition = "2018"
|
||||
|
||||
[[bin]]
|
||||
name = "zap"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
colored = "2"
|
||||
glob = "0.3"
|
||||
gumdrop = "~0.8.0"
|
||||
handlebars = "~3.5"
|
||||
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"] }
|
||||
|
@ -17,3 +18,4 @@ serde_derive = "~1.0"
|
|||
serde_json = "~1.0"
|
||||
serde_yaml = "~0.8"
|
||||
ssh2 = "~0.9.0"
|
||||
zap-parser = { path = "../parser" }
|
||||
|
|
|
@ -17,11 +17,23 @@ pub struct Group {
|
|||
pub struct Target {
|
||||
pub name: String,
|
||||
pub uri: String,
|
||||
pub config: Option<Config>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Config {
|
||||
#[serde(default = "default_transport")]
|
||||
pub transport: Transport,
|
||||
pub ssh: Option<SshConfig>,
|
||||
}
|
||||
fn default_transport() -> Transport {
|
||||
Transport::Ssh
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct SshConfig {
|
||||
pub user: String,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
|
|
166
cli/src/main.rs
166
cli/src/main.rs
|
@ -1,4 +1,7 @@
|
|||
use colored::*;
|
||||
use gumdrop::Options;
|
||||
use log::*;
|
||||
use std::collections::HashMap;
|
||||
use std::io::BufReader;
|
||||
|
||||
mod inventory;
|
||||
|
@ -6,9 +9,10 @@ mod transport;
|
|||
|
||||
use crate::inventory::*;
|
||||
use crate::transport::ssh::Ssh;
|
||||
use crate::transport::Transport;
|
||||
use zap_parser::*;
|
||||
|
||||
fn main() {
|
||||
pretty_env_logger::init();
|
||||
let opts = MyOptions::parse_args_default_or_exit();
|
||||
|
||||
if opts.command.is_none() {
|
||||
|
@ -26,23 +30,115 @@ fn main() {
|
|||
};
|
||||
|
||||
match opts.command.unwrap() {
|
||||
Command::Cmd(runopts) => {
|
||||
println!("run a command: {:?}", runopts);
|
||||
if let Some(group) = inventory.groups.iter().find(|g| g.name == runopts.targets) {
|
||||
std::process::exit(runner.run_group(&runopts.command, &group, &inventory));
|
||||
}
|
||||
|
||||
if let Some(target) = inventory.targets.iter().find(|t| t.name == runopts.targets) {
|
||||
println!("run a command: {:?}", runopts);
|
||||
std::process::exit(runner.run(&runopts.command, &target));
|
||||
}
|
||||
|
||||
println!("Couldn't find a target named `{}`", runopts.targets);
|
||||
}
|
||||
Command::Cmd(opts) => handle_cmd(opts, &runner, inventory),
|
||||
Command::Task(opts) => handle_task(opts, &runner, inventory),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn load_ztasks() -> Vec<Task> {
|
||||
use glob::glob;
|
||||
let mut tasks = vec![];
|
||||
|
||||
for entry in glob("tasks/**/*.ztask").expect("Failed to read glob pattern") {
|
||||
match entry {
|
||||
Ok(path) => {
|
||||
if let Ok(task) = Task::from_path(&path) {
|
||||
info!("loaded ztask: {}", task.name);
|
||||
tasks.push(task);
|
||||
}
|
||||
}
|
||||
Err(e) => println!("{:?}", e),
|
||||
}
|
||||
}
|
||||
tasks
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will handle a task
|
||||
*/
|
||||
fn handle_task(opts: TaskOpts, runner: &dyn crate::transport::Transport, inventory: Inventory) {
|
||||
println!("running task: {:?}", opts);
|
||||
|
||||
for task in load_ztasks() {
|
||||
if task.name == opts.task {
|
||||
let mut parameters = HashMap::new();
|
||||
|
||||
/*
|
||||
* XXX: This is very primitive way, there must be a better way to take
|
||||
* arbitrary command line parameters than this.
|
||||
*/
|
||||
for parameter in opts.parameter.iter() {
|
||||
let parts: Vec<&str> = parameter.split("=").collect();
|
||||
if parts.len() == 2 {
|
||||
parameters.insert(parts[0].to_string(), parts[1].to_string());
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(script) = task.get_script() {
|
||||
let command = render_command(&script, ¶meters);
|
||||
|
||||
// TODO: refactor with handle_cmd
|
||||
if let Some(group) = inventory.groups.iter().find(|g| g.name == opts.targets) {
|
||||
std::process::exit(runner.run_group(&command, &group, &inventory));
|
||||
}
|
||||
|
||||
if let Some(target) = inventory.targets.iter().find(|t| t.name == opts.targets) {
|
||||
std::process::exit(runner.run(&command, &target));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will handle executing a single specified command on the target(s)
|
||||
* identified in the `opts`.
|
||||
*
|
||||
* In the case of a single target, the status code from the executed command will
|
||||
* be propogated up.
|
||||
*
|
||||
* In the case of multiple targets, any non-zero status code will be used to exit
|
||||
* non-zero.
|
||||
*/
|
||||
fn handle_cmd(opts: CmdOpts, runner: &dyn crate::transport::Transport, inventory: Inventory) {
|
||||
if let Some(group) = inventory.groups.iter().find(|g| g.name == opts.targets) {
|
||||
std::process::exit(runner.run_group(&opts.command, &group, &inventory));
|
||||
}
|
||||
|
||||
if let Some(target) = inventory.targets.iter().find(|t| t.name == opts.targets) {
|
||||
println!("{}", format!("run a command: {:?}", opts).green());
|
||||
std::process::exit(runner.run(&opts.command, &target));
|
||||
}
|
||||
|
||||
println!(
|
||||
"{}",
|
||||
format!("Couldn't find a target named `{}`", opts.targets).red()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* render_command will handle injecting the parameters for a given command
|
||||
* into the string where appropriate, using the Handlebars syntax.
|
||||
*
|
||||
* If the template fails to render, then this will just return the command it
|
||||
* was given
|
||||
*/
|
||||
fn render_command(cmd: &str, parameters: &HashMap<String, String>) -> String {
|
||||
use handlebars::Handlebars;
|
||||
|
||||
let handlebars = Handlebars::new();
|
||||
match handlebars.render_template(cmd, parameters) {
|
||||
Ok(rendered) => {
|
||||
return rendered;
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Failed to render command ({:?}): {}", err, cmd);
|
||||
return cmd.to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Options)]
|
||||
struct MyOptions {
|
||||
// Options here can be accepted with any command (or none at all),
|
||||
|
@ -73,7 +169,9 @@ enum Command {
|
|||
#[options(help = "show help for a command")]
|
||||
Help(HelpOpts),
|
||||
#[options(help = "Run a single command on a target(s)")]
|
||||
Cmd(RunOpts),
|
||||
Cmd(CmdOpts),
|
||||
#[options(help = "Execute a task on a target(s)")]
|
||||
Task(TaskOpts),
|
||||
}
|
||||
|
||||
#[derive(Debug, Options)]
|
||||
|
@ -81,12 +179,44 @@ struct HelpOpts {
|
|||
#[options(free)]
|
||||
free: Vec<String>,
|
||||
}
|
||||
|
||||
// Options accepted for the `make` command
|
||||
#[derive(Debug, Options)]
|
||||
struct RunOpts {
|
||||
struct CmdOpts {
|
||||
#[options(free, help = "Command to execute on the target(s)")]
|
||||
command: String,
|
||||
#[options(help = "Name of a target or group")]
|
||||
targets: String,
|
||||
}
|
||||
#[derive(Debug, Options)]
|
||||
struct TaskOpts {
|
||||
#[options(free, help = "Task to execute, must exist in ZAP_PATH")]
|
||||
task: String,
|
||||
#[options(short = "p", help = "Parameter values")]
|
||||
parameter: Vec<String>,
|
||||
#[options(help = "Name of a target or group")]
|
||||
targets: String,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_render_command() {
|
||||
let cmd = "echo \"{{msg}}\"";
|
||||
let mut params = HashMap::new();
|
||||
params.insert("msg".to_string(), "hello".to_string());
|
||||
|
||||
let output = render_command(&cmd, ¶ms);
|
||||
assert_eq!(output, "echo \"hello\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_render_command_bad_template() {
|
||||
let cmd = "echo \"{{msg\"";
|
||||
let mut params = HashMap::new();
|
||||
params.insert("msg".to_string(), "hello".to_string());
|
||||
|
||||
let output = render_command(&cmd, ¶ms);
|
||||
assert_eq!(output, "echo \"{{msg\"");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,11 +35,25 @@ impl Transport for Ssh {
|
|||
let mut sess = Session::new().unwrap();
|
||||
sess.set_tcp_stream(tcp);
|
||||
sess.handshake().unwrap();
|
||||
sess.userauth_agent(&std::env::var("USER").unwrap())
|
||||
.unwrap();
|
||||
|
||||
let mut authenticated = false;
|
||||
|
||||
if let Some(config) = &target.config {
|
||||
if let Some(sshconfig) = &config.ssh {
|
||||
// requires PasswordAuthentication yes
|
||||
sess.userauth_password(&sshconfig.user, &sshconfig.password)
|
||||
.unwrap();
|
||||
authenticated = true;
|
||||
}
|
||||
}
|
||||
if !authenticated {
|
||||
sess.userauth_agent(&std::env::var("USER").unwrap())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let mut channel = sess.channel_session().unwrap();
|
||||
channel.exec(command).unwrap();
|
||||
|
||||
let mut s = String::new();
|
||||
channel.read_to_string(&mut s).unwrap();
|
||||
print!("{}", s);
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
FROM ubuntu:20.04
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y openssh-server libssl-dev
|
||||
RUN mkdir /var/run/sshd
|
||||
|
||||
RUN echo 'root:root' |chpasswd
|
||||
|
||||
RUN sed -ri 's/^#?PermitRootLogin\s+.*/PermitRootLogin yes/' /etc/ssh/sshd_config
|
||||
RUN sed -ri 's/UsePAM yes/#UsePAM yes/g' /etc/ssh/sshd_config
|
||||
|
||||
RUN mkdir /root/.ssh
|
||||
|
||||
RUN apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||
|
||||
EXPOSE 22
|
||||
EXPOSE 80
|
||||
|
||||
CMD ["/usr/sbin/sshd", "-D"]
|
|
@ -0,0 +1,4 @@
|
|||
= Managing simple web containers
|
||||
|
||||
This is an example from the link:https://puppet.com/docs/bolt/latest/getting_started_with_bolt.html[Bolt documentation]
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
version: '3'
|
||||
services:
|
||||
target1:
|
||||
build: .
|
||||
ports:
|
||||
- '3000:80'
|
||||
- '2000:22'
|
||||
container_name: target1
|
||||
target2:
|
||||
build: .
|
||||
ports:
|
||||
- '3001:80'
|
||||
- '2001:22'
|
||||
container_name: target2
|
|
@ -9,6 +9,12 @@ targets:
|
|||
uri: 192.168.1.41
|
||||
- name: gopher
|
||||
uri: 192.168.1.41
|
||||
- name: zap-freebsd
|
||||
uri: 192.168.1.224
|
||||
config:
|
||||
ssh:
|
||||
user: root
|
||||
password: root
|
||||
|
||||
config:
|
||||
transport: ssh
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "zap-parser"
|
||||
version = "0.1.0"
|
||||
authors = ["R. Tyler Croy <rtyler@brokenco.de>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
pest = "~2.1"
|
||||
pest_derive = "~2.1"
|
|
@ -0,0 +1,185 @@
|
|||
#[macro_use]
|
||||
extern crate pest;
|
||||
#[macro_use]
|
||||
extern crate pest_derive;
|
||||
|
||||
use pest::error::Error as PestError;
|
||||
use pest::error::ErrorVariant;
|
||||
use pest::iterators::Pairs;
|
||||
use pest::Parser;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[grammar = "task.pest"]
|
||||
struct TaskParser;
|
||||
|
||||
pub struct Task {
|
||||
pub name: String,
|
||||
inline: Option<String>,
|
||||
}
|
||||
|
||||
impl Task {
|
||||
pub fn get_script(&self) -> Option<&String> {
|
||||
self.inline.as_ref()
|
||||
}
|
||||
|
||||
pub fn new(name: &str) -> Self {
|
||||
Task {
|
||||
name: name.to_string(),
|
||||
inline: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse(parser: &mut Pairs<Rule>) -> Result<Self, PestError<Rule>> {
|
||||
let mut task: Option<Self> = None;
|
||||
|
||||
while let Some(parsed) = parser.next() {
|
||||
match parsed.as_rule() {
|
||||
Rule::identifier => {
|
||||
task = Some(Task::new(parsed.as_str()));
|
||||
}
|
||||
Rule::script => {
|
||||
let script = parse_str(&mut parsed.into_inner())?;
|
||||
|
||||
if let Some(ref mut task) = task {
|
||||
task.inline = Some(script);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(task) = task {
|
||||
return Ok(task);
|
||||
} else {
|
||||
return Err(PestError::new_from_pos(
|
||||
ErrorVariant::CustomError {
|
||||
message: "Could not find a valid task definition".to_string(),
|
||||
},
|
||||
/* TODO: Find a better thing to report */
|
||||
pest::Position::from_start(""),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_str(buf: &str) -> Result<Self, PestError<Rule>> {
|
||||
let mut parser = TaskParser::parse(Rule::task, buf)?;
|
||||
while let Some(parsed) = parser.next() {
|
||||
match parsed.as_rule() {
|
||||
Rule::task => {
|
||||
return Task::parse(&mut parsed.into_inner());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
return Err(PestError::new_from_pos(
|
||||
ErrorVariant::CustomError {
|
||||
message: "Could not find a valid task definition".to_string(),
|
||||
},
|
||||
pest::Position::from_start(buf),
|
||||
));
|
||||
}
|
||||
|
||||
pub fn from_path(path: &PathBuf) -> Result<Self, PestError<Rule>> {
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
|
||||
match File::open(path) {
|
||||
Ok(mut file) => {
|
||||
let mut contents = String::new();
|
||||
|
||||
if let Err(e) = file.read_to_string(&mut contents) {
|
||||
return Err(PestError::new_from_pos(
|
||||
ErrorVariant::CustomError {
|
||||
message: format!("{}", e),
|
||||
},
|
||||
pest::Position::from_start(""),
|
||||
));
|
||||
} else {
|
||||
return Self::from_str(&contents);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(PestError::new_from_pos(
|
||||
ErrorVariant::CustomError {
|
||||
message: format!("{}", e),
|
||||
},
|
||||
pest::Position::from_start(""),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parser utility function to fish out the _actual_ string value for something
|
||||
* that is looking like a string Rule
|
||||
*
|
||||
*/
|
||||
fn parse_str(parser: &mut Pairs<Rule>) -> Result<String, PestError<Rule>> {
|
||||
while let Some(parsed) = parser.next() {
|
||||
match parsed.as_rule() {
|
||||
Rule::string => {
|
||||
return parse_str(&mut parsed.into_inner());
|
||||
}
|
||||
Rule::double_quoted => {
|
||||
return parse_str(&mut parsed.into_inner());
|
||||
}
|
||||
Rule::inner_double_str => {
|
||||
return Ok(parsed.as_str().to_string());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
return Err(PestError::new_from_pos(
|
||||
ErrorVariant::CustomError {
|
||||
message: "Could not parse out a string value".to_string(),
|
||||
},
|
||||
/* TODO: Find a better thing to report */
|
||||
pest::Position::from_start(""),
|
||||
));
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parse_simple_script_task() {
|
||||
let buf = r#"task Install {
|
||||
parameters {
|
||||
package {
|
||||
required = true
|
||||
help = "Name of package to be installed"
|
||||
type = string
|
||||
}
|
||||
}
|
||||
script {
|
||||
inline = "zypper in -y ${ZAP_PACKAGE}"
|
||||
}
|
||||
}"#;
|
||||
let _task = TaskParser::parse(Rule::task, buf).unwrap().next().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_no_parameters() {
|
||||
let buf = r#"task PrintEnv {
|
||||
script {
|
||||
inline = "env"
|
||||
}
|
||||
}"#;
|
||||
let _task = TaskParser::parse(Rule::task, buf).unwrap().next().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_task_fn() {
|
||||
let buf = r#"task PrintEnv {
|
||||
script {
|
||||
inline = "env"
|
||||
}
|
||||
}"#;
|
||||
let task = Task::from_str(buf).expect("Failed to parse the task");
|
||||
assert_eq!(task.name, "PrintEnv");
|
||||
assert_eq!(task.get_script().unwrap(), "env");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/// This describes the task definition grammar for Zap
|
||||
|
||||
taskfile = _{ SOI
|
||||
~ task+
|
||||
~ EOI }
|
||||
|
||||
task = { "task"
|
||||
~ identifier
|
||||
~ opening_brace
|
||||
~ parameters?
|
||||
~ script
|
||||
~ closing_brace
|
||||
}
|
||||
|
||||
// An identifier will be used to refer to the task later
|
||||
identifier = { ASCII_ALPHA ~ (ASCII_ALPHANUMERIC | "_")* }
|
||||
|
||||
parameters = { "parameters"
|
||||
~ opening_brace
|
||||
~ parameter+
|
||||
~ closing_brace
|
||||
}
|
||||
parameter = { identifier
|
||||
~ opening_brace
|
||||
~ required?
|
||||
~ help
|
||||
~ ptype
|
||||
~ closing_brace
|
||||
}
|
||||
|
||||
required = { "required" ~ equals ~ bool }
|
||||
help = { "help" ~ equals ~ string }
|
||||
ptype = { "type" ~ equals ~ typedef }
|
||||
|
||||
|
||||
script = { "script"
|
||||
~ opening_brace
|
||||
~ (script_inline)
|
||||
~ closing_brace
|
||||
}
|
||||
script_inline = _{ "inline" ~ equals ~ string }
|
||||
|
||||
opening_brace = _{ "{" }
|
||||
closing_brace = _{ "}" }
|
||||
equals = _{ "=" }
|
||||
quote = _{ "\"" }
|
||||
|
||||
string = { double_quoted }
|
||||
double_quoted = ${ (quote ~ inner_double_str ~ quote) }
|
||||
inner_double_str = @{ (!("\"" | "\\") ~ ANY)* ~ (escape ~ inner_double_str)? }
|
||||
escape = @{ "\\" ~ ("\"" | "\\" | "r" | "n" | "t" | "0" | "'" | code | unicode) }
|
||||
code = @{ "x" ~ hex_digit{2} }
|
||||
unicode = @{ "u" ~ opening_brace ~ hex_digit{2, 6} ~ closing_brace }
|
||||
hex_digit = @{ '0'..'9' | 'a'..'f' | 'A'..'F' }
|
||||
|
||||
typedef = { string_type }
|
||||
string_type = { "string" }
|
||||
|
||||
bool = { truthy | falsey }
|
||||
truthy = { "true" }
|
||||
falsey = { "false" }
|
||||
|
||||
block_comment = _{ "/*" ~ (block_comment | !"*/" ~ ANY)* ~ "*/" }
|
||||
COMMENT = _{ block_comment | ("//" ~ (!NEWLINE~ ANY)*) }
|
||||
WHITESPACE = _{ " " | "\t" | NEWLINE }
|
|
@ -0,0 +1,12 @@
|
|||
task Echo {
|
||||
parameters {
|
||||
msg {
|
||||
required = true
|
||||
help = "String to echo back to the client"
|
||||
type = string
|
||||
}
|
||||
}
|
||||
script {
|
||||
inline = "echo {{msg}}"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue