From fd8fe9ce11a130300d3987cfafe225a79a0fe019 Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Wed, 17 Oct 2018 19:50:06 +0200 Subject: [PATCH] Update --- src/build.rs | 24 ++++++ src/lib.rs | 1 + src/symbols/factory.rs | 116 ++++++++++++++++++++++----- src/symbols/mariadb/database_dump.rs | 19 +++-- 4 files changed, 132 insertions(+), 28 deletions(-) create mode 100644 src/build.rs diff --git a/src/build.rs b/src/build.rs new file mode 100644 index 0000000..b0cdc19 --- /dev/null +++ b/src/build.rs @@ -0,0 +1,24 @@ +use std::env; +use std::fs::{read_dir, File}; +use std::io::Read; +use std::io::Write; +use std::path::{Path, PathBuf}; + +fn get_const_name>(p: &P) -> String { + let mut file_name_without_extension = p.clone().into(); + file_name_without_extension.set_extension(""); + String::from(file_name_without_extension.file_name().unwrap().to_string_lossy()).to_uppercase() +} + +pub fn create_static_output_files(source_dir: &str) { + let out_dir = env::var("OUT_DIR").unwrap(); + let dest_path = Path::new(&out_dir).join("static_files.rs"); + let mut f = File::create(&dest_path).unwrap(); + for maybe_dir_entry in read_dir(source_dir).unwrap() { + let file_path = maybe_dir_entry.unwrap().path(); + let mut buffer = String::new(); + File::open(file_path.clone()).unwrap().read_to_string(&mut buffer).unwrap(); + let fence = buffer.chars().filter(|c| *c == '#').collect::() + "#"; + f.write_all(format!("pub const {}: &'static str = r{1}\"{2}\"{1};\n", get_const_name(&file_path), fence, buffer).as_bytes()).unwrap(); + } +} diff --git a/src/lib.rs b/src/lib.rs index 13f1b8f..9e65852 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,6 +23,7 @@ extern crate regex; extern crate users; pub mod bin; +pub mod build; pub mod command_runner; pub mod factory; pub mod loggers; diff --git a/src/symbols/factory.rs b/src/symbols/factory.rs index b087699..ffb2e7d 100644 --- a/src/symbols/factory.rs +++ b/src/symbols/factory.rs @@ -2,31 +2,50 @@ use std::ops::Deref; use std::path::Path; use command_runner::{CommandRunner, SetuidCommandRunner}; -use storage::SimpleStorage; +use storage::{SimpleStorage, Storage}; use symbols::{Action, Symbol, SymbolRunner}; use symbols::acme::{AcmeCert, AcmeCertChain}; use symbols::file::File; use symbols::git::checkout::GitCheckout; use symbols::hook::Hook; use symbols::list::ListAction; -use symbols::nginx::server::NginxServer; +use symbols::mariadb::{DatabaseDump, MariaDBDatabase, MariaDBUser}; +use symbols::nginx::server::{NginxServer, server_config}; use symbols::owner::Owner; use symbols::stored_directory::{StoredDirectory, StorageDirection}; use symbols::systemd::reload::ReloadService; use symbols::tls::SelfSignedTlsCert; -pub struct SymbolFactory<'a, C: 'a + CommandRunner, R: 'a + SymbolRunner>{ +pub trait Policy { + fn user_name_for_host(&self, host_name: &'static str) -> String; +} + +pub struct DefaultPolicy; + +impl Policy for DefaultPolicy { + fn user_name_for_host(&self, host_name: &'static str) -> String { + host_name.split('.').rev().fold(String::new(), |result, part| if result.len() > 0 { result + "_" } else { result } + part) + } +} + +pub struct SymbolFactory<'a, C: 'a + CommandRunner, R: 'a + SymbolRunner, P: 'a + Policy>{ command_runner: &'a C, acme_command_runner: SetuidCommandRunner<'a, C>, - symbol_runner: &'a R + symbol_runner: &'a R, + policy: &'a P } -impl<'b, C: 'b + CommandRunner, R: 'b + SymbolRunner> SymbolFactory<'b, C, R> { - pub fn new(command_runner: &'b C, symbol_runner: &'b R) -> Self { +impl<'b, C: 'b + CommandRunner, R: 'b + SymbolRunner, P: 'b + Policy> SymbolFactory<'b, C, R, P> { + pub fn new(command_runner: &'b C, symbol_runner: &'b R, policy: &'b P) -> Self { let acme_user = "acme"; // FIXME: CONFIG let acme_command_runner = SetuidCommandRunner::new(acme_user, command_runner); - SymbolFactory { command_runner: command_runner, acme_command_runner: acme_command_runner, symbol_runner: symbol_runner } + SymbolFactory { + command_runner: command_runner, + acme_command_runner: acme_command_runner, + symbol_runner: symbol_runner, + policy: policy + } } pub fn get_nginx_acme_server<'a, 'c: 'a, S: 'a + Symbol>(&'c self, host: &'static str, nginx_server_symbol: S) -> Box { @@ -61,11 +80,11 @@ impl<'b, C: 'b + CommandRunner, R: 'b + SymbolRunner> SymbolFactory<'b, C, R> { )).into_action(self.symbol_runner) } - pub fn get_php_fpm_pool_socket_path<'a>(&'a self, user_name: &'static str) -> String { + fn get_php_fpm_pool_socket_path<'a>(&'a self, user_name: &str) -> String { format!("/run/php/{}.sock", user_name) } - pub fn get_php_fpm_pool<'a>(&'a self, user_name: &'static str) -> Box { + fn get_php_fpm_pool<'a>(&'a self, user_name: &str) -> Box { let socket = self.get_php_fpm_pool_socket_path(user_name); Box::new(Hook::new( File::new( @@ -84,14 +103,63 @@ pm.max_children = 10" )).into_action(self.symbol_runner) } - pub fn get_nginx_php_server<'a>(&'a self, host_name: &'static str, user_name: &'static str, root_dir: &'static str) -> NginxServer<'a, C, String> { - let socket = self.get_php_fpm_pool_socket_path(user_name); - NginxServer::new_php( - host_name, - socket.into(), - root_dir, - self.command_runner - ) + pub fn serve_php<'a>(&'a self, host_name: &'static str, root_dir: &'static str) -> Box { + let user_name = self.policy.user_name_for_host(host_name); + let socket = self.get_php_fpm_pool_socket_path(&user_name); + Box::new(ListAction::new(vec![ + self.get_php_fpm_pool(&user_name), + self.get_nginx_acme_server(host_name, + NginxServer::new_php( + host_name, + socket.into(), + root_dir, + self.command_runner + ) + ) + ])) + } + + pub fn serve_dokuwiki<'a>(&'a self, host_name: &'static str, root_dir: &'static str) -> Box { + let user_name = self.policy.user_name_for_host(host_name); + let socket = self.get_php_fpm_pool_socket_path(&user_name); + Box::new(ListAction::new(vec![ + self.get_php_fpm_pool(&user_name), + self.get_nginx_acme_server(host_name, + NginxServer::new( + host_name, + server_config("hostname", &format!(" + root {}; + index doku.php; + location ~ [^/]\\.php(/|$) {{ + fastcgi_pass unix:{}; + include \"snippets/fastcgi-php.conf\"; + }} + + location ~ /(data/|conf/|bin/|inc/|install.php) {{ deny all; }} + + location / {{ try_files $uri $uri/ @dokuwiki; }} + + location @dokuwiki {{ + # rewrites \"doku.php/\" out of the URLs if you set the userewrite setting to .htaccess in dokuwiki config page + rewrite ^/_media/(.*) /lib/exe/fetch.php?media=$1 last; + rewrite ^/_detail/(.*) /lib/exe/detail.php?media=$1 last; + rewrite ^/_export/([^/]+)/(.*) /doku.php?do=export_$1&id=$2 last; + rewrite ^/(.*) /doku.php?id=$1&$args last; + }} + ", + root_dir, + socket)), + self.command_runner + )) + ])) + } + + pub fn serve_redir<'a>(&'a self, host_name: &'static str, target: &'static str) -> Box { + 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 { + self.get_nginx_acme_server(host_name, NginxServer::new_static(host_name, dir, self.command_runner)) } pub fn get_stored_directory<'a, T: Into>(&'a self, storage_name: &'static str, target: T) -> (Box, Box) { @@ -103,6 +171,18 @@ pm.max_children = 10" ) } + pub fn get_mariadb_database<'a>(&'a self, name: &'static str) -> Box { + let db_dump = SimpleStorage::new("/root/data".to_string(), format!("{}.sql", name)); + 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(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 { + Box::new(MariaDBUser::new(user_name.into(), self.command_runner)).into_action(self.symbol_runner) + } + pub fn get_git_checkout<'a, T: 'a + AsRef>(&'a self, target: T, source: &'a str, branch: &'a str) -> Box { Box::new(GitCheckout::new(target, source, branch, self.command_runner)).into_action(self.symbol_runner) } @@ -111,7 +191,7 @@ pm.max_children = 10" Box::new(Owner::new(file, user.into(), self.command_runner)).into_action(self.symbol_runner) } - pub fn get_file<'a, F: 'a + Deref, P: 'a + AsRef>(&'a self, path: P, content: F) -> Box { + pub fn get_file<'a, F: 'a + Deref, Q: 'a + AsRef>(&'a self, path: Q, content: F) -> Box { Box::new(File::new(path, content)).into_action(self.symbol_runner) } } diff --git a/src/symbols/mariadb/database_dump.rs b/src/symbols/mariadb/database_dump.rs index 8a0db4a..02cc114 100644 --- a/src/symbols/mariadb/database_dump.rs +++ b/src/symbols/mariadb/database_dump.rs @@ -1,4 +1,3 @@ -use std::borrow::Cow; use std::error::Error; use std::fmt; use std::str::FromStr; @@ -7,14 +6,14 @@ use command_runner::CommandRunner; use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner}; use storage::Storage; -pub struct DatabaseDump<'a, C, S> where C: 'a + CommandRunner, S: Storage { - db_name: Cow<'a, str>, +pub struct DatabaseDump<'a, N, C, S> where N: 'a + AsRef, C: 'a + CommandRunner, S: Storage { + db_name: N, storage: S, command_runner: &'a C } -impl<'a, C: CommandRunner, S> DatabaseDump<'a, C, S> where S: Storage { - pub fn new(db_name: Cow<'a, str>, storage: S, command_runner: &'a C) -> Self { +impl<'a, N: AsRef, C: CommandRunner, S: Storage> DatabaseDump<'a, N, C, S> { + pub fn new(db_name: N, storage: S, command_runner: &'a C) -> Self { DatabaseDump { db_name: db_name, storage: storage, @@ -28,22 +27,22 @@ impl<'a, C: CommandRunner, S> DatabaseDump<'a, C, S> where S: Storage { } } -impl<'a, C: CommandRunner, S> fmt::Display for DatabaseDump<'a, C, S> where S: Storage { +impl<'a, N: AsRef, C: CommandRunner, S: Storage> fmt::Display for DatabaseDump<'a, N, C, S> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Dump MariaDB Database {}", self.db_name) + write!(f, "Dump MariaDB Database {}", self.db_name.as_ref()) } } -impl<'a, C: CommandRunner, S> Symbol for DatabaseDump<'a, C, S> where S: Storage { +impl<'a, N: AsRef, C: CommandRunner, S: Storage> Symbol for DatabaseDump<'a, N, C, S> { fn target_reached(&self) -> Result> { 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))); + 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_right() == "NULL" { return Ok(false); } Ok(try!(u64::from_str(modified_date.trim_right())) <= dump_date) } fn execute(&self) -> Result<(), Box> { - self.command_runner.run_successfully("sh", &["-c", &format!("mysqldump '{}' > {}", self.db_name, 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 SymbolRunner) -> Box {