Browse Source

Cargo format

master
Adrian Heine 3 years ago
parent
commit
8c0224e983
  1. 1
      rustfmt.toml
  2. 19
      src/bin.rs
  3. 24
      src/build.rs
  4. 104
      src/command_runner.rs
  5. 80
      src/factory.rs
  6. 23
      src/lib.rs
  7. 9
      src/loggers.rs
  8. 14
      src/repository.rs
  9. 150
      src/schema.rs
  10. 22
      src/storage.rs
  11. 42
      src/symbols/acme/account_key.rs
  12. 75
      src/symbols/acme/cert.rs
  13. 65
      src/symbols/acme/chain.rs
  14. 49
      src/symbols/acme/user.rs
  15. 38
      src/symbols/dir.rs
  16. 221
      src/symbols/factory.rs
  17. 47
      src/symbols/file.rs
  18. 48
      src/symbols/git/checkout.rs
  19. 25
      src/symbols/git/submodules.rs
  20. 87
      src/symbols/hook.rs
  21. 31
      src/symbols/list.rs
  22. 33
      src/symbols/mariadb/database.rs
  23. 45
      src/symbols/mariadb/database_dump.rs
  24. 2
      src/symbols/mariadb/mod.rs
  25. 35
      src/symbols/mariadb/user.rs
  26. 10
      src/symbols/mod.rs
  27. 110
      src/symbols/nginx/server.rs
  28. 8
      src/symbols/noop.rs
  29. 37
      src/symbols/npm.rs
  30. 44
      src/symbols/owner.rs
  31. 55
      src/symbols/postgresql/database.rs
  32. 45
      src/symbols/postgresql/database_dump.rs
  33. 107
      src/symbols/stored_directory.rs
  34. 119
      src/symbols/systemd/node_js_user_service.rs
  35. 18
      src/symbols/systemd/reload.rs
  36. 26
      src/symbols/systemd/user_session.rs
  37. 35
      src/symbols/tls/csr.rs
  38. 30
      src/symbols/tls/key.rs
  39. 75
      src/symbols/tls/self_signed_cert.rs
  40. 87
      src/symbols/user.rs
  41. 83
      src/symbols/wordpress/plugin.rs
  42. 112
      src/symbols/wordpress/translation.rs
  43. 13
      tests/file.rs
  44. 96
      tests/storage.rs

1
rustfmt.toml

@ -0,0 +1 @@
tab_spaces = 2

19
src/bin.rs

@ -1,13 +1,22 @@
use std::process::exit;
use std::env;
use std::process::exit;
pub fn schematics_main(run: &dyn Fn (bool) -> Result<(), ()>) {
pub fn schematics_main(run: &dyn Fn(bool) -> Result<(), ()>) {
let args: Vec<String> = env::args().collect();
let dry_run = match args.len() {
1 => false,
2 => if args[1] == "--dry-run" { true } else { panic!() },
_ => panic!()
2 => {
if args[1] == "--dry-run" {
true
} else {
panic!()
}
}
_ => panic!(),
};
exit(match run(dry_run) { Ok(_) => 0, Err(_) => 1 });
exit(match run(dry_run) {
Ok(_) => 0,
Err(_) => 1,
});
}

24
src/build.rs

@ -7,7 +7,13 @@ use std::path::{Path, PathBuf};
fn get_const_name<P: Clone + Into<PathBuf>>(p: &P) -> String {
let mut file_name_without_extension = p.clone().into();
file_name_without_extension.set_extension("");
String::from(file_name_without_extension.file_name().unwrap().to_string_lossy()).to_uppercase()
String::from(
file_name_without_extension
.file_name()
.unwrap()
.to_string_lossy(),
)
.to_uppercase()
}
pub fn create_static_output_files(source_dir: &str) {
@ -17,8 +23,20 @@ pub fn create_static_output_files(source_dir: &str) {
for maybe_dir_entry in read_dir(source_dir).unwrap() {
let file_path = maybe_dir_entry.unwrap().path();
let mut buffer = String::new();
File::open(file_path.clone()).unwrap().read_to_string(&mut buffer).unwrap();
File::open(file_path.clone())
.unwrap()
.read_to_string(&mut buffer)
.unwrap();
let fence = buffer.chars().filter(|c| *c == '#').collect::<String>() + "#";
f.write_all(format!("pub const {}: &str = r{1}\"{2}\"{1};\n", get_const_name(&file_path), fence, buffer).as_bytes()).unwrap();
f.write_all(
format!(
"pub const {}: &str = r{1}\"{2}\"{1};\n",
get_const_name(&file_path),
fence,
buffer
)
.as_bytes(),
)
.unwrap();
}
}

104
src/command_runner.rs

@ -5,22 +5,35 @@ use std::process::Command;
use std::process::Output;
pub trait CommandRunner {
fn run_with_args<S: AsRef<OsStr> + ?Sized>(&self, program: &str, args: &[&S]) -> IoResult<Output>;
fn get_output<S: AsRef<OsStr> + ?Sized>(&self, program: &str, args: &[&S]) -> Result<Vec<u8>, Box<dyn Error>> {
fn run_with_args<S: AsRef<OsStr> + ?Sized>(&self, program: &str, args: &[&S])
-> IoResult<Output>;
fn get_output<S: AsRef<OsStr> + ?Sized>(
&self,
program: &str,
args: &[&S],
) -> Result<Vec<u8>, Box<dyn Error>> {
let output = try!(self.run_with_args(program, args));
if !output.status.success() {
return Err(try!(String::from_utf8(output.stderr)).into());
}
Ok(output.stdout)
}
fn get_stderr<S: AsRef<OsStr> + ?Sized>(&self, program: &str, args: &[&S]) -> Result<Vec<u8>, Box<dyn Error>> {
fn get_stderr<S: AsRef<OsStr> + ?Sized>(
&self,
program: &str,
args: &[&S],
) -> Result<Vec<u8>, Box<dyn Error>> {
let output = try!(self.run_with_args(program, args));
if !output.status.success() {
return Err(try!(String::from_utf8(output.stderr)).into());
}
Ok(output.stderr)
}
fn run_successfully<S: AsRef<OsStr> + ?Sized>(&self, program: &str, args: &[&S]) -> Result<(), Box<dyn Error>> {
fn run_successfully<S: AsRef<OsStr> + ?Sized>(
&self,
program: &str,
args: &[&S],
) -> Result<(), Box<dyn Error>> {
let output = try!(self.run_with_args(program, args));
if output.status.success() {
Ok(())
@ -34,7 +47,11 @@ pub trait CommandRunner {
pub struct StdCommandRunner;
impl CommandRunner for StdCommandRunner {
fn run_with_args<S: AsRef<OsStr> + ?Sized>(&self, program: &str, args: &[&S]) -> IoResult<Output> {
fn run_with_args<S: AsRef<OsStr> + ?Sized>(
&self,
program: &str,
args: &[&S],
) -> IoResult<Output> {
// FIXME: logger
//println!("{} {:?}", program, args);
let res = Command::new(program).args(args).output();
@ -44,28 +61,43 @@ impl CommandRunner for StdCommandRunner {
}
#[derive(Debug)]
pub struct SetuidCommandRunner<'a, C> where C: 'a + CommandRunner {
pub struct SetuidCommandRunner<'a, C>
where
C: 'a + CommandRunner,
{
command_runner: &'a C,
user_name: &'a str
user_name: &'a str,
}
impl<'a, C> SetuidCommandRunner<'a, C> where C: 'a + CommandRunner {
impl<'a, C> SetuidCommandRunner<'a, C>
where
C: 'a + CommandRunner,
{
pub fn new(user_name: &'a str, command_runner: &'a C) -> SetuidCommandRunner<'a, C> {
SetuidCommandRunner { command_runner, user_name }
SetuidCommandRunner {
command_runner,
user_name,
}
}
}
use std::os::unix::process::CommandExt;
use std::env;
use std::os::unix::process::CommandExt;
use users::get_user_by_name;
struct TempSetEnv<'a> { name: &'a str, old_value: Option<String> }
struct TempSetEnv<'a> {
name: &'a str,
old_value: Option<String>,
}
impl<'a> TempSetEnv<'a> {
fn new(name: &'a str, new_value: String) -> TempSetEnv<'a> {
let old_value = env::var(name);
env::set_var(name, new_value);
TempSetEnv { name, old_value: old_value.ok() }
TempSetEnv {
name,
old_value: old_value.ok(),
}
}
}
@ -73,14 +105,23 @@ impl<'a> Drop for TempSetEnv<'a> {
fn drop(&mut self) {
match self.old_value {
Some(ref val) => env::set_var(self.name, val),
None => env::remove_var(self.name)
None => env::remove_var(self.name),
}
}
}
impl<'a, C> CommandRunner for SetuidCommandRunner<'a, C> where C: 'a + CommandRunner {
fn run_with_args<S: AsRef<OsStr> + ?Sized>(&self, program: &str, args: &[&S]) -> IoResult<Output> {
let uid = get_user_by_name(self.user_name).expect("User does not exist").uid();
impl<'a, C> CommandRunner for SetuidCommandRunner<'a, C>
where
C: 'a + CommandRunner,
{
fn run_with_args<S: AsRef<OsStr> + ?Sized>(
&self,
program: &str,
args: &[&S],
) -> IoResult<Output> {
let uid = get_user_by_name(self.user_name)
.expect("User does not exist")
.uid();
let set_home = TempSetEnv::new("HOME", format!("/home/{}", self.user_name));
let set_dbus = TempSetEnv::new("XDG_RUNTIME_DIR", format!("/run/user/{}", uid));
//println!("{} {:?}", program, args);
@ -91,21 +132,37 @@ impl<'a, C> CommandRunner for SetuidCommandRunner<'a, C> where C: 'a + CommandRu
}
#[derive(Debug)]
pub struct SuCommandRunner<'a, C> where C: 'a + CommandRunner {
pub struct SuCommandRunner<'a, C>
where
C: 'a + CommandRunner,
{
command_runner: &'a C,
user_name: &'a str
user_name: &'a str,
}
impl<'a, C> SuCommandRunner<'a, C> where C: 'a + CommandRunner {
impl<'a, C> SuCommandRunner<'a, C>
where
C: 'a + CommandRunner,
{
pub fn new(user_name: &'a str, command_runner: &'a C) -> SuCommandRunner<'a, C> {
SuCommandRunner { command_runner, user_name }
SuCommandRunner {
command_runner,
user_name,
}
}
}
// Su doesn't set XDG_RUNTIME_DIR
// https://github.com/systemd/systemd/blob/master/src/login/pam_systemd.c#L439
impl<'a, C> CommandRunner for SuCommandRunner<'a, C> where C: 'a + CommandRunner {
fn run_with_args<S: AsRef<OsStr> + ?Sized>(&self, program: &str, args: &[&S]) -> IoResult<Output> {
impl<'a, C> CommandRunner for SuCommandRunner<'a, C>
where
C: 'a + CommandRunner,
{
fn run_with_args<S: AsRef<OsStr> + ?Sized>(
&self,
program: &str,
args: &[&S],
) -> IoResult<Output> {
let raw_new_args = [self.user_name, "-s", "/usr/bin/env", "--", program];
let mut new_args: Vec<&OsStr> = raw_new_args.iter().map(|s| s.as_ref()).collect();
let old_args: Vec<&OsStr> = args.iter().map(|s| s.as_ref()).collect();
@ -115,5 +172,4 @@ impl<'a, C> CommandRunner for SuCommandRunner<'a, C> where C: 'a + CommandRunner
}
#[cfg(test)]
mod test {
}
mod test {}

80
src/factory.rs

@ -5,29 +5,35 @@ use loggers::StdErrLogger;
use repository::SymbolRepository;
use resources::Resource;
use schema::{NonRepeatingSymbolRunner, ReportingSymbolRunner, RequirementsResolvingSymbolRunner};
use symbols::{Symbol, SymbolRunner};
use symbols::dir::Dir;
use symbols::list::List;
use symbols::owner::Owner;
use symbols::systemd::user_session::SystemdUserSession;
use symbols::tls::{TlsCsr, TlsKey};
use symbols::user::{User, UserAdder};
use symbols::user::SystemUserAdder;
use symbols::user::{User, UserAdder};
use symbols::{Symbol, SymbolRunner};
#[derive(Default)]
pub struct Factory {
}
pub struct Factory {}
impl Factory {
pub fn new() -> Self {
Default::default()
}
pub fn get_repo<'a, CR: CommandRunner>(&self, command_runner: &'a CR) -> DefaultSymbolRepository<'a, SystemUserAdder<'a, CR>, CR> {
pub fn get_repo<'a, CR: CommandRunner>(
&self,
command_runner: &'a CR,
) -> DefaultSymbolRepository<'a, SystemUserAdder<'a, CR>, CR> {
DefaultSymbolRepository::new(command_runner)
}
pub fn get_symbol_runner<'a, RUNNER: SymbolRunner, REPO: SymbolRepository<'a>>(&self, symbol_runner: &'a RUNNER, repo: &'a REPO) -> Box<dyn 'a + SymbolRunner> {
pub fn get_symbol_runner<'a, RUNNER: SymbolRunner, REPO: SymbolRepository<'a>>(
&self,
symbol_runner: &'a RUNNER,
repo: &'a REPO,
) -> Box<dyn 'a + SymbolRunner> {
let runner1 = ReportingSymbolRunner::new(symbol_runner, StdErrLogger);
let runner2 = NonRepeatingSymbolRunner::new(runner1);
Box::new(RequirementsResolvingSymbolRunner::new(runner2, repo))
@ -42,7 +48,7 @@ pub struct DefaultSymbolRepository<'a, A: 'a + UserAdder, C: 'a + CommandRunner>
home_config: Regex,
csr: Regex,
private_key: Regex,
systemd_linger: Regex
systemd_linger: Regex,
}
impl<'a, C: 'a + CommandRunner> DefaultSymbolRepository<'a, SystemUserAdder<'a, C>, C> {
@ -54,54 +60,64 @@ impl<'a, C: 'a + CommandRunner> DefaultSymbolRepository<'a, SystemUserAdder<'a,
home_config: Regex::new("^/home/([^/]+)/.config(?:/|$)").unwrap(),
csr: Regex::new("^/etc/ssl/local_certs/([^/]+).csr$").unwrap(),
private_key: Regex::new("^/etc/ssl/private/([^/]+).key$").unwrap(),
systemd_linger: Regex::new("^/var/lib/systemd/linger/([^/]+)$").unwrap()
systemd_linger: Regex::new("^/var/lib/systemd/linger/([^/]+)$").unwrap(),
}
}
}
impl<'a, C: CommandRunner> SymbolRepository<'a> for DefaultSymbolRepository<'a, SystemUserAdder<'a, C>, C> {
impl<'a, C: CommandRunner> SymbolRepository<'a>
for DefaultSymbolRepository<'a, SystemUserAdder<'a, C>, C>
{
fn get_symbol(&'a self, resource: &Resource) -> Option<Box<dyn Symbol + 'a>> {
match resource.get_type() {
"user" => Some(Box::new(User::new(
resource.get_value().to_string().into(),
self.command_runner,
&self.user_adder
))),
resource.get_value().to_string().into(),
self.command_runner,
&self.user_adder,
))),
"dir" => {
let value = resource.get_value();
Some(
if let Some(matches) = self.home_config.captures(value) {
Box::new(List::new(vec![
Box::new(Dir::new(value.to_string())),
Box::new(Owner::new(value.to_string(), matches[1].to_string().into(), self.command_runner))
])) as Box<dyn Symbol>
} else if let Some(matches) = self.home.captures(value) {
Box::new(
User::new(matches[1].to_string().into(), self.command_runner, &self.user_adder)
) as Box<dyn Symbol>
} else { Box::new(Dir::new(value.to_string())) as Box<dyn Symbol> }
)
},
Some(if let Some(matches) = self.home_config.captures(value) {
Box::new(List::new(vec![
Box::new(Dir::new(value.to_string())),
Box::new(Owner::new(
value.to_string(),
matches[1].to_string().into(),
self.command_runner,
)),
])) as Box<dyn Symbol>
} else if let Some(matches) = self.home.captures(value) {
Box::new(User::new(
matches[1].to_string().into(),
self.command_runner,
&self.user_adder,
)) as Box<dyn Symbol>
} else {
Box::new(Dir::new(value.to_string())) as Box<dyn Symbol>
})
}
"file" => {
let value = resource.get_value();
if let Some(matches) = self.csr.captures(value) {
Some(Box::new(TlsCsr::new(
matches[1].to_string().into(),
self.command_runner
self.command_runner,
)) as Box<dyn Symbol>)
} else if let Some(matches) = self.private_key.captures(value) {
Some(Box::new(TlsKey::new(
matches[1].to_string().into(),
self.command_runner
self.command_runner,
)) as Box<dyn Symbol>)
} else if let Some(matches) = self.systemd_linger.captures(value) {
Some(Box::new(SystemdUserSession::new(
matches[1].to_string().into(),
self.command_runner
self.command_runner,
)) as Box<dyn Symbol>)
} else { None }
},
_ => None
} else {
None
}
}
_ => None,
}
}
}

23
src/lib.rs

@ -1,22 +1,21 @@
// rustfmt
#![deny(trivial_numeric_casts, unsafe_code,
unstable_features, unused_extern_crates,
unused_import_braces, unused_qualifications,
variant_size_differences
#![deny(
trivial_numeric_casts,
unsafe_code,
unstable_features,
unused_extern_crates,
unused_import_braces,
unused_qualifications,
variant_size_differences
)]
#![warn(
unused_results
)]
#![warn(unused_results)]
/*
#![warn(missing_docs, trivial_casts,
missing_copy_implementations,
missing_debug_implementations
)]
*/
#![allow(box_pointers)]
extern crate regex;
@ -27,8 +26,8 @@ pub mod build;
pub mod command_runner;
pub mod factory;
pub mod loggers;
pub mod symbols;
pub mod schema;
pub mod repository;
pub mod resources;
pub mod schema;
pub mod storage;
pub mod symbols;

9
src/loggers.rs

@ -18,7 +18,7 @@ impl Logger for StdErrLogger {
}
pub struct FilteringLogger<'a> {
logger: &'a mut dyn Logger
logger: &'a mut dyn Logger,
}
impl<'a> FilteringLogger<'a> {
@ -28,14 +28,11 @@ impl<'a> FilteringLogger<'a> {
}
impl<'a> Logger for FilteringLogger<'a> {
fn debug(&mut self, _str: &str) {
return
}
fn debug(&mut self, _str: &str) {}
fn write(&mut self, str: &str) {
self.logger.write(str)
}
}
#[cfg(test)]
mod test {
}
mod test {}

14
src/repository.rs

@ -1,20 +1,23 @@
use std::collections::HashMap;
use symbols::Symbol;
use resources::Resource;
use symbols::Symbol;
pub trait SymbolRepository<'a> {
fn get_symbol(&'a self, resource: &Resource) -> Option<Box<dyn Symbol + 'a>>;
}
impl<'a, C> SymbolRepository<'a> for C where C: Fn(&Resource) -> Option<Box<dyn Symbol + 'a>> {
impl<'a, C> SymbolRepository<'a> for C
where
C: Fn(&Resource) -> Option<Box<dyn Symbol + 'a>>,
{
fn get_symbol(&'a self, resource: &Resource) -> Option<Box<dyn Symbol + 'a>> {
self(resource)
}
}
pub struct DispatchingSymbolRepository<'a> {
repositories: HashMap<&'a str, Box<dyn SymbolRepository<'a> + 'a>>
repositories: HashMap<&'a str, Box<dyn SymbolRepository<'a> + 'a>>,
}
impl<'a> DispatchingSymbolRepository<'a> {
@ -25,6 +28,9 @@ impl<'a> DispatchingSymbolRepository<'a> {
impl<'a> SymbolRepository<'a> for DispatchingSymbolRepository<'a> {
fn get_symbol(&'a self, resource: &Resource) -> Option<Box<dyn Symbol + 'a>> {
self.repositories.get(resource.get_type()).and_then(|repo| repo.get_symbol(resource))
self
.repositories
.get(resource.get_type())
.and_then(|repo| repo.get_symbol(resource))
}
}

150
src/schema.rs

@ -9,20 +9,20 @@ use symbols::{Symbol, SymbolRunner};
#[derive(Debug)]
pub enum SymbolRunError {
Symbol(Box<dyn Error>),
ExecuteDidNotReach(())
ExecuteDidNotReach(()),
}
impl Error for SymbolRunError {
fn description(&self) -> &str {
match self {
SymbolRunError::Symbol(_) => "Symbol execution error",
SymbolRunError::ExecuteDidNotReach(_) => "Target not reached after executing symbol"
SymbolRunError::ExecuteDidNotReach(_) => "Target not reached after executing symbol",
}
}
fn cause(&self) -> Option<&dyn Error> {
match self {
SymbolRunError::Symbol(ref e) => Some(&**e),
SymbolRunError::ExecuteDidNotReach(_) => None
SymbolRunError::ExecuteDidNotReach(_) => None,
}
}
}
@ -31,24 +31,25 @@ impl fmt::Display for SymbolRunError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
SymbolRunError::Symbol(ref e) => write!(f, "{}", e),
SymbolRunError::ExecuteDidNotReach(_) => write!(f, "{}", self.description())
SymbolRunError::ExecuteDidNotReach(_) => write!(f, "{}", self.description()),
}
}
}
pub struct InitializingSymbolRunner<L: Logger> {
logger: RefCell<L>
logger: RefCell<L>,
}
impl<L: Logger> InitializingSymbolRunner<L> {
pub fn new(logger: L) -> Self {
Self { logger: RefCell::new(logger) }
Self {
logger: RefCell::new(logger),
}
}
}
impl<L: Logger> SymbolRunner for InitializingSymbolRunner<L> {
fn run_symbol(&self, symbol: &dyn Symbol) -> Result<(), Box<dyn Error>>
{
fn run_symbol(&self, symbol: &dyn Symbol) -> Result<(), Box<dyn Error>> {
let mut logger = self.logger.borrow_mut();
let target_reached = try!(symbol.target_reached());
if target_reached {
@ -58,7 +59,13 @@ impl<L: Logger> SymbolRunner for InitializingSymbolRunner<L> {
logger.write(format!("Executing {}", symbol).as_str());
try!(symbol.execute());
let target_reached = try!(symbol.target_reached());
logger.debug(format!("Symbol reports target_reached: {:?} (should be true)", target_reached).as_str());
logger.debug(
format!(
"Symbol reports target_reached: {:?} (should be true)",
target_reached
)
.as_str(),
);
if !target_reached {
return Err(Box::new(SymbolRunError::ExecuteDidNotReach(())));
}
@ -68,18 +75,19 @@ impl<L: Logger> SymbolRunner for InitializingSymbolRunner<L> {
}
pub struct DrySymbolRunner<L: Logger> {
logger: RefCell<L>
logger: RefCell<L>,
}
impl<L: Logger> DrySymbolRunner<L> {
pub fn new(logger: L) -> Self {
Self { logger: RefCell::new(logger) }
Self {
logger: RefCell::new(logger),
}
}
}
impl<L: Logger> SymbolRunner for DrySymbolRunner<L> {
fn run_symbol(&self, symbol: &dyn Symbol) -> Result<(), Box<dyn Error>>
{
fn run_symbol(&self, symbol: &dyn Symbol) -> Result<(), Box<dyn Error>> {
let mut logger = self.logger.borrow_mut();
let target_reached = try!(symbol.target_reached());
logger.debug(format!("Symbol reports target_reached: {:?}", target_reached).as_str());
@ -92,22 +100,29 @@ impl<L: Logger> SymbolRunner for DrySymbolRunner<L> {
pub struct ReportingSymbolRunner<'a, R: 'a + SymbolRunner, L: Logger>(&'a R, RefCell<L>);
impl<'a, R, L> ReportingSymbolRunner<'a, R, L> where R: SymbolRunner, L: Logger {
impl<'a, R, L> ReportingSymbolRunner<'a, R, L>
where
R: SymbolRunner,
L: Logger,
{
pub fn new(symbol_runner: &'a R, logger: L) -> Self {
ReportingSymbolRunner(symbol_runner, RefCell::new(logger))
}
}
impl<'a, R, L> SymbolRunner for ReportingSymbolRunner<'a, R, L> where R: SymbolRunner, L: Logger {
fn run_symbol(&self, symbol: &dyn Symbol) -> Result<(), Box<dyn Error>>
{
impl<'a, R, L> SymbolRunner for ReportingSymbolRunner<'a, R, L>
where
R: SymbolRunner,
L: Logger,
{
fn run_symbol(&self, symbol: &dyn Symbol) -> Result<(), Box<dyn Error>> {
let mut logger = self.1.borrow_mut();
logger.debug(format!("Running symbol {}", symbol).as_str());
let res = self.0.run_symbol(symbol);
match res {
Err(ref e) => {
logger.write(format!("Failed on {} with {}, aborting.", symbol, e).as_str());
},
}
Ok(_) => {
logger.debug(format!("Successfully finished {}", symbol).as_str());
}
@ -116,23 +131,28 @@ impl<'a, R, L> SymbolRunner for ReportingSymbolRunner<'a, R, L> where R: SymbolR
}
}
use std::collections::HashSet;
use resources::Resource;
use std::collections::HashSet;
pub struct NonRepeatingSymbolRunner<R: SymbolRunner> {
upstream: R,
done: RefCell<HashSet<Resource>>
done: RefCell<HashSet<Resource>>,
}
impl<R> NonRepeatingSymbolRunner<R> where R: SymbolRunner {
impl<R> NonRepeatingSymbolRunner<R>
where
R: SymbolRunner,
{
pub fn new(symbol_runner: R) -> Self {
NonRepeatingSymbolRunner{ upstream: symbol_runner, done: RefCell::new(HashSet::new()) }
NonRepeatingSymbolRunner {
upstream: symbol_runner,
done: RefCell::new(HashSet::new()),
}
}
}
impl<R: SymbolRunner> SymbolRunner for NonRepeatingSymbolRunner<R> {
fn run_symbol(&self, symbol: &dyn Symbol) -> Result<(), Box<dyn Error>>
{
fn run_symbol(&self, symbol: &dyn Symbol) -> Result<(), Box<dyn Error>> {
if let Some(resources) = symbol.provides() {
let mut done = self.done.borrow_mut();
let mut has_to_run = false;
@ -151,17 +171,29 @@ impl<R: SymbolRunner> SymbolRunner for NonRepeatingSymbolRunner<R> {
}
use std::marker::PhantomData;
pub struct RequirementsResolvingSymbolRunner<'a, 's, R: 'a + SymbolRunner, G: 'a + SymbolRepository<'s>>(R, &'a G, PhantomData<Box<dyn Symbol + 's>>);
impl<'s, 'a: 's, R, G> RequirementsResolvingSymbolRunner<'a, 's, R, G> where R: SymbolRunner, G: SymbolRepository<'s> {
pub struct RequirementsResolvingSymbolRunner<
'a,
's,
R: 'a + SymbolRunner,
G: 'a + SymbolRepository<'s>,
>(R, &'a G, PhantomData<Box<dyn Symbol + 's>>);
impl<'s, 'a: 's, R, G> RequirementsResolvingSymbolRunner<'a, 's, R, G>
where
R: SymbolRunner,
G: SymbolRepository<'s>,
{
pub fn new(symbol_runner: R, symbol_repo: &'a G) -> Self {
RequirementsResolvingSymbolRunner(symbol_runner, symbol_repo, PhantomData)
}
}
impl<'s, 'a: 's, R, G> SymbolRunner for RequirementsResolvingSymbolRunner<'a, 's, R, G> where R: SymbolRunner, G: SymbolRepository<'s> {
fn run_symbol(&self, symbol: &dyn Symbol) -> Result<(), Box<dyn Error>>
{
impl<'s, 'a: 's, R, G> SymbolRunner for RequirementsResolvingSymbolRunner<'a, 's, R, G>
where
R: SymbolRunner,
G: SymbolRepository<'s>,
{
fn run_symbol(&self, symbol: &dyn Symbol) -> Result<(), Box<dyn Error>> {
for resource in symbol.get_prerequisites() {
if let Some(dep) = self.1.get_symbol(&resource) {
try!(dep.as_action(self).run());
@ -180,13 +212,13 @@ mod test {
use std::fmt;
use loggers::Logger;
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction};
use schema::SymbolRunner;
use schema::InitializingSymbolRunner;
use schema::SymbolRunner;
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction};
#[derive(Debug, PartialEq, Clone)]
enum DummySymbolError {
Error(())
Error(()),
}
impl Error for DummySymbolError {
@ -203,7 +235,7 @@ mod test {
struct DummySymbol<'a> {
_execute: &'a dyn Fn() -> Result<(), Box<dyn Error>>,
_target_reached: &'a dyn Fn() -> Result<bool, Box<dyn Error>>
_target_reached: &'a dyn Fn() -> Result<bool, Box<dyn Error>>,
}
impl<'b> Symbol for DummySymbol<'b> {
@ -218,7 +250,10 @@ mod test {
Box::new(SymbolAction::new(runner, self))
}
fn into_action<'a>(self: Box<Self>, runner: &'a dyn SymbolRunner) -> Box<dyn Action + 'a> where Self: 'a {
fn into_action<'a>(self: Box<Self>, runner: &'a dyn SymbolRunner) -> Box<dyn Action + 'a>
where
Self: 'a,
{
Box::new(OwnedSymbolAction::new(runner, *self))
}
}
@ -230,13 +265,19 @@ mod test {
}
impl<'a> DummySymbol<'a> {
fn new(target_reached: &'a dyn Fn() -> Result<bool, Box<dyn Error>>, execute: &'a dyn Fn() -> Result<(), Box<dyn Error>>) -> DummySymbol<'a> {
DummySymbol { _target_reached: target_reached, _execute: execute }
fn new(
target_reached: &'a dyn Fn() -> Result<bool, Box<dyn Error>>,
execute: &'a dyn Fn() -> Result<(), Box<dyn Error>>,
) -> DummySymbol<'a> {
DummySymbol {
_target_reached: target_reached,
_execute: execute,
}
}
}
struct DummyLogger {
log: Vec<String>
log: Vec<String>,
}
impl DummyLogger {
@ -256,28 +297,33 @@ mod test {
#[test]
fn nothing_needed_to_be_done() {
let result = InitializingSymbolRunner::new(DummyLogger::new()).run_symbol(&DummySymbol::new(&|| Ok(true), &|| Ok(())));
let result = InitializingSymbolRunner::new(DummyLogger::new())
.run_symbol(&DummySymbol::new(&|| Ok(true), &|| Ok(())));
assert!(result.is_ok());
}
#[test]
fn everything_is_ok() {
let first = RefCell::new(true);
let result = InitializingSymbolRunner::new(DummyLogger::new()).run_symbol(&DummySymbol::new(&|| {
let mut _first = first.borrow_mut();
Ok(if *_first {
*_first = false;
true
} else {
false
})
}, &|| Ok(())));
let result = InitializingSymbolRunner::new(DummyLogger::new()).run_symbol(&DummySymbol::new(
&|| {
let mut _first = first.borrow_mut();
Ok(if *_first {
*_first = false;
true
} else {
false
})
},
&|| Ok(()),
));
assert!(result.is_ok());
}
#[test]
fn executing_did_not_change_state() {
let result = InitializingSymbolRunner::new(DummyLogger::new()).run_symbol(&DummySymbol::new(&|| Ok(false), &|| Ok(())));
let result = InitializingSymbolRunner::new(DummyLogger::new())
.run_symbol(&DummySymbol::new(&|| Ok(false), &|| Ok(())));
assert_eq!(
result.unwrap_err().description(),
"Target not reached after executing symbol"
@ -286,7 +332,11 @@ mod test {
#[test]
fn executing_did_not_work() {
let err = InitializingSymbolRunner::new(DummyLogger::new()).run_symbol(&DummySymbol::new(&|| Ok(false), &|| Err(Box::new(DummySymbolError::Error(()))))).unwrap_err();
let err = InitializingSymbolRunner::new(DummyLogger::new())
.run_symbol(&DummySymbol::new(&|| Ok(false), &|| {
Err(Box::new(DummySymbolError::Error(())))
}))
.unwrap_err();
assert_eq!(err.description(), "Description");
}
}

22
src/storage.rs

@ -20,14 +20,19 @@ impl SimpleStorage {
fn get_path(&self, date: Option<u64>) -> String {
match date {
Some(d) => format!("{}/_{}/{}", self.0, self.1, d),
None => format!("{}/_{}", self.0, self.1)
None => format!("{}/_{}", self.0, self.1),
}
}
}
impl Storage for SimpleStorage {
fn write_filename(&self) -> String {
self.get_path(Some(SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs()))
self.get_path(Some(
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs(),
))
}
fn read_filename(&self) -> Result<String, Box<dyn Error>> {
@ -37,8 +42,15 @@ impl Storage for SimpleStorage {
fn recent_date(&self) -> Result<u64, Box<dyn Error>> {
let dir = self.get_path(None);
try!(read_dir(dir))
.map(|entry| entry.ok().and_then(|e| e.file_name().into_string().ok()).and_then(|filename| u64::from_str(&filename).ok()))
.fold(None, |maybe_newest, maybe_time| maybe_newest.into_iter().chain(maybe_time).max())
.ok_or_else(|| "Not found".to_string().into())
.map(|entry| {
entry
.ok()
.and_then(|e| e.file_name().into_string().ok())
.and_then(|filename| u64::from_str(&filename).ok())
})
.fold(None, |maybe_newest, maybe_time| {
maybe_newest.into_iter().chain(maybe_time).max()
})
.ok_or_else(|| "Not found".to_string().into())
}
}

42
src/symbols/acme/account_key.rs

@ -9,12 +9,15 @@ use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
pub struct AcmeAccountKey<'a, C: 'a + CommandRunner> {
path: Cow<'a, Path>,
command_runner: &'a C
command_runner: &'a C,
}
impl<'a, C: CommandRunner> AcmeAccountKey<'a, C> {
pub fn new(path: Cow<'a, Path>, command_runner: &'a C) -> Self {
AcmeAccountKey { path, command_runner }
AcmeAccountKey {
path,
command_runner,
}
}
fn get_bytes(&self) -> u32 {
@ -33,27 +36,50 @@ impl<'a, C: CommandRunner> Symbol for AcmeAccountKey<'a, C> {
if !self.path.exists() {
return Ok(false);
}
let stdout = try!(self.command_runner.get_output("openssl", &["rsa".as_ref(), "-in".as_ref(), self.path.as_os_str(), "-noout".as_ref(), "-check".as_ref(), "-text".as_ref()]));
let stdout = try!(self.command_runner.get_output(
"openssl",
&[
"rsa".as_ref(),
"-in".as_ref(),
self.path.as_os_str(),
"-noout".as_ref(),
"-check".as_ref(),
"-text".as_ref()
]
));
Ok(stdout.starts_with(&format!("Private-Key: ({} bit)\n", self.get_bytes()).as_bytes()))
}
fn execute(&self) -> Result<(), Box<dyn Error>> {
self.command_runner.run_successfully("openssl", &["genrsa".as_ref(), "-out".as_ref(), self.path.as_os_str(), self.get_bytes().to_string().as_ref()])
self.command_runner.run_successfully(
"openssl",
&[
"genrsa".as_ref(),
"-out".as_ref(),
self.path.as_os_str(),
self.get_bytes().to_string().as_ref(),
],
)
}
fn get_prerequisites(&self) -> Vec<Resource> {
vec![ Resource::new("dir", self.path.parent().unwrap().to_string_lossy() ) ]
vec![Resource::new(
"dir",
self.path.parent().unwrap().to_string_lossy(),
)]
}
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
Box::new(SymbolAction::new(runner, self))
}
fn into_action<'b>(self: Box<Self>, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> where Self: 'b {
fn into_action<'b>(self: Box<Self>, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b>
where
Self: 'b,
{
Box::new(OwnedSymbolAction::new(runner, *self))
}
}
#[cfg(test)]
mod test {
}
mod test {}

75
src/symbols/acme/cert.rs

@ -6,17 +6,20 @@ use std::io::Write;
use std::path::Path;
use command_runner::CommandRunner;
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
use resources::Resource;
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
pub struct AcmeCert<'a, C: 'a + CommandRunner> {
domain: Cow<'a, str>,
command_runner: &'a C
command_runner: &'a C,
}
impl<'a, C: CommandRunner> AcmeCert<'a, C> {
pub fn new(domain: Cow<'a, str>, command_runner: &'a C) -> Self {
AcmeCert { domain, command_runner }
AcmeCert {
domain,
command_runner,
}
}
fn get_csr_path(&self) -> String {
@ -34,7 +37,7 @@ impl<'a, C: CommandRunner> fmt::Display for AcmeCert<'a, C> {
}
}
const DAYS_IN_SECONDS: u32 = 24*60*60;
const DAYS_IN_SECONDS: u32 = 24 * 60 * 60;
impl<'a, C: CommandRunner> Symbol for AcmeCert<'a, C> {
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
@ -42,10 +45,44 @@ impl<'a, C: CommandRunner> Symbol for AcmeCert<'a, C> {
return Ok(false);
}
let output = try!(self.command_runner.run_with_args("openssl", &["x509", "-in", &self.get_cert_path(), "-noout", "-subject", "-checkend", &(30*DAYS_IN_SECONDS).to_string()]));
if output.status.success() && output.stdout == format!("subject=CN = {}\nCertificate will not expire\n", self.domain).as_bytes() {
Ok(self.command_runner.run_successfully("openssl", &["verify", "--untrusted", "/home/acme/lets_encrypt_x3_cross_signed.pem", &self.get_cert_path()]).is_ok())
} else if output.status.code() == Some(1) && output.stdout == format!("subject=CN = {}\nCertificate will expire\n", self.domain).as_bytes() {
let output = try!(self.command_runner.run_with_args(
"openssl",
&[
"x509",
"-in",
&self.get_cert_path(),
"-noout",
"-subject",
"-checkend",
&(30 * DAYS_IN_SECONDS).to_string()
]
));
if output.status.success()
&& output.stdout
== format!(
"subject=CN = {}\nCertificate will not expire\n",
self.domain
)
.as_bytes()
{
Ok(
self
.command_runner
.run_successfully(
"openssl",
&[
"verify",
"--untrusted",
"/home/acme/lets_encrypt_x3_cross_signed.pem",
&self.get_cert_path(),
],
)
.is_ok(),
)
} else if output.status.code() == Some(1)
&& output.stdout
== format!("subject=CN = {}\nCertificate will expire\n", self.domain).as_bytes()
{
Ok(false)
} else {
Err(try!(String::from_utf8(output.stderr)).into())
@ -53,25 +90,37 @@ impl<'a, C: CommandRunner> Symbol for AcmeCert<'a, C> {
}
fn execute(&self) -> Result<(), Box<dyn Error>> {
let output = try!(self.command_runner.get_output("acme-tiny", &["--account-key", "/home/acme/account.key", "--csr", &self.get_csr_path(), "--acme-dir", "/home/acme/challenges/"]));
let output = try!(self.command_runner.get_output(
"acme-tiny",
&[
"--account-key",
"/home/acme/account.key",
"--csr",
&self.get_csr_path(),
"--acme-dir",
"/home/acme/challenges/"
]
));
let mut file = try!(FsFile::create(self.get_cert_path()));
try!(file.write_all(&output));
Ok(())
}
fn get_prerequisites(&self) -> Vec<Resource> {
vec![ Resource::new("file", self.get_csr_path()) ]
vec![Resource::new("file", self.get_csr_path())]
}
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
Box::new(SymbolAction::new(runner, self))
}
fn into_action<'b>(self: Box<Self>, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> where Self: 'b {
fn into_action<'b>(self: Box<Self>, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b>
where
Self: 'b,
{
Box::new(OwnedSymbolAction::new(runner, *self))
}
}
#[cfg(test)]
mod test {
}
mod test {}

65
src/symbols/acme/chain.rs

@ -6,17 +6,20 @@ use std::io::Write;
use std::path::Path;
use command_runner::CommandRunner;
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
use resources::Resource;
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
pub struct AcmeCertChain<'a, C: 'a + CommandRunner> {
domain: Cow<'a, str>,
command_runner: &'a C
command_runner: &'a C,
}
impl<'a, C: CommandRunner> AcmeCertChain<'a, C> {
pub fn new(domain: Cow<'a, str>, command_runner: &'a C) -> Self {
AcmeCertChain { domain, command_runner }
AcmeCertChain {
domain,
command_runner,
}
}
fn get_single_cert_path(&self) -> String {
@ -34,7 +37,7 @@ impl<'a, C: CommandRunner> fmt::Display for AcmeCertChain<'a, C> {
}
}
const DAYS_IN_SECONDS: u32 = 24*60*60;
const DAYS_IN_SECONDS: u32 = 24 * 60 * 60;
impl<'a, C: CommandRunner> Symbol for AcmeCertChain<'a, C> {
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
@ -42,34 +45,72 @@ impl<'a, C: CommandRunner> Symbol for AcmeCertChain<'a, C> {
return Ok(false);
}
let stdout = try!(self.command_runner.get_output("openssl", &["x509", "-in", &self.get_cert_chain_path(), "-noout", "-subject", "-checkend", &(30*DAYS_IN_SECONDS).to_string()]));
if stdout != format!("subject=CN = {}\nCertificate will not expire\n", self.domain).as_bytes() {
let stdout = try!(self.command_runner.get_output(
"openssl",
&[
"x509",
"-in",
&self.get_cert_chain_path(),
"-noout",
"-subject",
"-checkend",
&(30 * DAYS_IN_SECONDS).to_string()
]
));
if stdout
!= format!(
"subject=CN = {}\nCertificate will not expire\n",
self.domain
)
.as_bytes()
{
return Ok(false);
}
// FIXME: From my understanding, the -untrusted *.pem parameter shouldn't be necessary, but is necessary with openssl 1.1.0f-3
Ok(self.command_runner.run_successfully("openssl", &["verify", "-untrusted", "/home/acme/lets_encrypt_x3_cross_signed.pem", &self.get_cert_chain_path()]).is_ok())
Ok(
self
.command_runner
.run_successfully(
"openssl",
&[
"verify",
"-untrusted",
"/home/acme/lets_encrypt_x3_cross_signed.pem",
&self.get_cert_chain_path(),
],
)
.is_ok(),
)
}
fn execute(&self) -> Result<(), Box<dyn Error>> {
let output = try!(self.command_runner.get_output("cat", &[self.get_single_cert_path().as_ref(), "/home/acme/lets_encrypt_x3_cross_signed.pem"]));
let output = try!(self.command_runner.get_output(
"cat",
&[
self.get_single_cert_path().as_ref(),
"/home/acme/lets_encrypt_x3_cross_signed.pem"
]
));
let mut file = try!(FsFile::create(self.get_cert_chain_path()));
try!(file.write_all(&output));
Ok(())
}
fn get_prerequisites(&self) -> Vec<Resource> {
vec![ Resource::new("file", self.get_single_cert_path()) ]
vec![Resource::new("file", self.get_single_cert_path())]
}
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
Box::new(SymbolAction::new(runner, self))
}
fn into_action<'b>(self: Box<Self>, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> where Self: 'b {
fn into_action<'b>(self: Box<Self>, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b>
where
Self: 'b,
{
Box::new(OwnedSymbolAction::new(runner, *self))
}
}
#[cfg(test)]
mod test {
}
mod test {}

49
src/symbols/acme/user.rs

@ -1,17 +1,17 @@
use resources::Resource;
use std::borrow::{Borrow, Cow};
use std::error::Error;
use std::fmt;
use std::ops::Deref;
use std::path::PathBuf;
use resources::Resource;
use command_runner::CommandRunner;
use symbols::{Action, OwnedSymbolAction, SymbolAction, SymbolRunner, Symbol};
use symbols::acme::AcmeAccountKey;
use symbols::dir::Dir;
use symbols::file::File;
use symbols::list::List;
use symbols::owner::Owner;
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
pub struct AcmeUser<'a>(Cow<'a, str>);
@ -38,7 +38,10 @@ impl<'a> Symbol for AcmeUser<'a> {
Box::new(SymbolAction::new(runner, self))
}
fn into_action<'b>(self: Box<Self>, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> where Self: 'b {
fn into_action<'b>(self: Box<Self>, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b>
where
Self: 'b,
{
Box::new(OwnedSymbolAction::new(runner, *self))
}
}
@ -49,17 +52,41 @@ impl<'a> fmt::Display for AcmeUser<'a> {
}
}
pub fn new<'a, S: Into<Cow<'a, str>>, C: CommandRunner, P: 'a + Deref<Target=str>>(command_runner: &'a C, cert: P, user_name: S) -> Box<dyn Symbol + 'a> { // impl trait
pub fn new<'a, S: Into<Cow<'a, str>>, C: CommandRunner, P: 'a + Deref<Target = str>>(
command_runner: &'a C,
cert: P,
user_name: S,
) -> Box<dyn Symbol + 'a> {
// impl trait
let user_name_cow = user_name.into();
let account_key_file: PathBuf = ["/home", user_name_cow.borrow(), "account.key"].iter().collect();
let account_key_file: PathBuf = ["/home", user_name_cow.borrow(), "account.key"]
.iter()
.collect();
Box::new(List::new(vec![
Box::new(AcmeAccountKey::new(account_key_file.clone().into(), command_runner)),
Box::new(Owner::new(account_key_file.to_string_lossy().into_owned(), user_name_cow.clone(), command_runner)),
Box::new(AcmeAccountKey::new(
account_key_file.clone().into(),
command_runner,
)),
Box::new(Owner::new(
account_key_file.to_string_lossy().into_owned(),
user_name_cow.clone(),
command_runner,
)),
Box::new(Dir::new("/home/acme/challenges")),
Box::new(Owner::new("/home/acme/challenges", user_name_cow.clone(), command_runner)),
Box::new(Owner::new(
"/home/acme/challenges",
user_name_cow.clone(),
command_runner,
))