Cargo format
This commit is contained in:
parent
9bab810b91
commit
8c0224e983
44 changed files with 1784 additions and 611 deletions
1
rustfmt.toml
Normal file
1
rustfmt.toml
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
tab_spaces = 2
|
||||||
17
src/bin.rs
17
src/bin.rs
|
|
@ -1,13 +1,22 @@
|
||||||
use std::process::exit;
|
|
||||||
use std::env;
|
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 args: Vec<String> = env::args().collect();
|
||||||
let dry_run = match args.len() {
|
let dry_run = match args.len() {
|
||||||
1 => false,
|
1 => false,
|
||||||
2 => if args[1] == "--dry-run" { true } else { panic!() },
|
2 => {
|
||||||
_ => panic!()
|
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
24
src/build.rs
|
|
@ -7,7 +7,13 @@ use std::path::{Path, PathBuf};
|
||||||
fn get_const_name<P: Clone + Into<PathBuf>>(p: &P) -> String {
|
fn get_const_name<P: Clone + Into<PathBuf>>(p: &P) -> String {
|
||||||
let mut file_name_without_extension = p.clone().into();
|
let mut file_name_without_extension = p.clone().into();
|
||||||
file_name_without_extension.set_extension("");
|
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) {
|
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() {
|
for maybe_dir_entry in read_dir(source_dir).unwrap() {
|
||||||
let file_path = maybe_dir_entry.unwrap().path();
|
let file_path = maybe_dir_entry.unwrap().path();
|
||||||
let mut buffer = String::new();
|
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>() + "#";
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,22 +5,35 @@ use std::process::Command;
|
||||||
use std::process::Output;
|
use std::process::Output;
|
||||||
|
|
||||||
pub trait CommandRunner {
|
pub trait CommandRunner {
|
||||||
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])
|
||||||
fn get_output<S: AsRef<OsStr> + ?Sized>(&self, program: &str, args: &[&S]) -> Result<Vec<u8>, Box<dyn Error>> {
|
-> 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));
|
let output = try!(self.run_with_args(program, args));
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
return Err(try!(String::from_utf8(output.stderr)).into());
|
return Err(try!(String::from_utf8(output.stderr)).into());
|
||||||
}
|
}
|
||||||
Ok(output.stdout)
|
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));
|
let output = try!(self.run_with_args(program, args));
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
return Err(try!(String::from_utf8(output.stderr)).into());
|
return Err(try!(String::from_utf8(output.stderr)).into());
|
||||||
}
|
}
|
||||||
Ok(output.stderr)
|
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));
|
let output = try!(self.run_with_args(program, args));
|
||||||
if output.status.success() {
|
if output.status.success() {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -34,7 +47,11 @@ pub trait CommandRunner {
|
||||||
pub struct StdCommandRunner;
|
pub struct StdCommandRunner;
|
||||||
|
|
||||||
impl CommandRunner for 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
|
// FIXME: logger
|
||||||
//println!("{} {:?}", program, args);
|
//println!("{} {:?}", program, args);
|
||||||
let res = Command::new(program).args(args).output();
|
let res = Command::new(program).args(args).output();
|
||||||
|
|
@ -44,28 +61,43 @@ impl CommandRunner for StdCommandRunner {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SetuidCommandRunner<'a, C> where C: 'a + CommandRunner {
|
pub struct SetuidCommandRunner<'a, C>
|
||||||
|
where
|
||||||
|
C: 'a + CommandRunner,
|
||||||
|
{
|
||||||
command_runner: &'a C,
|
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> {
|
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::env;
|
||||||
|
use std::os::unix::process::CommandExt;
|
||||||
use users::get_user_by_name;
|
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> {
|
impl<'a> TempSetEnv<'a> {
|
||||||
fn new(name: &'a str, new_value: String) -> TempSetEnv<'a> {
|
fn new(name: &'a str, new_value: String) -> TempSetEnv<'a> {
|
||||||
let old_value = env::var(name);
|
let old_value = env::var(name);
|
||||||
env::set_var(name, new_value);
|
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) {
|
fn drop(&mut self) {
|
||||||
match self.old_value {
|
match self.old_value {
|
||||||
Some(ref val) => env::set_var(self.name, val),
|
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 {
|
impl<'a, C> CommandRunner for SetuidCommandRunner<'a, C>
|
||||||
fn run_with_args<S: AsRef<OsStr> + ?Sized>(&self, program: &str, args: &[&S]) -> IoResult<Output> {
|
where
|
||||||
let uid = get_user_by_name(self.user_name).expect("User does not exist").uid();
|
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_home = TempSetEnv::new("HOME", format!("/home/{}", self.user_name));
|
||||||
let set_dbus = TempSetEnv::new("XDG_RUNTIME_DIR", format!("/run/user/{}", uid));
|
let set_dbus = TempSetEnv::new("XDG_RUNTIME_DIR", format!("/run/user/{}", uid));
|
||||||
//println!("{} {:?}", program, args);
|
//println!("{} {:?}", program, args);
|
||||||
|
|
@ -91,21 +132,37 @@ impl<'a, C> CommandRunner for SetuidCommandRunner<'a, C> where C: 'a + CommandRu
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SuCommandRunner<'a, C> where C: 'a + CommandRunner {
|
pub struct SuCommandRunner<'a, C>
|
||||||
|
where
|
||||||
|
C: 'a + CommandRunner,
|
||||||
|
{
|
||||||
command_runner: &'a C,
|
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> {
|
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
|
// Su doesn't set XDG_RUNTIME_DIR
|
||||||
// https://github.com/systemd/systemd/blob/master/src/login/pam_systemd.c#L439
|
// 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 {
|
impl<'a, C> CommandRunner for SuCommandRunner<'a, C>
|
||||||
fn run_with_args<S: AsRef<OsStr> + ?Sized>(&self, program: &str, args: &[&S]) -> IoResult<Output> {
|
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 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 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();
|
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)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {}
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -5,29 +5,35 @@ use loggers::StdErrLogger;
|
||||||
use repository::SymbolRepository;
|
use repository::SymbolRepository;
|
||||||
use resources::Resource;
|
use resources::Resource;
|
||||||
use schema::{NonRepeatingSymbolRunner, ReportingSymbolRunner, RequirementsResolvingSymbolRunner};
|
use schema::{NonRepeatingSymbolRunner, ReportingSymbolRunner, RequirementsResolvingSymbolRunner};
|
||||||
use symbols::{Symbol, SymbolRunner};
|
|
||||||
use symbols::dir::Dir;
|
use symbols::dir::Dir;
|
||||||
use symbols::list::List;
|
use symbols::list::List;
|
||||||
use symbols::owner::Owner;
|
use symbols::owner::Owner;
|
||||||
use symbols::systemd::user_session::SystemdUserSession;
|
use symbols::systemd::user_session::SystemdUserSession;
|
||||||
use symbols::tls::{TlsCsr, TlsKey};
|
use symbols::tls::{TlsCsr, TlsKey};
|
||||||
use symbols::user::{User, UserAdder};
|
|
||||||
use symbols::user::SystemUserAdder;
|
use symbols::user::SystemUserAdder;
|
||||||
|
use symbols::user::{User, UserAdder};
|
||||||
|
use symbols::{Symbol, SymbolRunner};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Factory {
|
pub struct Factory {}
|
||||||
}
|
|
||||||
|
|
||||||
impl Factory {
|
impl Factory {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Default::default()
|
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)
|
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 runner1 = ReportingSymbolRunner::new(symbol_runner, StdErrLogger);
|
||||||
let runner2 = NonRepeatingSymbolRunner::new(runner1);
|
let runner2 = NonRepeatingSymbolRunner::new(runner1);
|
||||||
Box::new(RequirementsResolvingSymbolRunner::new(runner2, repo))
|
Box::new(RequirementsResolvingSymbolRunner::new(runner2, repo))
|
||||||
|
|
@ -42,7 +48,7 @@ pub struct DefaultSymbolRepository<'a, A: 'a + UserAdder, C: 'a + CommandRunner>
|
||||||
home_config: Regex,
|
home_config: Regex,
|
||||||
csr: Regex,
|
csr: Regex,
|
||||||
private_key: Regex,
|
private_key: Regex,
|
||||||
systemd_linger: Regex
|
systemd_linger: Regex,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: 'a + CommandRunner> DefaultSymbolRepository<'a, SystemUserAdder<'a, C>, C> {
|
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(),
|
home_config: Regex::new("^/home/([^/]+)/.config(?:/|$)").unwrap(),
|
||||||
csr: Regex::new("^/etc/ssl/local_certs/([^/]+).csr$").unwrap(),
|
csr: Regex::new("^/etc/ssl/local_certs/([^/]+).csr$").unwrap(),
|
||||||
private_key: Regex::new("^/etc/ssl/private/([^/]+).key$").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>> {
|
fn get_symbol(&'a self, resource: &Resource) -> Option<Box<dyn Symbol + 'a>> {
|
||||||
match resource.get_type() {
|
match resource.get_type() {
|
||||||
"user" => Some(Box::new(User::new(
|
"user" => Some(Box::new(User::new(
|
||||||
resource.get_value().to_string().into(),
|
resource.get_value().to_string().into(),
|
||||||
self.command_runner,
|
self.command_runner,
|
||||||
&self.user_adder
|
&self.user_adder,
|
||||||
))),
|
))),
|
||||||
"dir" => {
|
"dir" => {
|
||||||
let value = resource.get_value();
|
let value = resource.get_value();
|
||||||
Some(
|
Some(if let Some(matches) = self.home_config.captures(value) {
|
||||||
if let Some(matches) = self.home_config.captures(value) {
|
|
||||||
Box::new(List::new(vec![
|
Box::new(List::new(vec![
|
||||||
Box::new(Dir::new(value.to_string())),
|
Box::new(Dir::new(value.to_string())),
|
||||||
Box::new(Owner::new(value.to_string(), matches[1].to_string().into(), self.command_runner))
|
Box::new(Owner::new(
|
||||||
|
value.to_string(),
|
||||||
|
matches[1].to_string().into(),
|
||||||
|
self.command_runner,
|
||||||
|
)),
|
||||||
])) as Box<dyn Symbol>
|
])) as Box<dyn Symbol>
|
||||||
} else if let Some(matches) = self.home.captures(value) {
|
} else if let Some(matches) = self.home.captures(value) {
|
||||||
Box::new(
|
Box::new(User::new(
|
||||||
User::new(matches[1].to_string().into(), self.command_runner, &self.user_adder)
|
matches[1].to_string().into(),
|
||||||
) as Box<dyn Symbol>
|
self.command_runner,
|
||||||
} else { Box::new(Dir::new(value.to_string())) as Box<dyn Symbol> }
|
&self.user_adder,
|
||||||
)
|
)) as Box<dyn Symbol>
|
||||||
},
|
} else {
|
||||||
|
Box::new(Dir::new(value.to_string())) as Box<dyn Symbol>
|
||||||
|
})
|
||||||
|
}
|
||||||
"file" => {
|
"file" => {
|
||||||
let value = resource.get_value();
|
let value = resource.get_value();
|
||||||
if let Some(matches) = self.csr.captures(value) {
|
if let Some(matches) = self.csr.captures(value) {
|
||||||
Some(Box::new(TlsCsr::new(
|
Some(Box::new(TlsCsr::new(
|
||||||
matches[1].to_string().into(),
|
matches[1].to_string().into(),
|
||||||
self.command_runner
|
self.command_runner,
|
||||||
)) as Box<dyn Symbol>)
|
)) as Box<dyn Symbol>)
|
||||||
} else if let Some(matches) = self.private_key.captures(value) {
|
} else if let Some(matches) = self.private_key.captures(value) {
|
||||||
Some(Box::new(TlsKey::new(
|
Some(Box::new(TlsKey::new(
|
||||||
matches[1].to_string().into(),
|
matches[1].to_string().into(),
|
||||||
self.command_runner
|
self.command_runner,
|
||||||
)) as Box<dyn Symbol>)
|
)) as Box<dyn Symbol>)
|
||||||
} else if let Some(matches) = self.systemd_linger.captures(value) {
|
} else if let Some(matches) = self.systemd_linger.captures(value) {
|
||||||
Some(Box::new(SystemdUserSession::new(
|
Some(Box::new(SystemdUserSession::new(
|
||||||
matches[1].to_string().into(),
|
matches[1].to_string().into(),
|
||||||
self.command_runner
|
self.command_runner,
|
||||||
)) as Box<dyn Symbol>)
|
)) as Box<dyn Symbol>)
|
||||||
} else { None }
|
} else {
|
||||||
},
|
None
|
||||||
_ => None
|
}
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
21
src/lib.rs
21
src/lib.rs
|
|
@ -1,22 +1,21 @@
|
||||||
// rustfmt
|
// rustfmt
|
||||||
|
|
||||||
#![deny(trivial_numeric_casts, unsafe_code,
|
#![deny(
|
||||||
unstable_features, unused_extern_crates,
|
trivial_numeric_casts,
|
||||||
unused_import_braces, unused_qualifications,
|
unsafe_code,
|
||||||
|
unstable_features,
|
||||||
|
unused_extern_crates,
|
||||||
|
unused_import_braces,
|
||||||
|
unused_qualifications,
|
||||||
variant_size_differences
|
variant_size_differences
|
||||||
)]
|
)]
|
||||||
|
#![warn(unused_results)]
|
||||||
#![warn(
|
|
||||||
unused_results
|
|
||||||
)]
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#![warn(missing_docs, trivial_casts,
|
#![warn(missing_docs, trivial_casts,
|
||||||
missing_copy_implementations,
|
missing_copy_implementations,
|
||||||
missing_debug_implementations
|
missing_debug_implementations
|
||||||
)]
|
)]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#![allow(box_pointers)]
|
#![allow(box_pointers)]
|
||||||
|
|
||||||
extern crate regex;
|
extern crate regex;
|
||||||
|
|
@ -27,8 +26,8 @@ pub mod build;
|
||||||
pub mod command_runner;
|
pub mod command_runner;
|
||||||
pub mod factory;
|
pub mod factory;
|
||||||
pub mod loggers;
|
pub mod loggers;
|
||||||
pub mod symbols;
|
|
||||||
pub mod schema;
|
|
||||||
pub mod repository;
|
pub mod repository;
|
||||||
pub mod resources;
|
pub mod resources;
|
||||||
|
pub mod schema;
|
||||||
pub mod storage;
|
pub mod storage;
|
||||||
|
pub mod symbols;
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ impl Logger for StdErrLogger {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FilteringLogger<'a> {
|
pub struct FilteringLogger<'a> {
|
||||||
logger: &'a mut dyn Logger
|
logger: &'a mut dyn Logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FilteringLogger<'a> {
|
impl<'a> FilteringLogger<'a> {
|
||||||
|
|
@ -28,14 +28,11 @@ impl<'a> FilteringLogger<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Logger for FilteringLogger<'a> {
|
impl<'a> Logger for FilteringLogger<'a> {
|
||||||
fn debug(&mut self, _str: &str) {
|
fn debug(&mut self, _str: &str) {}
|
||||||
return
|
|
||||||
}
|
|
||||||
fn write(&mut self, str: &str) {
|
fn write(&mut self, str: &str) {
|
||||||
self.logger.write(str)
|
self.logger.write(str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {}
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,23 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use symbols::Symbol;
|
|
||||||
use resources::Resource;
|
use resources::Resource;
|
||||||
|
use symbols::Symbol;
|
||||||
|
|
||||||
pub trait SymbolRepository<'a> {
|
pub trait SymbolRepository<'a> {
|
||||||
fn get_symbol(&'a self, resource: &Resource) -> Option<Box<dyn Symbol + '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>> {
|
fn get_symbol(&'a self, resource: &Resource) -> Option<Box<dyn Symbol + 'a>> {
|
||||||
self(resource)
|
self(resource)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DispatchingSymbolRepository<'a> {
|
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> {
|
impl<'a> DispatchingSymbolRepository<'a> {
|
||||||
|
|
@ -25,6 +28,9 @@ impl<'a> DispatchingSymbolRepository<'a> {
|
||||||
|
|
||||||
impl<'a> SymbolRepository<'a> for DispatchingSymbolRepository<'a> {
|
impl<'a> SymbolRepository<'a> for DispatchingSymbolRepository<'a> {
|
||||||
fn get_symbol(&'a self, resource: &Resource) -> Option<Box<dyn Symbol + '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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
130
src/schema.rs
130
src/schema.rs
|
|
@ -9,20 +9,20 @@ use symbols::{Symbol, SymbolRunner};
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum SymbolRunError {
|
pub enum SymbolRunError {
|
||||||
Symbol(Box<dyn Error>),
|
Symbol(Box<dyn Error>),
|
||||||
ExecuteDidNotReach(())
|
ExecuteDidNotReach(()),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error for SymbolRunError {
|
impl Error for SymbolRunError {
|
||||||
fn description(&self) -> &str {
|
fn description(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
SymbolRunError::Symbol(_) => "Symbol execution error",
|
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> {
|
fn cause(&self) -> Option<&dyn Error> {
|
||||||
match self {
|
match self {
|
||||||
SymbolRunError::Symbol(ref e) => Some(&**e),
|
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 {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
SymbolRunError::Symbol(ref e) => write!(f, "{}", e),
|
SymbolRunError::Symbol(ref e) => write!(f, "{}", e),
|
||||||
SymbolRunError::ExecuteDidNotReach(_) => write!(f, "{}", self.description())
|
SymbolRunError::ExecuteDidNotReach(_) => write!(f, "{}", self.description()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct InitializingSymbolRunner<L: Logger> {
|
pub struct InitializingSymbolRunner<L: Logger> {
|
||||||
logger: RefCell<L>
|
logger: RefCell<L>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Logger> InitializingSymbolRunner<L> {
|
impl<L: Logger> InitializingSymbolRunner<L> {
|
||||||
pub fn new(logger: L) -> Self {
|
pub fn new(logger: L) -> Self {
|
||||||
Self { logger: RefCell::new(logger) }
|
Self {
|
||||||
|
logger: RefCell::new(logger),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Logger> SymbolRunner for InitializingSymbolRunner<L> {
|
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 mut logger = self.logger.borrow_mut();
|
||||||
let target_reached = try!(symbol.target_reached());
|
let target_reached = try!(symbol.target_reached());
|
||||||
if target_reached {
|
if target_reached {
|
||||||
|
|
@ -58,7 +59,13 @@ impl<L: Logger> SymbolRunner for InitializingSymbolRunner<L> {
|
||||||
logger.write(format!("Executing {}", symbol).as_str());
|
logger.write(format!("Executing {}", symbol).as_str());
|
||||||
try!(symbol.execute());
|
try!(symbol.execute());
|
||||||
let target_reached = try!(symbol.target_reached());
|
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 {
|
if !target_reached {
|
||||||
return Err(Box::new(SymbolRunError::ExecuteDidNotReach(())));
|
return Err(Box::new(SymbolRunError::ExecuteDidNotReach(())));
|
||||||
}
|
}
|
||||||
|
|
@ -68,18 +75,19 @@ impl<L: Logger> SymbolRunner for InitializingSymbolRunner<L> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DrySymbolRunner<L: Logger> {
|
pub struct DrySymbolRunner<L: Logger> {
|
||||||
logger: RefCell<L>
|
logger: RefCell<L>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Logger> DrySymbolRunner<L> {
|
impl<L: Logger> DrySymbolRunner<L> {
|
||||||
pub fn new(logger: L) -> Self {
|
pub fn new(logger: L) -> Self {
|
||||||
Self { logger: RefCell::new(logger) }
|
Self {
|
||||||
|
logger: RefCell::new(logger),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Logger> SymbolRunner for DrySymbolRunner<L> {
|
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 mut logger = self.logger.borrow_mut();
|
||||||
let target_reached = try!(symbol.target_reached());
|
let target_reached = try!(symbol.target_reached());
|
||||||
logger.debug(format!("Symbol reports target_reached: {:?}", target_reached).as_str());
|
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>);
|
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 {
|
pub fn new(symbol_runner: &'a R, logger: L) -> Self {
|
||||||
ReportingSymbolRunner(symbol_runner, RefCell::new(logger))
|
ReportingSymbolRunner(symbol_runner, RefCell::new(logger))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, R, L> SymbolRunner for ReportingSymbolRunner<'a, R, L> where R: SymbolRunner, L: Logger {
|
impl<'a, R, L> SymbolRunner for ReportingSymbolRunner<'a, R, L>
|
||||||
fn run_symbol(&self, symbol: &dyn Symbol) -> Result<(), Box<dyn Error>>
|
where
|
||||||
|
R: SymbolRunner,
|
||||||
|
L: Logger,
|
||||||
{
|
{
|
||||||
|
fn run_symbol(&self, symbol: &dyn Symbol) -> Result<(), Box<dyn Error>> {
|
||||||
let mut logger = self.1.borrow_mut();
|
let mut logger = self.1.borrow_mut();
|
||||||
logger.debug(format!("Running symbol {}", symbol).as_str());
|
logger.debug(format!("Running symbol {}", symbol).as_str());
|
||||||
let res = self.0.run_symbol(symbol);
|
let res = self.0.run_symbol(symbol);
|
||||||
match res {
|
match res {
|
||||||
Err(ref e) => {
|
Err(ref e) => {
|
||||||
logger.write(format!("Failed on {} with {}, aborting.", symbol, e).as_str());
|
logger.write(format!("Failed on {} with {}, aborting.", symbol, e).as_str());
|
||||||
},
|
}
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
logger.debug(format!("Successfully finished {}", symbol).as_str());
|
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 resources::Resource;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
pub struct NonRepeatingSymbolRunner<R: SymbolRunner> {
|
pub struct NonRepeatingSymbolRunner<R: SymbolRunner> {
|
||||||
upstream: R,
|
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 {
|
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> {
|
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() {
|
if let Some(resources) = symbol.provides() {
|
||||||
let mut done = self.done.borrow_mut();
|
let mut done = self.done.borrow_mut();
|
||||||
let mut has_to_run = false;
|
let mut has_to_run = false;
|
||||||
|
|
@ -151,17 +171,29 @@ impl<R: SymbolRunner> SymbolRunner for NonRepeatingSymbolRunner<R> {
|
||||||
}
|
}
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
pub struct RequirementsResolvingSymbolRunner<'a, 's, R: 'a + SymbolRunner, G: 'a + SymbolRepository<'s>>(R, &'a G, PhantomData<Box<dyn Symbol + '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> {
|
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 {
|
pub fn new(symbol_runner: R, symbol_repo: &'a G) -> Self {
|
||||||
RequirementsResolvingSymbolRunner(symbol_runner, symbol_repo, PhantomData)
|
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> {
|
impl<'s, 'a: 's, R, G> SymbolRunner for RequirementsResolvingSymbolRunner<'a, 's, R, G>
|
||||||
fn run_symbol(&self, symbol: &dyn Symbol) -> Result<(), Box<dyn Error>>
|
where
|
||||||
|
R: SymbolRunner,
|
||||||
|
G: SymbolRepository<'s>,
|
||||||
{
|
{
|
||||||
|
fn run_symbol(&self, symbol: &dyn Symbol) -> Result<(), Box<dyn Error>> {
|
||||||
for resource in symbol.get_prerequisites() {
|
for resource in symbol.get_prerequisites() {
|
||||||
if let Some(dep) = self.1.get_symbol(&resource) {
|
if let Some(dep) = self.1.get_symbol(&resource) {
|
||||||
try!(dep.as_action(self).run());
|
try!(dep.as_action(self).run());
|
||||||
|
|
@ -180,13 +212,13 @@ mod test {
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use loggers::Logger;
|
use loggers::Logger;
|
||||||
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction};
|
|
||||||
use schema::SymbolRunner;
|
|
||||||
use schema::InitializingSymbolRunner;
|
use schema::InitializingSymbolRunner;
|
||||||
|
use schema::SymbolRunner;
|
||||||
|
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
enum DummySymbolError {
|
enum DummySymbolError {
|
||||||
Error(())
|
Error(()),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error for DummySymbolError {
|
impl Error for DummySymbolError {
|
||||||
|
|
@ -203,7 +235,7 @@ mod test {
|
||||||
|
|
||||||
struct DummySymbol<'a> {
|
struct DummySymbol<'a> {
|
||||||
_execute: &'a dyn Fn() -> Result<(), Box<dyn Error>>,
|
_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> {
|
impl<'b> Symbol for DummySymbol<'b> {
|
||||||
|
|
@ -218,7 +250,10 @@ mod test {
|
||||||
Box::new(SymbolAction::new(runner, self))
|
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))
|
Box::new(OwnedSymbolAction::new(runner, *self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -230,13 +265,19 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> DummySymbol<'a> {
|
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> {
|
fn new(
|
||||||
DummySymbol { _target_reached: target_reached, _execute: execute }
|
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 {
|
struct DummyLogger {
|
||||||
log: Vec<String>
|
log: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DummyLogger {
|
impl DummyLogger {
|
||||||
|
|
@ -256,14 +297,16 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn nothing_needed_to_be_done() {
|
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());
|
assert!(result.is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn everything_is_ok() {
|
fn everything_is_ok() {
|
||||||
let first = RefCell::new(true);
|
let first = RefCell::new(true);
|
||||||
let result = InitializingSymbolRunner::new(DummyLogger::new()).run_symbol(&DummySymbol::new(&|| {
|
let result = InitializingSymbolRunner::new(DummyLogger::new()).run_symbol(&DummySymbol::new(
|
||||||
|
&|| {
|
||||||
let mut _first = first.borrow_mut();
|
let mut _first = first.borrow_mut();
|
||||||
Ok(if *_first {
|
Ok(if *_first {
|
||||||
*_first = false;
|
*_first = false;
|
||||||
|
|
@ -271,13 +314,16 @@ mod test {
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
})
|
})
|
||||||
}, &|| Ok(())));
|
},
|
||||||
|
&|| Ok(()),
|
||||||
|
));
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn executing_did_not_change_state() {
|
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!(
|
assert_eq!(
|
||||||
result.unwrap_err().description(),
|
result.unwrap_err().description(),
|
||||||
"Target not reached after executing symbol"
|
"Target not reached after executing symbol"
|
||||||
|
|
@ -286,7 +332,11 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn executing_did_not_work() {
|
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");
|
assert_eq!(err.description(), "Description");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,14 +20,19 @@ impl SimpleStorage {
|
||||||
fn get_path(&self, date: Option<u64>) -> String {
|
fn get_path(&self, date: Option<u64>) -> String {
|
||||||
match date {
|
match date {
|
||||||
Some(d) => format!("{}/_{}/{}", self.0, self.1, d),
|
Some(d) => format!("{}/_{}/{}", self.0, self.1, d),
|
||||||
None => format!("{}/_{}", self.0, self.1)
|
None => format!("{}/_{}", self.0, self.1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Storage for SimpleStorage {
|
impl Storage for SimpleStorage {
|
||||||
fn write_filename(&self) -> String {
|
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>> {
|
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>> {
|
fn recent_date(&self) -> Result<u64, Box<dyn Error>> {
|
||||||
let dir = self.get_path(None);
|
let dir = self.get_path(None);
|
||||||
try!(read_dir(dir))
|
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()))
|
.map(|entry| {
|
||||||
.fold(None, |maybe_newest, maybe_time| maybe_newest.into_iter().chain(maybe_time).max())
|
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())
|
.ok_or_else(|| "Not found".to_string().into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,15 @@ use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||||
|
|
||||||
pub struct AcmeAccountKey<'a, C: 'a + CommandRunner> {
|
pub struct AcmeAccountKey<'a, C: 'a + CommandRunner> {
|
||||||
path: Cow<'a, Path>,
|
path: Cow<'a, Path>,
|
||||||
command_runner: &'a C
|
command_runner: &'a C,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: CommandRunner> AcmeAccountKey<'a, C> {
|
impl<'a, C: CommandRunner> AcmeAccountKey<'a, C> {
|
||||||
pub fn new(path: Cow<'a, Path>, command_runner: &'a C) -> Self {
|
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 {
|
fn get_bytes(&self) -> u32 {
|
||||||
|
|
@ -33,27 +36,50 @@ impl<'a, C: CommandRunner> Symbol for AcmeAccountKey<'a, C> {
|
||||||
if !self.path.exists() {
|
if !self.path.exists() {
|
||||||
return Ok(false);
|
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()))
|
Ok(stdout.starts_with(&format!("Private-Key: ({} bit)\n", self.get_bytes()).as_bytes()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
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> {
|
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> {
|
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
|
||||||
Box::new(SymbolAction::new(runner, self))
|
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))
|
Box::new(OwnedSymbolAction::new(runner, *self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {}
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -6,17 +6,20 @@ use std::io::Write;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use command_runner::CommandRunner;
|
use command_runner::CommandRunner;
|
||||||
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
|
||||||
use resources::Resource;
|
use resources::Resource;
|
||||||
|
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||||
|
|
||||||
pub struct AcmeCert<'a, C: 'a + CommandRunner> {
|
pub struct AcmeCert<'a, C: 'a + CommandRunner> {
|
||||||
domain: Cow<'a, str>,
|
domain: Cow<'a, str>,
|
||||||
command_runner: &'a C
|
command_runner: &'a C,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: CommandRunner> AcmeCert<'a, C> {
|
impl<'a, C: CommandRunner> AcmeCert<'a, C> {
|
||||||
pub fn new(domain: Cow<'a, str>, command_runner: &'a C) -> Self {
|
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 {
|
fn get_csr_path(&self) -> String {
|
||||||
|
|
@ -42,10 +45,44 @@ impl<'a, C: CommandRunner> Symbol for AcmeCert<'a, C> {
|
||||||
return Ok(false);
|
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()]));
|
let output = try!(self.command_runner.run_with_args(
|
||||||
if output.status.success() && output.stdout == format!("subject=CN = {}\nCertificate will not expire\n", self.domain).as_bytes() {
|
"openssl",
|
||||||
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() {
|
"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)
|
Ok(false)
|
||||||
} else {
|
} else {
|
||||||
Err(try!(String::from_utf8(output.stderr)).into())
|
Err(try!(String::from_utf8(output.stderr)).into())
|
||||||
|
|
@ -53,7 +90,17 @@ impl<'a, C: CommandRunner> Symbol for AcmeCert<'a, C> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
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()));
|
let mut file = try!(FsFile::create(self.get_cert_path()));
|
||||||
try!(file.write_all(&output));
|
try!(file.write_all(&output));
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -67,11 +114,13 @@ impl<'a, C: CommandRunner> Symbol for AcmeCert<'a, C> {
|
||||||
Box::new(SymbolAction::new(runner, self))
|
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))
|
Box::new(OwnedSymbolAction::new(runner, *self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {}
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -6,17 +6,20 @@ use std::io::Write;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use command_runner::CommandRunner;
|
use command_runner::CommandRunner;
|
||||||
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
|
||||||
use resources::Resource;
|
use resources::Resource;
|
||||||
|
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||||
|
|
||||||
pub struct AcmeCertChain<'a, C: 'a + CommandRunner> {
|
pub struct AcmeCertChain<'a, C: 'a + CommandRunner> {
|
||||||
domain: Cow<'a, str>,
|
domain: Cow<'a, str>,
|
||||||
command_runner: &'a C
|
command_runner: &'a C,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: CommandRunner> AcmeCertChain<'a, C> {
|
impl<'a, C: CommandRunner> AcmeCertChain<'a, C> {
|
||||||
pub fn new(domain: Cow<'a, str>, command_runner: &'a C) -> Self {
|
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 {
|
fn get_single_cert_path(&self) -> String {
|
||||||
|
|
@ -42,16 +45,52 @@ impl<'a, C: CommandRunner> Symbol for AcmeCertChain<'a, C> {
|
||||||
return Ok(false);
|
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()]));
|
let stdout = try!(self.command_runner.get_output(
|
||||||
if stdout != format!("subject=CN = {}\nCertificate will not expire\n", self.domain).as_bytes() {
|
"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);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
// FIXME: From my understanding, the -untrusted *.pem parameter shouldn't be necessary, but is necessary with openssl 1.1.0f-3
|
// 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>> {
|
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()));
|
let mut file = try!(FsFile::create(self.get_cert_chain_path()));
|
||||||
try!(file.write_all(&output));
|
try!(file.write_all(&output));
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -65,11 +104,13 @@ impl<'a, C: CommandRunner> Symbol for AcmeCertChain<'a, C> {
|
||||||
Box::new(SymbolAction::new(runner, self))
|
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))
|
Box::new(OwnedSymbolAction::new(runner, *self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {}
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
|
use resources::Resource;
|
||||||
use std::borrow::{Borrow, Cow};
|
use std::borrow::{Borrow, Cow};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use resources::Resource;
|
|
||||||
|
|
||||||
use command_runner::CommandRunner;
|
use command_runner::CommandRunner;
|
||||||
use symbols::{Action, OwnedSymbolAction, SymbolAction, SymbolRunner, Symbol};
|
|
||||||
use symbols::acme::AcmeAccountKey;
|
use symbols::acme::AcmeAccountKey;
|
||||||
use symbols::dir::Dir;
|
use symbols::dir::Dir;
|
||||||
use symbols::file::File;
|
use symbols::file::File;
|
||||||
use symbols::list::List;
|
use symbols::list::List;
|
||||||
use symbols::owner::Owner;
|
use symbols::owner::Owner;
|
||||||
|
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||||
|
|
||||||
pub struct AcmeUser<'a>(Cow<'a, str>);
|
pub struct AcmeUser<'a>(Cow<'a, str>);
|
||||||
|
|
||||||
|
|
@ -38,7 +38,10 @@ impl<'a> Symbol for AcmeUser<'a> {
|
||||||
Box::new(SymbolAction::new(runner, self))
|
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))
|
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 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(List::new(vec![
|
||||||
Box::new(AcmeAccountKey::new(account_key_file.clone().into(), command_runner)),
|
Box::new(AcmeAccountKey::new(
|
||||||
Box::new(Owner::new(account_key_file.to_string_lossy().into_owned(), user_name_cow.clone(), command_runner)),
|
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(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,
|
||||||
|
)),
|
||||||
Box::new(Dir::new("/etc/ssl/local_certs")),
|
Box::new(Dir::new("/etc/ssl/local_certs")),
|
||||||
Box::new(Owner::new("/etc/ssl/local_certs", user_name_cow, command_runner)),
|
Box::new(Owner::new(
|
||||||
Box::new(File::new("/home/acme/lets_encrypt_x3_cross_signed.pem", cert))
|
"/etc/ssl/local_certs",
|
||||||
|
user_name_cow,
|
||||||
|
command_runner,
|
||||||
|
)),
|
||||||
|
Box::new(File::new(
|
||||||
|
"/home/acme/lets_encrypt_x3_cross_signed.pem",
|
||||||
|
cert,
|
||||||
|
)),
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,17 +7,26 @@ use std::path::Path;
|
||||||
use resources::Resource;
|
use resources::Resource;
|
||||||
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||||
|
|
||||||
pub struct Dir<D> where D: AsRef<str> {
|
pub struct Dir<D>
|
||||||
path: D
|
where
|
||||||
|
D: AsRef<str>,
|
||||||
|
{
|
||||||
|
path: D,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D> Dir<D> where D: AsRef<str> {
|
impl<D> Dir<D>
|
||||||
|
where
|
||||||
|
D: AsRef<str>,
|
||||||
|
{
|
||||||
pub fn new(path: D) -> Self {
|
pub fn new(path: D) -> Self {
|
||||||
Dir { path }
|
Dir { path }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D> Symbol for Dir<D> where D: AsRef<str> {
|
impl<D> Symbol for Dir<D>
|
||||||
|
where
|
||||||
|
D: AsRef<str>,
|
||||||
|
{
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
let metadata = fs::metadata(self.path.as_ref());
|
let metadata = fs::metadata(self.path.as_ref());
|
||||||
// Check if dir exists
|
// Check if dir exists
|
||||||
|
|
@ -31,7 +40,10 @@ impl<D> Symbol for Dir<D> where D: AsRef<str> {
|
||||||
if metadata.unwrap().is_dir() {
|
if metadata.unwrap().is_dir() {
|
||||||
Ok(true)
|
Ok(true)
|
||||||
} else {
|
} else {
|
||||||
Err(Box::new(io::Error::new(io::ErrorKind::AlreadyExists, "Could not create a directory, non-directory file exists")))
|
Err(Box::new(io::Error::new(
|
||||||
|
io::ErrorKind::AlreadyExists,
|
||||||
|
"Could not create a directory, non-directory file exists",
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,12 +67,18 @@ impl<D> Symbol for Dir<D> where D: AsRef<str> {
|
||||||
Box::new(SymbolAction::new(runner, self))
|
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))
|
Box::new(OwnedSymbolAction::new(runner, *self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D> fmt::Display for Dir<D> where D: AsRef<str> {
|
impl<D> fmt::Display for Dir<D>
|
||||||
|
where
|
||||||
|
D: AsRef<str>,
|
||||||
|
{
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
write!(f, "Dir {}", self.path.as_ref())
|
write!(f, "Dir {}", self.path.as_ref())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,18 +4,18 @@ use std::path::Path;
|
||||||
|
|
||||||
use command_runner::{CommandRunner, SetuidCommandRunner};
|
use command_runner::{CommandRunner, SetuidCommandRunner};
|
||||||
use storage::{SimpleStorage, Storage};
|
use storage::{SimpleStorage, Storage};
|
||||||
use symbols::{Action, Symbol, SymbolRunner};
|
|
||||||
use symbols::acme::{AcmeCert, AcmeCertChain};
|
use symbols::acme::{AcmeCert, AcmeCertChain};
|
||||||
use symbols::file::File;
|
use symbols::file::File;
|
||||||
use symbols::git::checkout::GitCheckout;
|
use symbols::git::checkout::GitCheckout;
|
||||||
use symbols::hook::Hook;
|
use symbols::hook::Hook;
|
||||||
use symbols::list::ListAction;
|
use symbols::list::ListAction;
|
||||||
use symbols::mariadb::{DatabaseDump, MariaDBDatabase, MariaDBUser};
|
use symbols::mariadb::{DatabaseDump, MariaDBDatabase, MariaDBUser};
|
||||||
use symbols::nginx::server::{NginxServer, server_config, php_server_config_snippet};
|
use symbols::nginx::server::{php_server_config_snippet, server_config, NginxServer};
|
||||||
use symbols::owner::Owner;
|
use symbols::owner::Owner;
|
||||||
use symbols::stored_directory::{StoredDirectory, StorageDirection};
|
use symbols::stored_directory::{StorageDirection, StoredDirectory};
|
||||||
use symbols::systemd::reload::ReloadService;
|
use symbols::systemd::reload::ReloadService;
|
||||||
use symbols::tls::SelfSignedTlsCert;
|
use symbols::tls::SelfSignedTlsCert;
|
||||||
|
use symbols::{Action, Symbol, SymbolRunner};
|
||||||
|
|
||||||
pub trait Policy {
|
pub trait Policy {
|
||||||
fn user_name_for_host(&self, host_name: &'static str) -> String;
|
fn user_name_for_host(&self, host_name: &'static str) -> String;
|
||||||
|
|
@ -36,7 +36,7 @@ pub struct SymbolFactory<'a, C: 'a + CommandRunner, R: 'a + SymbolRunner, P: 'a
|
||||||
command_runner: &'a C,
|
command_runner: &'a C,
|
||||||
acme_command_runner: SetuidCommandRunner<'a, C>,
|
acme_command_runner: SetuidCommandRunner<'a, C>,
|
||||||
symbol_runner: &'a R,
|
symbol_runner: &'a R,
|
||||||
policy: &'a P
|
policy: &'a P,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'b, C: 'b + CommandRunner, R: 'b + SymbolRunner, P: 'b + Policy> SymbolFactory<'b, C, R, P> {
|
impl<'b, C: 'b + CommandRunner, R: 'b + SymbolRunner, P: 'b + Policy> SymbolFactory<'b, C, R, P> {
|
||||||
|
|
@ -44,39 +44,45 @@ impl<'b, C: 'b + CommandRunner, R: 'b + SymbolRunner, P: 'b + Policy> SymbolFact
|
||||||
let acme_user = "acme"; // FIXME: CONFIG
|
let acme_user = "acme"; // FIXME: CONFIG
|
||||||
|
|
||||||
let acme_command_runner = SetuidCommandRunner::new(acme_user, command_runner);
|
let acme_command_runner = SetuidCommandRunner::new(acme_user, command_runner);
|
||||||
SymbolFactory { command_runner, acme_command_runner, symbol_runner, policy }
|
SymbolFactory {
|
||||||
|
command_runner,
|
||||||
|
acme_command_runner,
|
||||||
|
symbol_runner,
|
||||||
|
policy,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_nginx_acme_server<'a, 'c: 'a, S: 'a + Symbol>(&'c self, host: &'static str, nginx_server_symbol: S) -> Box<dyn Action + 'a> {
|
pub fn get_nginx_acme_server<'a, 'c: 'a, S: 'a + Symbol>(
|
||||||
|
&'c self,
|
||||||
|
host: &'static str,
|
||||||
|
nginx_server_symbol: S,
|
||||||
|
) -> Box<dyn Action + 'a> {
|
||||||
Box::new(ListAction::new(vec![
|
Box::new(ListAction::new(vec![
|
||||||
Box::new(SelfSignedTlsCert::new(
|
Box::new(SelfSignedTlsCert::new(host.into(), self.command_runner))
|
||||||
host.into(),
|
.into_action(self.symbol_runner),
|
||||||
self.command_runner
|
|
||||||
)).into_action(self.symbol_runner),
|
|
||||||
Box::new(Hook::new(
|
Box::new(Hook::new(
|
||||||
nginx_server_symbol,
|
nginx_server_symbol,
|
||||||
ReloadService::new("nginx", self.command_runner)
|
ReloadService::new("nginx", self.command_runner),
|
||||||
)).into_action(self.symbol_runner),
|
))
|
||||||
Box::new(AcmeCert::new(
|
.into_action(self.symbol_runner),
|
||||||
host.into(),
|
Box::new(AcmeCert::new(host.into(), &self.acme_command_runner))
|
||||||
&self.acme_command_runner
|
.into_action(self.symbol_runner),
|
||||||
)).into_action(self.symbol_runner),
|
|
||||||
Box::new(Hook::new(
|
Box::new(Hook::new(
|
||||||
AcmeCertChain::new(
|
AcmeCertChain::new(host.into(), &self.acme_command_runner),
|
||||||
host.into(),
|
ReloadService::new("nginx", self.command_runner),
|
||||||
&self.acme_command_runner
|
))
|
||||||
),
|
.into_action(self.symbol_runner),
|
||||||
ReloadService::new("nginx", self.command_runner)
|
|
||||||
)).into_action(self.symbol_runner)
|
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
pub fn get_nginx_acme_challenge_config<'a>(&'a self) -> Box<dyn Action + 'a> {
|
pub fn get_nginx_acme_challenge_config<'a>(&'a self) -> Box<dyn Action + 'a> {
|
||||||
Box::new(File::new(
|
Box::new(File::new(
|
||||||
"/etc/nginx/snippets/acme-challenge.conf", "location ^~ /.well-known/acme-challenge/ {
|
"/etc/nginx/snippets/acme-challenge.conf",
|
||||||
|
"location ^~ /.well-known/acme-challenge/ {
|
||||||
alias /home/acme/challenges/;
|
alias /home/acme/challenges/;
|
||||||
try_files $uri =404;
|
try_files $uri =404;
|
||||||
}"
|
}",
|
||||||
)).into_action(self.symbol_runner)
|
))
|
||||||
|
.into_action(self.symbol_runner)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_php_fpm_pool_socket_path<'a>(&'a self, user_name: &str) -> String {
|
fn get_php_fpm_pool_socket_path<'a>(&'a self, user_name: &str) -> String {
|
||||||
|
|
@ -99,47 +105,66 @@ pm = ondemand
|
||||||
pm.max_children = 10
|
pm.max_children = 10
|
||||||
catch_workers_output = yes
|
catch_workers_output = yes
|
||||||
env[PATH] = /usr/local/bin:/usr/bin:/bin
|
env[PATH] = /usr/local/bin:/usr/bin:/bin
|
||||||
"
|
",
|
||||||
, user_name, socket)),
|
user_name, socket
|
||||||
ReloadService::new("php7.0-fpm", self.command_runner)
|
),
|
||||||
)).into_action(self.symbol_runner)
|
),
|
||||||
|
ReloadService::new("php7.0-fpm", self.command_runner),
|
||||||
|
))
|
||||||
|
.into_action(self.symbol_runner)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serve_php<'a>(&'a self, host_name: &'static str, root_dir: Cow<'a, str>) -> Box<dyn Action + 'a> {
|
pub fn serve_php<'a>(
|
||||||
|
&'a self,
|
||||||
|
host_name: &'static str,
|
||||||
|
root_dir: Cow<'a, str>,
|
||||||
|
) -> Box<dyn Action + 'a> {
|
||||||
let user_name = self.policy.user_name_for_host(host_name);
|
let user_name = self.policy.user_name_for_host(host_name);
|
||||||
let socket = self.get_php_fpm_pool_socket_path(&user_name);
|
let socket = self.get_php_fpm_pool_socket_path(&user_name);
|
||||||
Box::new(ListAction::new(vec![
|
Box::new(ListAction::new(vec![
|
||||||
self.get_php_fpm_pool(&user_name),
|
self.get_php_fpm_pool(&user_name),
|
||||||
self.get_nginx_acme_server(host_name,
|
self.get_nginx_acme_server(
|
||||||
NginxServer::new_php(
|
|
||||||
host_name,
|
host_name,
|
||||||
socket.into(),
|
NginxServer::new_php(host_name, socket.into(), root_dir, self.command_runner),
|
||||||
root_dir,
|
),
|
||||||
self.command_runner
|
|
||||||
)
|
|
||||||
)
|
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serve_wordpress<'a>(&'a self, host_name: &'static str, root_dir: Cow<'a, str>) -> Box<dyn Action + 'a> {
|
pub fn serve_wordpress<'a>(
|
||||||
|
&'a self,
|
||||||
|
host_name: &'static str,
|
||||||
|
root_dir: Cow<'a, str>,
|
||||||
|
) -> Box<dyn Action + 'a> {
|
||||||
let user_name = self.policy.user_name_for_host(host_name);
|
let user_name = self.policy.user_name_for_host(host_name);
|
||||||
let socket = self.get_php_fpm_pool_socket_path(&user_name);
|
let socket = self.get_php_fpm_pool_socket_path(&user_name);
|
||||||
Box::new(ListAction::new(vec![
|
Box::new(ListAction::new(vec![
|
||||||
self.get_php_fpm_pool(&user_name),
|
self.get_php_fpm_pool(&user_name),
|
||||||
self.get_nginx_acme_server(host_name,
|
self.get_nginx_acme_server(
|
||||||
|
host_name,
|
||||||
NginxServer::new(
|
NginxServer::new(
|
||||||
host_name,
|
host_name,
|
||||||
server_config(host_name, &format!("{}
|
server_config(
|
||||||
|
host_name,
|
||||||
|
&format!(
|
||||||
|
"{}
|
||||||
location / {{
|
location / {{
|
||||||
try_files $uri $uri/ /index.php?$args;
|
try_files $uri $uri/ /index.php?$args;
|
||||||
}}
|
}}
|
||||||
", php_server_config_snippet(socket.into(), root_dir))),
|
",
|
||||||
self.command_runner
|
php_server_config_snippet(socket.into(), root_dir)
|
||||||
))
|
),
|
||||||
|
),
|
||||||
|
self.command_runner,
|
||||||
|
),
|
||||||
|
),
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serve_dokuwiki<'a>(&'a self, host_name: &'static str, root_dir: &'static str) -> Box<dyn Action + 'a> {
|
pub fn serve_dokuwiki<'a>(
|
||||||
|
&'a self,
|
||||||
|
host_name: &'static str,
|
||||||
|
root_dir: &'static str,
|
||||||
|
) -> Box<dyn Action + 'a> {
|
||||||
let user_name = self.policy.user_name_for_host(host_name);
|
let user_name = self.policy.user_name_for_host(host_name);
|
||||||
let socket = self.get_php_fpm_pool_socket_path(&user_name);
|
let socket = self.get_php_fpm_pool_socket_path(&user_name);
|
||||||
Box::new(ListAction::new(vec![
|
Box::new(ListAction::new(vec![
|
||||||
|
|
@ -174,15 +199,23 @@ env[PATH] = /usr/local/bin:/usr/bin:/bin
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serve_nextcloud<'a>(&'a self, host_name: &'static str, root_dir: Cow<'a, str>) -> Box<dyn Action + 'a> {
|
pub fn serve_nextcloud<'a>(
|
||||||
|
&'a self,
|
||||||
|
host_name: &'static str,
|
||||||
|
root_dir: Cow<'a, str>,
|
||||||
|
) -> Box<dyn Action + 'a> {
|
||||||
let user_name = self.policy.user_name_for_host(host_name);
|
let user_name = self.policy.user_name_for_host(host_name);
|
||||||
let socket = self.get_php_fpm_pool_socket_path(&user_name);
|
let socket = self.get_php_fpm_pool_socket_path(&user_name);
|
||||||
Box::new(ListAction::new(vec![
|
Box::new(ListAction::new(vec![
|
||||||
self.get_php_fpm_pool(&user_name),
|
self.get_php_fpm_pool(&user_name),
|
||||||
self.get_nginx_acme_server(host_name,
|
self.get_nginx_acme_server(
|
||||||
|
host_name,
|
||||||
NginxServer::new(
|
NginxServer::new(
|
||||||
host_name,
|
host_name,
|
||||||
server_config(host_name, &format!("{}
|
server_config(
|
||||||
|
host_name,
|
||||||
|
&format!(
|
||||||
|
"{}
|
||||||
client_max_body_size 500M;
|
client_max_body_size 500M;
|
||||||
|
|
||||||
# Disable gzip to avoid the removal of the ETag header
|
# Disable gzip to avoid the removal of the ETag header
|
||||||
|
|
@ -230,50 +263,106 @@ env[PATH] = /usr/local/bin:/usr/bin:/bin
|
||||||
location ~* \\.(?:jpg|jpeg|gif|bmp|ico|png|swf)$ {{
|
location ~* \\.(?:jpg|jpeg|gif|bmp|ico|png|swf)$ {{
|
||||||
access_log off;
|
access_log off;
|
||||||
}}
|
}}
|
||||||
", php_server_config_snippet(socket.into(), root_dir))),
|
",
|
||||||
self.command_runner
|
php_server_config_snippet(socket.into(), root_dir)
|
||||||
))
|
),
|
||||||
|
),
|
||||||
|
self.command_runner,
|
||||||
|
),
|
||||||
|
),
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serve_redir<'a>(&'a self, host_name: &'static str, target: &'static str) -> Box<dyn Action + 'a> {
|
pub fn serve_redir<'a>(
|
||||||
self.get_nginx_acme_server(host_name, NginxServer::new_redir(host_name, target, self.command_runner))
|
&'a self,
|
||||||
|
host_name: &'static str,
|
||||||
|
target: &'static str,
|
||||||
|
) -> Box<dyn Action + 'a> {
|
||||||
|
self.get_nginx_acme_server(
|
||||||
|
host_name,
|
||||||
|
NginxServer::new_redir(host_name, target, self.command_runner),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serve_static<'a>(&'a self, host_name: &'static str, dir: &'a str) -> Box<dyn Action + 'a> {
|
pub fn serve_static<'a>(&'a self, host_name: &'static str, dir: &'a str) -> Box<dyn Action + 'a> {
|
||||||
self.get_nginx_acme_server(host_name, NginxServer::new_static(host_name, dir, self.command_runner))
|
self.get_nginx_acme_server(
|
||||||
|
host_name,
|
||||||
|
NginxServer::new_static(host_name, dir, self.command_runner),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_stored_directory<'a, T: Into<String>>(&'a self, storage_name: &'static str, target: T) -> (Box<dyn Action + 'a>, Box<dyn Action + 'a>) {
|
pub fn get_stored_directory<'a, T: Into<String>>(
|
||||||
|
&'a self,
|
||||||
|
storage_name: &'static str,
|
||||||
|
target: T,
|
||||||
|
) -> (Box<dyn Action + 'a>, Box<dyn Action + 'a>) {
|
||||||
let data = SimpleStorage::new("/root/data".to_string(), storage_name.to_string());
|
let data = SimpleStorage::new("/root/data".to_string(), storage_name.to_string());
|
||||||
let string_target = target.into();
|
let string_target = target.into();
|
||||||
(
|
(
|
||||||
Box::new(StoredDirectory::new(string_target.clone().into(), data.clone(), StorageDirection::Save, self.command_runner)).into_action(self.symbol_runner),
|
Box::new(StoredDirectory::new(
|
||||||
Box::new(StoredDirectory::new(string_target.into(), data.clone(), StorageDirection::Load, self.command_runner)).into_action(self.symbol_runner)
|
string_target.clone().into(),
|
||||||
|
data.clone(),
|
||||||
|
StorageDirection::Save,
|
||||||
|
self.command_runner,
|
||||||
|
))
|
||||||
|
.into_action(self.symbol_runner),
|
||||||
|
Box::new(StoredDirectory::new(
|
||||||
|
string_target.into(),
|
||||||
|
data.clone(),
|
||||||
|
StorageDirection::Load,
|
||||||
|
self.command_runner,
|
||||||
|
))
|
||||||
|
.into_action(self.symbol_runner),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_mariadb_database<'a>(&'a self, name: &'static str) -> Box<dyn Action + 'a> {
|
pub fn get_mariadb_database<'a>(&'a self, name: &'static str) -> Box<dyn Action + 'a> {
|
||||||
let db_dump = SimpleStorage::new("/root/data".to_string(), format!("{}.sql", name));
|
let db_dump = SimpleStorage::new("/root/data".to_string(), format!("{}.sql", name));
|
||||||
Box::new(ListAction::new(vec![
|
Box::new(ListAction::new(vec![
|
||||||
Box::new(MariaDBDatabase::new(name.into(), db_dump.read_filename().unwrap().into(), self.command_runner)).into_action(self.symbol_runner),
|
Box::new(MariaDBDatabase::new(
|
||||||
Box::new(DatabaseDump::new(name, db_dump, self.command_runner)).into_action(self.symbol_runner)
|
name.into(),
|
||||||
|
db_dump.read_filename().unwrap().into(),
|
||||||
|
self.command_runner,
|
||||||
|
))
|
||||||
|
.into_action(self.symbol_runner),
|
||||||
|
Box::new(DatabaseDump::new(name, db_dump, self.command_runner))
|
||||||
|
.into_action(self.symbol_runner),
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_mariadb_user<'a>(&'a self, user_name: &'static str) -> Box<dyn Action + 'a> {
|
pub fn get_mariadb_user<'a>(&'a self, user_name: &'static str) -> Box<dyn Action + 'a> {
|
||||||
Box::new(MariaDBUser::new(user_name.into(), self.command_runner)).into_action(self.symbol_runner)
|
Box::new(MariaDBUser::new(user_name.into(), self.command_runner))
|
||||||
|
.into_action(self.symbol_runner)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_git_checkout<'a, T: 'a + AsRef<str>>(&'a self, target: T, source: &'a str, branch: &'a str) -> Box<dyn Action + 'a> {
|
pub fn get_git_checkout<'a, T: 'a + AsRef<str>>(
|
||||||
Box::new(GitCheckout::new(target, source, branch, self.command_runner)).into_action(self.symbol_runner)
|
&'a self,
|
||||||
|
target: T,
|
||||||
|
source: &'a str,
|
||||||
|
branch: &'a str,
|
||||||
|
) -> Box<dyn Action + 'a> {
|
||||||
|
Box::new(GitCheckout::new(
|
||||||
|
target,
|
||||||
|
source,
|
||||||
|
branch,
|
||||||
|
self.command_runner,
|
||||||
|
))
|
||||||
|
.into_action(self.symbol_runner)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_owner<'a, F: 'a + AsRef<str>>(&'a self, file: F, user: &'a str) -> Box<dyn Action + 'a> {
|
pub fn get_owner<'a, F: 'a + AsRef<str>>(
|
||||||
|
&'a self,
|
||||||
|
file: F,
|
||||||
|
user: &'a str,
|
||||||
|
) -> Box<dyn Action + 'a> {
|
||||||
Box::new(Owner::new(file, user.into(), self.command_runner)).into_action(self.symbol_runner)
|
Box::new(Owner::new(file, user.into(), self.command_runner)).into_action(self.symbol_runner)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_file<'a, F: 'a + Deref<Target=str>, Q: 'a + AsRef<Path>>(&'a self, path: Q, content: F) -> Box<dyn Action + 'a> {
|
pub fn get_file<'a, F: 'a + Deref<Target = str>, Q: 'a + AsRef<Path>>(
|
||||||
|
&'a self,
|
||||||
|
path: Q,
|
||||||
|
content: F,
|
||||||
|
) -> Box<dyn Action + 'a> {
|
||||||
Box::new(File::new(path, content)).into_action(self.symbol_runner)
|
Box::new(File::new(path, content)).into_action(self.symbol_runner)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,21 +6,33 @@ use std::io::{Read, Write};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
|
||||||
use resources::Resource;
|
use resources::Resource;
|
||||||
|
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||||
|
|
||||||
pub struct File<C, D> where C: Deref<Target=str>, D: AsRef<Path> {
|
pub struct File<C, D>
|
||||||
|
where
|
||||||
|
C: Deref<Target = str>,
|
||||||
|
D: AsRef<Path>,
|
||||||
|
{
|
||||||
path: D,
|
path: D,
|
||||||
content: C
|
content: C,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C, D> File<C, D> where C: Deref<Target=str>, D: AsRef<Path> {
|
impl<C, D> File<C, D>
|
||||||
|
where
|
||||||
|
C: Deref<Target = str>,
|
||||||
|
D: AsRef<Path>,
|
||||||
|
{
|
||||||
pub fn new(path: D, content: C) -> Self {
|
pub fn new(path: D, content: C) -> Self {
|
||||||
File { path, content }
|
File { path, content }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C, D> Symbol for File<C, D> where C: Deref<Target=str>, D: AsRef<Path> {
|
impl<C, D> Symbol for File<C, D>
|
||||||
|
where
|
||||||
|
C: Deref<Target = str>,
|
||||||
|
D: AsRef<Path>,
|
||||||
|
{
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
let file = FsFile::open(self.path.as_ref());
|
let file = FsFile::open(self.path.as_ref());
|
||||||
// Check if file exists
|
// Check if file exists
|
||||||
|
|
@ -37,9 +49,9 @@ impl<C, D> Symbol for File<C, D> where C: Deref<Target=str>, D: AsRef<Path> {
|
||||||
loop {
|
loop {
|
||||||
match (file_content.next(), target_content.next()) {
|
match (file_content.next(), target_content.next()) {
|
||||||
(None, None) => return Ok(true),
|
(None, None) => return Ok(true),
|
||||||
(Some(Ok(a)), Some(b)) if a == b => {},
|
(Some(Ok(a)), Some(b)) if a == b => {}
|
||||||
(Some(Err(e)), _) => return Err(Box::new(e)),
|
(Some(Err(e)), _) => return Err(Box::new(e)),
|
||||||
(_, _) => return Ok(false)
|
(_, _) => return Ok(false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -51,19 +63,32 @@ impl<C, D> Symbol for File<C, D> where C: Deref<Target=str>, D: AsRef<Path> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_prerequisites(&self) -> Vec<Resource> {
|
fn get_prerequisites(&self) -> Vec<Resource> {
|
||||||
vec![ Resource::new("dir", Path::new(self.path.as_ref()).parent().unwrap().to_string_lossy() ) ]
|
vec![Resource::new(
|
||||||
|
"dir",
|
||||||
|
Path::new(self.path.as_ref())
|
||||||
|
.parent()
|
||||||
|
.unwrap()
|
||||||
|
.to_string_lossy(),
|
||||||
|
)]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_action<'a>(&'a self, runner: &'a dyn SymbolRunner) -> Box<dyn Action + 'a> {
|
fn as_action<'a>(&'a self, runner: &'a dyn SymbolRunner) -> Box<dyn Action + 'a> {
|
||||||
Box::new(SymbolAction::new(runner, self))
|
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))
|
Box::new(OwnedSymbolAction::new(runner, *self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C, D> fmt::Display for File<C, D> where C: Deref<Target=str>, D: AsRef<Path> {
|
impl<C, D> fmt::Display for File<C, D>
|
||||||
|
where
|
||||||
|
C: Deref<Target = str>,
|
||||||
|
D: AsRef<Path>,
|
||||||
|
{
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
write!(f, "File {}", self.path.as_ref().display())
|
write!(f, "File {}", self.path.as_ref().display())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,18 +11,29 @@ pub struct GitCheckout<'a, C: 'a + CommandRunner, T: AsRef<str>> {
|
||||||
target: T,
|
target: T,
|
||||||
source: &'a str,
|
source: &'a str,
|
||||||
branch: &'a str,
|
branch: &'a str,
|
||||||
command_runner: &'a C
|
command_runner: &'a C,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: CommandRunner, T: AsRef<str>> GitCheckout<'a, C, T> {
|
impl<'a, C: CommandRunner, T: AsRef<str>> GitCheckout<'a, C, T> {
|
||||||
pub fn new(target: T, source: &'a str, branch: &'a str, command_runner: &'a C) -> Self {
|
pub fn new(target: T, source: &'a str, branch: &'a str, command_runner: &'a C) -> Self {
|
||||||
GitCheckout { target, source, branch, command_runner }
|
GitCheckout {
|
||||||
|
target,
|
||||||
|
source,
|
||||||
|
branch,
|
||||||
|
command_runner,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: CommandRunner, T: AsRef<str>> fmt::Display for GitCheckout<'a, C, T> {
|
impl<'a, C: CommandRunner, T: AsRef<str>> fmt::Display for GitCheckout<'a, C, T> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "Checkout {} (branch {}) into {}", self.source, self.branch, self.target.as_ref())
|
write!(
|
||||||
|
f,
|
||||||
|
"Checkout {} (branch {}) into {}",
|
||||||
|
self.source,
|
||||||
|
self.branch,
|
||||||
|
self.target.as_ref()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -54,7 +65,18 @@ impl<'a, C: CommandRunner, T: AsRef<str>> Symbol for GitCheckout<'a, C, T> {
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
if !Path::new(self.target.as_ref()).exists() {
|
if !Path::new(self.target.as_ref()).exists() {
|
||||||
return self.command_runner.run_successfully("git", &["clone", "--depth", "1", "-b", self.branch, self.source, self.target.as_ref()]);
|
return self.command_runner.run_successfully(
|
||||||
|
"git",
|
||||||
|
&[
|
||||||
|
"clone",
|
||||||
|
"--depth",
|
||||||
|
"1",
|
||||||
|
"-b",
|
||||||
|
self.branch,
|
||||||
|
self.source,
|
||||||
|
self.target.as_ref(),
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
try!(self._run_in_target_repo(&["fetch", self.source, self.branch]));
|
try!(self._run_in_target_repo(&["fetch", self.source, self.branch]));
|
||||||
try!(self._run_in_target_repo(&["merge", "FETCH_HEAD"]));
|
try!(self._run_in_target_repo(&["merge", "FETCH_HEAD"]));
|
||||||
|
|
@ -62,7 +84,13 @@ impl<'a, C: CommandRunner, T: AsRef<str>> Symbol for GitCheckout<'a, C, T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_prerequisites(&self) -> Vec<Resource> {
|
fn get_prerequisites(&self) -> Vec<Resource> {
|
||||||
vec![ Resource::new("dir", Path::new(self.target.as_ref()).parent().unwrap().to_string_lossy()) ]
|
vec![Resource::new(
|
||||||
|
"dir",
|
||||||
|
Path::new(self.target.as_ref())
|
||||||
|
.parent()
|
||||||
|
.unwrap()
|
||||||
|
.to_string_lossy(),
|
||||||
|
)]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn provides(&self) -> Option<Vec<Resource>> {
|
fn provides(&self) -> Option<Vec<Resource>> {
|
||||||
|
|
@ -73,11 +101,13 @@ impl<'a, C: CommandRunner, T: AsRef<str>> Symbol for GitCheckout<'a, C, T> {
|
||||||
Box::new(SymbolAction::new(runner, self))
|
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))
|
Box::new(OwnedSymbolAction::new(runner, *self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {}
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,15 @@ use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||||
|
|
||||||
pub struct GitSubmodules<'a, C: 'a + CommandRunner> {
|
pub struct GitSubmodules<'a, C: 'a + CommandRunner> {
|
||||||
target: &'a str,
|
target: &'a str,
|
||||||
command_runner: &'a C
|
command_runner: &'a C,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: CommandRunner> GitSubmodules<'a, C> {
|
impl<'a, C: CommandRunner> GitSubmodules<'a, C> {
|
||||||
pub fn new(target: &'a str, command_runner: &'a C) -> Self {
|
pub fn new(target: &'a str, command_runner: &'a C) -> Self {
|
||||||
GitSubmodules { target, command_runner }
|
GitSubmodules {
|
||||||
|
target,
|
||||||
|
command_runner,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -35,8 +38,14 @@ impl<'a, C: CommandRunner> Symbol for GitSubmodules<'a, C> {
|
||||||
if !Path::new(self.target).exists() {
|
if !Path::new(self.target).exists() {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
let output = try!(String::from_utf8(try!(self._run_in_target_repo(&["submodule", "status"]))));
|
let output = try!(String::from_utf8(try!(
|
||||||
Ok(output.lines().all(|line| line.is_empty() || line.starts_with(' ')))
|
self._run_in_target_repo(&["submodule", "status"])
|
||||||
|
)));
|
||||||
|
Ok(
|
||||||
|
output
|
||||||
|
.lines()
|
||||||
|
.all(|line| line.is_empty() || line.starts_with(' ')),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
|
|
@ -48,11 +57,13 @@ impl<'a, C: CommandRunner> Symbol for GitSubmodules<'a, C> {
|
||||||
Box::new(SymbolAction::new(runner, self))
|
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))
|
Box::new(OwnedSymbolAction::new(runner, *self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {}
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -4,21 +4,39 @@ use std::fmt;
|
||||||
use resources::Resource;
|
use resources::Resource;
|
||||||
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||||
|
|
||||||
pub struct Hook<A, B> where A: Symbol, B: Symbol {
|
pub struct Hook<A, B>
|
||||||
|
where
|
||||||
|
A: Symbol,
|
||||||
|
B: Symbol,
|
||||||
|
{
|
||||||
a: A,
|
a: A,
|
||||||
b: B
|
b: B,
|
||||||
}
|
}
|
||||||
|
|
||||||
// A and B are executed if either A or B are not reached
|
// A and B are executed if either A or B are not reached
|
||||||
impl<A, B> Hook<A, B> where A: Symbol, B: Symbol {
|
impl<A, B> Hook<A, B>
|
||||||
|
where
|
||||||
|
A: Symbol,
|
||||||
|
B: Symbol,
|
||||||
|
{
|
||||||
pub fn new(a: A, b: B) -> Self {
|
pub fn new(a: A, b: B) -> Self {
|
||||||
Hook { a, b }
|
Hook { a, b }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A, B> Symbol for Hook<A, B> where A: Symbol, B: Symbol {
|
impl<A, B> Symbol for Hook<A, B>
|
||||||
|
where
|
||||||
|
A: Symbol,
|
||||||
|
B: Symbol,
|
||||||
|
{
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
self.a.target_reached().and_then(|reached| if reached { self.b.target_reached() } else { Ok(reached) })
|
self.a.target_reached().and_then(|reached| {
|
||||||
|
if reached {
|
||||||
|
self.b.target_reached()
|
||||||
|
} else {
|
||||||
|
Ok(reached)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
|
|
@ -41,19 +59,30 @@ impl<A, B> Symbol for Hook<A, B> where A: Symbol, B: Symbol {
|
||||||
if let Some(provides) = self.b.provides() {
|
if let Some(provides) = self.b.provides() {
|
||||||
r.extend(provides.into_iter());
|
r.extend(provides.into_iter());
|
||||||
}
|
}
|
||||||
if r.is_empty() { None } else { Some(r) }
|
if r.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(r)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_action<'a>(&'a self, runner: &'a dyn SymbolRunner) -> Box<dyn Action + 'a> {
|
fn as_action<'a>(&'a self, runner: &'a dyn SymbolRunner) -> Box<dyn Action + 'a> {
|
||||||
Box::new(SymbolAction::new(runner, self))
|
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))
|
Box::new(OwnedSymbolAction::new(runner, *self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A, B> fmt::Display for Hook<A, B> where A: Symbol, B: Symbol {
|
impl<A, B> fmt::Display for Hook<A, B>
|
||||||
|
where
|
||||||
|
A: Symbol,
|
||||||
|
B: Symbol,
|
||||||
|
{
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
write!(f, "Hook {} and then {}", self.a, self.b)
|
write!(f, "Hook {} and then {}", self.a, self.b)
|
||||||
}
|
}
|
||||||
|
|
@ -64,36 +93,58 @@ mod test {
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
|
||||||
use symbols::hook::Hook;
|
use symbols::hook::Hook;
|
||||||
|
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||||
|
|
||||||
struct ErrSymbol(String);
|
struct ErrSymbol(String);
|
||||||
impl Symbol for ErrSymbol {
|
impl Symbol for ErrSymbol {
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> { Err(self.0.clone().into()) }
|
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> { Err(self.0.clone().into()) }
|
Err(self.0.clone().into())
|
||||||
|
}
|
||||||
|
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
|
Err(self.0.clone().into())
|
||||||
|
}
|
||||||
fn as_action<'a>(&'a self, runner: &'a dyn SymbolRunner) -> Box<dyn Action + 'a> {
|
fn as_action<'a>(&'a self, runner: &'a dyn SymbolRunner) -> Box<dyn Action + 'a> {
|
||||||
Box::new(SymbolAction::new(runner, self))
|
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))
|
Box::new(OwnedSymbolAction::new(runner, *self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl fmt::Display for ErrSymbol { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>{ write!(f, "") } }
|
impl fmt::Display for ErrSymbol {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
|
write!(f, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct OkSymbol(bool);
|
struct OkSymbol(bool);
|
||||||
impl Symbol for OkSymbol {
|
impl Symbol for OkSymbol {
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> { Ok(self.0) }
|
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> { Ok(()) }
|
Ok(self.0)
|
||||||
|
}
|
||||||
|
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
fn as_action<'a>(&'a self, runner: &'a dyn SymbolRunner) -> Box<dyn Action + 'a> {
|
fn as_action<'a>(&'a self, runner: &'a dyn SymbolRunner) -> Box<dyn Action + 'a> {
|
||||||
Box::new(SymbolAction::new(runner, self))
|
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))
|
Box::new(OwnedSymbolAction::new(runner, *self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl fmt::Display for OkSymbol { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>{ write!(f, "") } }
|
impl fmt::Display for OkSymbol {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
|
write!(f, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn first_target_reached_fails() {
|
fn first_target_reached_fails() {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use resources::Resource;
|
||||||
use symbols::{Action, Symbol, SymbolRunner};
|
use symbols::{Action, Symbol, SymbolRunner};
|
||||||
|
|
||||||
pub struct List<'a> {
|
pub struct List<'a> {
|
||||||
symbols: Vec<Box<dyn Symbol + 'a>>
|
symbols: Vec<Box<dyn Symbol + 'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> List<'a> {
|
impl<'a> List<'a> {
|
||||||
|
|
@ -35,7 +35,9 @@ impl<'a> Symbol for List<'a> {
|
||||||
let mut r = vec![];
|
let mut r = vec![];
|
||||||
for symbol in &self.symbols {
|
for symbol in &self.symbols {
|
||||||
for req in symbol.get_prerequisites() {
|
for req in symbol.get_prerequisites() {
|
||||||
if self.provides().map_or(true, |p| !p.contains(&req)) { r.push(req) }
|
if self.provides().map_or(true, |p| !p.contains(&req)) {
|
||||||
|
r.push(req)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
r
|
r
|
||||||
|
|
@ -48,21 +50,34 @@ impl<'a> Symbol for List<'a> {
|
||||||
r.extend(provides.into_iter());
|
r.extend(provides.into_iter());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if r.is_empty() { None } else { Some(r) }
|
if r.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(r)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
|
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
|
||||||
Box::new(SymbolListAction::new(runner, &self.symbols))
|
Box::new(SymbolListAction::new(runner, &self.symbols))
|
||||||
}
|
}
|
||||||
|
|
||||||
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>
|
||||||
Box::new(ListAction::new(self.symbols.into_iter().map(|s| s.into_action(runner)).collect()))
|
where
|
||||||
|
Self: 'b,
|
||||||
|
{
|
||||||
|
Box::new(ListAction::new(
|
||||||
|
self
|
||||||
|
.symbols
|
||||||
|
.into_iter()
|
||||||
|
.map(|s| s.into_action(runner))
|
||||||
|
.collect(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SymbolListAction<'a> {
|
struct SymbolListAction<'a> {
|
||||||
runner: &'a dyn SymbolRunner,
|
runner: &'a dyn SymbolRunner,
|
||||||
symbols: &'a [Box<dyn Symbol + 'a>]
|
symbols: &'a [Box<dyn Symbol + 'a>],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> SymbolListAction<'a> {
|
impl<'a> SymbolListAction<'a> {
|
||||||
|
|
@ -81,7 +96,7 @@ impl<'a> Action for SymbolListAction<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ListAction<'a> {
|
pub struct ListAction<'a> {
|
||||||
actions: Vec<Box<dyn Action + 'a>>
|
actions: Vec<Box<dyn Action + 'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ListAction<'a> {
|
impl<'a> ListAction<'a> {
|
||||||
|
|
|
||||||
|
|
@ -8,16 +8,22 @@ use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||||
pub struct MariaDBDatabase<'a, C: 'a + CommandRunner> {
|
pub struct MariaDBDatabase<'a, C: 'a + CommandRunner> {
|
||||||
db_name: Cow<'a, str>,
|
db_name: Cow<'a, str>,
|
||||||
seed_file: Cow<'a, str>,
|
seed_file: Cow<'a, str>,
|
||||||
command_runner: &'a C
|
command_runner: &'a C,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: CommandRunner> MariaDBDatabase<'a, C> {
|
impl<'a, C: CommandRunner> MariaDBDatabase<'a, C> {
|
||||||
pub fn new(db_name: Cow<'a, str>, seed_file: Cow<'a, str>, command_runner: &'a C) -> Self {
|
pub fn new(db_name: Cow<'a, str>, seed_file: Cow<'a, str>, command_runner: &'a C) -> Self {
|
||||||
MariaDBDatabase { db_name, seed_file, command_runner }
|
MariaDBDatabase {
|
||||||
|
db_name,
|
||||||
|
seed_file,
|
||||||
|
command_runner,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_sql(&self, sql: &str) -> Result<String, Box<dyn Error>> {
|
fn run_sql(&self, sql: &str) -> Result<String, Box<dyn Error>> {
|
||||||
let b = try!(self.command_runner.get_output("mariadb", &["--skip-column-names", "-B", "-e", sql]));
|
let b = try!(self
|
||||||
|
.command_runner
|
||||||
|
.get_output("mariadb", &["--skip-column-names", "-B", "-e", sql]));
|
||||||
Ok(try!(String::from_utf8(b)))
|
Ok(try!(String::from_utf8(b)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -30,23 +36,34 @@ impl<'a, C: CommandRunner> fmt::Display for MariaDBDatabase<'a, C> {
|
||||||
|
|
||||||
impl<'a, C: CommandRunner> Symbol for MariaDBDatabase<'a, C> {
|
impl<'a, C: CommandRunner> Symbol for MariaDBDatabase<'a, C> {
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
Ok(try!(self.run_sql(&format!("SHOW DATABASES LIKE '{}'", self.db_name))).trim_end() == self.db_name)
|
Ok(
|
||||||
|
try!(self.run_sql(&format!("SHOW DATABASES LIKE '{}'", self.db_name))).trim_end()
|
||||||
|
== self.db_name,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
try!(self.run_sql(&format!("CREATE DATABASE {}", self.db_name)));
|
try!(self.run_sql(&format!("CREATE DATABASE {}", self.db_name)));
|
||||||
self.command_runner.run_successfully("sh", &["-c", &format!("mariadb '{}' < {}", self.db_name, self.seed_file)])
|
self.command_runner.run_successfully(
|
||||||
|
"sh",
|
||||||
|
&[
|
||||||
|
"-c",
|
||||||
|
&format!("mariadb '{}' < {}", self.db_name, self.seed_file),
|
||||||
|
],
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
|
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
|
||||||
Box::new(SymbolAction::new(runner, self))
|
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))
|
Box::new(OwnedSymbolAction::new(runner, *self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {}
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -3,22 +3,33 @@ use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use command_runner::CommandRunner;
|
use command_runner::CommandRunner;
|
||||||
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
|
||||||
use storage::Storage;
|
use storage::Storage;
|
||||||
|
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||||
|
|
||||||
pub struct DatabaseDump<'a, N, C, S> where N: 'a + AsRef<str>, C: 'a + CommandRunner, S: Storage {
|
pub struct DatabaseDump<'a, N, C, S>
|
||||||
|
where
|
||||||
|
N: 'a + AsRef<str>,
|
||||||
|
C: 'a + CommandRunner,
|
||||||
|
S: Storage,
|
||||||
|
{
|
||||||
db_name: N,
|
db_name: N,
|
||||||
storage: S,
|
storage: S,
|
||||||
command_runner: &'a C
|
command_runner: &'a C,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, N: AsRef<str>, C: CommandRunner, S: Storage> DatabaseDump<'a, N, C, S> {
|
impl<'a, N: AsRef<str>, C: CommandRunner, S: Storage> DatabaseDump<'a, N, C, S> {
|
||||||
pub fn new(db_name: N, storage: S, command_runner: &'a C) -> Self {
|
pub fn new(db_name: N, storage: S, command_runner: &'a C) -> Self {
|
||||||
DatabaseDump { db_name, storage, command_runner }
|
DatabaseDump {
|
||||||
|
db_name,
|
||||||
|
storage,
|
||||||
|
command_runner,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_sql(&self, sql: &str) -> Result<String, Box<dyn Error>> {
|
fn run_sql(&self, sql: &str) -> Result<String, Box<dyn Error>> {
|
||||||
let b = try!(self.command_runner.get_output("mariadb", &["--skip-column-names", "-B", "-e", sql]));
|
let b = try!(self
|
||||||
|
.command_runner
|
||||||
|
.get_output("mariadb", &["--skip-column-names", "-B", "-e", sql]));
|
||||||
Ok(try!(String::from_utf8(b)))
|
Ok(try!(String::from_utf8(b)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -33,23 +44,37 @@ impl<'a, N: AsRef<str>, C: CommandRunner, S: Storage> Symbol for DatabaseDump<'a
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
let dump_date = try!(self.storage.recent_date());
|
let dump_date = try!(self.storage.recent_date());
|
||||||
let modified_date = try!(self.run_sql(&format!("select UNIX_TIMESTAMP(MAX(UPDATE_TIME)) from information_schema.tables WHERE table_schema = '{}'", self.db_name.as_ref())));
|
let modified_date = try!(self.run_sql(&format!("select UNIX_TIMESTAMP(MAX(UPDATE_TIME)) from information_schema.tables WHERE table_schema = '{}'", self.db_name.as_ref())));
|
||||||
if modified_date.trim_end() == "NULL" { return Ok(false); }
|
if modified_date.trim_end() == "NULL" {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
Ok(try!(u64::from_str(modified_date.trim_end())) <= dump_date)
|
Ok(try!(u64::from_str(modified_date.trim_end())) <= dump_date)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
self.command_runner.run_successfully("sh", &["-c", &format!("mysqldump '{}' > {}", self.db_name.as_ref(), self.storage.write_filename())])
|
self.command_runner.run_successfully(
|
||||||
|
"sh",
|
||||||
|
&[
|
||||||
|
"-c",
|
||||||
|
&format!(
|
||||||
|
"mysqldump '{}' > {}",
|
||||||
|
self.db_name.as_ref(),
|
||||||
|
self.storage.write_filename()
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
|
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
|
||||||
Box::new(SymbolAction::new(runner, self))
|
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))
|
Box::new(OwnedSymbolAction::new(runner, *self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {}
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
mod database;
|
mod database;
|
||||||
mod user;
|
|
||||||
mod database_dump;
|
mod database_dump;
|
||||||
|
mod user;
|
||||||
|
|
||||||
pub use self::database::MariaDBDatabase;
|
pub use self::database::MariaDBDatabase;
|
||||||
pub use self::database_dump::DatabaseDump;
|
pub use self::database_dump::DatabaseDump;
|
||||||
|
|
|
||||||
|
|
@ -8,16 +8,21 @@ use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||||
|
|
||||||
pub struct MariaDBUser<'a, C: 'a + CommandRunner> {
|
pub struct MariaDBUser<'a, C: 'a + CommandRunner> {
|
||||||
user_name: Cow<'a, str>,
|
user_name: Cow<'a, str>,
|
||||||
command_runner: &'a C
|
command_runner: &'a C,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: CommandRunner> MariaDBUser<'a, C> {
|
impl<'a, C: CommandRunner> MariaDBUser<'a, C> {
|
||||||
pub fn new(user_name: Cow<'a, str>, command_runner: &'a C) -> Self {
|
pub fn new(user_name: Cow<'a, str>, command_runner: &'a C) -> Self {
|
||||||
MariaDBUser { user_name, command_runner }
|
MariaDBUser {
|
||||||
|
user_name,
|
||||||
|
command_runner,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_sql(&self, sql: &str) -> Result<String, Box<dyn Error>> {
|
fn run_sql(&self, sql: &str) -> Result<String, Box<dyn Error>> {
|
||||||
let b = try!(self.command_runner.get_output("mariadb", &["--skip-column-names", "-B", "-e", sql]));
|
let b = try!(self
|
||||||
|
.command_runner
|
||||||
|
.get_output("mariadb", &["--skip-column-names", "-B", "-e", sql]));
|
||||||
Ok(try!(String::from_utf8(b)))
|
Ok(try!(String::from_utf8(b)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -30,11 +35,21 @@ impl<'a, C: CommandRunner> fmt::Display for MariaDBUser<'a, C> {
|
||||||
|
|
||||||
impl<'a, C: CommandRunner> Symbol for MariaDBUser<'a, C> {
|
impl<'a, C: CommandRunner> Symbol for MariaDBUser<'a, C> {
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
Ok(try!(self.run_sql(&format!("SELECT User FROM mysql.user WHERE User = '{}' AND plugin = 'unix_socket'", self.user_name))).trim_end() == self.user_name)
|
Ok(
|
||||||
|
try!(self.run_sql(&format!(
|
||||||
|
"SELECT User FROM mysql.user WHERE User = '{}' AND plugin = 'unix_socket'",
|
||||||
|
self.user_name
|
||||||
|
)))
|
||||||
|
.trim_end()
|
||||||
|
== self.user_name,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
try!(self.run_sql(&format!("GRANT ALL ON {0}.* TO {0} IDENTIFIED VIA unix_socket", self.user_name)));
|
try!(self.run_sql(&format!(
|
||||||
|
"GRANT ALL ON {0}.* TO {0} IDENTIFIED VIA unix_socket",
|
||||||
|
self.user_name
|
||||||
|
)));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -46,11 +61,13 @@ impl<'a, C: CommandRunner> Symbol for MariaDBUser<'a, C> {
|
||||||
Box::new(SymbolAction::new(runner, self))
|
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))
|
Box::new(OwnedSymbolAction::new(runner, *self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {}
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
|
use resources::Resource;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use resources::Resource;
|
|
||||||
|
|
||||||
pub trait Action {
|
pub trait Action {
|
||||||
fn run(&self) -> Result<(), Box<dyn Error>>;
|
fn run(&self) -> Result<(), Box<dyn Error>>;
|
||||||
|
|
@ -27,13 +27,15 @@ pub trait Symbol: Display {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
fn as_action<'a>(&'a self, runner: &'a dyn SymbolRunner) -> Box<dyn Action + 'a>;
|
fn as_action<'a>(&'a self, runner: &'a dyn SymbolRunner) -> Box<dyn Action + 'a>;
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SymbolAction
|
// SymbolAction
|
||||||
pub struct SymbolAction<'a, S: Symbol + 'a> {
|
pub struct SymbolAction<'a, S: Symbol + 'a> {
|
||||||
runner: &'a dyn SymbolRunner,
|
runner: &'a dyn SymbolRunner,
|
||||||
symbol: &'a S
|
symbol: &'a S,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, S: Symbol> SymbolAction<'a, S> {
|
impl<'a, S: Symbol> SymbolAction<'a, S> {
|
||||||
|
|
@ -50,7 +52,7 @@ impl<'a, S: Symbol> Action for SymbolAction<'a, S> {
|
||||||
|
|
||||||
pub struct OwnedSymbolAction<'a, S: Symbol + 'a> {
|
pub struct OwnedSymbolAction<'a, S: Symbol + 'a> {
|
||||||
runner: &'a dyn SymbolRunner,
|
runner: &'a dyn SymbolRunner,
|
||||||
symbol: S
|
symbol: S,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, S: Symbol + 'a> OwnedSymbolAction<'a, S> {
|
impl<'a, S: Symbol + 'a> OwnedSymbolAction<'a, S> {
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,14 @@ use std::io;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
use command_runner::CommandRunner;
|
use command_runner::CommandRunner;
|
||||||
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
|
||||||
use symbols::file::File as FileSymbol;
|
|
||||||
use resources::Resource;
|
use resources::Resource;
|
||||||
|
use symbols::file::File as FileSymbol;
|
||||||
|
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum NginxServerError<E: Error> {
|
pub enum NginxServerError<E: Error> {
|
||||||
ExecError(E),
|
ExecError(E),
|
||||||
GenericError
|
GenericError,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<io::Error> for NginxServerError<io::Error> {
|
impl From<io::Error> for NginxServerError<io::Error> {
|
||||||
|
|
@ -24,13 +24,13 @@ impl<E: Error> Error for NginxServerError<E> {
|
||||||
fn description(&self) -> &str {
|
fn description(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
NginxServerError::ExecError(ref e) => e.description(),
|
NginxServerError::ExecError(ref e) => e.description(),
|
||||||
NginxServerError::GenericError => "Generic error"
|
NginxServerError::GenericError => "Generic error",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn cause(&self) -> Option<&dyn Error> {
|
fn cause(&self) -> Option<&dyn Error> {
|
||||||
match self {
|
match self {
|
||||||
NginxServerError::ExecError(ref e) => Some(e),
|
NginxServerError::ExecError(ref e) => Some(e),
|
||||||
_ => None
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -41,7 +41,10 @@ impl<E: Error> fmt::Display for NginxServerError<E> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct NginxServer<'a, C: 'a + CommandRunner, T> where T: Deref<Target=str> {
|
pub struct NginxServer<'a, C: 'a + CommandRunner, T>
|
||||||
|
where
|
||||||
|
T: Deref<Target = str>,
|
||||||
|
{
|
||||||
command_runner: &'a C,
|
command_runner: &'a C,
|
||||||
file: FileSymbol<T, String>,
|
file: FileSymbol<T, String>,
|
||||||
}
|
}
|
||||||
|
|
@ -49,7 +52,8 @@ pub struct NginxServer<'a, C: 'a + CommandRunner, T> where T: Deref<Target=str>
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
pub fn server_config(domain: &str, content: &str) -> String {
|
pub fn server_config(domain: &str, content: &str) -> String {
|
||||||
format!("server {{
|
format!(
|
||||||
|
"server {{
|
||||||
listen 443 ssl http2;
|
listen 443 ssl http2;
|
||||||
server_name {0};
|
server_name {0};
|
||||||
include \"snippets/acme-challenge.conf\";
|
include \"snippets/acme-challenge.conf\";
|
||||||
|
|
@ -71,17 +75,25 @@ server {{
|
||||||
return 301 https://$host$request_uri;
|
return 301 https://$host$request_uri;
|
||||||
}}
|
}}
|
||||||
}}
|
}}
|
||||||
", domain, content)
|
",
|
||||||
|
domain, content
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn php_server_config_snippet<'a>(socket_path: Cow<'a, str>, static_path: Cow<'a, str>) -> String {
|
pub fn php_server_config_snippet<'a>(
|
||||||
format!("
|
socket_path: Cow<'a, str>,
|
||||||
|
static_path: Cow<'a, str>,
|
||||||
|
) -> String {
|
||||||
|
format!(
|
||||||
|
"
|
||||||
root {};
|
root {};
|
||||||
index index.html index.php;
|
index index.html index.php;
|
||||||
location ~ [^/]\\.php(/|$) {{
|
location ~ [^/]\\.php(/|$) {{
|
||||||
fastcgi_pass unix:{};
|
fastcgi_pass unix:{};
|
||||||
include \"snippets/fastcgi-php.conf\";
|
include \"snippets/fastcgi-php.conf\";
|
||||||
}}", static_path, socket_path)
|
}}",
|
||||||
|
static_path, socket_path
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait SocketSpec {
|
pub trait SocketSpec {
|
||||||
|
|
@ -110,14 +122,26 @@ impl SocketSpec for LocalTcpSocket {
|
||||||
|
|
||||||
impl<'a, C: CommandRunner> NginxServer<'a, C, String> {
|
impl<'a, C: CommandRunner> NginxServer<'a, C, String> {
|
||||||
pub fn new_redir(domain: &'a str, target: &'a str, command_runner: &'a C) -> Self {
|
pub fn new_redir(domain: &'a str, target: &'a str, command_runner: &'a C) -> Self {
|
||||||
let content = server_config(domain, &format!("location / {{
|
let content = server_config(
|
||||||
|
domain,
|
||||||
|
&format!(
|
||||||
|
"location / {{
|
||||||
return 301 $scheme://{}$request_uri;
|
return 301 $scheme://{}$request_uri;
|
||||||
}}", target));
|
}}",
|
||||||
|
target
|
||||||
|
),
|
||||||
|
);
|
||||||
NginxServer::new(domain, content, command_runner)
|
NginxServer::new(domain, content, command_runner)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_proxy<S: SocketSpec>(domain: &'a str, socket_path: S, static_path: &'a str, command_runner: &'a C) -> Self {
|
pub fn new_proxy<S: SocketSpec>(
|
||||||
let proxy_content = format!("location / {{
|
domain: &'a str,
|
||||||
|
socket_path: S,
|
||||||
|
static_path: &'a str,
|
||||||
|
command_runner: &'a C,
|
||||||
|
) -> Self {
|
||||||
|
let proxy_content = format!(
|
||||||
|
"location / {{
|
||||||
try_files $uri @proxy;
|
try_files $uri @proxy;
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
|
@ -125,25 +149,44 @@ location @proxy {{
|
||||||
include fastcgi_params;
|
include fastcgi_params;
|
||||||
proxy_pass http://{};
|
proxy_pass http://{};
|
||||||
proxy_redirect off;
|
proxy_redirect off;
|
||||||
}}", socket_path.to_nginx());
|
}}",
|
||||||
|
socket_path.to_nginx()
|
||||||
|
);
|
||||||
|
|
||||||
let content = server_config(domain, &format!("
|
let content = server_config(
|
||||||
|
domain,
|
||||||
|
&format!(
|
||||||
|
"
|
||||||
root {};
|
root {};
|
||||||
{}
|
{}
|
||||||
", static_path, proxy_content));
|
",
|
||||||
|
static_path, proxy_content
|
||||||
|
),
|
||||||
|
);
|
||||||
NginxServer::new(domain, content, command_runner)
|
NginxServer::new(domain, content, command_runner)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_php(domain: &'a str, socket_path: Cow<'a, str>, static_path: Cow<'a, str>, command_runner: &'a C) -> Self {
|
pub fn new_php(
|
||||||
|
domain: &'a str,
|
||||||
|
socket_path: Cow<'a, str>,
|
||||||
|
static_path: Cow<'a, str>,
|
||||||
|
command_runner: &'a C,
|
||||||
|
) -> Self {
|
||||||
let content = server_config(domain, &php_server_config_snippet(socket_path, static_path));
|
let content = server_config(domain, &php_server_config_snippet(socket_path, static_path));
|
||||||
NginxServer::new(domain, content, command_runner)
|
NginxServer::new(domain, content, command_runner)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_static(domain: &'a str, static_path: &'a str, command_runner: &'a C) -> Self {
|
pub fn new_static(domain: &'a str, static_path: &'a str, command_runner: &'a C) -> Self {
|
||||||
let content = server_config(domain, &format!("
|
let content = server_config(
|
||||||
|
domain,
|
||||||
|
&format!(
|
||||||
|
"
|
||||||
root {};
|
root {};
|
||||||
try_files $uri $uri/ $uri.html =404;
|
try_files $uri $uri/ $uri.html =404;
|
||||||
", static_path));
|
",
|
||||||
|
static_path
|
||||||
|
),
|
||||||
|
);
|
||||||
NginxServer::new(domain, content, command_runner)
|
NginxServer::new(domain, content, command_runner)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -151,12 +194,15 @@ location @proxy {{
|
||||||
let file_path = String::from("/etc/nginx/sites-enabled/") + domain;
|
let file_path = String::from("/etc/nginx/sites-enabled/") + domain;
|
||||||
NginxServer {
|
NginxServer {
|
||||||
command_runner,
|
command_runner,
|
||||||
file: FileSymbol::new(file_path, content)
|
file: FileSymbol::new(file_path, content),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: CommandRunner, T> Symbol for NginxServer<'a, C, T> where T: Deref<Target=str> {
|
impl<'a, C: CommandRunner, T> Symbol for NginxServer<'a, C, T>
|
||||||
|
where
|
||||||
|
T: Deref<Target = str>,
|
||||||
|
{
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
if !try!(self.file.target_reached()) {
|
if !try!(self.file.target_reached()) {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
|
|
@ -167,7 +213,9 @@ impl<'a, C: CommandRunner, T> Symbol for NginxServer<'a, C, T> where T: Deref<Ta
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
try!(self.file.execute());
|
try!(self.file.execute());
|
||||||
self.command_runner.run_successfully("systemctl", &["reload-or-restart", "nginx"])
|
self
|
||||||
|
.command_runner
|
||||||
|
.run_successfully("systemctl", &["reload-or-restart", "nginx"])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_prerequisites(&self) -> Vec<Resource> {
|
fn get_prerequisites(&self) -> Vec<Resource> {
|
||||||
|
|
@ -178,12 +226,18 @@ impl<'a, C: CommandRunner, T> Symbol for NginxServer<'a, C, T> where T: Deref<Ta
|
||||||
Box::new(SymbolAction::new(runner, self))
|
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))
|
Box::new(OwnedSymbolAction::new(runner, *self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: CommandRunner, T> fmt::Display for NginxServer<'a, C, T> where T: Deref<Target=str> {
|
impl<'a, C: CommandRunner, T> fmt::Display for NginxServer<'a, C, T>
|
||||||
|
where
|
||||||
|
T: Deref<Target = str>,
|
||||||
|
{
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
write!(f, "Nginx server config")
|
write!(f, "Nginx server config")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,10 @@ impl Symbol for NoopSymbol {
|
||||||
Box::new(SymbolAction::new(runner, self))
|
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))
|
Box::new(OwnedSymbolAction::new(runner, *self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -26,4 +29,3 @@ impl Display for NoopSymbol {
|
||||||
write!(f, "Noop")
|
write!(f, "Noop")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,15 @@ use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||||
|
|
||||||
pub struct NpmInstall<'a, C: 'a + CommandRunner> {
|
pub struct NpmInstall<'a, C: 'a + CommandRunner> {
|
||||||
target: &'a str,
|
target: &'a str,
|
||||||
command_runner: &'a C
|
command_runner: &'a C,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: CommandRunner> NpmInstall<'a, C> {
|
impl<'a, C: CommandRunner> NpmInstall<'a, C> {
|
||||||
pub fn new(target: &'a str, command_runner: &'a C) -> Self {
|
pub fn new(target: &'a str, command_runner: &'a C) -> Self {
|
||||||
NpmInstall { target, command_runner }
|
NpmInstall {
|
||||||
|
target,
|
||||||
|
command_runner,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -27,23 +30,41 @@ impl<'a, C: CommandRunner> Symbol for NpmInstall<'a, C> {
|
||||||
if !Path::new(self.target).exists() {
|
if !Path::new(self.target).exists() {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
let result = try!(self.command_runner.run_with_args("sh", &["-c", &format!("cd '{}' && npm ls", self.target)]));
|
let result = try!(self
|
||||||
Ok(result.status.success() && !String::from_utf8(result.stdout).unwrap().contains("(empty)"))
|
.command_runner
|
||||||
|
.run_with_args("sh", &["-c", &format!("cd '{}' && npm ls", self.target)]));
|
||||||
|
Ok(
|
||||||
|
result.status.success()
|
||||||
|
&& !String::from_utf8(result.stdout)
|
||||||
|
.unwrap()
|
||||||
|
.contains("(empty)"),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
self.command_runner.run_successfully("sh", &["-c", &format!("cd '{}' && npm install --production --unsafe-perm", self.target)])
|
self.command_runner.run_successfully(
|
||||||
|
"sh",
|
||||||
|
&[
|
||||||
|
"-c",
|
||||||
|
&format!(
|
||||||
|
"cd '{}' && npm install --production --unsafe-perm",
|
||||||
|
self.target
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
|
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
|
||||||
Box::new(SymbolAction::new(runner, self))
|
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))
|
Box::new(OwnedSymbolAction::new(runner, *self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {}
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -11,19 +11,32 @@ use command_runner::CommandRunner;
|
||||||
use resources::Resource;
|
use resources::Resource;
|
||||||
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||||
|
|
||||||
pub struct Owner<'a, C: 'a + CommandRunner, D> where D: AsRef<str> {
|
pub struct Owner<'a, C: 'a + CommandRunner, D>
|
||||||
|
where
|
||||||
|
D: AsRef<str>,
|
||||||
|
{
|
||||||
path: D,
|
path: D,
|
||||||
user_name: Cow<'a, str>,
|
user_name: Cow<'a, str>,
|
||||||
command_runner: &'a C
|
command_runner: &'a C,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: CommandRunner, D> Owner<'a, C, D> where D: AsRef<str> {
|
impl<'a, C: CommandRunner, D> Owner<'a, C, D>
|
||||||
|
where
|
||||||
|
D: AsRef<str>,
|
||||||
|
{
|
||||||
pub fn new(path: D, user_name: Cow<'a, str>, command_runner: &'a C) -> Self {
|
pub fn new(path: D, user_name: Cow<'a, str>, command_runner: &'a C) -> Self {
|
||||||
Owner { path, user_name, command_runner }
|
Owner {
|
||||||
|
path,
|
||||||
|
user_name,
|
||||||
|
command_runner,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: CommandRunner, D> Symbol for Owner<'a, C, D> where D: AsRef<str> {
|
impl<'a, C: CommandRunner, D> Symbol for Owner<'a, C, D>
|
||||||
|
where
|
||||||
|
D: AsRef<str>,
|
||||||
|
{
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
if !Path::new(self.path.as_ref()).exists() {
|
if !Path::new(self.path.as_ref()).exists() {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
|
|
@ -34,7 +47,10 @@ impl<'a, C: CommandRunner, D> Symbol for Owner<'a, C, D> where D: AsRef<str> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
self.command_runner.run_successfully("chown", &["-R", self.user_name.borrow(), self.path.as_ref()])
|
self.command_runner.run_successfully(
|
||||||
|
"chown",
|
||||||
|
&["-R", self.user_name.borrow(), self.path.as_ref()],
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_prerequisites(&self) -> Vec<Resource> {
|
fn get_prerequisites(&self) -> Vec<Resource> {
|
||||||
|
|
@ -45,12 +61,18 @@ impl<'a, C: CommandRunner, D> Symbol for Owner<'a, C, D> where D: AsRef<str> {
|
||||||
Box::new(SymbolAction::new(runner, self))
|
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))
|
Box::new(OwnedSymbolAction::new(runner, *self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: CommandRunner, D> fmt::Display for Owner<'a, C, D> where D: AsRef<str> {
|
impl<'a, C: CommandRunner, D> fmt::Display for Owner<'a, C, D>
|
||||||
|
where
|
||||||
|
D: AsRef<str>,
|
||||||
|
{
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
write!(f, "Owner {} for {}", self.user_name, self.path.as_ref())
|
write!(f, "Owner {} for {}", self.user_name, self.path.as_ref())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,16 +8,23 @@ use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||||
pub struct PostgreSQLDatabase<'a, C: 'a + CommandRunner> {
|
pub struct PostgreSQLDatabase<'a, C: 'a + CommandRunner> {
|
||||||
name: Cow<'a, str>,
|
name: Cow<'a, str>,
|
||||||
seed_file: Cow<'a, str>,
|
seed_file: Cow<'a, str>,
|
||||||
command_runner: &'a C
|
command_runner: &'a C,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: CommandRunner> PostgreSQLDatabase<'a, C> {
|
impl<'a, C: CommandRunner> PostgreSQLDatabase<'a, C> {
|
||||||
pub fn new(name: Cow<'a, str>, seed_file: Cow<'a, str>, command_runner: &'a C) -> Self {
|
pub fn new(name: Cow<'a, str>, seed_file: Cow<'a, str>, command_runner: &'a C) -> Self {
|
||||||
PostgreSQLDatabase { name, seed_file, command_runner }
|
PostgreSQLDatabase {
|
||||||
|
name,
|
||||||
|
seed_file,
|
||||||
|
command_runner,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_sql(&self, sql: &str) -> Result<String, Box<dyn Error>> {
|
fn run_sql(&self, sql: &str) -> Result<String, Box<dyn Error>> {
|
||||||
let b = try!(self.command_runner.get_output("su", &["-", "postgres", "-c", &format!("psql -t -c \"{}\"", sql)]));
|
let b = try!(self.command_runner.get_output(
|
||||||
|
"su",
|
||||||
|
&["-", "postgres", "-c", &format!("psql -t -c \"{}\"", sql)]
|
||||||
|
));
|
||||||
Ok(try!(String::from_utf8(b)))
|
Ok(try!(String::from_utf8(b)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -30,24 +37,52 @@ impl<'a, C: CommandRunner> fmt::Display for PostgreSQLDatabase<'a, C> {
|
||||||
|
|
||||||
impl<'a, C: CommandRunner> Symbol for PostgreSQLDatabase<'a, C> {
|
impl<'a, C: CommandRunner> Symbol for PostgreSQLDatabase<'a, C> {
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
Ok(try!(self.run_sql(&format!("SELECT datname FROM pg_database WHERE datname LIKE '{}'", self.name))).trim() == self.name)
|
Ok(
|
||||||
|
try!(self.run_sql(&format!(
|
||||||
|
"SELECT datname FROM pg_database WHERE datname LIKE '{}'",
|
||||||
|
self.name
|
||||||
|
)))
|
||||||
|
.trim()
|
||||||
|
== self.name,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
self.command_runner.run_successfully("su", &["-", "postgres", "-c", &format!("createuser {}", self.name)])?;
|
self.command_runner.run_successfully(
|
||||||
self.command_runner.run_successfully("su", &["-", "postgres", "-c", &format!("createdb -E UTF8 -T template0 -O {} {0}", self.name)])?;
|
"su",
|
||||||
self.command_runner.run_successfully("su", &["-", "postgres", "-c", &format!("psql '{}' < {}", self.name, self.seed_file)])
|
&["-", "postgres", "-c", &format!("createuser {}", self.name)],
|
||||||
|
)?;
|
||||||
|
self.command_runner.run_successfully(
|
||||||
|
"su",
|
||||||
|
&[
|
||||||
|
"-",
|
||||||
|
"postgres",
|
||||||
|
"-c",
|
||||||
|
&format!("createdb -E UTF8 -T template0 -O {} {0}", self.name),
|
||||||
|
],
|
||||||
|
)?;
|
||||||
|
self.command_runner.run_successfully(
|
||||||
|
"su",
|
||||||
|
&[
|
||||||
|
"-",
|
||||||
|
"postgres",
|
||||||
|
"-c",
|
||||||
|
&format!("psql '{}' < {}", self.name, self.seed_file),
|
||||||
|
],
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
|
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
|
||||||
Box::new(SymbolAction::new(runner, self))
|
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))
|
Box::new(OwnedSymbolAction::new(runner, *self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {}
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -3,22 +3,33 @@ use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use command_runner::CommandRunner;
|
use command_runner::CommandRunner;
|
||||||
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
|
||||||
use storage::Storage;
|
use storage::Storage;
|
||||||
|
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||||
|
|
||||||
pub struct DatabaseDump<'a, N, C, S> where N: 'a + AsRef<str>, C: 'a + CommandRunner, S: Storage {
|
pub struct DatabaseDump<'a, N, C, S>
|
||||||
|
where
|
||||||
|
N: 'a + AsRef<str>,
|
||||||
|
C: 'a + CommandRunner,
|
||||||
|
S: Storage,
|
||||||
|
{
|
||||||
db_name: N,
|
db_name: N,
|
||||||
storage: S,
|
storage: S,
|
||||||
command_runner: &'a C
|
command_runner: &'a C,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, N: AsRef<str>, C: CommandRunner, S: Storage> DatabaseDump<'a, N, C, S> {
|
impl<'a, N: AsRef<str>, C: CommandRunner, S: Storage> DatabaseDump<'a, N, C, S> {
|
||||||
pub fn new(db_name: N, storage: S, command_runner: &'a C) -> Self {
|
pub fn new(db_name: N, storage: S, command_runner: &'a C) -> Self {
|
||||||
DatabaseDump { db_name, storage, command_runner }
|
DatabaseDump {
|
||||||
|
db_name,
|
||||||
|
storage,
|
||||||
|
command_runner,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_sql(&self, sql: &str) -> Result<String, Box<dyn Error>> {
|
fn run_sql(&self, sql: &str) -> Result<String, Box<dyn Error>> {
|
||||||
let b = try!(self.command_runner.get_output("mariadb", &["--skip-column-names", "-B", "-e", sql]));
|
let b = try!(self
|
||||||
|
.command_runner
|
||||||
|
.get_output("mariadb", &["--skip-column-names", "-B", "-e", sql]));
|
||||||
Ok(try!(String::from_utf8(b)))
|
Ok(try!(String::from_utf8(b)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -33,23 +44,37 @@ impl<'a, N: AsRef<str>, C: CommandRunner, S: Storage> Symbol for DatabaseDump<'a
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
let dump_date = try!(self.storage.recent_date());
|
let dump_date = try!(self.storage.recent_date());
|
||||||
let modified_date = try!(self.run_sql(&format!("select UNIX_TIMESTAMP(MAX(UPDATE_TIME)) from information_schema.tables WHERE table_schema = '{}'", self.db_name.as_ref())));
|
let modified_date = try!(self.run_sql(&format!("select UNIX_TIMESTAMP(MAX(UPDATE_TIME)) from information_schema.tables WHERE table_schema = '{}'", self.db_name.as_ref())));
|
||||||
if modified_date.trim_end() == "NULL" { return Ok(false); }
|
if modified_date.trim_end() == "NULL" {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
Ok(try!(u64::from_str(modified_date.trim_end())) <= dump_date)
|
Ok(try!(u64::from_str(modified_date.trim_end())) <= dump_date)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
self.command_runner.run_successfully("sh", &["-c", &format!("mysqldump '{}' > {}", self.db_name.as_ref(), self.storage.write_filename())])
|
self.command_runner.run_successfully(
|
||||||
|
"sh",
|
||||||
|
&[
|
||||||
|
"-c",
|
||||||
|
&format!(
|
||||||
|
"mysqldump '{}' > {}",
|
||||||
|
self.db_name.as_ref(),
|
||||||
|
self.storage.write_filename()
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
|
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
|
||||||
Box::new(SymbolAction::new(runner, self))
|
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))
|
Box::new(OwnedSymbolAction::new(runner, *self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {}
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -8,32 +8,52 @@ use std::str::FromStr;
|
||||||
|
|
||||||
use command_runner::CommandRunner;
|
use command_runner::CommandRunner;
|
||||||
use resources::Resource;
|
use resources::Resource;
|
||||||
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
|
||||||
use storage::Storage;
|
use storage::Storage;
|
||||||
|
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum StorageDirection { Load, Save }
|
pub enum StorageDirection {
|
||||||
|
Load,
|
||||||
|
Save,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct StoredDirectory<'a, S, C: 'a + CommandRunner> where S: Storage {
|
pub struct StoredDirectory<'a, S, C: 'a + CommandRunner>
|
||||||
|
where
|
||||||
|
S: Storage,
|
||||||
|
{
|
||||||
path: Cow<'a, str>,
|
path: Cow<'a, str>,
|
||||||
storage: S,
|
storage: S,
|
||||||
dir: StorageDirection,
|
dir: StorageDirection,
|
||||||
command_runner: &'a C
|
command_runner: &'a C,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, S, C: CommandRunner> StoredDirectory<'a, S, C> where S: Storage {
|
impl<'a, S, C: CommandRunner> StoredDirectory<'a, S, C>
|
||||||
|
where
|
||||||
|
S: Storage,
|
||||||
|
{
|
||||||
pub fn new(path: Cow<'a, str>, storage: S, dir: StorageDirection, command_runner: &'a C) -> Self {
|
pub fn new(path: Cow<'a, str>, storage: S, dir: StorageDirection, command_runner: &'a C) -> Self {
|
||||||
StoredDirectory { path, storage, dir, command_runner }
|
StoredDirectory {
|
||||||
|
path,
|
||||||
|
storage,
|
||||||
|
dir,
|
||||||
|
command_runner,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, S, C: CommandRunner> fmt::Display for StoredDirectory<'a, S, C> where S: Storage {
|
impl<'a, S, C: CommandRunner> fmt::Display for StoredDirectory<'a, S, C>
|
||||||
|
where
|
||||||
|
S: Storage,
|
||||||
|
{
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "Stored directory {} ({:?})", self.path, self.dir)
|
write!(f, "Stored directory {} ({:?})", self.path, self.dir)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, S, C: CommandRunner> Symbol for StoredDirectory<'a, S, C> where S: Storage {
|
impl<'a, S, C: CommandRunner> Symbol for StoredDirectory<'a, S, C>
|
||||||
|
where
|
||||||
|
S: Storage,
|
||||||
|
{
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
let metadata = fs::metadata(self.path.as_ref());
|
let metadata = fs::metadata(self.path.as_ref());
|
||||||
// Check if dir exists
|
// Check if dir exists
|
||||||
|
|
@ -45,33 +65,72 @@ impl<'a, S, C: CommandRunner> Symbol for StoredDirectory<'a, S, C> where S: Stor
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if !metadata.unwrap().is_dir() {
|
if !metadata.unwrap().is_dir() {
|
||||||
return Err(Box::new(io::Error::new(io::ErrorKind::AlreadyExists, "Could not create a directory, non-directory file exists")));
|
return Err(Box::new(io::Error::new(
|
||||||
|
io::ErrorKind::AlreadyExists,
|
||||||
|
"Could not create a directory, non-directory file exists",
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let dump_date = try!(self.storage.recent_date());
|
let dump_date = try!(self.storage.recent_date());
|
||||||
let output = try!(self.command_runner.get_output("sh", &["-c", &format!("find {} -printf '%T@\\n' | sort -r | head -n1 | grep '^[0-9]\\+' -o", self.path)]));
|
let output = try!(self.command_runner.get_output(
|
||||||
|
"sh",
|
||||||
|
&[
|
||||||
|
"-c",
|
||||||
|
&format!(
|
||||||
|
"find {} -printf '%T@\\n' | sort -r | head -n1 | grep '^[0-9]\\+' -o",
|
||||||
|
self.path
|
||||||
|
)
|
||||||
|
]
|
||||||
|
));
|
||||||
let modified_date = try!(u64::from_str(try!(String::from_utf8(output)).trim_end()));
|
let modified_date = try!(u64::from_str(try!(String::from_utf8(output)).trim_end()));
|
||||||
if if self.dir == StorageDirection::Save { modified_date > dump_date } else { dump_date > modified_date } {
|
if if self.dir == StorageDirection::Save {
|
||||||
let output = try!(self.command_runner.run_with_args("diff", &["-rq", &try!(self.storage.read_filename()), self.path.borrow()]));
|
modified_date > dump_date
|
||||||
|
} else {
|
||||||
|
dump_date > modified_date
|
||||||
|
} {
|
||||||
|
let output = try!(self.command_runner.run_with_args(
|
||||||
|
"diff",
|
||||||
|
&[
|
||||||
|
"-rq",
|
||||||
|
&try!(self.storage.read_filename()),
|
||||||
|
self.path.borrow()
|
||||||
|
]
|
||||||
|
));
|
||||||
match output.status.code() {
|
match output.status.code() {
|
||||||
Some(0) => Ok(true),
|
Some(0) => Ok(true),
|
||||||
Some(1) => Ok(false),
|
Some(1) => Ok(false),
|
||||||
_ => Err(try!(String::from_utf8(output.stderr)).into())
|
_ => Err(try!(String::from_utf8(output.stderr)).into()),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(true)
|
||||||
}
|
}
|
||||||
} else { Ok(true) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
if self.dir == StorageDirection::Load {
|
if self.dir == StorageDirection::Load {
|
||||||
try!(self.command_runner.run_successfully("rm", &["-rf", self.path.borrow()]));
|
try!(self
|
||||||
self.command_runner.run_successfully("cp", &["-a", &try!(self.storage.read_filename()), self.path.borrow()])
|
.command_runner
|
||||||
|
.run_successfully("rm", &["-rf", self.path.borrow()]));
|
||||||
|
self.command_runner.run_successfully(
|
||||||
|
"cp",
|
||||||
|
&[
|
||||||
|
"-a",
|
||||||
|
&try!(self.storage.read_filename()),
|
||||||
|
self.path.borrow(),
|
||||||
|
],
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
self.command_runner.run_successfully("cp", &["-a", self.path.borrow(), &self.storage.write_filename()])
|
self.command_runner.run_successfully(
|
||||||
|
"cp",
|
||||||
|
&["-a", self.path.borrow(), &self.storage.write_filename()],
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_prerequisites(&self) -> Vec<Resource> {
|
fn get_prerequisites(&self) -> Vec<Resource> {
|
||||||
if self.dir == StorageDirection::Save { return vec![]; }
|
if self.dir == StorageDirection::Save {
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
if let Some(parent) = Path::new(self.path.as_ref()).parent() {
|
if let Some(parent) = Path::new(self.path.as_ref()).parent() {
|
||||||
vec![Resource::new("dir", parent.to_string_lossy())]
|
vec![Resource::new("dir", parent.to_string_lossy())]
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -91,11 +150,13 @@ impl<'a, S, C: CommandRunner> Symbol for StoredDirectory<'a, S, C> where S: Stor
|
||||||
Box::new(SymbolAction::new(runner, self))
|
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))
|
Box::new(OwnedSymbolAction::new(runner, *self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {}
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,22 @@
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io;
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
use std::io;
|
||||||
|
use std::ops::Deref;
|
||||||
use std::process::Output;
|
use std::process::Output;
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
use command_runner::{CommandRunner, SetuidCommandRunner};
|
use command_runner::{CommandRunner, SetuidCommandRunner};
|
||||||
use resources::Resource;
|
use resources::Resource;
|
||||||
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
|
||||||
use symbols::file::File as FileSymbol;
|
use symbols::file::File as FileSymbol;
|
||||||
|
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum NodeJsSystemdUserServiceError<E: Error> {
|
pub enum NodeJsSystemdUserServiceError<E: Error> {
|
||||||
ActivationFailed(io::Result<Output>),
|
ActivationFailed(io::Result<Output>),
|
||||||
ExecError(E),
|
ExecError(E),
|
||||||
GenericError
|
GenericError,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<io::Error> for NodeJsSystemdUserServiceError<io::Error> {
|
impl From<io::Error> for NodeJsSystemdUserServiceError<io::Error> {
|
||||||
|
|
@ -30,13 +30,13 @@ impl<E: Error> Error for NodeJsSystemdUserServiceError<E> {
|
||||||
match self {
|
match self {
|
||||||
NodeJsSystemdUserServiceError::ExecError(ref e) => e.description(),
|
NodeJsSystemdUserServiceError::ExecError(ref e) => e.description(),
|
||||||
NodeJsSystemdUserServiceError::GenericError => "Generic error",
|
NodeJsSystemdUserServiceError::GenericError => "Generic error",
|
||||||
NodeJsSystemdUserServiceError::ActivationFailed(_) => "Activation of service failed"
|
NodeJsSystemdUserServiceError::ActivationFailed(_) => "Activation of service failed",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn cause(&self) -> Option<&dyn Error> {
|
fn cause(&self) -> Option<&dyn Error> {
|
||||||
match self {
|
match self {
|
||||||
NodeJsSystemdUserServiceError::ExecError(ref e) => Some(e),
|
NodeJsSystemdUserServiceError::ExecError(ref e) => Some(e),
|
||||||
_ => None
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -51,19 +51,37 @@ impl<E: Error> fmt::Display for NodeJsSystemdUserServiceError<E> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct NodeJsSystemdUserService<'a, C, R> where C: Deref<Target=str>, R: CommandRunner {
|
pub struct NodeJsSystemdUserService<'a, C, R>
|
||||||
|
where
|
||||||
|
C: Deref<Target = str>,
|
||||||
|
R: CommandRunner,
|
||||||
|
{
|
||||||
service_name: &'a str,
|
service_name: &'a str,
|
||||||
user_name: &'a str,
|
user_name: &'a str,
|
||||||
command_runner: R,
|
command_runner: R,
|
||||||
file: FileSymbol<C, String>
|
file: FileSymbol<C, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, R> NodeJsSystemdUserService<'a, String, SetuidCommandRunner<'a, R>> where R: CommandRunner {
|
impl<'a, R> NodeJsSystemdUserService<'a, String, SetuidCommandRunner<'a, R>>
|
||||||
pub fn new(home: &'a str, user_name: &'a str, service_name: &'a str, path: &'a str, command_runner: &'a R) -> Self {
|
where
|
||||||
let file_path = format!("{}/.config/systemd/user/{}.service", home.trim_end(), service_name);
|
R: CommandRunner,
|
||||||
|
{
|
||||||
|
pub fn new(
|
||||||
|
home: &'a str,
|
||||||
|
user_name: &'a str,
|
||||||
|
service_name: &'a str,
|
||||||
|
path: &'a str,
|
||||||
|
command_runner: &'a R,
|
||||||
|
) -> Self {
|
||||||
|
let file_path = format!(
|
||||||
|
"{}/.config/systemd/user/{}.service",
|
||||||
|
home.trim_end(),
|
||||||
|
service_name
|
||||||
|
);
|
||||||
|
|
||||||
let port = format!("/var/tmp/{}-{}.socket", user_name, service_name);
|
let port = format!("/var/tmp/{}-{}.socket", user_name, service_name);
|
||||||
let content = format!("[Service]
|
let content = format!(
|
||||||
|
"[Service]
|
||||||
Environment=NODE_ENV=production
|
Environment=NODE_ENV=production
|
||||||
Environment=PORT={1}
|
Environment=PORT={1}
|
||||||
ExecStartPre=/bin/rm -f {1}
|
ExecStartPre=/bin/rm -f {1}
|
||||||
|
|
@ -78,19 +96,24 @@ Restart=always
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=default.target
|
WantedBy=default.target
|
||||||
", path, port);
|
",
|
||||||
|
path, port
|
||||||
|
);
|
||||||
|
|
||||||
NodeJsSystemdUserService {
|
NodeJsSystemdUserService {
|
||||||
service_name,
|
service_name,
|
||||||
user_name,
|
user_name,
|
||||||
command_runner: SetuidCommandRunner::new(user_name, command_runner),
|
command_runner: SetuidCommandRunner::new(user_name, command_runner),
|
||||||
file: FileSymbol::new(file_path, content)
|
file: FileSymbol::new(file_path, content),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C, R> NodeJsSystemdUserService<'a, C, R> where C: Deref<Target=str>, R: CommandRunner {
|
impl<'a, C, R> NodeJsSystemdUserService<'a, C, R>
|
||||||
|
where
|
||||||
|
C: Deref<Target = str>,
|
||||||
|
R: CommandRunner,
|
||||||
|
{
|
||||||
fn systemctl_wait_for_dbus(&self, args: &[&str]) -> Result<String, Box<dyn Error>> {
|
fn systemctl_wait_for_dbus(&self, args: &[&str]) -> Result<String, Box<dyn Error>> {
|
||||||
let mut tries = 5;
|
let mut tries = 5;
|
||||||
loop {
|
loop {
|
||||||
|
|
@ -102,7 +125,11 @@ impl<'a, C, R> NodeJsSystemdUserService<'a, C, R> where C: Deref<Target=str>, R:
|
||||||
return Err(stderr.into());
|
return Err(stderr.into());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Ok(try!(String::from_utf8(result.stdout)).trim_end().to_string());
|
return Ok(
|
||||||
|
try!(String::from_utf8(result.stdout))
|
||||||
|
.trim_end()
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
tries -= 1;
|
tries -= 1;
|
||||||
if tries == 0 {
|
if tries == 0 {
|
||||||
|
|
@ -114,18 +141,36 @@ impl<'a, C, R> NodeJsSystemdUserService<'a, C, R> where C: Deref<Target=str>, R:
|
||||||
|
|
||||||
fn check_if_service(&self) -> Result<bool, Box<dyn Error>> {
|
fn check_if_service(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
loop {
|
loop {
|
||||||
let active_state = try!(self.systemctl_wait_for_dbus(&["--user", "show", "--property", "ActiveState", self.service_name]));
|
let active_state = try!(self.systemctl_wait_for_dbus(&[
|
||||||
|
"--user",
|
||||||
|
"show",
|
||||||
|
"--property",
|
||||||
|
"ActiveState",
|
||||||
|
self.service_name
|
||||||
|
]));
|
||||||
match active_state.as_ref() {
|
match active_state.as_ref() {
|
||||||
"ActiveState=activating" => sleep(Duration::from_millis(500)),
|
"ActiveState=activating" => sleep(Duration::from_millis(500)),
|
||||||
"ActiveState=active" => return Ok(true),
|
"ActiveState=active" => return Ok(true),
|
||||||
"ActiveState=failed" => return Err(Box::new(NodeJsSystemdUserServiceError::ActivationFailed(self.command_runner.run_with_args("journalctl", &["--user", &format!("--user-unit={}", self.service_name)])) as NodeJsSystemdUserServiceError<io::Error>)),
|
"ActiveState=failed" => {
|
||||||
_ => return Ok(false)
|
return Err(Box::new(NodeJsSystemdUserServiceError::ActivationFailed(
|
||||||
|
self.command_runner.run_with_args(
|
||||||
|
"journalctl",
|
||||||
|
&["--user", &format!("--user-unit={}", self.service_name)],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
as NodeJsSystemdUserServiceError<io::Error>))
|
||||||
|
}
|
||||||
|
_ => return Ok(false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C, R> Symbol for NodeJsSystemdUserService<'a, C, R> where C: Deref<Target=str>, R: CommandRunner {
|
impl<'a, C, R> Symbol for NodeJsSystemdUserService<'a, C, R>
|
||||||
|
where
|
||||||
|
C: Deref<Target = str>,
|
||||||
|
R: CommandRunner,
|
||||||
|
{
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
if !(try!(self.file.target_reached())) {
|
if !(try!(self.file.target_reached())) {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
|
|
@ -139,15 +184,22 @@ impl<'a, C, R> Symbol for NodeJsSystemdUserService<'a, C, R> where C: Deref<Targ
|
||||||
try!(self.systemctl_wait_for_dbus(&["--user", "restart", self.service_name]));
|
try!(self.systemctl_wait_for_dbus(&["--user", "restart", self.service_name]));
|
||||||
|
|
||||||
if !(try!(self.check_if_service())) {
|
if !(try!(self.check_if_service())) {
|
||||||
return Err(Box::new(NodeJsSystemdUserServiceError::GenericError as NodeJsSystemdUserServiceError<io::Error>));
|
return Err(Box::new(
|
||||||
|
NodeJsSystemdUserServiceError::GenericError as NodeJsSystemdUserServiceError<io::Error>,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let file_name = format!("/var/tmp/{}-{}.socket", self.user_name, self.service_name);
|
let file_name = format!("/var/tmp/{}-{}.socket", self.user_name, self.service_name);
|
||||||
fs::metadata(&file_name).map(|_| ()).map_err(|e| Box::new(e) as Box<dyn Error>)
|
fs::metadata(&file_name)
|
||||||
|
.map(|_| ())
|
||||||
|
.map_err(|e| Box::new(e) as Box<dyn Error>)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_prerequisites(&self) -> Vec<Resource> {
|
fn get_prerequisites(&self) -> Vec<Resource> {
|
||||||
let mut r = vec![ Resource::new("file", format!("/var/lib/systemd/linger/{}", self.user_name)) ];
|
let mut r = vec![Resource::new(
|
||||||
|
"file",
|
||||||
|
format!("/var/lib/systemd/linger/{}", self.user_name),
|
||||||
|
)];
|
||||||
r.extend(self.file.get_prerequisites().into_iter());
|
r.extend(self.file.get_prerequisites().into_iter());
|
||||||
r
|
r
|
||||||
}
|
}
|
||||||
|
|
@ -156,13 +208,24 @@ impl<'a, C, R> Symbol for NodeJsSystemdUserService<'a, C, R> where C: Deref<Targ
|
||||||
Box::new(SymbolAction::new(runner, self))
|
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))
|
Box::new(OwnedSymbolAction::new(runner, *self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C, R> fmt::Display for NodeJsSystemdUserService<'a, C, R> where C: Deref<Target=str>, R: CommandRunner {
|
impl<'a, C, R> fmt::Display for NodeJsSystemdUserService<'a, C, R>
|
||||||
|
where
|
||||||
|
C: Deref<Target = str>,
|
||||||
|
R: CommandRunner,
|
||||||
|
{
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
write!(f, "Systemd Node.js user service unit for {}", self.service_name)
|
write!(
|
||||||
|
f,
|
||||||
|
"Systemd Node.js user service unit for {}",
|
||||||
|
self.service_name
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,15 @@ use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||||
|
|
||||||
pub struct ReloadService<'a, C: 'a + CommandRunner> {
|
pub struct ReloadService<'a, C: 'a + CommandRunner> {
|
||||||
service: &'a str,
|
service: &'a str,
|
||||||
command_runner: &'a C
|
command_runner: &'a C,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: CommandRunner> ReloadService<'a, C> {
|
impl<'a, C: CommandRunner> ReloadService<'a, C> {
|
||||||
pub fn new(service: &'a str, command_runner: &'a C) -> Self {
|
pub fn new(service: &'a str, command_runner: &'a C) -> Self {
|
||||||
ReloadService { service, command_runner }
|
ReloadService {
|
||||||
|
service,
|
||||||
|
command_runner,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -21,14 +24,19 @@ impl<'a, C: CommandRunner> Symbol for ReloadService<'a, C> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
self.command_runner.run_successfully("systemctl", &["reload-or-restart", self.service])
|
self
|
||||||
|
.command_runner
|
||||||
|
.run_successfully("systemctl", &["reload-or-restart", self.service])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
|
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
|
||||||
Box::new(SymbolAction::new(runner, self))
|
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))
|
Box::new(OwnedSymbolAction::new(runner, *self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,20 +9,20 @@ use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum SystemdUserSessionError<E: Error> {
|
pub enum SystemdUserSessionError<E: Error> {
|
||||||
ExecError(E),
|
ExecError(E),
|
||||||
GenericError
|
GenericError,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Error> Error for SystemdUserSessionError<E> {
|
impl<E: Error> Error for SystemdUserSessionError<E> {
|
||||||
fn description(&self) -> &str {
|
fn description(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
SystemdUserSessionError::ExecError(ref e) => e.description(),
|
SystemdUserSessionError::ExecError(ref e) => e.description(),
|
||||||
SystemdUserSessionError::GenericError => "Generic error"
|
SystemdUserSessionError::GenericError => "Generic error",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn cause(&self) -> Option<&dyn Error> {
|
fn cause(&self) -> Option<&dyn Error> {
|
||||||
match self {
|
match self {
|
||||||
SystemdUserSessionError::ExecError(ref e) => Some(e),
|
SystemdUserSessionError::ExecError(ref e) => Some(e),
|
||||||
_ => None
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -35,12 +35,15 @@ impl<E: Error> fmt::Display for SystemdUserSessionError<E> {
|
||||||
|
|
||||||
pub struct SystemdUserSession<'a, C: 'a + CommandRunner> {
|
pub struct SystemdUserSession<'a, C: 'a + CommandRunner> {
|
||||||
user_name: Cow<'a, str>,
|
user_name: Cow<'a, str>,
|
||||||
command_runner: &'a C
|
command_runner: &'a C,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: CommandRunner> SystemdUserSession<'a, C> {
|
impl<'a, C: CommandRunner> SystemdUserSession<'a, C> {
|
||||||
pub fn new(user_name: Cow<'a, str>, command_runner: &'a C) -> Self {
|
pub fn new(user_name: Cow<'a, str>, command_runner: &'a C) -> Self {
|
||||||
SystemdUserSession { user_name, command_runner }
|
SystemdUserSession {
|
||||||
|
user_name,
|
||||||
|
command_runner,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,14 +56,19 @@ impl<'a, C: CommandRunner> Symbol for SystemdUserSession<'a, C> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
self.command_runner.run_successfully("loginctl", &["enable-linger", self.user_name.borrow()])
|
self
|
||||||
|
.command_runner
|
||||||
|
.run_successfully("loginctl", &["enable-linger", self.user_name.borrow()])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
|
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
|
||||||
Box::new(SymbolAction::new(runner, self))
|
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))
|
Box::new(OwnedSymbolAction::new(runner, *self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,15 @@ use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||||
|
|
||||||
pub struct TlsCsr<'a, C: 'a + CommandRunner> {
|
pub struct TlsCsr<'a, C: 'a + CommandRunner> {
|
||||||
domain: Cow<'a, str>,
|
domain: Cow<'a, str>,
|
||||||
command_runner: &'a C
|
command_runner: &'a C,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: CommandRunner> TlsCsr<'a, C> {
|
impl<'a, C: CommandRunner> TlsCsr<'a, C> {
|
||||||
pub fn new(domain: Cow<'a, str>, command_runner: &'a C) -> Self {
|
pub fn new(domain: Cow<'a, str>, command_runner: &'a C) -> Self {
|
||||||
TlsCsr { domain, command_runner }
|
TlsCsr {
|
||||||
|
domain,
|
||||||
|
command_runner,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_key_path(&self) -> String {
|
fn get_key_path(&self) -> String {
|
||||||
|
|
@ -38,12 +41,28 @@ impl<'a, C: CommandRunner> Symbol for TlsCsr<'a, C> {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
let output = try!(self.command_runner.get_stderr("openssl", &["req", "-in", &self.get_csr_path(), "-noout", "-verify"]));
|
let output = try!(self.command_runner.get_stderr(
|
||||||
|
"openssl",
|
||||||
|
&["req", "-in", &self.get_csr_path(), "-noout", "-verify"]
|
||||||
|
));
|
||||||
Ok(output == b"verify OK\n")
|
Ok(output == b"verify OK\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
try!(self.command_runner.run_successfully("openssl", &["req", "-new", "-sha256", "-key", &self.get_key_path(), "-out", &self.get_csr_path(), "-subj", &format!("/CN={}", self.domain)]));
|
try!(self.command_runner.run_successfully(
|
||||||
|
"openssl",
|
||||||
|
&[
|
||||||
|
"req",
|
||||||
|
"-new",
|
||||||
|
"-sha256",
|
||||||
|
"-key",
|
||||||
|
&self.get_key_path(),
|
||||||
|
"-out",
|
||||||
|
&self.get_csr_path(),
|
||||||
|
"-subj",
|
||||||
|
&format!("/CN={}", self.domain)
|
||||||
|
]
|
||||||
|
));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,11 +74,13 @@ impl<'a, C: CommandRunner> Symbol for TlsCsr<'a, C> {
|
||||||
Box::new(SymbolAction::new(runner, self))
|
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))
|
Box::new(OwnedSymbolAction::new(runner, *self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {}
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,15 @@ use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||||
|
|
||||||
pub struct TlsKey<'a, C: 'a + CommandRunner> {
|
pub struct TlsKey<'a, C: 'a + CommandRunner> {
|
||||||
domain: Cow<'a, str>,
|
domain: Cow<'a, str>,
|
||||||
command_runner: &'a C
|
command_runner: &'a C,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: CommandRunner> TlsKey<'a, C> {
|
impl<'a, C: CommandRunner> TlsKey<'a, C> {
|
||||||
pub fn new(domain: Cow<'a, str>, command_runner: &'a C) -> Self {
|
pub fn new(domain: Cow<'a, str>, command_runner: &'a C) -> Self {
|
||||||
TlsKey { domain, command_runner }
|
TlsKey {
|
||||||
|
domain,
|
||||||
|
command_runner,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_path(&self) -> String {
|
fn get_path(&self) -> String {
|
||||||
|
|
@ -37,23 +40,36 @@ impl<'a, C: CommandRunner> Symbol for TlsKey<'a, C> {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
let output = try!(self.command_runner.get_output("openssl", &["rsa", "-in", &self.get_path(), "-noout", "-check", "-text"]));
|
let output = try!(self.command_runner.get_output(
|
||||||
|
"openssl",
|
||||||
|
&["rsa", "-in", &self.get_path(), "-noout", "-check", "-text"]
|
||||||
|
));
|
||||||
Ok(output.starts_with(&format!("Private-Key: ({} bit)\n", self.get_bytes()).as_bytes()))
|
Ok(output.starts_with(&format!("Private-Key: ({} bit)\n", self.get_bytes()).as_bytes()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
self.command_runner.run_successfully("openssl", &["genrsa", "-out", &self.get_path(), &self.get_bytes().to_string()])
|
self.command_runner.run_successfully(
|
||||||
|
"openssl",
|
||||||
|
&[
|
||||||
|
"genrsa",
|
||||||
|
"-out",
|
||||||
|
&self.get_path(),
|
||||||
|
&self.get_bytes().to_string(),
|
||||||
|
],
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
|
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
|
||||||
Box::new(SymbolAction::new(runner, self))
|
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))
|
Box::new(OwnedSymbolAction::new(runner, *self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {}
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,15 @@ use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||||
|
|
||||||
pub struct SelfSignedTlsCert<'a, C: 'a + CommandRunner> {
|
pub struct SelfSignedTlsCert<'a, C: 'a + CommandRunner> {
|
||||||
domain: Cow<'a, str>,
|
domain: Cow<'a, str>,
|
||||||
command_runner: &'a C
|
command_runner: &'a C,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: CommandRunner> SelfSignedTlsCert<'a, C> {
|
impl<'a, C: CommandRunner> SelfSignedTlsCert<'a, C> {
|
||||||
pub fn new(domain: Cow<'a, str>, command_runner: &'a C) -> Self {
|
pub fn new(domain: Cow<'a, str>, command_runner: &'a C) -> Self {
|
||||||
SelfSignedTlsCert { domain, command_runner }
|
SelfSignedTlsCert {
|
||||||
|
domain,
|
||||||
|
command_runner,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_key_path(&self) -> String {
|
fn get_key_path(&self) -> String {
|
||||||
|
|
@ -39,22 +42,58 @@ impl<'a, C: CommandRunner> Symbol for SelfSignedTlsCert<'a, C> {
|
||||||
if !Path::new(&self.get_cert_path()).exists() {
|
if !Path::new(&self.get_cert_path()).exists() {
|
||||||
return Ok(false);
|
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()]));
|
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()
|
||||||
|
]
|
||||||
|
));
|
||||||
println!("{}", output.status.code().unwrap());
|
println!("{}", output.status.code().unwrap());
|
||||||
match output.status.code() {
|
match output.status.code() {
|
||||||
Some(0) => Ok(output.stdout == format!("subject=CN = {}\nCertificate will not expire\n", self.domain).as_bytes()),
|
Some(0) => Ok(
|
||||||
Some(_) => if output.stdout == format!("subject=CN = {}\nCertificate will expire\n", self.domain).as_bytes() {
|
output.stdout
|
||||||
|
== format!(
|
||||||
|
"subject=CN = {}\nCertificate will not expire\n",
|
||||||
|
self.domain
|
||||||
|
)
|
||||||
|
.as_bytes(),
|
||||||
|
),
|
||||||
|
Some(_) => {
|
||||||
|
if output.stdout
|
||||||
|
== format!("subject=CN = {}\nCertificate will expire\n", self.domain).as_bytes()
|
||||||
|
{
|
||||||
Ok(false)
|
Ok(false)
|
||||||
} else {
|
} else {
|
||||||
Err("Exit code non-zero, but wrong stdout".to_string().into())
|
Err("Exit code non-zero, but wrong stdout".to_string().into())
|
||||||
},
|
|
||||||
_ => Err("Apparently killed by signal".to_string().into())
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
_ => Err("Apparently killed by signal".to_string().into()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
self.command_runner.run_successfully("openssl", &["req", "-x509", "-sha256", "-days", "90", "-key", &self.get_key_path(), "-out", &self.get_cert_path(), "-subj", &format!("/CN={}", self.domain)])
|
self.command_runner.run_successfully(
|
||||||
|
"openssl",
|
||||||
|
&[
|
||||||
|
"req",
|
||||||
|
"-x509",
|
||||||
|
"-sha256",
|
||||||
|
"-days",
|
||||||
|
"90",
|
||||||
|
"-key",
|
||||||
|
&self.get_key_path(),
|
||||||
|
"-out",
|
||||||
|
&self.get_cert_path(),
|
||||||
|
"-subj",
|
||||||
|
&format!("/CN={}", self.domain),
|
||||||
|
],
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_prerequisites(&self) -> Vec<Resource> {
|
fn get_prerequisites(&self) -> Vec<Resource> {
|
||||||
|
|
@ -65,11 +104,13 @@ println!("{}", output.status.code().unwrap());
|
||||||
Box::new(SymbolAction::new(runner, self))
|
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))
|
Box::new(OwnedSymbolAction::new(runner, *self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {}
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||||
pub enum UserAdderError {
|
pub enum UserAdderError {
|
||||||
AlreadyExists,
|
AlreadyExists,
|
||||||
UnknownError,
|
UnknownError,
|
||||||
ImplError(Box<dyn Error>)
|
ImplError(Box<dyn Error>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error for UserAdderError {
|
impl Error for UserAdderError {
|
||||||
|
|
@ -18,13 +18,13 @@ impl Error for UserAdderError {
|
||||||
match self {
|
match self {
|
||||||
UserAdderError::AlreadyExists => "User already exists",
|
UserAdderError::AlreadyExists => "User already exists",
|
||||||
UserAdderError::UnknownError => "Unknown error",
|
UserAdderError::UnknownError => "Unknown error",
|
||||||
UserAdderError::ImplError(_) => "User adding error"
|
UserAdderError::ImplError(_) => "User adding error",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn cause(&self) -> Option<&dyn Error> {
|
fn cause(&self) -> Option<&dyn Error> {
|
||||||
match self {
|
match self {
|
||||||
UserAdderError::ImplError(ref e) => Some(e.as_ref()),
|
UserAdderError::ImplError(ref e) => Some(e.as_ref()),
|
||||||
_ => None
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -33,7 +33,7 @@ impl fmt::Display for UserAdderError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self.cause() {
|
match self.cause() {
|
||||||
Some(e) => write!(f, "{} (cause: {})", self.description(), e),
|
Some(e) => write!(f, "{} (cause: {})", self.description(), e),
|
||||||
None => write!(f, "{}", self.description())
|
None => write!(f, "{}", self.description()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -44,18 +44,18 @@ pub trait UserAdder {
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum UserError {
|
pub enum UserError {
|
||||||
GenericError
|
GenericError,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error for UserError {
|
impl Error for UserError {
|
||||||
fn description(&self) -> &str {
|
fn description(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
UserError::GenericError => "Could not find out if user exists"
|
UserError::GenericError => "Could not find out if user exists",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn cause(&self) -> Option<&dyn Error> {
|
fn cause(&self) -> Option<&dyn Error> {
|
||||||
match self {
|
match self {
|
||||||
_ => None
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -64,7 +64,7 @@ impl fmt::Display for UserError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self.cause() {
|
match self.cause() {
|
||||||
Some(e) => write!(f, "{} (cause: {})", self.description(), e),
|
Some(e) => write!(f, "{} (cause: {})", self.description(), e),
|
||||||
None => write!(f, "{}", self.description())
|
None => write!(f, "{}", self.description()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -72,12 +72,16 @@ impl fmt::Display for UserError {
|
||||||
pub struct User<'a, C: 'a + CommandRunner, A: 'a + UserAdder> {
|
pub struct User<'a, C: 'a + CommandRunner, A: 'a + UserAdder> {
|
||||||
user_name: Cow<'a, str>,
|
user_name: Cow<'a, str>,
|
||||||
command_runner: &'a C,
|
command_runner: &'a C,
|
||||||
user_adder: &'a A
|
user_adder: &'a A,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: CommandRunner, A: 'a + UserAdder> User<'a, C, A> {
|
impl<'a, C: CommandRunner, A: 'a + UserAdder> User<'a, C, A> {
|
||||||
pub fn new(user_name: Cow<'a, str>, command_runner: &'a C, user_adder: &'a A) -> Self {
|
pub fn new(user_name: Cow<'a, str>, command_runner: &'a C, user_adder: &'a A) -> Self {
|
||||||
User { user_name, command_runner, user_adder }
|
User {
|
||||||
|
user_name,
|
||||||
|
command_runner,
|
||||||
|
user_adder,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,16 +93,21 @@ impl<'a, C: CommandRunner, A: 'a + UserAdder> fmt::Display for User<'a, C, A> {
|
||||||
|
|
||||||
impl<'a, C: CommandRunner, A: 'a + UserAdder> Symbol for User<'a, C, A> {
|
impl<'a, C: CommandRunner, A: 'a + UserAdder> Symbol for User<'a, C, A> {
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
let output = try!(self.command_runner.run_with_args("getent", &["passwd", &*self.user_name]));
|
let output = try!(self
|
||||||
|
.command_runner
|
||||||
|
.run_with_args("getent", &["passwd", &*self.user_name]));
|
||||||
match output.status.code() {
|
match output.status.code() {
|
||||||
Some(2) => Ok(false),
|
Some(2) => Ok(false),
|
||||||
Some(0) => Ok(true),
|
Some(0) => Ok(true),
|
||||||
_ => Err(Box::new(UserError::GenericError))
|
_ => Err(Box::new(UserError::GenericError)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
self.user_adder.add_user(&*self.user_name).map_err(|e| Box::new(e) as Box<dyn Error>)
|
self
|
||||||
|
.user_adder
|
||||||
|
.add_user(&*self.user_name)
|
||||||
|
.map_err(|e| Box::new(e) as Box<dyn Error>)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn provides(&self) -> Option<Vec<Resource>> {
|
fn provides(&self) -> Option<Vec<Resource>> {
|
||||||
|
|
@ -109,13 +118,16 @@ impl<'a, C: CommandRunner, A: 'a + UserAdder> Symbol for User<'a, C, A> {
|
||||||
Box::new(SymbolAction::new(runner, self))
|
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))
|
Box::new(OwnedSymbolAction::new(runner, *self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SystemUserAdder<'a, C: 'a + CommandRunner> {
|
pub struct SystemUserAdder<'a, C: 'a + CommandRunner> {
|
||||||
command_runner: &'a C
|
command_runner: &'a C,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: CommandRunner> SystemUserAdder<'a, C> {
|
impl<'a, C: CommandRunner> SystemUserAdder<'a, C> {
|
||||||
|
|
@ -130,27 +142,26 @@ impl<'a, C: CommandRunner> UserAdder for SystemUserAdder<'a, C> {
|
||||||
"adduser",
|
"adduser",
|
||||||
&[
|
&[
|
||||||
// "-m", // Necessary for Fedora, not accepted in Debian
|
// "-m", // Necessary for Fedora, not accepted in Debian
|
||||||
"--system",
|
"--system", user_name,
|
||||||
user_name
|
],
|
||||||
]);
|
);
|
||||||
match output {
|
match output {
|
||||||
Ok(output) => match output.status.code() {
|
Ok(output) => match output.status.code() {
|
||||||
Some(0) => Ok(()),
|
Some(0) => Ok(()),
|
||||||
Some(1) =>
|
Some(1) => {
|
||||||
{
|
|
||||||
println!("{:?}", output);
|
println!("{:?}", output);
|
||||||
Err(UserAdderError::AlreadyExists)},
|
Err(UserAdderError::AlreadyExists)
|
||||||
Some(_) =>
|
}
|
||||||
{
|
Some(_) => {
|
||||||
println!("{:?}", output);
|
println!("{:?}", output);
|
||||||
Err(UserAdderError::UnknownError)
|
Err(UserAdderError::UnknownError)
|
||||||
},
|
}
|
||||||
None => {
|
None => {
|
||||||
println!("{:?}", output);
|
println!("{:?}", output);
|
||||||
Err(UserAdderError::UnknownError)
|
Err(UserAdderError::UnknownError)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
Err(e) => Err(UserAdderError::ImplError(Box::new(e))),
|
||||||
Err(e) => Err(UserAdderError::ImplError(Box::new(e)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -161,10 +172,10 @@ mod test {
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use command_runner::StdCommandRunner;
|
use command_runner::StdCommandRunner;
|
||||||
use symbols::Symbol;
|
|
||||||
use symbols::user::User;
|
use symbols::user::User;
|
||||||
use symbols::user::UserAdder;
|
use symbols::user::UserAdder;
|
||||||
use symbols::user::UserAdderError;
|
use symbols::user::UserAdderError;
|
||||||
|
use symbols::Symbol;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
struct DummyError;
|
struct DummyError;
|
||||||
|
|
@ -190,13 +201,21 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_target_reached_nonexisting() {
|
fn test_target_reached_nonexisting() {
|
||||||
let symbol = User { user_name: "nonexisting".into(), command_runner: &StdCommandRunner, user_adder: &DummyUserAdder };
|
let symbol = User {
|
||||||
|
user_name: "nonexisting".into(),
|
||||||
|
command_runner: &StdCommandRunner,
|
||||||
|
user_adder: &DummyUserAdder,
|
||||||
|
};
|
||||||
assert_eq!(symbol.target_reached().unwrap(), false);
|
assert_eq!(symbol.target_reached().unwrap(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_target_reached_root() {
|
fn test_target_reached_root() {
|
||||||
let symbol = User { user_name: "root".into(), command_runner: &StdCommandRunner, user_adder: &DummyUserAdder };
|
let symbol = User {
|
||||||
|
user_name: "root".into(),
|
||||||
|
command_runner: &StdCommandRunner,
|
||||||
|
user_adder: &DummyUserAdder,
|
||||||
|
};
|
||||||
assert_eq!(symbol.target_reached().unwrap(), true);
|
assert_eq!(symbol.target_reached().unwrap(), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,26 +8,44 @@ use std::ops::Deref;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use command_runner::CommandRunner;
|
use command_runner::CommandRunner;
|
||||||
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
|
||||||
use resources::Resource;
|
use resources::Resource;
|
||||||
|
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||||
|
|
||||||
pub struct WordpressPlugin<'a, C, R> where C: Deref<Target=str>, R: 'a + CommandRunner {
|
pub struct WordpressPlugin<'a, C, R>
|
||||||
|
where
|
||||||
|
C: Deref<Target = str>,
|
||||||
|
R: 'a + CommandRunner,
|
||||||
|
{
|
||||||
base: C,
|
base: C,
|
||||||
name: C,
|
name: C,
|
||||||
command_runner: &'a R
|
command_runner: &'a R,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C, R> WordpressPlugin<'a, C, R> where C: Deref<Target=str>, R: CommandRunner {
|
impl<'a, C, R> WordpressPlugin<'a, C, R>
|
||||||
|
where
|
||||||
|
C: Deref<Target = str>,
|
||||||
|
R: CommandRunner,
|
||||||
|
{
|
||||||
pub fn new(base: C, name: C, command_runner: &'a R) -> Self {
|
pub fn new(base: C, name: C, command_runner: &'a R) -> Self {
|
||||||
WordpressPlugin { base, name, command_runner }
|
WordpressPlugin {
|
||||||
|
base,
|
||||||
|
name,
|
||||||
|
command_runner,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_path(&self) -> PathBuf {
|
fn get_path(&self) -> PathBuf {
|
||||||
Path::new(&*self.base).join("wp-content/plugins").join(&*self.name)
|
Path::new(&*self.base)
|
||||||
|
.join("wp-content/plugins")
|
||||||
|
.join(&*self.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C, R> Symbol for WordpressPlugin<'a, C, R> where C: Deref<Target=str>, R: CommandRunner {
|
impl<'a, C, R> Symbol for WordpressPlugin<'a, C, R>
|
||||||
|
where
|
||||||
|
C: Deref<Target = str>,
|
||||||
|
R: CommandRunner,
|
||||||
|
{
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
if !self.get_path().exists() {
|
if !self.get_path().exists() {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
|
|
@ -43,7 +61,7 @@ impl<'a, C, R> Symbol for WordpressPlugin<'a, C, R> where C: Deref<Target=str>,
|
||||||
} else {
|
} else {
|
||||||
Err(Box::new(e))
|
Err(Box::new(e))
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
Ok(file) => {
|
Ok(file) => {
|
||||||
let reader = BufReader::new(file);
|
let reader = BufReader::new(file);
|
||||||
let regex = Regex::new("(?m)^(Plugin URI|Version): (.+)$")?;
|
let regex = Regex::new("(?m)^(Plugin URI|Version): (.+)$")?;
|
||||||
|
|
@ -58,22 +76,45 @@ impl<'a, C, R> Symbol for WordpressPlugin<'a, C, R> where C: Deref<Target=str>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let upstream = try!(self.command_runner.get_output("curl", &["--form", &format!(r###"plugins={{"plugins":{{"{0}/{0}.php":{{"Version":"{1}", "PluginURI":"{2}"}}}}}}"###, &*self.name, version, plugin_uri), "https://api.wordpress.org/plugins/update-check/1.1/"]));
|
let upstream = try!(self.command_runner.get_output(
|
||||||
|
"curl",
|
||||||
|
&[
|
||||||
|
"--form",
|
||||||
|
&format!(
|
||||||
|
r###"plugins={{"plugins":{{"{0}/{0}.php":{{"Version":"{1}", "PluginURI":"{2}"}}}}}}"###,
|
||||||
|
&*self.name, version, plugin_uri
|
||||||
|
),
|
||||||
|
"https://api.wordpress.org/plugins/update-check/1.1/"
|
||||||
|
]
|
||||||
|
));
|
||||||
Ok(try!(String::from_utf8(upstream)).contains(r###""plugins":[]"###))
|
Ok(try!(String::from_utf8(upstream)).contains(r###""plugins":[]"###))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
let source = format!("https://downloads.wordpress.org/plugin/{}.zip", &*self.name);
|
let source = format!("https://downloads.wordpress.org/plugin/{}.zip", &*self.name);
|
||||||
let zip = format!("/tmp/{}.zip", &*self.name);
|
let zip = format!("/tmp/{}.zip", &*self.name);
|
||||||
try!(self.command_runner.run_successfully("curl", &[source.as_ref() as &str, "-o", zip.as_ref()]));
|
try!(self
|
||||||
try!(self.command_runner.run_successfully("rm", &["-rf", &self.get_path().to_string_lossy()]));
|
.command_runner
|
||||||
self.command_runner.run_successfully("unzip", &[zip.as_ref(), "-d".as_ref(), Path::new(&*self.base).join("wp-content/plugins").as_os_str()])
|
.run_successfully("curl", &[source.as_ref() as &str, "-o", zip.as_ref()]));
|
||||||
|
try!(self
|
||||||
|
.command_runner
|
||||||
|
.run_successfully("rm", &["-rf", &self.get_path().to_string_lossy()]));
|
||||||
|
self.command_runner.run_successfully(
|
||||||
|
"unzip",
|
||||||
|
&[
|
||||||
|
zip.as_ref(),
|
||||||
|
"-d".as_ref(),
|
||||||
|
Path::new(&*self.base)
|
||||||
|
.join("wp-content/plugins")
|
||||||
|
.as_os_str(),
|
||||||
|
],
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_prerequisites(&self) -> Vec<Resource> {
|
fn get_prerequisites(&self) -> Vec<Resource> {
|
||||||
match self.get_path().parent() {
|
match self.get_path().parent() {
|
||||||
Some(p) => vec![Resource::new("dir", p.to_string_lossy())],
|
Some(p) => vec![Resource::new("dir", p.to_string_lossy())],
|
||||||
None => vec![]
|
None => vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -81,14 +122,20 @@ impl<'a, C, R> Symbol for WordpressPlugin<'a, C, R> where C: Deref<Target=str>,
|
||||||
Box::new(SymbolAction::new(runner, self))
|
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))
|
Box::new(OwnedSymbolAction::new(runner, *self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C, R> fmt::Display for WordpressPlugin<'a, C, R> where C: Deref<Target=str>, R: CommandRunner {
|
impl<'a, C, R> fmt::Display for WordpressPlugin<'a, C, R>
|
||||||
|
where
|
||||||
|
C: Deref<Target = str>,
|
||||||
|
R: CommandRunner,
|
||||||
|
{
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
write!(f, "WordpressPlugin {}", &*self.name)
|
write!(f, "WordpressPlugin {}", &*self.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,34 +8,66 @@ use std::io::{BufRead, BufReader};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use command_runner::CommandRunner;
|
use command_runner::CommandRunner;
|
||||||
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
|
||||||
use resources::Resource;
|
use resources::Resource;
|
||||||
|
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||||
|
|
||||||
pub struct WordpressTranslation<'a, C, D, R> where C: AsRef<str>, D: AsRef<str>, R: 'a + CommandRunner {
|
pub struct WordpressTranslation<'a, C, D, R>
|
||||||
|
where
|
||||||
|
C: AsRef<str>,
|
||||||
|
D: AsRef<str>,
|
||||||
|
R: 'a + CommandRunner,
|
||||||
|
{
|
||||||
path: D,
|
path: D,
|
||||||
version: &'a str,
|
version: &'a str,
|
||||||
locale: C,
|
locale: C,
|
||||||
command_runner: &'a R
|
command_runner: &'a R,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C, R> WordpressTranslation<'a, C, String, R> where C: AsRef<str>, R: CommandRunner {
|
impl<'a, C, R> WordpressTranslation<'a, C, String, R>
|
||||||
|
where
|
||||||
|
C: AsRef<str>,
|
||||||
|
R: CommandRunner,
|
||||||
|
{
|
||||||
pub fn new<D: AsRef<str>>(path: D, version: &'a str, locale: C, command_runner: &'a R) -> Self {
|
pub fn new<D: AsRef<str>>(path: D, version: &'a str, locale: C, command_runner: &'a R) -> Self {
|
||||||
WordpressTranslation {
|
WordpressTranslation {
|
||||||
path: Path::new(path.as_ref()).join("wp-content/languages").to_string_lossy().to_string(),
|
path: Path::new(path.as_ref())
|
||||||
|
.join("wp-content/languages")
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_string(),
|
||||||
version,
|
version,
|
||||||
locale,
|
locale,
|
||||||
command_runner
|
command_runner,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C, D, R> WordpressTranslation<'a, C, D, R> where C: AsRef<str>, D: AsRef<str>, R: CommandRunner {
|
impl<'a, C, D, R> WordpressTranslation<'a, C, D, R>
|
||||||
|
where
|
||||||
|
C: AsRef<str>,
|
||||||
|
D: AsRef<str>,
|
||||||
|
R: CommandRunner,
|
||||||
|
{
|
||||||
fn get_pairs(&self) -> Vec<(String, String)> {
|
fn get_pairs(&self) -> Vec<(String, String)> {
|
||||||
let version_x = self.version.trim_end_matches(|c: char| c.is_digit(10)).to_owned() + "x";
|
let version_x = self
|
||||||
|
.version
|
||||||
|
.trim_end_matches(|c: char| c.is_digit(10))
|
||||||
|
.to_owned()
|
||||||
|
+ "x";
|
||||||
let locale: &str = self.locale.as_ref();
|
let locale: &str = self.locale.as_ref();
|
||||||
let path_locale = if locale == "de_DE" { "de".to_owned() } else { locale.to_lowercase().replace('_', "-") };
|
let path_locale = if locale == "de_DE" {
|
||||||
|
"de".to_owned()
|
||||||
|
} else {
|
||||||
|
locale.to_lowercase().replace('_', "-")
|
||||||
|
};
|
||||||
let mut res = vec![];
|
let mut res = vec![];
|
||||||
for &(in_slug, out_slug) in [("", ""), ("cc/", "continents-cities-"), ("admin/", "admin-"), ("admin/network/", "admin-network-")].iter() {
|
for &(in_slug, out_slug) in [
|
||||||
|
("", ""),
|
||||||
|
("cc/", "continents-cities-"),
|
||||||
|
("admin/", "admin-"),
|
||||||
|
("admin/network/", "admin-network-"),
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
{
|
||||||
for format in ["po", "mo"].iter() {
|
for format in ["po", "mo"].iter() {
|
||||||
res.push((
|
res.push((
|
||||||
format!("https://translate.wordpress.org/projects/wp/{}/{}{}/default/export-translations?format={}", version_x, in_slug, path_locale, format),
|
format!("https://translate.wordpress.org/projects/wp/{}/{}{}/default/export-translations?format={}", version_x, in_slug, path_locale, format),
|
||||||
|
|
@ -47,7 +79,12 @@ impl<'a, C, D, R> WordpressTranslation<'a, C, D, R> where C: AsRef<str>, D: AsRe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C, D, R> Symbol for WordpressTranslation<'a, C, D, R> where C: AsRef<str>, D: AsRef<str>, R: CommandRunner {
|
impl<'a, C, D, R> Symbol for WordpressTranslation<'a, C, D, R>
|
||||||
|
where
|
||||||
|
C: AsRef<str>,
|
||||||
|
D: AsRef<str>,
|
||||||
|
R: CommandRunner,
|
||||||
|
{
|
||||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||||
let mut newest = String::new();
|
let mut newest = String::new();
|
||||||
let match_date = Regex::new("(?m)^\"PO-Revision-Date: (.+)\\+0000\\\\n\"$").unwrap();
|
let match_date = Regex::new("(?m)^\"PO-Revision-Date: (.+)\\+0000\\\\n\"$").unwrap();
|
||||||
|
|
@ -60,8 +97,9 @@ impl<'a, C, D, R> Symbol for WordpressTranslation<'a, C, D, R> where C: AsRef<st
|
||||||
} else {
|
} else {
|
||||||
Err(Box::new(e))
|
Err(Box::new(e))
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
Ok(file) => if target.ends_with(".po") {
|
Ok(file) => {
|
||||||
|
if target.ends_with(".po") {
|
||||||
let reader = BufReader::new(file);
|
let reader = BufReader::new(file);
|
||||||
for content in reader.lines() {
|
for content in reader.lines() {
|
||||||
if let Some(match_result) = match_date.captures(&try!(content)) {
|
if let Some(match_result) = match_date.captures(&try!(content)) {
|
||||||
|
|
@ -72,13 +110,28 @@ impl<'a, C, D, R> Symbol for WordpressTranslation<'a, C, D, R> where C: AsRef<st
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let upstream = try!(self.command_runner.get_output("curl", &[&format!("https://api.wordpress.org/core/version-check/1.7/?version={}&locale={}", self.version, self.locale.as_ref())]));
|
}
|
||||||
Ok(try!(String::from_utf8(upstream)).contains(&format!(r###"language":"{}","version":"{}","updated":"{}"###, self.locale.as_ref(), self.version, newest)))
|
let upstream = try!(self.command_runner.get_output(
|
||||||
|
"curl",
|
||||||
|
&[&format!(
|
||||||
|
"https://api.wordpress.org/core/version-check/1.7/?version={}&locale={}",
|
||||||
|
self.version,
|
||||||
|
self.locale.as_ref()
|
||||||
|
)]
|
||||||
|
));
|
||||||
|
Ok(try!(String::from_utf8(upstream)).contains(&format!(
|
||||||
|
r###"language":"{}","version":"{}","updated":"{}"###,
|
||||||
|
self.locale.as_ref(),
|
||||||
|
self.version,
|
||||||
|
newest
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||||
for (source, target) in self.get_pairs() {
|
for (source, target) in self.get_pairs() {
|
||||||
try!(self.command_runner.run_successfully("curl", &["--compressed", "-o", &target, &source]));
|
try!(self
|
||||||
|
.command_runner
|
||||||
|
.run_successfully("curl", &["--compressed", "-o", &target, &source]));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -91,14 +144,21 @@ impl<'a, C, D, R> Symbol for WordpressTranslation<'a, C, D, R> where C: AsRef<st
|
||||||
Box::new(SymbolAction::new(runner, self))
|
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))
|
Box::new(OwnedSymbolAction::new(runner, *self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C, D, R> fmt::Display for WordpressTranslation<'a, C, D, R> where C: AsRef<str>, D: AsRef<str>, R: CommandRunner {
|
impl<'a, C, D, R> fmt::Display for WordpressTranslation<'a, C, D, R>
|
||||||
|
where
|
||||||
|
C: AsRef<str>,
|
||||||
|
D: AsRef<str>,
|
||||||
|
R: CommandRunner,
|
||||||
|
{
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
write!(f, "WordpressTranslation {}", self.path.as_ref())
|
write!(f, "WordpressTranslation {}", self.path.as_ref())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
extern crate schematics;
|
extern crate schematics;
|
||||||
extern crate tempdir;
|
extern crate tempdir;
|
||||||
|
|
||||||
|
use schematics::symbols::file::File as FileSymbol;
|
||||||
|
use schematics::symbols::Symbol;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use tempdir::TempDir;
|
use tempdir::TempDir;
|
||||||
use schematics::symbols::Symbol;
|
|
||||||
use schematics::symbols::file::File as FileSymbol;
|
|
||||||
|
|
||||||
fn get_dir(content: Option<&str>) -> TempDir {
|
fn get_dir(content: Option<&str>) -> TempDir {
|
||||||
let tmp_dir = TempDir::new("unittest").expect("create temp dir");
|
let tmp_dir = TempDir::new("unittest").expect("create temp dir");
|
||||||
|
|
@ -15,12 +15,17 @@ fn get_dir(content: Option<&str>) -> TempDir {
|
||||||
}
|
}
|
||||||
let file_path = tmp_dir.path().join("filename");
|
let file_path = tmp_dir.path().join("filename");
|
||||||
let mut tmp_file = File::create(file_path).expect("create temp file");
|
let mut tmp_file = File::create(file_path).expect("create temp file");
|
||||||
tmp_file.write_all(content.unwrap().as_bytes()).expect("write temp file");
|
tmp_file
|
||||||
|
.write_all(content.unwrap().as_bytes())
|
||||||
|
.expect("write temp file");
|
||||||
tmp_dir
|
tmp_dir
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_symbol(path: &Path) -> FileSymbol<&str, String> {
|
fn get_symbol(path: &Path) -> FileSymbol<&str, String> {
|
||||||
FileSymbol::new(String::from(path.join("filename").to_str().unwrap()), "target content")
|
FileSymbol::new(
|
||||||
|
String::from(path.join("filename").to_str().unwrap()),
|
||||||
|
"target content",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normal cases
|
// Normal cases
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,10 @@ extern crate schematics;
|
||||||
extern crate tempdir;
|
extern crate tempdir;
|
||||||
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
use schematics::storage::{SimpleStorage, Storage};
|
||||||
use std::fs::{create_dir, File};
|
use std::fs::{create_dir, File};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use tempdir::TempDir;
|
use tempdir::TempDir;
|
||||||
use schematics::storage::{SimpleStorage, Storage};
|
|
||||||
|
|
||||||
fn get_dir<'a, I: IntoIterator<Item = &'a &'a str>>(content: I) -> TempDir {
|
fn get_dir<'a, I: IntoIterator<Item = &'a &'a str>>(content: I) -> TempDir {
|
||||||
let tmp_dir = TempDir::new("unittest").expect("create temp dir");
|
let tmp_dir = TempDir::new("unittest").expect("create temp dir");
|
||||||
|
|
@ -30,8 +30,15 @@ fn single_file() {
|
||||||
let dir = get_dir(&["12345"]);
|
let dir = get_dir(&["12345"]);
|
||||||
let storage = get_storage(dir.path());
|
let storage = get_storage(dir.path());
|
||||||
|
|
||||||
assert!(Regex::new(&format!("^{}/_filename/\\d+$", dir.path().display())).unwrap().is_match(&storage.write_filename()));
|
assert!(
|
||||||
assert_eq!(dir.path().join("_filename").join("12345"), Path::new(&storage.read_filename().unwrap()));
|
Regex::new(&format!("^{}/_filename/\\d+$", dir.path().display()))
|
||||||
|
.unwrap()
|
||||||
|
.is_match(&storage.write_filename())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
dir.path().join("_filename").join("12345"),
|
||||||
|
Path::new(&storage.read_filename().unwrap())
|
||||||
|
);
|
||||||
assert_eq!(storage.recent_date().unwrap(), 12345);
|
assert_eq!(storage.recent_date().unwrap(), 12345);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,8 +47,15 @@ fn two_files() {
|
||||||
let dir = get_dir(&["12345", "23456"]);
|
let dir = get_dir(&["12345", "23456"]);
|
||||||
let storage = get_storage(dir.path());
|
let storage = get_storage(dir.path());
|
||||||
|
|
||||||
assert!(Regex::new(&format!("^{}/_filename/\\d+$", dir.path().display())).unwrap().is_match(&storage.write_filename()));
|
assert!(
|
||||||
assert_eq!(dir.path().join("_filename").join("23456"), Path::new(&storage.read_filename().unwrap()));
|
Regex::new(&format!("^{}/_filename/\\d+$", dir.path().display()))
|
||||||
|
.unwrap()
|
||||||
|
.is_match(&storage.write_filename())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
dir.path().join("_filename").join("23456"),
|
||||||
|
Path::new(&storage.read_filename().unwrap())
|
||||||
|
);
|
||||||
assert_eq!(storage.recent_date().unwrap(), 23456);
|
assert_eq!(storage.recent_date().unwrap(), 23456);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -50,8 +64,15 @@ fn another_two_files() {
|
||||||
let dir = get_dir(&["23456", "12345"]);
|
let dir = get_dir(&["23456", "12345"]);
|
||||||
let storage = get_storage(dir.path());
|
let storage = get_storage(dir.path());
|
||||||
|
|
||||||
assert!(Regex::new(&format!("^{}/_filename/\\d+$", dir.path().display())).unwrap().is_match(&storage.write_filename()));
|
assert!(
|
||||||
assert_eq!(dir.path().join("_filename").join("23456"), Path::new(&storage.read_filename().unwrap()));
|
Regex::new(&format!("^{}/_filename/\\d+$", dir.path().display()))
|
||||||
|
.unwrap()
|
||||||
|
.is_match(&storage.write_filename())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
dir.path().join("_filename").join("23456"),
|
||||||
|
Path::new(&storage.read_filename().unwrap())
|
||||||
|
);
|
||||||
assert_eq!(storage.recent_date().unwrap(), 23456);
|
assert_eq!(storage.recent_date().unwrap(), 23456);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -60,8 +81,15 @@ fn three_files() {
|
||||||
let dir = get_dir(&["23456", "9", "12345"]);
|
let dir = get_dir(&["23456", "9", "12345"]);
|
||||||
let storage = get_storage(dir.path());
|
let storage = get_storage(dir.path());
|
||||||
|
|
||||||
assert!(Regex::new(&format!("^{}/_filename/\\d+$", dir.path().display())).unwrap().is_match(&storage.write_filename()));
|
assert!(
|
||||||
assert_eq!(dir.path().join("_filename").join("23456"), Path::new(&storage.read_filename().unwrap()));
|
Regex::new(&format!("^{}/_filename/\\d+$", dir.path().display()))
|
||||||
|
.unwrap()
|
||||||
|
.is_match(&storage.write_filename())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
dir.path().join("_filename").join("23456"),
|
||||||
|
Path::new(&storage.read_filename().unwrap())
|
||||||
|
);
|
||||||
assert_eq!(storage.recent_date().unwrap(), 23456);
|
assert_eq!(storage.recent_date().unwrap(), 23456);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,11 +100,21 @@ fn empty_storage() {
|
||||||
let dir = TempDir::new("unittest").expect("create temp dir");
|
let dir = TempDir::new("unittest").expect("create temp dir");
|
||||||
let storage = get_storage(dir.path());
|
let storage = get_storage(dir.path());
|
||||||
|
|
||||||
assert!(Regex::new(&format!("^{}/_filename/\\d+$", dir.path().display())).unwrap().is_match(&storage.write_filename()));
|
assert!(
|
||||||
|
Regex::new(&format!("^{}/_filename/\\d+$", dir.path().display()))
|
||||||
|
.unwrap()
|
||||||
|
.is_match(&storage.write_filename())
|
||||||
|
);
|
||||||
assert!(storage.read_filename().is_err());
|
assert!(storage.read_filename().is_err());
|
||||||
assert_eq!(storage.read_filename().unwrap_err().description(), "entity not found");
|
assert_eq!(
|
||||||
|
storage.read_filename().unwrap_err().description(),
|
||||||
|
"entity not found"
|
||||||
|
);
|
||||||
assert!(storage.recent_date().is_err());
|
assert!(storage.recent_date().is_err());
|
||||||
assert_eq!(storage.recent_date().unwrap_err().description(), "entity not found");
|
assert_eq!(
|
||||||
|
storage.recent_date().unwrap_err().description(),
|
||||||
|
"entity not found"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -84,11 +122,21 @@ fn empty_storage_for_filename() {
|
||||||
let dir = get_dir(&[]);
|
let dir = get_dir(&[]);
|
||||||
let storage = get_storage(dir.path());
|
let storage = get_storage(dir.path());
|
||||||
|
|
||||||
assert!(Regex::new(&format!("^{}/_filename/\\d+$", dir.path().display())).unwrap().is_match(&storage.write_filename()));
|
assert!(
|
||||||
|
Regex::new(&format!("^{}/_filename/\\d+$", dir.path().display()))
|
||||||
|
.unwrap()
|
||||||
|
.is_match(&storage.write_filename())
|
||||||
|
);
|
||||||
assert!(storage.read_filename().is_err());
|
assert!(storage.read_filename().is_err());
|
||||||
assert_eq!(storage.read_filename().unwrap_err().description(), "Not found");
|
assert_eq!(
|
||||||
|
storage.read_filename().unwrap_err().description(),
|
||||||
|
"Not found"
|
||||||
|
);
|
||||||
assert!(storage.recent_date().is_err());
|
assert!(storage.recent_date().is_err());
|
||||||
assert_eq!(storage.recent_date().unwrap_err().description(), "Not found");
|
assert_eq!(
|
||||||
|
storage.recent_date().unwrap_err().description(),
|
||||||
|
"Not found"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -96,9 +144,19 @@ fn bad_file() {
|
||||||
let dir = get_dir(&["abba"]);
|
let dir = get_dir(&["abba"]);
|
||||||
let storage = get_storage(dir.path());
|
let storage = get_storage(dir.path());
|
||||||
|
|
||||||
assert!(Regex::new(&format!("^{}/_filename/\\d+$", dir.path().display())).unwrap().is_match(&storage.write_filename()));
|
assert!(
|
||||||
|
Regex::new(&format!("^{}/_filename/\\d+$", dir.path().display()))
|
||||||
|
.unwrap()
|
||||||
|
.is_match(&storage.write_filename())
|
||||||
|
);
|
||||||
assert!(storage.read_filename().is_err());
|
assert!(storage.read_filename().is_err());
|
||||||
assert_eq!(storage.read_filename().unwrap_err().description(), "Not found");
|
assert_eq!(
|
||||||
|
storage.read_filename().unwrap_err().description(),
|
||||||
|
"Not found"
|
||||||
|
);
|
||||||
assert!(storage.recent_date().is_err());
|
assert!(storage.recent_date().is_err());
|
||||||
assert_eq!(storage.recent_date().unwrap_err().description(), "Not found");
|
assert_eq!(
|
||||||
|
storage.recent_date().unwrap_err().description(),
|
||||||
|
"Not found"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue