Compare commits

...

9 Commits
v0.1.1 ... main

Author SHA1 Message Date
R Tyler Croy eac957569c Upgrade dependencies 2021-05-23 11:50:50 -07:00
R Tyler Croy e3717a4cef Revert "Checking in an abandoned work in progress on task URI management"
This reverts commit 403de15964.
2021-01-31 15:02:00 -08:00
R Tyler Croy 403de15964 Checking in an abandoned work in progress on task URI management 2021-01-31 15:01:42 -08:00
R Tyler Croy 443b7f878d Document built-in step
Only one at the moment
2021-01-01 20:44:22 -08:00
R Tyler Croy 35e186f828 Explore syntax for built-in tasks. Implementation is still ... crap 2021-01-01 15:39:06 -08:00
R Tyler Croy e2a5f74c3a More refactoring to clean up the ssh transport a bit 2021-01-01 15:14:21 -08:00
R Tyler Croy c6d62687b4 Add Transport.file_exists to clean up the run method for builtin tasks 2021-01-01 15:03:14 -08:00
R Tyler Croy 4888aa9ecf Make the .ztask suffix not required when loading in the zplan
Fixes #3
2021-01-01 14:41:42 -08:00
R Tyler Croy 50ab1a5b14 Restructuring the file tree to make it easier to start adding "native tasks"
Some number of tasks I will want to be "builtin" and need to know a little bit
more about the transport in order to make that work.

The traits, they are coming.

See #1
2021-01-01 14:32:51 -08:00
14 changed files with 362 additions and 186 deletions

232
Cargo.lock generated
View File

@ -2,9 +2,9 @@
# It is not intended for manual editing.
[[package]]
name = "aho-corasick"
version = "0.7.15"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
dependencies = [
"memchr",
]
@ -61,15 +61,15 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
[[package]]
name = "byteorder"
version = "1.3.4"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cc"
version = "1.0.66"
version = "1.0.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
[[package]]
name = "cfg-if"
@ -77,6 +77,12 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cloudabi"
version = "0.0.3"
@ -108,9 +114,9 @@ dependencies = [
[[package]]
name = "dtoa"
version = "0.4.7"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88d7ed2934d741c6b37e33e3832298e8850b53fd2d2bea03873375596c7cea4e"
checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0"
[[package]]
name = "env_logger"
@ -132,10 +138,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
[[package]]
name = "generic-array"
version = "0.12.3"
name = "form_urlencoded"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
dependencies = [
"matches",
"percent-encoding",
]
[[package]]
name = "generic-array"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
dependencies = [
"typenum",
]
@ -162,23 +178,23 @@ dependencies = [
[[package]]
name = "handlebars"
version = "3.5.2"
version = "3.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "964d0e99a61fe9b1b347389b77ebf8b7e1587b70293676aaca7d27e59b9073b2"
checksum = "4498fc115fa7d34de968184e473529abb40eeb6be8bc5f7faba3d08c316cb3e3"
dependencies = [
"log",
"pest",
"pest_derive",
"quick-error 2.0.0",
"quick-error 2.0.1",
"serde",
"serde_json",
]
[[package]]
name = "hermit-abi"
version = "0.1.17"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8"
checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
dependencies = [
"libc",
]
@ -192,6 +208,17 @@ dependencies = [
"quick-error 1.2.3",
]
[[package]]
name = "idna"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
dependencies = [
"matches",
"unicode-bidi",
"unicode-normalization",
]
[[package]]
name = "itoa"
version = "0.4.7"
@ -206,15 +233,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.81"
version = "0.2.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb"
checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e"
[[package]]
name = "libssh2-sys"
version = "0.2.20"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df40b13fe7ea1be9b9dffa365a51273816c345fc1811478b57ed7d964fbfc4ce"
checksum = "e0186af0d8f171ae6b9c4c90ec51898bad5d08a2d5e470903a50d9ad8959cbee"
dependencies = [
"cc",
"libc",
@ -226,9 +253,9 @@ dependencies = [
[[package]]
name = "libz-sys"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "602113192b08db8f38796c4e85c39e960c145965140e918018bcde1952429655"
checksum = "de5435b8549c16d423ed0c03dbaafe57cf6c3344744f1242520d59c9d8ecec66"
dependencies = [
"cc",
"libc",
@ -238,9 +265,9 @@ dependencies = [
[[package]]
name = "linked-hash-map"
version = "0.5.3"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a"
checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
[[package]]
name = "lock_api"
@ -253,11 +280,11 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.11"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
]
[[package]]
@ -267,10 +294,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
[[package]]
name = "memchr"
version = "2.3.4"
name = "matches"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
[[package]]
name = "memchr"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
[[package]]
name = "opaque-debug"
@ -280,9 +313,9 @@ checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
[[package]]
name = "openssl-sys"
version = "0.9.60"
version = "0.9.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "921fc71883267538946025deffb622905ecad223c28efbfdef9bb59a0175f3e6"
checksum = "b6b0d6fb7d80f877617dfcb014e605e2b5ab2fb0afdf27935219bb6bd984cb98"
dependencies = [
"autocfg",
"cc",
@ -307,7 +340,7 @@ version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"cloudabi",
"libc",
"redox_syscall",
@ -315,6 +348,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"
@ -376,9 +415,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.24"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038"
dependencies = [
"unicode-xid",
]
@ -391,15 +430,15 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quick-error"
version = "2.0.0"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ac73b1112776fc109b2e61909bc46c7e1bf0d7f690ffb1676553acce16d5cda"
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]]
name = "quote"
version = "1.0.8"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df"
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
dependencies = [
"proc-macro2",
]
@ -412,21 +451,20 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
[[package]]
name = "regex"
version = "1.4.2"
version = "1.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c"
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
"thread_local",
]
[[package]]
name = "regex-syntax"
version = "0.6.21"
version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "ryu"
@ -442,18 +480,18 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "serde"
version = "1.0.118"
version = "1.0.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800"
checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.118"
version = "1.0.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df"
checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43"
dependencies = [
"proc-macro2",
"quote",
@ -462,9 +500,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.61"
version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a"
checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79"
dependencies = [
"itoa",
"ryu",
@ -473,9 +511,9 @@ dependencies = [
[[package]]
name = "serde_yaml"
version = "0.8.14"
version = "0.8.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7baae0a99f1a324984bcdc5f0718384c1f69775f1c7eec8b859b71b443e3fd7"
checksum = "15654ed4ab61726bf918a39cb8d98a2e2995b002387807fa6ba58fdf7f59bb23"
dependencies = [
"dtoa",
"linked-hash-map",
@ -497,15 +535,15 @@ dependencies = [
[[package]]
name = "smallvec"
version = "1.6.0"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a55ca5f3b68e41c979bf8c46a6f1da892ca4db8f94023ce0bd32407573b1ac0"
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
[[package]]
name = "ssh2"
version = "0.9.0"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed024de0a5e6944fe3080a3745e7a5649c5517ea2a6cfb16333f94f89c248985"
checksum = "d876d4d57f6bbf2245d43f7ec53759461f801a446d3693704aa6d27b257844d7"
dependencies = [
"bitflags",
"libc",
@ -515,9 +553,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.56"
version = "1.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9802ddde94170d186eeee5005b798d9c159fa970403f1be19976d0cfb939b72"
checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82"
dependencies = [
"proc-macro2",
"quote",
@ -534,19 +572,25 @@ dependencies = [
]
[[package]]
name = "thread_local"
version = "1.0.1"
name = "tinyvec"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342"
dependencies = [
"lazy_static",
"tinyvec_macros",
]
[[package]]
name = "typenum"
version = "1.12.0"
name = "tinyvec_macros"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "typenum"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
[[package]]
name = "ucd-trie"
@ -555,16 +599,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
[[package]]
name = "unicode-xid"
version = "0.2.1"
name = "unicode-bidi"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0"
dependencies = [
"matches",
]
[[package]]
name = "unicode-normalization"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07fbfce1c8a97d547e8b5334978438d9d6ec8c20e38f56d4a4374d181493eaef"
dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "url"
version = "2.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c"
dependencies = [
"form_urlencoded",
"idna",
"matches",
"percent-encoding",
]
[[package]]
name = "vcpkg"
version = "0.2.11"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb"
checksum = "025ce40a007e1907e58d5bc1a594def78e5573bb0b1160bc389634e8f12e4faa"
[[package]]
name = "winapi"
@ -599,35 +673,39 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "yaml-rust"
version = "0.4.4"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d"
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [
"linked-hash-map",
]
[[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",
]

View File

@ -1,3 +1,5 @@
:toc: macro
= Zap
A simple cross-platform orchestration and configuration management tool.
@ -12,6 +14,8 @@ Zap borrows ideas from
link:https://puppet.com/docs/bolt/latest/bolt.html[Puppet Bolt]. but leaves
some of the Puppet-based legacy from Bolt behind.
toc::[]
== Getting Started
Zap is still very early in its development, but if you would like to give it a
@ -139,12 +143,50 @@ 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'
}
----
== Built-in Tasks
Zap comes with a number of tasks that are built into `zap` itself. These can be
referenced in the task declarations in plans via the `zap://` URL.
=== `sh`
The `sh` task will execute the given script via `/bin/sh` on the target.
.Example
[source]
----
task 'zap://sh' {
script = '''
pwd
echo ${SHELL}
'''
}
----
.Parameter
|===
| Name | Required | Description
| `script`
| yes
| A shell script
| `provides`
| no
| When this file is present indicates that the script should not be re-run
| `unless`
| no
| When this script returns zero exit status, the script should not be re-run
|==

View File

@ -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" }
gumdrop = "0"
log = "0"
pretty_env_logger = "0"
serde = { version = "1", features = ["derive", "rc"] }
serde_yaml = "0"
zap-model = { version = "0", path = "../model" }

View File

@ -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(

View File

@ -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'
}

View File

@ -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]
handlebars = "~3.5"
log = "0.4"
pest = "~2.1"
pest_derive = "~2.1"
colored = "2"
handlebars = "3"
log = "0"
pest = "2"
pest_derive = "2"
# Needed for deserializing JSON messages _and_ managing our configuration
# effectively
serde = { version = "1", features = ["derive", "rc"] }
serde_derive = "1"
serde_json = "1"
serde_yaml = "0"
ssh2 = "0"
url = "2"

View File

@ -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 {}

View File

@ -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");

View File

@ -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");
}
}

1
model/src/tasks/mod.rs Normal file
View File

@ -0,0 +1 @@

View File

@ -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;
}

View File

@ -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 {

View File

@ -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}}
'''
}
}