TRUC
This commit is contained in:
parent
7d9e3d63b7
commit
89ef66a694
142
Cargo.lock
generated
142
Cargo.lock
generated
|
@ -11,6 +11,73 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"memoffset",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctor"
|
||||
version = "0.2.0"
|
||||
|
@ -39,6 +106,12 @@ dependencies = [
|
|||
"shared_child",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
||||
|
||||
[[package]]
|
||||
name = "erased-serde"
|
||||
version = "0.3.25"
|
||||
|
@ -65,6 +138,15 @@ version = "0.4.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inventory"
|
||||
version = "0.3.5"
|
||||
|
@ -93,6 +175,25 @@ version = "2.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.17.1"
|
||||
|
@ -127,6 +228,28 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.7.3"
|
||||
|
@ -144,12 +267,25 @@ version = "0.6.29"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||
|
||||
[[package]]
|
||||
name = "ron"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "300a51053b1cb55c80b7a9fde4120726ddf25ca241a1cbb926626f62fb136bff"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bitflags",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustible"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"duct",
|
||||
"rayon",
|
||||
"regex",
|
||||
"ron",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"snafu",
|
||||
|
@ -162,6 +298,12 @@ version = "1.0.13"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.160"
|
||||
|
|
|
@ -15,6 +15,8 @@ typetag = "0.2"
|
|||
# Error management
|
||||
#thiserror = "1"
|
||||
snafu = "0.7"
|
||||
ron = "0.8"
|
||||
rayon = "1.7"
|
||||
|
||||
[lib]
|
||||
name = "rustible"
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
WIP ansible-like system for sysadmin in rust
|
||||
|
||||
TODO: finish test.sh for testing things
|
||||
|
|
6
examples/facts.rs
Normal file
6
examples/facts.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
use rustible::Facts;
|
||||
|
||||
fn main() {
|
||||
let facts = Facts::new();
|
||||
println!("rustible running system: {}", facts.os.family().as_str());
|
||||
}
|
|
@ -1,5 +1,3 @@
|
|||
use rustible::Facts;
|
||||
|
||||
use rustible::modules::{
|
||||
PlaybookRun,
|
||||
package::{PackageModule as Package, PackageState, PackageError},
|
||||
|
@ -25,9 +23,6 @@ impl From<PackageError> for PlaybookError {
|
|||
}
|
||||
|
||||
fn main() -> Result<(), PlaybookError> {
|
||||
let facts = Facts::new();
|
||||
println!("rustible running system: {}", facts.os.family().as_str());
|
||||
|
||||
let mut playbook = PlaybookRun::new();
|
||||
|
||||
let pkg = Package::new()
|
||||
|
@ -39,7 +34,7 @@ fn main() -> Result<(), PlaybookError> {
|
|||
let cmd = Command::new()
|
||||
.program("hello")
|
||||
.arg("-g")
|
||||
.arg("\"Welcome to rustible!\"")
|
||||
.arg("\"Welcome to rustible!\"")
|
||||
.build();
|
||||
|
||||
let res = playbook.run(cmd)?;
|
||||
|
|
88
src/main.rs
88
src/main.rs
|
@ -1,11 +1,28 @@
|
|||
use rustible::Facts;
|
||||
use ron::value::Value;
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use std::env::args;
|
||||
|
||||
use rustible::Facts;
|
||||
use rustible::modules::{
|
||||
Module, PlaybookRun,
|
||||
package::{PackageModule as Package, PackageState, PackageError},
|
||||
command::{CommandModule as Command, CommandError},
|
||||
Module, PlaybookRun, ModuleSetup,
|
||||
package::{PackageModule as Package, PackageState, PackageError, PackageArgs},
|
||||
command::{CommandModule as Command, CommandError, CommandArgs},
|
||||
};
|
||||
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct DeclarativePlaybook {
|
||||
modules: Vec<DeclarativeModule>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct DeclarativeModule {
|
||||
name: String,
|
||||
module: String,
|
||||
args: Value,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize)]
|
||||
pub enum PlaybookError {
|
||||
Command(CommandError),
|
||||
|
@ -26,48 +43,35 @@ impl From<PackageError> for PlaybookError {
|
|||
|
||||
fn main() -> Result<(), PlaybookError> {
|
||||
let facts = Facts::new();
|
||||
println!("Hello, world! Running system {}", facts.os.family().as_str());
|
||||
|
||||
|
||||
let mut playbook = PlaybookRun::new();
|
||||
|
||||
let cmd = Command::new().program("echo").args(&vec!("lol".to_string())).build();
|
||||
let res = cmd.run()?;
|
||||
println!("{}", res.success());
|
||||
|
||||
let cmd = Command::new().program("echo").args(&vec!("lol".to_string())).build();
|
||||
playbook.run(cmd)?;
|
||||
|
||||
let cmd = Command::new()
|
||||
.creates("/tmp/lol")
|
||||
.program("touch")
|
||||
.arg("/tmp/lol")
|
||||
.build();
|
||||
playbook.run(cmd)?;
|
||||
let mut cli_args = args();
|
||||
let test_playbook = cli_args.nth(1).expect("First argument should be playbook.ron");
|
||||
let test_shell = cli_args.nth(0).expect("Second argument should be test.sh");
|
||||
|
||||
let pkg = Package::new()
|
||||
.name("hello")
|
||||
.state(PackageState::Present)
|
||||
.build();
|
||||
playbook.run(pkg)?;
|
||||
let test_playbook_content = std::fs::read_to_string(&test_playbook).unwrap();
|
||||
|
||||
let pkg = Package::new()
|
||||
.name("hello")
|
||||
.state(PackageState::Absent)
|
||||
.build();
|
||||
playbook.run(pkg)?;
|
||||
let playbook_ron: DeclarativePlaybook = ron::from_str(&test_playbook_content).unwrap();
|
||||
|
||||
let pkg = Package::new()
|
||||
.name(vec!("hello", "sl"))
|
||||
.state(PackageState::Present)
|
||||
.build();
|
||||
playbook.run(pkg)?;
|
||||
|
||||
let pkg = Package::new()
|
||||
.name(&[ "hello", "sl" ])
|
||||
.state(PackageState::Absent)
|
||||
.build();
|
||||
playbook.run(pkg)?;
|
||||
playbook.print_json_pretty();
|
||||
for task in playbook_ron.modules {
|
||||
println!("{}", task.name);
|
||||
match task.module.as_str() {
|
||||
"command" => {
|
||||
let args: CommandArgs = task.args.into_rust().unwrap();
|
||||
let module = args.with_facts(&facts);
|
||||
playbook.run(module)?;
|
||||
//println!("{:?}", module);
|
||||
}, "package" => {
|
||||
let args: PackageArgs = task.args.into_rust().unwrap();
|
||||
let module = args.with_facts(&facts);
|
||||
playbook.run(module)?;
|
||||
//println!("{:?}", module);
|
||||
}, _ => {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,11 +9,11 @@ use crate::utils::cmd::Cmd;
|
|||
pub struct NoCmd;
|
||||
|
||||
pub struct NoCondition;
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct CreatesCondition(pub PathBuf);
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct RemovesCondition(pub PathBuf);
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct BothConditions(pub CreatesCondition, pub RemovesCondition);
|
||||
|
||||
/// Build a [`CommandModule`].
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use super::builder::{NoCondition, CreatesCondition, RemovesCondition, BothConditions};
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum SomeCondition {
|
||||
#[serde(rename="creates")]
|
||||
Creates(CreatesCondition),
|
||||
|
|
|
@ -12,13 +12,24 @@ use crate::{Module, ModuleSetup, Facts};
|
|||
|
||||
/// A copy of the argument passed to the [`CommandBuilder`],
|
||||
/// as returned serialized in the task run output
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct CommandArgs {
|
||||
#[serde(flatten)]
|
||||
pub args: Cmd,
|
||||
pub condition: Option<SomeCondition>,
|
||||
}
|
||||
|
||||
impl ModuleSetup<CommandModule, CommandArgs, CommandStatus, CommandError> for CommandArgs {
|
||||
fn with_facts(self, _facts: &Facts) -> CommandModule {
|
||||
CommandModule {
|
||||
cmd: self.args,
|
||||
condition: self.condition,
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CommandModule {
|
||||
cmd: Cmd,
|
||||
condition: Option<SomeCondition>,
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use rayon::prelude::*;
|
||||
|
||||
use crate::utils::cmd::{Cmd, CmdOutput};
|
||||
|
||||
use super::{PackageError, PackageList, PackageManager};
|
||||
use super::{PackageError, PackageList, PackageManager, IntoPackageList};
|
||||
|
||||
/// This structure is mostly used as an indirection for testing purposes.
|
||||
/// But you can use it to build your own apt commands.
|
||||
|
@ -40,6 +42,14 @@ impl AptCmd {
|
|||
#[derive(Debug)]
|
||||
pub struct AptManager;
|
||||
|
||||
impl AptManager {
|
||||
fn is_installed_list(&self, list: &PackageList) -> Result<bool, PackageError> {
|
||||
let res = Cmd::new("dpkg").arg("-s").args(list.list()).run()?;
|
||||
|
||||
Ok(res.success)
|
||||
}
|
||||
}
|
||||
|
||||
impl PackageManager for AptManager {
|
||||
fn name(&self) -> &'static str {
|
||||
"apt"
|
||||
|
@ -54,7 +64,26 @@ impl PackageManager for AptManager {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn is_installed(&self, pkg: &str) -> Result<bool, PackageError> {
|
||||
let res = Cmd::new("dpkg").arg("-s").arg(pkg).run()?;
|
||||
|
||||
Ok(res.success)
|
||||
}
|
||||
|
||||
fn install(&self, list: PackageList) -> Result<(), PackageError> {
|
||||
// Filter out already installed packages
|
||||
// let list: Vec<String> = list.list().par_iter().filter_map(|pkg| {
|
||||
// if ! self.is_installed(&pkg).unwrap() {
|
||||
// Some(pkg.to_string())
|
||||
// } else {
|
||||
// None
|
||||
// }
|
||||
// }).collect();
|
||||
if self.is_installed_list(&list)? {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
//let res = AptCmd::install(list.into_package_list()).run()?;
|
||||
let res = AptCmd::install(list).run()?;
|
||||
|
||||
if ! res.success {
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
use serde::Serialize;
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
|
||||
use crate::utils::serde::string_or_seq_string;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct PackageList {
|
||||
#[serde(deserialize_with = "string_or_seq_string")]
|
||||
pub list: Vec<String>,
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use serde::Serialize;
|
||||
use serde::{Serialize, Deserialize, Deserializer};
|
||||
|
||||
use crate::{Module, ModuleSetup};
|
||||
use crate::facts::{Facts, os::OsFamily};
|
||||
|
@ -19,12 +19,25 @@ pub use builder::{PackageArgsBuilder, NoManager};
|
|||
|
||||
pub type PackageStatus = ();
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct PackageArgs {
|
||||
name: PackageList,
|
||||
state: PackageState,
|
||||
}
|
||||
|
||||
impl ModuleSetup<PackageModule<Box<dyn PackageManager>>, PackageArgs, (), PackageError> for PackageArgs {
|
||||
fn with_facts(self, facts: &Facts) -> PackageModule<Box<dyn PackageManager>> {
|
||||
let manager: Box<dyn PackageManager> = match facts.os.family() {
|
||||
OsFamily::Debian => Box::new(AptManager),
|
||||
OsFamily::Archlinux => Box::new(PacmanManager),
|
||||
};
|
||||
|
||||
PackageModule {
|
||||
args: self,
|
||||
manager,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
|
||||
pub enum PackageState {
|
||||
Present,
|
||||
|
@ -32,6 +45,21 @@ pub enum PackageState {
|
|||
Latest,
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for PackageState {
|
||||
fn deserialize<D>(de: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let variant = String::deserialize(de)?;
|
||||
Ok(match variant.as_str() {
|
||||
"present" => PackageState::Present,
|
||||
"absent" => PackageState::Absent,
|
||||
"latest" => PackageState::Latest,
|
||||
_other => unimplemented!(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct PackageModule<Manager> {
|
||||
manager: Manager,
|
||||
|
@ -78,6 +106,12 @@ impl ModuleSetup<PackageModule<Box<dyn PackageManager>>, PackageArgs, (), Packag
|
|||
}
|
||||
}
|
||||
|
||||
// Stupid impl just for tests
|
||||
impl ModuleSetup<PackageModule<Box<dyn PackageManager>>, PackageArgs, (), PackageError> for PackageModule<Box<dyn PackageManager>> {
|
||||
fn with_facts(self, _facts: &Facts) -> PackageModule<Box<dyn PackageManager>> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl PackageModule<NoManager> {
|
||||
pub fn new() -> PackageArgsBuilder<builder::NoPackage, builder::NoState, builder::NoManager> {
|
||||
|
@ -100,6 +134,7 @@ impl From<std::io::Error> for PackageError {
|
|||
pub trait PackageManager: std::fmt::Debug {
|
||||
fn name(&self) -> &'static str;
|
||||
fn update(&self) -> Result<(), PackageError>;
|
||||
fn is_installed(&self, pkg: &str) -> Result<bool, PackageError>;
|
||||
fn install(&self, list: PackageList) -> Result<(), PackageError>;
|
||||
fn remove(&self, list: PackageList) -> Result<(), PackageError>;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,12 @@ impl PacmanCmd {
|
|||
cmd
|
||||
}
|
||||
|
||||
pub fn is_installed(pkg: &str) -> PacmanCmd {
|
||||
let mut cmd = Self::new();
|
||||
cmd.0 = cmd.0.arg("-Qi").arg(pkg);
|
||||
cmd
|
||||
}
|
||||
|
||||
pub fn install(list: PackageList) -> PacmanCmd {
|
||||
let mut cmd = Self::new();
|
||||
cmd.0 = cmd.0.arg("-S").args(list.list());
|
||||
|
@ -56,6 +62,12 @@ impl PackageManager for PacmanManager {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn is_installed(&self, pkg: &str) -> Result<bool, PackageError> {
|
||||
let res = PacmanCmd::is_installed(pkg).run()?;
|
||||
|
||||
Ok(res.success)
|
||||
}
|
||||
|
||||
fn install(&self, list: PackageList) -> Result<(), PackageError> {
|
||||
let res = PacmanCmd::install(list).run()?;
|
||||
|
||||
|
|
|
@ -3,13 +3,17 @@ use duct::cmd as duct_cmd;
|
|||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Cmd {
|
||||
pub program: String,
|
||||
pub args: Vec<String>,
|
||||
pub stdin: Option<String>,
|
||||
pub chdir: Option<PathBuf>,
|
||||
pub env: HashMap<String, String>,
|
||||
program: String,
|
||||
#[serde(default)]
|
||||
args: Vec<String>,
|
||||
#[serde(default)]
|
||||
stdin: Option<String>,
|
||||
#[serde(default)]
|
||||
chdir: Option<PathBuf>,
|
||||
#[serde(default)]
|
||||
env: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl Cmd {
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
pub mod cmd;
|
||||
pub mod serde;
|
||||
|
|
34
src/utils/serde.rs
Normal file
34
src/utils/serde.rs
Normal file
|
@ -0,0 +1,34 @@
|
|||
use serde::de::{Deserialize, Deserializer, SeqAccess, Visitor};
|
||||
use serde::de::value::SeqAccessDeserializer;
|
||||
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
// https://stackoverflow.com/questions/41151080/deserialize-a-json-string-or-array-of-strings-into-a-vec
|
||||
pub fn string_or_seq_string<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
|
||||
where D: Deserializer<'de>
|
||||
{
|
||||
struct StringOrVec(PhantomData<Vec<String>>);
|
||||
|
||||
impl<'de> Visitor<'de> for StringOrVec {
|
||||
type Value = Vec<String>;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("string or list of strings")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where E: serde::de::Error
|
||||
{
|
||||
Ok(vec![value.to_owned()])
|
||||
}
|
||||
|
||||
fn visit_seq<S>(self, visitor: S) -> Result<Self::Value, S::Error>
|
||||
where S: SeqAccess<'de>
|
||||
{
|
||||
Deserialize::deserialize(SeqAccessDeserializer::new(visitor))
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_any(StringOrVec(PhantomData))
|
||||
}
|
91
test.sh
Executable file
91
test.sh
Executable file
|
@ -0,0 +1,91 @@
|
|||
#! /usr/bin/env bash
|
||||
|
||||
help() {
|
||||
echo "test.sh [SERVER]"
|
||||
echo "Start the rustible test suite on a certain machine (localhost by default)"
|
||||
echo "Requires lxc-ramdisk to be installed: https://kl.netlib.re/gitea/selfhoster1312/lxc-ramdisk"
|
||||
}
|
||||
|
||||
build() {
|
||||
if cargo build --release 2>&1 >/dev/null; then
|
||||
echo "$(pwd)"/target/release/rustible
|
||||
ret=0
|
||||
else
|
||||
echo "BUILD FAILED."
|
||||
ret=1
|
||||
fi
|
||||
|
||||
return $ret
|
||||
}
|
||||
|
||||
gen_rand_name() {
|
||||
echo rustible-"$RANDOM"
|
||||
}
|
||||
|
||||
execute_localhost() {
|
||||
while true; do
|
||||
TEST_NAME="$(gen_rand_name)"
|
||||
TEST_PATH=/tmp/"$TEST_NAME"
|
||||
if [ ! -e "$TEST_PATH" ]; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Start test $TEST_NAME"
|
||||
cp target/release/rustible "$TEST_PATH"
|
||||
}
|
||||
|
||||
execute_on_server() {
|
||||
SERVER="$1"
|
||||
if ssh "$SERVER" true >/dev/null 2>&1; then
|
||||
echo "Connection to "$1" successful"
|
||||
else
|
||||
echo "ERROR: Connection failed to "$1""
|
||||
return 1
|
||||
fi
|
||||
|
||||
while true; do
|
||||
TEST_NAME="$(gen_rand_name)"
|
||||
TEST_PATH=/tmp/"$TEST_NAME"
|
||||
if ! ssh "$1" ls "$TEST_PATH" >/dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Start test "$TEST_NAME" ("$TEST_PATH")"
|
||||
if ! scp target/release/rustible "$SERVER":"$TEST_PATH"; then
|
||||
echo "ERROR: Copy to "$SERVER" failed"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Move to crate directory
|
||||
OLDDIR="$(pwd)"
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# Figure out rustible test binary path
|
||||
BIN_PATH="$(build)"
|
||||
if [ ! $? -eq 0 ]; then
|
||||
# Compilation failed, abort
|
||||
echo "$BIN_PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$1" = "" ]]; then
|
||||
execute_localhost
|
||||
ret=$?
|
||||
else
|
||||
case "$1" in
|
||||
"help" | "-h" | "--help")
|
||||
help
|
||||
ret=0
|
||||
;;
|
||||
*)
|
||||
execute_on_server "$1"
|
||||
ret=$?
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
cd "$OLDDIR"
|
||||
exit $ret
|
19
tests/hello/playbook.ron
Normal file
19
tests/hello/playbook.ron
Normal file
|
@ -0,0 +1,19 @@
|
|||
(
|
||||
modules: [
|
||||
(
|
||||
name: "install hello",
|
||||
module: "package",
|
||||
args: (
|
||||
name: [ "hello", "cmake", "firefox-esr", "libreoffice", "make", "gcc", "zstd", "zlib1g", "zlib1g-dev", "zenity", "zenity-common", "yt-dlp", "yelp", "yelp-xsl", "xz-utils", "xserver-xorg", "xtrans-dev" ],
|
||||
state: "present",
|
||||
)
|
||||
), (
|
||||
name: "say hello",
|
||||
module: "command",
|
||||
args: (
|
||||
program: "hello",
|
||||
args: [ "-g", "welcome to rustible" ],
|
||||
),
|
||||
)
|
||||
]
|
||||
)
|
7
tests/hello/test.sh
Normal file
7
tests/hello/test.sh
Normal file
|
@ -0,0 +1,7 @@
|
|||
#! /usr/bin/env bash
|
||||
|
||||
# Check that the playbook run returned "welcome to rustible"
|
||||
echo "$STDOUT" | tail -n 3 | grep -P "\^"Welcome to rustible!\""
|
||||
|
||||
# Check that hello is effectively installed
|
||||
hello -g "test" | grep -P "^test$"
|
Loading…
Reference in New Issue
Block a user