Command runner args, Paths and AsRef
This commit is contained in:
parent
5d5e9dfcb4
commit
3ccf64fac1
32 changed files with 696 additions and 721 deletions
|
|
@ -1,3 +1,4 @@
|
|||
use std::borrow::Cow;
|
||||
use std::error::Error;
|
||||
use std::ffi::OsStr;
|
||||
use std::io::{Result as IoResult, Write};
|
||||
|
|
@ -5,53 +6,40 @@ use std::process::Command;
|
|||
use std::process::Output;
|
||||
use std::process::Stdio;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! args {
|
||||
($($x:expr),*) => (
|
||||
&[$($x.as_ref()),*]
|
||||
);
|
||||
($($x:expr,)*) => (args![$($x),*]) // handle trailing commas
|
||||
}
|
||||
|
||||
pub trait CommandRunner {
|
||||
fn run_with_args_and_stdin<S: AsRef<OsStr> + ?Sized>(
|
||||
fn run_with_args_and_stdin(
|
||||
&self,
|
||||
program: &str,
|
||||
args: &[&S],
|
||||
args: &[&OsStr],
|
||||
stdin: &str,
|
||||
) -> IoResult<Output>;
|
||||
fn run_with_args<S: AsRef<OsStr> + ?Sized>(
|
||||
&self,
|
||||
program: &str,
|
||||
args: &[&S],
|
||||
) -> IoResult<Output> {
|
||||
fn run_with_args(&self, program: &str, args: &[&OsStr]) -> IoResult<Output> {
|
||||
self.run_with_args_and_stdin(program, args, "")
|
||||
}
|
||||
fn get_output<S: AsRef<OsStr> + ?Sized>(
|
||||
&self,
|
||||
program: &str,
|
||||
args: &[&S],
|
||||
) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||
fn get_output(&self, program: &str, args: &[&OsStr]) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||
let output = self.run_with_args(program, args)?;
|
||||
if !output.status.success() {
|
||||
return Err(String::from_utf8(output.stderr)?.into());
|
||||
}
|
||||
Ok(output.stdout)
|
||||
}
|
||||
fn get_stderr<S: AsRef<OsStr> + ?Sized>(
|
||||
&self,
|
||||
program: &str,
|
||||
args: &[&S],
|
||||
) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||
fn get_stderr(&self, program: &str, args: &[&OsStr]) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||
let output = self.run_with_args(program, args)?;
|
||||
if !output.status.success() {
|
||||
return Err(String::from_utf8(output.stderr)?.into());
|
||||
}
|
||||
Ok(output.stderr)
|
||||
}
|
||||
fn run_successfully<S: AsRef<OsStr> + ?Sized>(
|
||||
&self,
|
||||
program: &str,
|
||||
args: &[&S],
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let output = self.run_with_args(program, args)?;
|
||||
if output.status.success() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(String::from_utf8(output.stderr)?.into())
|
||||
}
|
||||
fn run_successfully(&self, program: &str, args: &[&OsStr]) -> Result<(), Box<dyn Error>> {
|
||||
self.get_output(program, args).map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -59,10 +47,10 @@ pub trait CommandRunner {
|
|||
pub struct StdCommandRunner;
|
||||
|
||||
impl CommandRunner for StdCommandRunner {
|
||||
fn run_with_args_and_stdin<S: AsRef<OsStr> + ?Sized>(
|
||||
fn run_with_args_and_stdin(
|
||||
&self,
|
||||
program: &str,
|
||||
args: &[&S],
|
||||
args: &[&OsStr],
|
||||
input: &str,
|
||||
) -> IoResult<Output> {
|
||||
// FIXME: logger
|
||||
|
|
@ -90,14 +78,14 @@ where
|
|||
C: 'a + CommandRunner,
|
||||
{
|
||||
command_runner: &'a C,
|
||||
user_name: &'a str,
|
||||
user_name: Cow<'a, str>,
|
||||
}
|
||||
|
||||
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: Cow<'a, str>, command_runner: &'a C) -> SetuidCommandRunner<'a, C> {
|
||||
SetuidCommandRunner {
|
||||
command_runner,
|
||||
user_name,
|
||||
|
|
@ -138,13 +126,13 @@ impl<'a, C> CommandRunner for SetuidCommandRunner<'a, C>
|
|||
where
|
||||
C: 'a + CommandRunner,
|
||||
{
|
||||
fn run_with_args_and_stdin<S: AsRef<OsStr> + ?Sized>(
|
||||
fn run_with_args_and_stdin(
|
||||
&self,
|
||||
program: &str,
|
||||
args: &[&S],
|
||||
args: &[&OsStr],
|
||||
input: &str,
|
||||
) -> IoResult<Output> {
|
||||
let uid = get_user_by_name(self.user_name)
|
||||
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));
|
||||
|
|
@ -196,10 +184,10 @@ impl<'a, C> CommandRunner for SuCommandRunner<'a, C>
|
|||
where
|
||||
C: 'a + CommandRunner,
|
||||
{
|
||||
fn run_with_args_and_stdin<S: AsRef<OsStr> + ?Sized>(
|
||||
fn run_with_args_and_stdin(
|
||||
&self,
|
||||
program: &str,
|
||||
args: &[&S],
|
||||
args: &[&OsStr],
|
||||
input: &str,
|
||||
) -> IoResult<Output> {
|
||||
let raw_new_args = [self.user_name, "-s", "/usr/bin/env", "--", program];
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ impl<'a, C: CommandRunner> SymbolRepository<'a>
|
|||
Box::new(Dir::new(value.to_string())),
|
||||
Box::new(Owner::new(
|
||||
value.to_string(),
|
||||
matches[1].to_string().into(),
|
||||
matches[1].to_string(),
|
||||
self.command_runner,
|
||||
)),
|
||||
])) as Box<dyn Symbol>
|
||||
|
|
@ -110,7 +110,7 @@ impl<'a, C: CommandRunner> SymbolRepository<'a>
|
|||
)) as Box<dyn Symbol>)
|
||||
} else if let Some(matches) = self.systemd_linger.captures(value) {
|
||||
Some(Box::new(SystemdUserSession::new(
|
||||
matches[1].to_string().into(),
|
||||
matches[1].to_string(),
|
||||
self.command_runner,
|
||||
)) as Box<dyn Symbol>)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ macro_rules! for_each_tuple_ {
|
|||
}
|
||||
#[macro_export]
|
||||
macro_rules! for_each_tuple {
|
||||
( $m:ident ) => (
|
||||
for_each_tuple_! { $m !! A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, }
|
||||
);
|
||||
( $m:ident ) => {
|
||||
for_each_tuple_! { $m !! A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, }
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ mod for_each_tuple;
|
|||
|
||||
pub mod bin;
|
||||
pub mod build;
|
||||
#[macro_use]
|
||||
pub mod command_runner;
|
||||
pub mod factory;
|
||||
pub mod loggers;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
use std::borrow::Cow;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::path::Path;
|
||||
|
|
@ -7,13 +6,13 @@ use command_runner::CommandRunner;
|
|||
use resources::Resource;
|
||||
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||
|
||||
pub struct AcmeAccountKey<'a, C: 'a + CommandRunner> {
|
||||
path: Cow<'a, Path>,
|
||||
pub struct AcmeAccountKey<'a, P: AsRef<Path>, C: CommandRunner> {
|
||||
path: P,
|
||||
command_runner: &'a C,
|
||||
}
|
||||
|
||||
impl<'a, C: CommandRunner> AcmeAccountKey<'a, C> {
|
||||
pub fn new(path: Cow<'a, Path>, command_runner: &'a C) -> Self {
|
||||
impl<'a, P: AsRef<Path>, C: CommandRunner> AcmeAccountKey<'a, P, C> {
|
||||
pub fn new(path: P, command_runner: &'a C) -> Self {
|
||||
AcmeAccountKey {
|
||||
path,
|
||||
command_runner,
|
||||
|
|
@ -25,26 +24,26 @@ impl<'a, C: CommandRunner> AcmeAccountKey<'a, C> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, C: CommandRunner> fmt::Display for AcmeAccountKey<'a, C> {
|
||||
impl<'a, P: AsRef<Path>, C: CommandRunner> fmt::Display for AcmeAccountKey<'a, P, C> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "AcmeAccountKey {}", self.path.display())
|
||||
write!(f, "AcmeAccountKey {}", self.path.as_ref().display())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: CommandRunner> Symbol for AcmeAccountKey<'a, C> {
|
||||
impl<'a, P: AsRef<Path>, C: CommandRunner> Symbol for AcmeAccountKey<'a, P, C> {
|
||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||
if !self.path.exists() {
|
||||
if !self.path.as_ref().exists() {
|
||||
return Ok(false);
|
||||
}
|
||||
let stdout = 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(),
|
||||
args![
|
||||
"rsa",
|
||||
"-in",
|
||||
self.path.as_ref(),
|
||||
"-noout",
|
||||
"-check",
|
||||
"-text",
|
||||
],
|
||||
)?;
|
||||
Ok(stdout.starts_with(&format!("Private-Key: ({} bit)\n", self.get_bytes()).as_bytes()))
|
||||
|
|
@ -53,20 +52,21 @@ impl<'a, C: CommandRunner> Symbol for AcmeAccountKey<'a, C> {
|
|||
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(),
|
||||
args![
|
||||
"genrsa",
|
||||
"-out",
|
||||
self.path.as_ref(),
|
||||
self.get_bytes().to_string(),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
fn get_prerequisites(&self) -> Vec<Resource> {
|
||||
vec![Resource::new(
|
||||
"dir",
|
||||
self.path.parent().unwrap().to_string_lossy(),
|
||||
)]
|
||||
if let Some(parent) = self.path.as_ref().parent() {
|
||||
vec![Resource::new("dir", parent.to_str().unwrap())]
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
|
||||
|
|
|
|||
|
|
@ -1,67 +1,66 @@
|
|||
use std::borrow::Cow;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::fs::File as FsFile;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use command_runner::CommandRunner;
|
||||
use resources::Resource;
|
||||
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||
|
||||
pub struct AcmeCert<'a, C: 'a + CommandRunner> {
|
||||
domain: Cow<'a, str>,
|
||||
pub struct AcmeCert<'a, D: AsRef<str>, C: CommandRunner> {
|
||||
domain: D,
|
||||
command_runner: &'a C,
|
||||
}
|
||||
|
||||
impl<'a, C: CommandRunner> AcmeCert<'a, C> {
|
||||
pub fn new(domain: Cow<'a, str>, command_runner: &'a C) -> Self {
|
||||
impl<'a, D: AsRef<str>, C: CommandRunner> AcmeCert<'a, D, C> {
|
||||
pub fn new(domain: D, command_runner: &'a C) -> Self {
|
||||
AcmeCert {
|
||||
domain,
|
||||
command_runner,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_csr_path(&self) -> String {
|
||||
format!("/etc/ssl/local_certs/{}.csr", self.domain)
|
||||
fn get_csr_path(&self) -> PathBuf {
|
||||
format!("/etc/ssl/local_certs/{}.csr", self.domain.as_ref()).into()
|
||||
}
|
||||
|
||||
fn get_cert_path(&self) -> String {
|
||||
format!("/etc/ssl/local_certs/{}.crt", self.domain)
|
||||
fn get_cert_path(&self) -> PathBuf {
|
||||
format!("/etc/ssl/local_certs/{}.crt", self.domain.as_ref()).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: CommandRunner> fmt::Display for AcmeCert<'a, C> {
|
||||
impl<'a, D: AsRef<str>, C: CommandRunner> fmt::Display for AcmeCert<'a, D, C> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "AcmeCert {}", self.domain)
|
||||
write!(f, "AcmeCert {}", self.domain.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
const DAYS_IN_SECONDS: u32 = 24 * 60 * 60;
|
||||
|
||||
impl<'a, C: CommandRunner> Symbol for AcmeCert<'a, C> {
|
||||
impl<'a, D: AsRef<str>, C: CommandRunner> Symbol for AcmeCert<'a, D, C> {
|
||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||
if !Path::new(&self.get_cert_path()).exists() {
|
||||
if !self.get_cert_path().exists() {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let output = self.command_runner.run_with_args(
|
||||
"openssl",
|
||||
&[
|
||||
args![
|
||||
"x509",
|
||||
"-in",
|
||||
&self.get_cert_path(),
|
||||
self.get_cert_path(),
|
||||
"-noout",
|
||||
"-subject",
|
||||
"-checkend",
|
||||
&(30 * DAYS_IN_SECONDS).to_string(),
|
||||
(30 * DAYS_IN_SECONDS).to_string(),
|
||||
],
|
||||
)?;
|
||||
if output.status.success()
|
||||
&& output.stdout
|
||||
== format!(
|
||||
"subject=CN = {}\nCertificate will not expire\n",
|
||||
self.domain
|
||||
self.domain.as_ref()
|
||||
)
|
||||
.as_bytes()
|
||||
{
|
||||
|
|
@ -70,18 +69,22 @@ impl<'a, C: CommandRunner> Symbol for AcmeCert<'a, C> {
|
|||
.command_runner
|
||||
.run_successfully(
|
||||
"openssl",
|
||||
&[
|
||||
args![
|
||||
"verify",
|
||||
"--untrusted",
|
||||
"/home/acme/lets_encrypt_x3_cross_signed.pem",
|
||||
&self.get_cert_path(),
|
||||
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()
|
||||
== format!(
|
||||
"subject=CN = {}\nCertificate will expire\n",
|
||||
self.domain.as_ref()
|
||||
)
|
||||
.as_bytes()
|
||||
{
|
||||
Ok(false)
|
||||
} else {
|
||||
|
|
@ -92,11 +95,11 @@ impl<'a, C: CommandRunner> Symbol for AcmeCert<'a, C> {
|
|||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||
let output = self.command_runner.get_output(
|
||||
"acme-tiny",
|
||||
&[
|
||||
args![
|
||||
"--account-key",
|
||||
"/home/acme/account.key",
|
||||
"--csr",
|
||||
&self.get_csr_path(),
|
||||
self.get_csr_path(),
|
||||
"--acme-dir",
|
||||
"/home/acme/challenges/",
|
||||
],
|
||||
|
|
@ -107,7 +110,7 @@ impl<'a, C: CommandRunner> Symbol for AcmeCert<'a, C> {
|
|||
}
|
||||
|
||||
fn get_prerequisites(&self) -> Vec<Resource> {
|
||||
vec![Resource::new("file", self.get_csr_path())]
|
||||
vec![Resource::new("file", self.get_csr_path().to_str().unwrap())]
|
||||
}
|
||||
|
||||
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
|
||||
|
|
|
|||
|
|
@ -1,66 +1,65 @@
|
|||
use std::borrow::Cow;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::fs::File as FsFile;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use command_runner::CommandRunner;
|
||||
use resources::Resource;
|
||||
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||
|
||||
pub struct AcmeCertChain<'a, C: 'a + CommandRunner> {
|
||||
domain: Cow<'a, str>,
|
||||
pub struct AcmeCertChain<'a, D: AsRef<str>, C: CommandRunner> {
|
||||
domain: D,
|
||||
command_runner: &'a C,
|
||||
}
|
||||
|
||||
impl<'a, C: CommandRunner> AcmeCertChain<'a, C> {
|
||||
pub fn new(domain: Cow<'a, str>, command_runner: &'a C) -> Self {
|
||||
impl<'a, D: AsRef<str>, C: CommandRunner> AcmeCertChain<'a, D, C> {
|
||||
pub fn new(domain: D, command_runner: &'a C) -> Self {
|
||||
AcmeCertChain {
|
||||
domain,
|
||||
command_runner,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_single_cert_path(&self) -> String {
|
||||
format!("/etc/ssl/local_certs/{}.crt", self.domain)
|
||||
fn get_single_cert_path(&self) -> PathBuf {
|
||||
format!("/etc/ssl/local_certs/{}.crt", self.domain.as_ref()).into()
|
||||
}
|
||||
|
||||
fn get_cert_chain_path(&self) -> String {
|
||||
format!("/etc/ssl/local_certs/{}.chained.crt", self.domain)
|
||||
fn get_cert_chain_path(&self) -> PathBuf {
|
||||
format!("/etc/ssl/local_certs/{}.chained.crt", self.domain.as_ref()).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: CommandRunner> fmt::Display for AcmeCertChain<'a, C> {
|
||||
impl<'a, D: AsRef<str>, C: CommandRunner> fmt::Display for AcmeCertChain<'a, D, C> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "AcmeCertChain {}", self.domain)
|
||||
write!(f, "AcmeCertChain {}", self.domain.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
const DAYS_IN_SECONDS: u32 = 24 * 60 * 60;
|
||||
|
||||
impl<'a, C: CommandRunner> Symbol for AcmeCertChain<'a, C> {
|
||||
impl<'a, D: AsRef<str>, C: CommandRunner> Symbol for AcmeCertChain<'a, D, C> {
|
||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||
if !Path::new(&self.get_cert_chain_path()).exists() {
|
||||
if !self.get_cert_chain_path().exists() {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let stdout = self.command_runner.get_output(
|
||||
"openssl",
|
||||
&[
|
||||
args![
|
||||
"x509",
|
||||
"-in",
|
||||
&self.get_cert_chain_path(),
|
||||
self.get_cert_chain_path(),
|
||||
"-noout",
|
||||
"-subject",
|
||||
"-checkend",
|
||||
&(30 * DAYS_IN_SECONDS).to_string(),
|
||||
(30 * DAYS_IN_SECONDS).to_string(),
|
||||
],
|
||||
)?;
|
||||
if stdout
|
||||
!= format!(
|
||||
"subject=CN = {}\nCertificate will not expire\n",
|
||||
self.domain
|
||||
self.domain.as_ref()
|
||||
)
|
||||
.as_bytes()
|
||||
{
|
||||
|
|
@ -72,11 +71,11 @@ impl<'a, C: CommandRunner> Symbol for AcmeCertChain<'a, C> {
|
|||
.command_runner
|
||||
.run_successfully(
|
||||
"openssl",
|
||||
&[
|
||||
args![
|
||||
"verify",
|
||||
"-untrusted",
|
||||
"/home/acme/lets_encrypt_x3_cross_signed.pem",
|
||||
&self.get_cert_chain_path(),
|
||||
self.get_cert_chain_path(),
|
||||
],
|
||||
)
|
||||
.is_ok(),
|
||||
|
|
@ -86,8 +85,8 @@ impl<'a, C: CommandRunner> Symbol for AcmeCertChain<'a, C> {
|
|||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||
let output = self.command_runner.get_output(
|
||||
"cat",
|
||||
&[
|
||||
self.get_single_cert_path().as_ref(),
|
||||
args![
|
||||
self.get_single_cert_path(),
|
||||
"/home/acme/lets_encrypt_x3_cross_signed.pem",
|
||||
],
|
||||
)?;
|
||||
|
|
@ -97,7 +96,10 @@ impl<'a, C: CommandRunner> Symbol for AcmeCertChain<'a, C> {
|
|||
}
|
||||
|
||||
fn get_prerequisites(&self) -> Vec<Resource> {
|
||||
vec![Resource::new("file", self.get_single_cert_path())]
|
||||
vec![Resource::new(
|
||||
"file",
|
||||
self.get_single_cert_path().to_str().unwrap(),
|
||||
)]
|
||||
}
|
||||
|
||||
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,4 @@
|
|||
use resources::Resource;
|
||||
use std::borrow::{Borrow, Cow};
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::ops::Deref;
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use command_runner::CommandRunner;
|
||||
use symbols::acme::AcmeAccountKey;
|
||||
|
|
@ -11,72 +6,32 @@ use symbols::dir::Dir;
|
|||
use symbols::file::File;
|
||||
use symbols::list::List;
|
||||
use symbols::owner::Owner;
|
||||
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||
use symbols::Symbol;
|
||||
|
||||
pub struct AcmeUser<'a>(Cow<'a, str>);
|
||||
|
||||
impl<'a> AcmeUser<'a> {
|
||||
pub fn new<S: Into<Cow<'a, str>>>(user_name: S) -> Self {
|
||||
AcmeUser(user_name.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Symbol for AcmeUser<'a> {
|
||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||
Ok(false)
|
||||
}
|
||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||
Ok(())
|
||||
}
|
||||
fn get_prerequisites(&self) -> Vec<Resource> {
|
||||
vec![]
|
||||
}
|
||||
fn provides(&self) -> Option<Vec<Resource>> {
|
||||
None
|
||||
}
|
||||
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
|
||||
Box::new(SymbolAction::new(runner, self))
|
||||
}
|
||||
|
||||
fn into_action<'b>(self: Box<Self>, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b>
|
||||
where
|
||||
Self: 'b,
|
||||
{
|
||||
Box::new(OwnedSymbolAction::new(runner, *self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for AcmeUser<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "AcmeUser {}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
pub fn new<'a, R: CommandRunner, C: 'a + AsRef<str>, U: 'a + AsRef<str>, H: AsRef<Path>>(
|
||||
command_runner: &'a R,
|
||||
cert: C,
|
||||
user_name: U,
|
||||
home: H,
|
||||
) -> impl Symbol + 'a {
|
||||
// impl trait
|
||||
let user_name_cow = user_name.into();
|
||||
let account_key_file: PathBuf = ["/home", user_name_cow.borrow(), "account.key"]
|
||||
.iter()
|
||||
.collect();
|
||||
let path = |rel: &str| [home.as_ref(), rel.as_ref()].iter().collect::<PathBuf>();
|
||||
let account_key_file = path("account.key");
|
||||
List::from((
|
||||
AcmeAccountKey::new(account_key_file.clone().into(), command_runner),
|
||||
AcmeAccountKey::new(account_key_file.clone(), command_runner),
|
||||
// FIXME into or cow or clone
|
||||
Owner::new(
|
||||
account_key_file.to_string_lossy().into_owned(),
|
||||
user_name_cow.clone(),
|
||||
account_key_file,
|
||||
user_name.as_ref().to_owned(),
|
||||
command_runner,
|
||||
),
|
||||
Dir::new("/home/acme/challenges"),
|
||||
Dir::new(path("challenges")),
|
||||
Owner::new(
|
||||
"/home/acme/challenges",
|
||||
user_name_cow.clone(),
|
||||
path("challenges"),
|
||||
user_name.as_ref().to_owned(),
|
||||
command_runner,
|
||||
),
|
||||
Dir::new("/etc/ssl/local_certs"),
|
||||
Owner::new("/etc/ssl/local_certs", user_name_cow, command_runner),
|
||||
File::new("/home/acme/lets_encrypt_x3_cross_signed.pem", cert),
|
||||
Owner::new("/etc/ssl/local_certs", user_name, command_runner),
|
||||
File::new(path("lets_encrypt_x3_cross_signed.pem"), cert),
|
||||
))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,51 +1,39 @@
|
|||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::ops::Deref;
|
||||
|
||||
use command_runner::CommandRunner;
|
||||
use resources::Resource;
|
||||
|
||||
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||
|
||||
pub struct Cron<'r, C, U, R: 'r + CommandRunner>
|
||||
where
|
||||
C: Deref<Target = str>,
|
||||
U: Deref<Target = str>,
|
||||
{
|
||||
pub struct Cron<'r, C: AsRef<str>, U: AsRef<str>, R: CommandRunner> {
|
||||
user: U,
|
||||
content: C,
|
||||
command_runner: &'r R,
|
||||
}
|
||||
|
||||
impl<'r, U, R: 'r + CommandRunner> Cron<'r, String, U, R>
|
||||
where
|
||||
U: Deref<Target = str>,
|
||||
{
|
||||
pub fn new<C: Deref<Target = str>>(user: U, content: C, command_runner: &'r R) -> Self {
|
||||
impl<'r, U: AsRef<str>, R: CommandRunner> Cron<'r, String, U, R> {
|
||||
pub fn new<C: AsRef<str>>(user: U, content: C, command_runner: &'r R) -> Self {
|
||||
Cron {
|
||||
user,
|
||||
content: String::from(&*content) + "\n",
|
||||
content: String::from(content.as_ref()) + "\n",
|
||||
command_runner,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r, C, U, R: 'r + CommandRunner> Symbol for Cron<'r, C, U, R>
|
||||
where
|
||||
C: Deref<Target = str>,
|
||||
U: Deref<Target = str>,
|
||||
{
|
||||
impl<'r, C: AsRef<str>, U: AsRef<str>, R: CommandRunner> Symbol for Cron<'r, C, U, R> {
|
||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||
let tab = self
|
||||
.command_runner
|
||||
.get_output("crontab", &["-l", "-u", &self.user])?;
|
||||
Ok(tab == self.content.bytes().collect::<Vec<u8>>())
|
||||
.get_output("crontab", args!["-l", "-u", self.user.as_ref(),])?;
|
||||
Ok(tab == self.content.as_ref().bytes().collect::<Vec<u8>>())
|
||||
}
|
||||
|
||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||
let output = self.command_runner.run_with_args_and_stdin(
|
||||
"crontab",
|
||||
&["-u", &self.user, "-"],
|
||||
&self.content,
|
||||
args!["-u", self.user.as_ref(), "-",],
|
||||
self.content.as_ref(),
|
||||
)?;
|
||||
if !output.status.success() {
|
||||
return Err(String::from_utf8(output.stderr)?.into());
|
||||
|
|
@ -53,10 +41,6 @@ where
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn get_prerequisites(&self) -> Vec<Resource> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn as_action<'a>(&'a self, runner: &'a dyn SymbolRunner) -> Box<dyn Action + 'a> {
|
||||
Box::new(SymbolAction::new(runner, self))
|
||||
}
|
||||
|
|
@ -69,12 +53,8 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'r, C, U, R: 'r + CommandRunner> fmt::Display for Cron<'r, C, U, R>
|
||||
where
|
||||
C: Deref<Target = str>,
|
||||
U: Deref<Target = str>,
|
||||
{
|
||||
impl<'r, C: AsRef<str>, U: AsRef<str>, R: CommandRunner> fmt::Display for Cron<'r, C, U, R> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(f, "Cron {}", &*self.user)
|
||||
write!(f, "Cron {}", self.user.as_ref())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,44 +7,29 @@ use std::path::Path;
|
|||
use resources::Resource;
|
||||
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||
|
||||
pub struct Dir<D>
|
||||
where
|
||||
D: AsRef<str>,
|
||||
{
|
||||
pub struct Dir<D: AsRef<Path>> {
|
||||
path: D,
|
||||
}
|
||||
|
||||
impl<D> Dir<D>
|
||||
where
|
||||
D: AsRef<str>,
|
||||
{
|
||||
impl<D: AsRef<Path>> Dir<D> {
|
||||
pub fn new(path: D) -> Self {
|
||||
Dir { path }
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> Symbol for Dir<D>
|
||||
where
|
||||
D: AsRef<str>,
|
||||
{
|
||||
impl<D: AsRef<Path>> Symbol for Dir<D> {
|
||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||
let metadata = fs::metadata(self.path.as_ref());
|
||||
// Check if dir exists
|
||||
if let Err(e) = metadata {
|
||||
return if e.kind() == io::ErrorKind::NotFound {
|
||||
Ok(false)
|
||||
} else {
|
||||
Err(Box::new(e))
|
||||
};
|
||||
if !self.path.as_ref().exists() {
|
||||
return Ok(false);
|
||||
}
|
||||
if metadata.unwrap().is_dir() {
|
||||
Ok(true)
|
||||
} else {
|
||||
Err(Box::new(io::Error::new(
|
||||
let metadata = fs::metadata(self.path.as_ref())?;
|
||||
if !metadata.is_dir() {
|
||||
return Err(Box::new(io::Error::new(
|
||||
io::ErrorKind::AlreadyExists,
|
||||
"Could not create a directory, non-directory file exists",
|
||||
)))
|
||||
)));
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||
|
|
@ -52,15 +37,18 @@ where
|
|||
}
|
||||
|
||||
fn get_prerequisites(&self) -> Vec<Resource> {
|
||||
if let Some(parent) = Path::new(self.path.as_ref()).parent() {
|
||||
vec![Resource::new("dir", parent.to_string_lossy())]
|
||||
if let Some(parent) = self.path.as_ref().parent() {
|
||||
vec![Resource::new("dir", parent.to_str().unwrap())]
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
fn provides(&self) -> Option<Vec<Resource>> {
|
||||
Some(vec![Resource::new("dir", self.path.as_ref())])
|
||||
Some(vec![Resource::new(
|
||||
"dir",
|
||||
self.path.as_ref().to_str().unwrap(),
|
||||
)])
|
||||
}
|
||||
|
||||
fn as_action<'a>(&'a self, runner: &'a dyn SymbolRunner) -> Box<dyn Action + 'a> {
|
||||
|
|
@ -75,11 +63,8 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<D> fmt::Display for Dir<D>
|
||||
where
|
||||
D: AsRef<str>,
|
||||
{
|
||||
impl<D: AsRef<Path>> fmt::Display for Dir<D> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(f, "Dir {}", self.path.as_ref())
|
||||
write!(f, "Dir {}", self.path.as_ref().display())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,22 @@
|
|||
use std::borrow::Cow;
|
||||
use std::ops::Deref;
|
||||
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use command_runner::{CommandRunner, SetuidCommandRunner};
|
||||
use storage::{SimpleStorage, Storage};
|
||||
use symbols::acme::{AcmeCert, AcmeCertChain};
|
||||
use symbols::acme::{newAcmeUser, AcmeCert, AcmeCertChain};
|
||||
use symbols::cron::Cron;
|
||||
use symbols::file::File;
|
||||
use symbols::git::checkout::GitCheckout;
|
||||
use symbols::hook::Hook;
|
||||
use symbols::list::List;
|
||||
use symbols::mariadb::{DatabaseDump, MariaDBDatabase, MariaDBUser};
|
||||
use symbols::nginx::server::{php_server_config_snippet, server_config, NginxServer};
|
||||
use symbols::nginx::server::{server_config, NginxServer};
|
||||
use symbols::owner::Owner;
|
||||
use symbols::stored_directory::{StorageDirection, StoredDirectory};
|
||||
use symbols::systemd::reload::ReloadService;
|
||||
use symbols::systemd::user_service::UserService;
|
||||
use symbols::tls::SelfSignedTlsCert;
|
||||
use symbols::{Symbol, SymbolRunner};
|
||||
|
||||
|
|
@ -22,8 +24,11 @@ pub trait Policy {
|
|||
fn user_name_for_host(&self, host_name: &'static str) -> String {
|
||||
host_name.split('.').rev().fold(String::new(), |result, part| if result.is_empty() { result } else { result + "_" } + part)
|
||||
}
|
||||
fn home_for_user(&self, user_name: &str) -> String {
|
||||
format!("/home/{}", user_name)
|
||||
fn home_for_user(&self, user_name: &str) -> PathBuf {
|
||||
format!("/home/{}", user_name).into()
|
||||
}
|
||||
fn socket_path(&self, user_name: &str, service_name: &str) -> PathBuf {
|
||||
format!("/var/tmp/{}-{}.socket", user_name, service_name).into()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -42,7 +47,7 @@ impl<'b, C: 'b + CommandRunner, R: 'b + SymbolRunner, P: 'b + Policy> SymbolFact
|
|||
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);
|
||||
let acme_command_runner = SetuidCommandRunner::new(acme_user.into(), command_runner);
|
||||
SymbolFactory {
|
||||
command_runner,
|
||||
acme_command_runner,
|
||||
|
|
@ -57,14 +62,14 @@ impl<'b, C: 'b + CommandRunner, R: 'b + SymbolRunner, P: 'b + Policy> SymbolFact
|
|||
nginx_server_symbol: S,
|
||||
) -> impl Symbol + 'a {
|
||||
List::from((
|
||||
SelfSignedTlsCert::new(host.into(), self.command_runner),
|
||||
SelfSignedTlsCert::new(host, self.command_runner),
|
||||
Hook::new(
|
||||
nginx_server_symbol,
|
||||
ReloadService::new("nginx", self.command_runner),
|
||||
),
|
||||
AcmeCert::new(host.into(), &self.acme_command_runner),
|
||||
AcmeCert::new(host, &self.acme_command_runner),
|
||||
Hook::new(
|
||||
AcmeCertChain::new(host.into(), &self.acme_command_runner),
|
||||
AcmeCertChain::new(host, &self.acme_command_runner),
|
||||
ReloadService::new("nginx", self.command_runner),
|
||||
),
|
||||
))
|
||||
|
|
@ -79,8 +84,8 @@ impl<'b, C: 'b + CommandRunner, R: 'b + SymbolRunner, P: 'b + Policy> SymbolFact
|
|||
)
|
||||
}
|
||||
|
||||
fn get_php_fpm_pool_socket_path<'a>(&'a self, user_name: &str) -> String {
|
||||
format!("/run/php/{}.sock", user_name)
|
||||
fn get_php_fpm_pool_socket_path<'a>(&'a self, user_name: &str) -> PathBuf {
|
||||
format!("/run/php/{}.sock", user_name).into()
|
||||
}
|
||||
|
||||
fn get_php_fpm_pool<'a>(&'a self, user_name: &str) -> impl Symbol + 'a {
|
||||
|
|
@ -100,17 +105,18 @@ pm.max_children = 10
|
|||
catch_workers_output = yes
|
||||
env[PATH] = /usr/local/bin:/usr/bin:/bin
|
||||
",
|
||||
user_name, socket
|
||||
user_name,
|
||||
socket.to_str().unwrap()
|
||||
),
|
||||
),
|
||||
ReloadService::new("php7.0-fpm", self.command_runner),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn serve_php<'a>(
|
||||
pub fn serve_php<'a, ROOT: AsRef<Path>>(
|
||||
&'a self,
|
||||
host_name: &'static str,
|
||||
root_dir: Cow<'a, str>,
|
||||
root_dir: ROOT,
|
||||
additional_config: &'a str,
|
||||
) -> impl Symbol + 'a {
|
||||
let user_name = self.policy.user_name_for_host(host_name);
|
||||
|
|
@ -121,7 +127,7 @@ env[PATH] = /usr/local/bin:/usr/bin:/bin
|
|||
host_name,
|
||||
NginxServer::new_php(
|
||||
host_name,
|
||||
socket.into(),
|
||||
socket,
|
||||
root_dir,
|
||||
self.command_runner,
|
||||
additional_config,
|
||||
|
|
@ -130,10 +136,10 @@ env[PATH] = /usr/local/bin:/usr/bin:/bin
|
|||
))
|
||||
}
|
||||
|
||||
pub fn serve_wordpress<'a>(
|
||||
pub fn serve_wordpress<'a, ROOT: 'a + AsRef<Path>>(
|
||||
&'a self,
|
||||
host_name: &'static str,
|
||||
root_dir: Cow<'a, str>,
|
||||
root_dir: ROOT,
|
||||
) -> impl Symbol + 'a {
|
||||
self.serve_php(
|
||||
host_name,
|
||||
|
|
@ -179,16 +185,16 @@ env[PATH] = /usr/local/bin:/usr/bin:/bin
|
|||
}}
|
||||
",
|
||||
root_dir,
|
||||
socket)),
|
||||
socket.to_str().unwrap())),
|
||||
self.command_runner
|
||||
))
|
||||
))
|
||||
}
|
||||
|
||||
pub fn serve_nextcloud<'a>(
|
||||
pub fn serve_nextcloud<'a, ROOT: 'a + AsRef<Path>>(
|
||||
&'a self,
|
||||
host_name: &'static str,
|
||||
root_dir: Cow<'a, str>,
|
||||
root_dir: ROOT,
|
||||
) -> impl Symbol + 'a {
|
||||
self.serve_php(
|
||||
host_name,
|
||||
|
|
@ -272,13 +278,13 @@ env[PATH] = /usr/local/bin:/usr/bin:/bin
|
|||
let string_target = target.into();
|
||||
(
|
||||
StoredDirectory::new(
|
||||
string_target.clone().into(),
|
||||
string_target.clone(),
|
||||
data.clone(),
|
||||
StorageDirection::Save,
|
||||
self.command_runner,
|
||||
),
|
||||
StoredDirectory::new(
|
||||
string_target.into(),
|
||||
string_target,
|
||||
data.clone(),
|
||||
StorageDirection::Load,
|
||||
self.command_runner,
|
||||
|
|
@ -290,11 +296,8 @@ env[PATH] = /usr/local/bin:/usr/bin:/bin
|
|||
let db_dump = SimpleStorage::new("/root/data".to_string(), format!("{}.sql", name));
|
||||
List::from((
|
||||
MariaDBDatabase::new(
|
||||
name.into(),
|
||||
db_dump
|
||||
.read_filename()
|
||||
.expect("Initial db dump missing")
|
||||
.into(),
|
||||
name,
|
||||
db_dump.read_filename().expect("Initial db dump missing"),
|
||||
self.command_runner,
|
||||
),
|
||||
DatabaseDump::new(name, db_dump, self.command_runner),
|
||||
|
|
@ -302,10 +305,10 @@ env[PATH] = /usr/local/bin:/usr/bin:/bin
|
|||
}
|
||||
|
||||
pub fn get_mariadb_user<'a>(&'a self, user_name: &'static str) -> impl Symbol + 'a {
|
||||
MariaDBUser::new(user_name.into(), self.command_runner)
|
||||
MariaDBUser::new(user_name, self.command_runner)
|
||||
}
|
||||
|
||||
pub fn get_git_checkout<'a, T: 'a + AsRef<str>>(
|
||||
pub fn get_git_checkout<'a, T: 'a + AsRef<Path>>(
|
||||
&'a self,
|
||||
target: T,
|
||||
source: &'a str,
|
||||
|
|
@ -314,11 +317,15 @@ env[PATH] = /usr/local/bin:/usr/bin:/bin
|
|||
GitCheckout::new(target, source, branch, self.command_runner)
|
||||
}
|
||||
|
||||
pub fn get_owner<'a, F: 'a + AsRef<str>>(&'a self, file: F, user: &'a str) -> impl Symbol + 'a {
|
||||
Owner::new(file, user.into(), self.command_runner)
|
||||
pub fn get_owner<'a, U: 'a + AsRef<str>, F: 'a + AsRef<Path>>(
|
||||
&'a self,
|
||||
file: F,
|
||||
user: U,
|
||||
) -> impl Symbol + 'a {
|
||||
Owner::new(file, user, self.command_runner)
|
||||
}
|
||||
|
||||
pub fn get_file<'a, F: 'a + Deref<Target = str>, Q: 'a + AsRef<Path>>(
|
||||
pub fn get_file<'a, F: 'a + AsRef<str>, Q: 'a + AsRef<Path>>(
|
||||
&'a self,
|
||||
path: Q,
|
||||
content: F,
|
||||
|
|
@ -326,11 +333,94 @@ env[PATH] = /usr/local/bin:/usr/bin:/bin
|
|||
File::new(path, content)
|
||||
}
|
||||
|
||||
pub fn get_cron<'a, T: 'a + Deref<Target = str>, U: 'a + Deref<Target = str>>(
|
||||
pub fn get_cron<'a, T: 'a + AsRef<str>, U: 'a + AsRef<str>>(
|
||||
&'a self,
|
||||
user: U,
|
||||
content: T,
|
||||
) -> impl Symbol + 'a {
|
||||
Cron::new(user, content, self.command_runner)
|
||||
}
|
||||
|
||||
pub fn get_acme_user<'a, S: 'a + AsRef<str>, D: 'a + AsRef<str>>(
|
||||
&'a self,
|
||||
cert: D,
|
||||
user_name: S,
|
||||
) -> impl Symbol + 'a {
|
||||
let home = self.policy.home_for_user(user_name.as_ref());
|
||||
newAcmeUser(self.command_runner, cert, user_name, home)
|
||||
}
|
||||
|
||||
pub fn get_systemd_user_service<'a>(
|
||||
&'a self,
|
||||
user_name: Cow<'a, str>,
|
||||
service_name: &'a str,
|
||||
config: Cow<'a, str>,
|
||||
) -> impl Symbol + 'a {
|
||||
let socket_path = self.policy.socket_path(&user_name, service_name);
|
||||
let home = self.policy.home_for_user(&user_name);
|
||||
UserService::new(
|
||||
socket_path.into(),
|
||||
home.into(),
|
||||
user_name,
|
||||
service_name,
|
||||
self.command_runner,
|
||||
config.into(),
|
||||
)
|
||||
}
|
||||
|
||||
fn get_nodejs_systemd_user_service<'a, T: Into<Cow<'a, Path>>>(
|
||||
&'a self,
|
||||
user_name: Cow<'a, str>,
|
||||
service_name: &'a str,
|
||||
path: T,
|
||||
) -> impl Symbol + 'a {
|
||||
let socket_path = self.policy.socket_path(&user_name, service_name);
|
||||
let home = self.policy.home_for_user(&user_name);
|
||||
UserService::new_nodejs(
|
||||
home.into(),
|
||||
user_name,
|
||||
service_name,
|
||||
path.into(),
|
||||
self.command_runner,
|
||||
socket_path.into(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn proxy_socket<'a, T: Into<Cow<'a, Path>>>(
|
||||
&'a self,
|
||||
host_name: &'static str,
|
||||
service_name: &'a str,
|
||||
root_dir: T,
|
||||
) -> impl Symbol + 'a {
|
||||
let user_name = self.policy.user_name_for_host(host_name);
|
||||
let socket_path = self.policy.socket_path(&user_name, service_name);
|
||||
self.get_nginx_acme_server(
|
||||
host_name,
|
||||
NginxServer::new_proxy(host_name, socket_path, root_dir.into(), self.command_runner),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn serve_nodejs<
|
||||
'a,
|
||||
'c: 'a,
|
||||
S: 'a + Symbol,
|
||||
T: 'a + Into<Cow<'a, Path>>,
|
||||
SP: 'a + Into<Cow<'a, Path>>,
|
||||
>(
|
||||
&'c self,
|
||||
host: &'static str,
|
||||
service_name: &'a str,
|
||||
path: T,
|
||||
static_path: SP,
|
||||
nodejs_symbol: S,
|
||||
) -> impl Symbol + 'a {
|
||||
let user_name = self.policy.user_name_for_host(host);
|
||||
List::from((
|
||||
Hook::new(
|
||||
nodejs_symbol,
|
||||
self.get_nodejs_systemd_user_service(user_name.into(), service_name, path),
|
||||
),
|
||||
self.proxy_socket(host, service_name, static_path),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,51 +1,34 @@
|
|||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::fs::File as FsFile;
|
||||
use std::io;
|
||||
|
||||
use std::io::{Read, Write};
|
||||
use std::ops::Deref;
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
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: AsRef<str>, D: AsRef<Path>> {
|
||||
path: D,
|
||||
content: C,
|
||||
}
|
||||
|
||||
impl<C, D> File<C, D>
|
||||
where
|
||||
C: Deref<Target = str>,
|
||||
D: AsRef<Path>,
|
||||
{
|
||||
impl<C: AsRef<str>, D: AsRef<Path>> File<C, D> {
|
||||
pub fn new(path: D, content: C) -> Self {
|
||||
File { path, content }
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, D> Symbol for File<C, D>
|
||||
where
|
||||
C: Deref<Target = str>,
|
||||
D: AsRef<Path>,
|
||||
{
|
||||
impl<C: AsRef<str>, D: AsRef<Path>> Symbol for File<C, D> {
|
||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||
let file = FsFile::open(self.path.as_ref());
|
||||
// Check if file exists
|
||||
if let Err(e) = file {
|
||||
return if e.kind() == io::ErrorKind::NotFound {
|
||||
Ok(false)
|
||||
} else {
|
||||
Err(Box::new(e))
|
||||
};
|
||||
if !self.path.as_ref().exists() {
|
||||
return Ok(false);
|
||||
}
|
||||
let file = FsFile::open(self.path.as_ref())?;
|
||||
// Check if content is the same
|
||||
let mut file_content = file.unwrap().bytes();
|
||||
let mut target_content = self.content.bytes();
|
||||
let mut file_content = file.bytes();
|
||||
let mut target_content = self.content.as_ref().bytes();
|
||||
loop {
|
||||
match (file_content.next(), target_content.next()) {
|
||||
(None, None) => return Ok(true),
|
||||
|
|
@ -58,18 +41,16 @@ where
|
|||
|
||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||
let mut file = FsFile::create(self.path.as_ref())?;
|
||||
file.write_all(self.content.as_bytes())?;
|
||||
file.write_all(self.content.as_ref().as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_prerequisites(&self) -> Vec<Resource> {
|
||||
vec![Resource::new(
|
||||
"dir",
|
||||
Path::new(self.path.as_ref())
|
||||
.parent()
|
||||
.unwrap()
|
||||
.to_string_lossy(),
|
||||
)]
|
||||
if let Some(parent) = self.path.as_ref().parent() {
|
||||
vec![Resource::new("dir", parent.to_str().unwrap())]
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
fn as_action<'a>(&'a self, runner: &'a dyn SymbolRunner) -> Box<dyn Action + 'a> {
|
||||
|
|
@ -84,11 +65,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<C, D> fmt::Display for File<C, D>
|
||||
where
|
||||
C: Deref<Target = str>,
|
||||
D: AsRef<Path>,
|
||||
{
|
||||
impl<C: AsRef<str>, D: AsRef<Path>> fmt::Display for File<C, D> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(f, "File {}", self.path.as_ref().display())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
use std::error::Error;
|
||||
use std::ffi::OsStr;
|
||||
use std::fmt;
|
||||
use std::fs::metadata;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
|
|
@ -7,14 +9,14 @@ use command_runner::CommandRunner;
|
|||
use resources::Resource;
|
||||
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||
|
||||
pub struct GitCheckout<'a, C: 'a + CommandRunner, T: AsRef<str>> {
|
||||
pub struct GitCheckout<'a, C: CommandRunner, T: AsRef<Path>> {
|
||||
target: T,
|
||||
source: &'a str,
|
||||
branch: &'a str,
|
||||
command_runner: &'a C,
|
||||
}
|
||||
|
||||
impl<'a, C: CommandRunner, T: AsRef<str>> GitCheckout<'a, C, T> {
|
||||
impl<'a, C: CommandRunner, T: AsRef<Path>> GitCheckout<'a, C, T> {
|
||||
pub fn new(target: T, source: &'a str, branch: &'a str, command_runner: &'a C) -> Self {
|
||||
GitCheckout {
|
||||
target,
|
||||
|
|
@ -25,29 +27,27 @@ impl<'a, C: CommandRunner, T: AsRef<str>> GitCheckout<'a, C, T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, C: CommandRunner, T: AsRef<str>> fmt::Display for GitCheckout<'a, C, T> {
|
||||
impl<'a, C: CommandRunner, T: AsRef<Path>> fmt::Display for GitCheckout<'a, C, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Checkout {} (branch {}) into {}",
|
||||
self.source,
|
||||
self.branch,
|
||||
self.target.as_ref()
|
||||
self.target.as_ref().display()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
use std::fs::metadata;
|
||||
|
||||
impl<'a, C: CommandRunner, T: AsRef<str>> GitCheckout<'a, C, T> {
|
||||
fn _run_in_target_repo(&self, args: &[&str]) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||
let mut new_args = vec!["-C", self.target.as_ref()];
|
||||
new_args.extend_from_slice(args);
|
||||
impl<'a, C: CommandRunner, T: AsRef<Path>> GitCheckout<'a, C, T> {
|
||||
fn _run_in_target_repo<S: AsRef<OsStr>>(&self, args: &[S]) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||
let mut new_args = vec![OsStr::new("-C"), self.target.as_ref().as_ref()];
|
||||
new_args.extend(args.iter().map(AsRef::as_ref));
|
||||
self.command_runner.get_output("git", &new_args)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: CommandRunner, T: AsRef<str>> Symbol for GitCheckout<'a, C, T> {
|
||||
impl<'a, C: CommandRunner, T: AsRef<Path>> Symbol for GitCheckout<'a, C, T> {
|
||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||
if let Err(e) = metadata(self.target.as_ref()) {
|
||||
return if e.kind() == io::ErrorKind::NotFound {
|
||||
|
|
@ -64,17 +64,17 @@ impl<'a, C: CommandRunner, T: AsRef<str>> Symbol for GitCheckout<'a, C, T> {
|
|||
}
|
||||
|
||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||
if !Path::new(self.target.as_ref()).exists() {
|
||||
if !self.target.as_ref().exists() {
|
||||
return self.command_runner.run_successfully(
|
||||
"git",
|
||||
&[
|
||||
"clone",
|
||||
"--depth",
|
||||
"1",
|
||||
"-b",
|
||||
self.branch,
|
||||
self.source,
|
||||
self.target.as_ref(),
|
||||
OsStr::new("clone"),
|
||||
"--depth".as_ref(),
|
||||
"1".as_ref(),
|
||||
"-b".as_ref(),
|
||||
self.branch.as_ref(),
|
||||
self.source.as_ref(),
|
||||
self.target.as_ref().as_ref(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
@ -86,15 +86,15 @@ impl<'a, C: CommandRunner, T: AsRef<str>> Symbol for GitCheckout<'a, C, T> {
|
|||
fn get_prerequisites(&self) -> Vec<Resource> {
|
||||
vec![Resource::new(
|
||||
"dir",
|
||||
Path::new(self.target.as_ref())
|
||||
.parent()
|
||||
.unwrap()
|
||||
.to_string_lossy(),
|
||||
self.target.as_ref().parent().unwrap().to_string_lossy(),
|
||||
)]
|
||||
}
|
||||
|
||||
fn provides(&self) -> Option<Vec<Resource>> {
|
||||
Some(vec![Resource::new("dir", self.target.as_ref().to_string())])
|
||||
Some(vec![Resource::new(
|
||||
"dir",
|
||||
self.target.as_ref().to_str().unwrap(),
|
||||
)])
|
||||
}
|
||||
|
||||
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
|
||||
|
|
|
|||
|
|
@ -1,17 +1,18 @@
|
|||
use std::error::Error;
|
||||
use std::ffi::OsStr;
|
||||
use std::fmt;
|
||||
use std::path::Path;
|
||||
|
||||
use command_runner::CommandRunner;
|
||||
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||
|
||||
pub struct GitSubmodules<'a, C: 'a + CommandRunner> {
|
||||
target: &'a str,
|
||||
pub struct GitSubmodules<'a, P: AsRef<Path>, C: CommandRunner> {
|
||||
target: P,
|
||||
command_runner: &'a C,
|
||||
}
|
||||
|
||||
impl<'a, C: CommandRunner> GitSubmodules<'a, C> {
|
||||
pub fn new(target: &'a str, command_runner: &'a C) -> Self {
|
||||
impl<'a, P: AsRef<Path>, C: CommandRunner> GitSubmodules<'a, P, C> {
|
||||
pub fn new(target: P, command_runner: &'a C) -> Self {
|
||||
GitSubmodules {
|
||||
target,
|
||||
command_runner,
|
||||
|
|
@ -19,26 +20,27 @@ impl<'a, C: CommandRunner> GitSubmodules<'a, C> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, C: CommandRunner> fmt::Display for GitSubmodules<'a, C> {
|
||||
impl<'a, P: AsRef<Path>, C: CommandRunner> fmt::Display for GitSubmodules<'a, P, C> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Submodules for {}", self.target)
|
||||
write!(f, "Submodules for {}", self.target.as_ref().display())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: CommandRunner> GitSubmodules<'a, C> {
|
||||
fn _run_in_target_repo(&self, args: &[&str]) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||
let mut new_args = vec!["-C", self.target];
|
||||
impl<'a, P: AsRef<Path>, C: CommandRunner> GitSubmodules<'a, P, C> {
|
||||
fn _run_in_target_repo(&self, args: &[&OsStr]) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||
let mut new_args: Vec<&OsStr> = vec![];
|
||||
new_args.extend_from_slice(args!["-C", self.target.as_ref()]);
|
||||
new_args.extend_from_slice(args);
|
||||
self.command_runner.get_output("git", &new_args)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: CommandRunner> Symbol for GitSubmodules<'a, C> {
|
||||
impl<'a, P: AsRef<Path>, C: CommandRunner> Symbol for GitSubmodules<'a, P, C> {
|
||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||
if !Path::new(self.target).exists() {
|
||||
if !self.target.as_ref().exists() {
|
||||
return Ok(false);
|
||||
}
|
||||
let output = String::from_utf8(self._run_in_target_repo(&["submodule", "status"])?)?;
|
||||
let output = String::from_utf8(self._run_in_target_repo(args!["submodule", "status"])?)?;
|
||||
Ok(
|
||||
output
|
||||
.lines()
|
||||
|
|
@ -47,7 +49,7 @@ impl<'a, C: CommandRunner> Symbol for GitSubmodules<'a, C> {
|
|||
}
|
||||
|
||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||
self._run_in_target_repo(&["submodule", "update", "--init"])?;
|
||||
self._run_in_target_repo(args!["submodule", "update", "--init"])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
use std::borrow::Cow;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::path::Path;
|
||||
|
||||
use command_runner::CommandRunner;
|
||||
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||
|
||||
pub struct MariaDBDatabase<'a, C: 'a + CommandRunner> {
|
||||
db_name: Cow<'a, str>,
|
||||
seed_file: Cow<'a, str>,
|
||||
pub struct MariaDBDatabase<'a, D: AsRef<str>, S: AsRef<Path>, C: CommandRunner> {
|
||||
db_name: D,
|
||||
seed_file: S,
|
||||
command_runner: &'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 {
|
||||
impl<'a, D: AsRef<str>, S: AsRef<Path>, C: CommandRunner> MariaDBDatabase<'a, D, S, C> {
|
||||
pub fn new(db_name: D, seed_file: S, command_runner: &'a C) -> Self {
|
||||
MariaDBDatabase {
|
||||
db_name,
|
||||
seed_file,
|
||||
|
|
@ -23,34 +23,40 @@ impl<'a, C: CommandRunner> MariaDBDatabase<'a, C> {
|
|||
fn run_sql(&self, sql: &str) -> Result<String, Box<dyn Error>> {
|
||||
let b = self
|
||||
.command_runner
|
||||
.get_output("mariadb", &["--skip-column-names", "-B", "-e", sql])?;
|
||||
.get_output("mariadb", args!["--skip-column-names", "-B", "-e", sql])?;
|
||||
Ok(String::from_utf8(b)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: CommandRunner> fmt::Display for MariaDBDatabase<'a, C> {
|
||||
impl<'a, D: AsRef<str>, S: AsRef<Path>, C: CommandRunner> fmt::Display
|
||||
for MariaDBDatabase<'a, D, S, C>
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "MariaDB Database {}", self.db_name)
|
||||
write!(f, "MariaDB Database {}", self.db_name.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: CommandRunner> Symbol for MariaDBDatabase<'a, C> {
|
||||
impl<'a, D: AsRef<str>, S: AsRef<Path>, C: CommandRunner> Symbol for MariaDBDatabase<'a, D, S, C> {
|
||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||
Ok(
|
||||
self
|
||||
.run_sql(&format!("SHOW DATABASES LIKE '{}'", self.db_name))?
|
||||
.run_sql(&format!("SHOW DATABASES LIKE '{}'", self.db_name.as_ref()))?
|
||||
.trim_end()
|
||||
== self.db_name,
|
||||
== self.db_name.as_ref(),
|
||||
)
|
||||
}
|
||||
|
||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||
self.run_sql(&format!("CREATE DATABASE {}", self.db_name))?;
|
||||
self.run_sql(&format!("CREATE DATABASE {}", self.db_name.as_ref()))?;
|
||||
self.command_runner.run_successfully(
|
||||
"sh",
|
||||
&[
|
||||
args![
|
||||
"-c",
|
||||
&format!("mariadb '{}' < {}", self.db_name, self.seed_file),
|
||||
format!(
|
||||
"mariadb '{}' < {}",
|
||||
self.db_name.as_ref(),
|
||||
self.seed_file.as_ref().to_str().unwrap()
|
||||
),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,12 +6,7 @@ use command_runner::CommandRunner;
|
|||
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: AsRef<str>, C: CommandRunner, S: Storage> {
|
||||
db_name: N,
|
||||
storage: S,
|
||||
command_runner: &'a C,
|
||||
|
|
@ -29,7 +24,7 @@ impl<'a, N: AsRef<str>, C: CommandRunner, S: Storage> DatabaseDump<'a, N, C, S>
|
|||
fn run_sql(&self, sql: &str) -> Result<String, Box<dyn Error>> {
|
||||
let b = self
|
||||
.command_runner
|
||||
.get_output("mariadb", &["--skip-column-names", "-B", "-e", sql])?;
|
||||
.get_output("mariadb", args!["--skip-column-names", "-B", "-e", sql])?;
|
||||
Ok(String::from_utf8(b)?)
|
||||
}
|
||||
}
|
||||
|
|
@ -43,7 +38,7 @@ impl<'a, N: AsRef<str>, C: CommandRunner, S: Storage> fmt::Display for DatabaseD
|
|||
impl<'a, N: AsRef<str>, C: CommandRunner, S: Storage> Symbol for DatabaseDump<'a, N, C, S> {
|
||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||
let dump_date = 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 = 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);
|
||||
}
|
||||
|
|
@ -53,9 +48,9 @@ impl<'a, N: AsRef<str>, C: CommandRunner, S: Storage> Symbol for DatabaseDump<'a
|
|||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||
self.command_runner.run_successfully(
|
||||
"sh",
|
||||
&[
|
||||
args![
|
||||
"-c",
|
||||
&format!(
|
||||
format!(
|
||||
"mysqldump '{}' > {}",
|
||||
self.db_name.as_ref(),
|
||||
self.storage.write_filename()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
use std::borrow::Cow;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
|
||||
|
|
@ -6,13 +5,13 @@ use command_runner::CommandRunner;
|
|||
use resources::Resource;
|
||||
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||
|
||||
pub struct MariaDBUser<'a, C: 'a + CommandRunner> {
|
||||
user_name: Cow<'a, str>,
|
||||
pub struct MariaDBUser<'a, U: AsRef<str>, C: CommandRunner> {
|
||||
user_name: U,
|
||||
command_runner: &'a C,
|
||||
}
|
||||
|
||||
impl<'a, C: CommandRunner> MariaDBUser<'a, C> {
|
||||
pub fn new(user_name: Cow<'a, str>, command_runner: &'a C) -> Self {
|
||||
impl<'a, U: AsRef<str>, C: CommandRunner> MariaDBUser<'a, U, C> {
|
||||
pub fn new(user_name: U, command_runner: &'a C) -> Self {
|
||||
MariaDBUser {
|
||||
user_name,
|
||||
command_runner,
|
||||
|
|
@ -22,40 +21,40 @@ impl<'a, C: CommandRunner> MariaDBUser<'a, C> {
|
|||
fn run_sql(&self, sql: &str) -> Result<String, Box<dyn Error>> {
|
||||
let b = self
|
||||
.command_runner
|
||||
.get_output("mariadb", &["--skip-column-names", "-B", "-e", sql])?;
|
||||
.get_output("mariadb", args!["--skip-column-names", "-B", "-e", sql])?;
|
||||
Ok(String::from_utf8(b)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: CommandRunner> fmt::Display for MariaDBUser<'a, C> {
|
||||
impl<'a, U: AsRef<str>, C: CommandRunner> fmt::Display for MariaDBUser<'a, U, C> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "MariaDB User {}", self.user_name)
|
||||
write!(f, "MariaDB User {}", self.user_name.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: CommandRunner> Symbol for MariaDBUser<'a, C> {
|
||||
impl<'a, U: AsRef<str>, C: CommandRunner> Symbol for MariaDBUser<'a, U, C> {
|
||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||
Ok(
|
||||
self
|
||||
.run_sql(&format!(
|
||||
"SELECT User FROM mysql.user WHERE User = '{}' AND plugin = 'unix_socket'",
|
||||
self.user_name
|
||||
self.user_name.as_ref()
|
||||
))?
|
||||
.trim_end()
|
||||
== self.user_name,
|
||||
== self.user_name.as_ref(),
|
||||
)
|
||||
}
|
||||
|
||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||
self.run_sql(&format!(
|
||||
"GRANT ALL ON {0}.* TO {0} IDENTIFIED VIA unix_socket",
|
||||
self.user_name
|
||||
self.user_name.as_ref()
|
||||
))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_prerequisites(&self) -> Vec<Resource> {
|
||||
vec![Resource::new("user", self.user_name.to_string())]
|
||||
vec![Resource::new("user", self.user_name.as_ref())]
|
||||
}
|
||||
|
||||
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::ops::Deref;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use command_runner::CommandRunner;
|
||||
use resources::Resource;
|
||||
|
|
@ -41,16 +41,11 @@ 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: CommandRunner, T: AsRef<str>, P: AsRef<Path>> {
|
||||
command_runner: &'a C,
|
||||
file: FileSymbol<T, String>,
|
||||
file: FileSymbol<T, P>,
|
||||
}
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
pub fn server_config(domain: &str, content: &str) -> String {
|
||||
format!(
|
||||
"server {{
|
||||
|
|
@ -80,9 +75,9 @@ server {{
|
|||
)
|
||||
}
|
||||
|
||||
pub fn php_server_config_snippet<'a>(
|
||||
socket_path: Cow<'a, str>,
|
||||
static_path: Cow<'a, str>,
|
||||
pub fn php_server_config_snippet<'a, SOCKET: AsRef<Path>, STATIC: AsRef<Path>>(
|
||||
socket_path: SOCKET,
|
||||
static_path: STATIC,
|
||||
) -> String {
|
||||
format!(
|
||||
"
|
||||
|
|
@ -92,7 +87,8 @@ pub fn php_server_config_snippet<'a>(
|
|||
fastcgi_pass unix:{};
|
||||
include \"snippets/fastcgi-php.conf\";
|
||||
}}",
|
||||
static_path, socket_path
|
||||
static_path.as_ref().to_str().unwrap(),
|
||||
socket_path.as_ref().to_str().unwrap()
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -100,9 +96,12 @@ pub trait SocketSpec {
|
|||
fn to_nginx(&self) -> String;
|
||||
}
|
||||
|
||||
impl SocketSpec for &str {
|
||||
impl<T> SocketSpec for T
|
||||
where
|
||||
T: AsRef<Path>,
|
||||
{
|
||||
fn to_nginx(&self) -> String {
|
||||
format!("unix:{}:", self)
|
||||
format!("unix:{}:", self.as_ref().to_str().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -120,7 +119,7 @@ impl SocketSpec for LocalTcpSocket {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, C: CommandRunner> NginxServer<'a, C, String> {
|
||||
impl<'a, C: CommandRunner> NginxServer<'a, C, String, PathBuf> {
|
||||
pub fn new_redir(domain: &'a str, target: &'a str, command_runner: &'a C) -> Self {
|
||||
let content = server_config(
|
||||
domain,
|
||||
|
|
@ -134,10 +133,10 @@ impl<'a, C: CommandRunner> NginxServer<'a, C, String> {
|
|||
NginxServer::new(domain, content, command_runner)
|
||||
}
|
||||
|
||||
pub fn new_proxy<S: SocketSpec>(
|
||||
pub fn new_proxy<S: SocketSpec, STATIC: AsRef<Path>>(
|
||||
domain: &'a str,
|
||||
socket_path: S,
|
||||
static_path: &'a str,
|
||||
static_path: STATIC,
|
||||
command_runner: &'a C,
|
||||
) -> Self {
|
||||
let proxy_content = format!(
|
||||
|
|
@ -160,16 +159,17 @@ location @proxy {{
|
|||
root {};
|
||||
{}
|
||||
",
|
||||
static_path, proxy_content
|
||||
static_path.as_ref().to_str().unwrap(),
|
||||
proxy_content
|
||||
),
|
||||
);
|
||||
NginxServer::new(domain, content, command_runner)
|
||||
}
|
||||
|
||||
pub fn new_php(
|
||||
pub fn new_php<SOCKET: AsRef<Path>, STATIC: AsRef<Path>>(
|
||||
domain: &'a str,
|
||||
socket_path: Cow<'a, str>,
|
||||
static_path: Cow<'a, str>,
|
||||
socket_path: SOCKET,
|
||||
static_path: STATIC,
|
||||
command_runner: &'a C,
|
||||
additional_config: &'a str,
|
||||
) -> Self {
|
||||
|
|
@ -180,7 +180,11 @@ location @proxy {{
|
|||
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<S: AsRef<Path>>(
|
||||
domain: &'a str,
|
||||
static_path: S,
|
||||
command_runner: &'a C,
|
||||
) -> Self {
|
||||
let content = server_config(
|
||||
domain,
|
||||
&format!(
|
||||
|
|
@ -188,14 +192,14 @@ location @proxy {{
|
|||
root {};
|
||||
try_files $uri $uri/ $uri.html =404;
|
||||
",
|
||||
static_path
|
||||
static_path.as_ref().to_str().unwrap()
|
||||
),
|
||||
);
|
||||
NginxServer::new(domain, content, command_runner)
|
||||
}
|
||||
|
||||
pub fn new(domain: &'a str, content: String, command_runner: &'a C) -> Self {
|
||||
let file_path = String::from("/etc/nginx/sites-enabled/") + domain;
|
||||
let file_path: PathBuf = ["/etc/nginx/sites-enabled/", domain].iter().collect();
|
||||
NginxServer {
|
||||
command_runner,
|
||||
file: FileSymbol::new(file_path, content),
|
||||
|
|
@ -203,10 +207,7 @@ location @proxy {{
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, C: CommandRunner, T> Symbol for NginxServer<'a, C, T>
|
||||
where
|
||||
T: Deref<Target = str>,
|
||||
{
|
||||
impl<'a, C: CommandRunner, T: AsRef<str>, P: AsRef<Path>> Symbol for NginxServer<'a, C, T, P> {
|
||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||
if !self.file.target_reached()? {
|
||||
return Ok(false);
|
||||
|
|
@ -219,7 +220,7 @@ where
|
|||
self.file.execute()?;
|
||||
self
|
||||
.command_runner
|
||||
.run_successfully("systemctl", &["reload-or-restart", "nginx"])
|
||||
.run_successfully("systemctl", args!["reload-or-restart", "nginx"])
|
||||
}
|
||||
|
||||
fn get_prerequisites(&self) -> Vec<Resource> {
|
||||
|
|
@ -238,9 +239,8 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, C: CommandRunner, T> fmt::Display for NginxServer<'a, C, T>
|
||||
where
|
||||
T: Deref<Target = str>,
|
||||
impl<'a, C: CommandRunner, T: AsRef<str>, P: AsRef<Path>> fmt::Display
|
||||
for NginxServer<'a, C, T, P>
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(f, "Nginx server config")
|
||||
|
|
|
|||
|
|
@ -5,13 +5,13 @@ use std::path::Path;
|
|||
use command_runner::CommandRunner;
|
||||
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||
|
||||
pub struct NpmInstall<'a, C: 'a + CommandRunner> {
|
||||
target: &'a str,
|
||||
pub struct NpmInstall<'a, T: AsRef<Path>, C: CommandRunner> {
|
||||
target: T,
|
||||
command_runner: &'a C,
|
||||
}
|
||||
|
||||
impl<'a, C: CommandRunner> NpmInstall<'a, C> {
|
||||
pub fn new(target: &'a str, command_runner: &'a C) -> Self {
|
||||
impl<'a, T: AsRef<Path>, C: CommandRunner> NpmInstall<'a, T, C> {
|
||||
pub fn new(target: T, command_runner: &'a C) -> Self {
|
||||
NpmInstall {
|
||||
target,
|
||||
command_runner,
|
||||
|
|
@ -19,20 +19,28 @@ impl<'a, C: CommandRunner> NpmInstall<'a, C> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, C: CommandRunner> fmt::Display for NpmInstall<'a, C> {
|
||||
impl<'a, T: AsRef<Path>, C: CommandRunner> fmt::Display for NpmInstall<'a, T, C> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "npm install in {}", self.target)
|
||||
write!(
|
||||
f,
|
||||
"npm install in {}",
|
||||
self.target.as_ref().to_str().unwrap()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: CommandRunner> Symbol for NpmInstall<'a, C> {
|
||||
impl<'a, T: AsRef<Path>, C: CommandRunner> Symbol for NpmInstall<'a, T, C> {
|
||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||
if !Path::new(self.target).exists() {
|
||||
if !self.target.as_ref().exists() {
|
||||
return Ok(false);
|
||||
}
|
||||
let result = self
|
||||
.command_runner
|
||||
.run_with_args("sh", &["-c", &format!("cd '{}' && npm ls", self.target)])?;
|
||||
let result = self.command_runner.run_with_args(
|
||||
"sh",
|
||||
args![
|
||||
"-c",
|
||||
format!("cd '{}' && npm ls", self.target.as_ref().to_str().unwrap()),
|
||||
],
|
||||
)?;
|
||||
Ok(
|
||||
result.status.success()
|
||||
&& !String::from_utf8(result.stdout)
|
||||
|
|
@ -44,11 +52,11 @@ impl<'a, C: CommandRunner> Symbol for NpmInstall<'a, C> {
|
|||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||
self.command_runner.run_successfully(
|
||||
"sh",
|
||||
&[
|
||||
args![
|
||||
"-c",
|
||||
&format!(
|
||||
format!(
|
||||
"cd '{}' && npm install --production --unsafe-perm",
|
||||
self.target
|
||||
self.target.as_ref().to_str().unwrap()
|
||||
),
|
||||
],
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use std::borrow::{Borrow, Cow};
|
||||
|
||||
use std::error::Error;
|
||||
|
||||
use std::fmt;
|
||||
use std::fs;
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
|
|
@ -11,20 +12,14 @@ use command_runner::CommandRunner;
|
|||
use resources::Resource;
|
||||
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||
|
||||
pub struct Owner<'a, C: 'a + CommandRunner, D>
|
||||
where
|
||||
D: AsRef<str>,
|
||||
{
|
||||
pub struct Owner<'a, C: CommandRunner, D: AsRef<Path>, U: AsRef<str>> {
|
||||
path: D,
|
||||
user_name: Cow<'a, str>,
|
||||
user_name: U,
|
||||
command_runner: &'a C,
|
||||
}
|
||||
|
||||
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 {
|
||||
impl<'a, C: CommandRunner, D: AsRef<Path>, U: AsRef<str>> Owner<'a, C, D, U> {
|
||||
pub fn new(path: D, user_name: U, command_runner: &'a C) -> Self {
|
||||
Owner {
|
||||
path,
|
||||
user_name,
|
||||
|
|
@ -33,28 +28,25 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, C: CommandRunner, D> Symbol for Owner<'a, C, D>
|
||||
where
|
||||
D: AsRef<str>,
|
||||
{
|
||||
impl<'a, C: CommandRunner, D: AsRef<Path>, U: AsRef<str>> Symbol for Owner<'a, C, D, U> {
|
||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||
if !Path::new(self.path.as_ref()).exists() {
|
||||
return Ok(false);
|
||||
}
|
||||
let actual_uid = fs::metadata(self.path.as_ref()).unwrap().uid();
|
||||
let target_uid = get_user_by_name(self.user_name.borrow()).unwrap().uid();
|
||||
let target_uid = get_user_by_name(self.user_name.as_ref()).unwrap().uid();
|
||||
Ok(actual_uid == target_uid)
|
||||
}
|
||||
|
||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||
self.command_runner.run_successfully(
|
||||
"chown",
|
||||
&["-R", self.user_name.borrow(), self.path.as_ref()],
|
||||
)
|
||||
let uname: &str = self.user_name.as_ref();
|
||||
self
|
||||
.command_runner
|
||||
.run_successfully("chown", args!["-R", uname, self.path.as_ref(),])
|
||||
}
|
||||
|
||||
fn get_prerequisites(&self) -> Vec<Resource> {
|
||||
vec![Resource::new("user", self.user_name.to_string())]
|
||||
vec![Resource::new("user", self.user_name.as_ref())]
|
||||
}
|
||||
|
||||
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
|
||||
|
|
@ -69,11 +61,13 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, C: CommandRunner, D> fmt::Display for Owner<'a, C, D>
|
||||
where
|
||||
D: AsRef<str>,
|
||||
{
|
||||
impl<'a, C: CommandRunner, D: AsRef<Path>, U: AsRef<str>> fmt::Display for Owner<'a, C, D, U> {
|
||||
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.as_ref(),
|
||||
self.path.as_ref().display()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,17 @@
|
|||
use std::borrow::Cow;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
|
||||
use command_runner::CommandRunner;
|
||||
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||
|
||||
pub struct PostgreSQLDatabase<'a, C: 'a + CommandRunner> {
|
||||
name: Cow<'a, str>,
|
||||
seed_file: Cow<'a, str>,
|
||||
pub struct PostgreSQLDatabase<'a, N: AsRef<str>, S: AsRef<str>, C: CommandRunner> {
|
||||
name: N,
|
||||
seed_file: S,
|
||||
command_runner: &'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 {
|
||||
impl<'a, N: AsRef<str>, S: AsRef<str>, C: CommandRunner> PostgreSQLDatabase<'a, N, S, C> {
|
||||
pub fn new(name: N, seed_file: S, command_runner: &'a C) -> Self {
|
||||
PostgreSQLDatabase {
|
||||
name,
|
||||
seed_file,
|
||||
|
|
@ -23,52 +22,68 @@ impl<'a, C: CommandRunner> PostgreSQLDatabase<'a, C> {
|
|||
fn run_sql(&self, sql: &str) -> Result<String, Box<dyn Error>> {
|
||||
let b = self.command_runner.get_output(
|
||||
"su",
|
||||
&["-", "postgres", "-c", &format!("psql -t -c \"{}\"", sql)],
|
||||
args!["-", "postgres", "-c", format!("psql -t -c \"{}\"", sql)],
|
||||
)?;
|
||||
Ok(String::from_utf8(b)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: CommandRunner> fmt::Display for PostgreSQLDatabase<'a, C> {
|
||||
impl<'a, N: AsRef<str>, S: AsRef<str>, C: CommandRunner> fmt::Display
|
||||
for PostgreSQLDatabase<'a, N, S, C>
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "PostgreSQL Database {}", self.name)
|
||||
write!(f, "PostgreSQL Database {}", self.name.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: CommandRunner> Symbol for PostgreSQLDatabase<'a, C> {
|
||||
impl<'a, N: AsRef<str>, S: AsRef<str>, C: CommandRunner> Symbol
|
||||
for PostgreSQLDatabase<'a, N, S, C>
|
||||
{
|
||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||
Ok(
|
||||
self
|
||||
.run_sql(&format!(
|
||||
"SELECT datname FROM pg_database WHERE datname LIKE '{}'",
|
||||
self.name
|
||||
self.name.as_ref()
|
||||
))?
|
||||
.trim()
|
||||
== self.name,
|
||||
== self.name.as_ref(),
|
||||
)
|
||||
}
|
||||
|
||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||
self.command_runner.run_successfully(
|
||||
"su",
|
||||
&["-", "postgres", "-c", &format!("createuser {}", self.name)],
|
||||
)?;
|
||||
self.command_runner.run_successfully(
|
||||
"su",
|
||||
&[
|
||||
args![
|
||||
"-",
|
||||
"postgres",
|
||||
"-c",
|
||||
&format!("createdb -E UTF8 -T template0 -O {} {0}", self.name),
|
||||
format!("createuser {}", self.name.as_ref())
|
||||
],
|
||||
)?;
|
||||
self.command_runner.run_successfully(
|
||||
"su",
|
||||
&[
|
||||
args![
|
||||
"-",
|
||||
"postgres",
|
||||
"-c",
|
||||
&format!("psql '{}' < {}", self.name, self.seed_file),
|
||||
format!(
|
||||
"createdb -E UTF8 -T template0 -O {} {0}",
|
||||
self.name.as_ref()
|
||||
),
|
||||
],
|
||||
)?;
|
||||
self.command_runner.run_successfully(
|
||||
"su",
|
||||
args![
|
||||
"-",
|
||||
"postgres",
|
||||
"-c",
|
||||
format!(
|
||||
"psql '{}' < {}",
|
||||
self.name.as_ref(),
|
||||
self.seed_file.as_ref()
|
||||
),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,12 +6,7 @@ use command_runner::CommandRunner;
|
|||
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: AsRef<str>, C: CommandRunner, S: Storage> {
|
||||
db_name: N,
|
||||
storage: S,
|
||||
command_runner: &'a C,
|
||||
|
|
@ -29,7 +24,7 @@ impl<'a, N: AsRef<str>, C: CommandRunner, S: Storage> DatabaseDump<'a, N, C, S>
|
|||
fn run_sql(&self, sql: &str) -> Result<String, Box<dyn Error>> {
|
||||
let b = self
|
||||
.command_runner
|
||||
.get_output("mariadb", &["--skip-column-names", "-B", "-e", sql])?;
|
||||
.get_output("mariadb", args!["--skip-column-names", "-B", "-e", sql])?;
|
||||
Ok(String::from_utf8(b)?)
|
||||
}
|
||||
}
|
||||
|
|
@ -43,7 +38,7 @@ impl<'a, N: AsRef<str>, C: CommandRunner, S: Storage> fmt::Display for DatabaseD
|
|||
impl<'a, N: AsRef<str>, C: CommandRunner, S: Storage> Symbol for DatabaseDump<'a, N, C, S> {
|
||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||
let dump_date = 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 = 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);
|
||||
}
|
||||
|
|
@ -53,9 +48,9 @@ impl<'a, N: AsRef<str>, C: CommandRunner, S: Storage> Symbol for DatabaseDump<'a
|
|||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||
self.command_runner.run_successfully(
|
||||
"sh",
|
||||
&[
|
||||
args![
|
||||
"-c",
|
||||
&format!(
|
||||
format!(
|
||||
"mysqldump '{}' > {}",
|
||||
self.db_name.as_ref(),
|
||||
self.storage.write_filename()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
use std::borrow::{Borrow, Cow};
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::fs;
|
||||
|
|
@ -17,21 +16,15 @@ pub enum StorageDirection {
|
|||
Save,
|
||||
}
|
||||
|
||||
pub struct StoredDirectory<'a, S, C: 'a + CommandRunner>
|
||||
where
|
||||
S: Storage,
|
||||
{
|
||||
path: Cow<'a, str>,
|
||||
pub struct StoredDirectory<'a, P: AsRef<Path>, S: Storage, C: CommandRunner> {
|
||||
path: P,
|
||||
storage: S,
|
||||
dir: StorageDirection,
|
||||
command_runner: &'a C,
|
||||
}
|
||||
|
||||
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 {
|
||||
impl<'a, P: AsRef<Path>, S: Storage, C: CommandRunner> StoredDirectory<'a, P, S, C> {
|
||||
pub fn new(path: P, storage: S, dir: StorageDirection, command_runner: &'a C) -> Self {
|
||||
StoredDirectory {
|
||||
path,
|
||||
storage,
|
||||
|
|
@ -41,19 +34,20 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, S, C: CommandRunner> fmt::Display for StoredDirectory<'a, S, C>
|
||||
where
|
||||
S: Storage,
|
||||
impl<'a, P: AsRef<Path>, S: Storage, C: CommandRunner> fmt::Display
|
||||
for StoredDirectory<'a, P, S, C>
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Stored directory {} ({:?})", self.path, self.dir)
|
||||
write!(
|
||||
f,
|
||||
"Stored directory {} ({:?})",
|
||||
self.path.as_ref().display(),
|
||||
self.dir
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S, C: CommandRunner> Symbol for StoredDirectory<'a, S, C>
|
||||
where
|
||||
S: Storage,
|
||||
{
|
||||
impl<'a, P: AsRef<Path>, S: Storage, C: CommandRunner> Symbol for StoredDirectory<'a, P, S, C> {
|
||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||
let metadata = fs::metadata(self.path.as_ref());
|
||||
// Check if dir exists
|
||||
|
|
@ -74,11 +68,11 @@ where
|
|||
let dump_date = self.storage.recent_date()?;
|
||||
let output = self.command_runner.get_output(
|
||||
"sh",
|
||||
&[
|
||||
args![
|
||||
"-c",
|
||||
&format!(
|
||||
format!(
|
||||
"find {} -printf '%T@\\n' | sort -r | head -n1 | grep '^[0-9]\\+' -o",
|
||||
self.path
|
||||
self.path.as_ref().to_str().unwrap()
|
||||
),
|
||||
],
|
||||
)?;
|
||||
|
|
@ -90,7 +84,7 @@ where
|
|||
} {
|
||||
let output = self.command_runner.run_with_args(
|
||||
"diff",
|
||||
&["-rq", &self.storage.read_filename()?, self.path.borrow()],
|
||||
args!["-rq", self.storage.read_filename()?, self.path.as_ref()],
|
||||
)?;
|
||||
match output.status.code() {
|
||||
Some(0) => Ok(true),
|
||||
|
|
@ -106,15 +100,15 @@ where
|
|||
if self.dir == StorageDirection::Load {
|
||||
self
|
||||
.command_runner
|
||||
.run_successfully("rm", &["-rf", self.path.borrow()])?;
|
||||
.run_successfully("rm", args!["-rf", self.path.as_ref()])?;
|
||||
self.command_runner.run_successfully(
|
||||
"cp",
|
||||
&["-a", &self.storage.read_filename()?, self.path.borrow()],
|
||||
args!["-a", self.storage.read_filename()?, self.path.as_ref()],
|
||||
)
|
||||
} else {
|
||||
self.command_runner.run_successfully(
|
||||
"cp",
|
||||
&["-a", self.path.borrow(), &self.storage.write_filename()],
|
||||
args!["-a", self.path.as_ref(), self.storage.write_filename()],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -123,8 +117,8 @@ where
|
|||
if self.dir == StorageDirection::Save {
|
||||
return vec![];
|
||||
}
|
||||
if let Some(parent) = Path::new(self.path.as_ref()).parent() {
|
||||
vec![Resource::new("dir", parent.to_string_lossy())]
|
||||
if let Some(parent) = self.path.as_ref().parent() {
|
||||
vec![Resource::new("dir", parent.to_str().unwrap())]
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
|
|
@ -132,7 +126,10 @@ where
|
|||
|
||||
fn provides(&self) -> Option<Vec<Resource>> {
|
||||
if self.dir == StorageDirection::Load {
|
||||
Some(vec![Resource::new("dir", self.path.to_string())])
|
||||
Some(vec![Resource::new(
|
||||
"dir",
|
||||
self.path.as_ref().to_str().unwrap(),
|
||||
)])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ impl<'a, C: CommandRunner> Symbol for ReloadService<'a, C> {
|
|||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||
self
|
||||
.command_runner
|
||||
.run_successfully("systemctl", &["reload-or-restart", self.service])
|
||||
.run_successfully("systemctl", args!["reload-or-restart", self.service])
|
||||
}
|
||||
|
||||
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
use std::borrow::Cow;
|
||||
use std::error::Error;
|
||||
use std::ffi::OsStr;
|
||||
use std::fmt;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::ops::Deref;
|
||||
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Output;
|
||||
use std::thread::sleep;
|
||||
use std::time::Duration;
|
||||
|
|
@ -51,29 +55,23 @@ impl<E: Error> fmt::Display for UserServiceError<E> {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct UserService<'a, C, R>
|
||||
where
|
||||
C: Deref<Target = str>,
|
||||
R: CommandRunner,
|
||||
{
|
||||
pub struct UserService<'a, C: AsRef<str>, R: CommandRunner> {
|
||||
socket_path: Cow<'a, Path>,
|
||||
service_name: &'a str,
|
||||
user_name: &'a str,
|
||||
user_name: Cow<'a, str>,
|
||||
command_runner: R,
|
||||
file: FileSymbol<C, String>,
|
||||
config: FileSymbol<C, PathBuf>,
|
||||
}
|
||||
|
||||
impl<'a, R> UserService<'a, String, SetuidCommandRunner<'a, R>>
|
||||
where
|
||||
R: CommandRunner,
|
||||
{
|
||||
pub fn new_nodejs(
|
||||
home: &'a str,
|
||||
user_name: &'a str,
|
||||
impl<'a, R: CommandRunner> UserService<'a, String, SetuidCommandRunner<'a, R>> {
|
||||
pub fn new_nodejs<'b: 'a>(
|
||||
home: Cow<'a, Path>,
|
||||
user_name: Cow<'a, str>,
|
||||
service_name: &'a str,
|
||||
path: &'a str,
|
||||
path: Cow<'a, Path>,
|
||||
command_runner: &'a R,
|
||||
socket_path: Cow<'a, Path>,
|
||||
) -> Self {
|
||||
let port = format!("/var/tmp/{}-{}.socket", user_name, service_name);
|
||||
let content = format!(
|
||||
"[Service]
|
||||
Environment=NODE_ENV=production
|
||||
|
|
@ -91,39 +89,46 @@ Restart=always
|
|||
[Install]
|
||||
WantedBy=default.target
|
||||
",
|
||||
path, port
|
||||
path.to_str().unwrap(),
|
||||
socket_path.to_str().unwrap()
|
||||
);
|
||||
UserService::new(home, user_name, service_name, command_runner, content)
|
||||
UserService::new(
|
||||
socket_path,
|
||||
home,
|
||||
user_name,
|
||||
service_name,
|
||||
command_runner,
|
||||
content,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
home: &'a str,
|
||||
user_name: &'a str,
|
||||
socket_path: Cow<'a, Path>,
|
||||
home: Cow<'a, Path>,
|
||||
user_name: Cow<'a, str>,
|
||||
service_name: &'a str,
|
||||
command_runner: &'a R,
|
||||
content: String,
|
||||
) -> Self {
|
||||
let file_path = format!(
|
||||
"{}/.config/systemd/user/{}.service",
|
||||
home.trim_end(),
|
||||
service_name
|
||||
);
|
||||
let config_path: PathBuf = [
|
||||
home.as_ref(),
|
||||
format!(".config/systemd/user/{}.service", service_name).as_ref(),
|
||||
]
|
||||
.iter()
|
||||
.collect();
|
||||
|
||||
UserService {
|
||||
socket_path,
|
||||
service_name,
|
||||
user_name,
|
||||
user_name: user_name.clone(),
|
||||
command_runner: SetuidCommandRunner::new(user_name, command_runner),
|
||||
file: FileSymbol::new(file_path, content),
|
||||
config: FileSymbol::new(config_path, content),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C, R> UserService<'a, C, R>
|
||||
where
|
||||
C: Deref<Target = str>,
|
||||
R: CommandRunner,
|
||||
{
|
||||
fn systemctl_wait_for_dbus(&self, args: &[&str]) -> Result<String, Box<dyn Error>> {
|
||||
impl<'a, C: AsRef<str>, R: CommandRunner> UserService<'a, C, R> {
|
||||
fn systemctl_wait_for_dbus(&self, args: &[&OsStr]) -> Result<String, Box<dyn Error>> {
|
||||
let mut tries = 5;
|
||||
loop {
|
||||
let result = self.command_runner.run_with_args("systemctl", args)?;
|
||||
|
|
@ -146,7 +151,7 @@ where
|
|||
|
||||
fn check_if_service(&self) -> Result<bool, Box<dyn Error>> {
|
||||
loop {
|
||||
let active_state = self.systemctl_wait_for_dbus(&[
|
||||
let active_state = self.systemctl_wait_for_dbus(args![
|
||||
"--user",
|
||||
"show",
|
||||
"--property",
|
||||
|
|
@ -160,7 +165,7 @@ where
|
|||
return Err(Box::new(
|
||||
UserServiceError::ActivationFailed(self.command_runner.run_with_args(
|
||||
"journalctl",
|
||||
&["--user", &format!("--user-unit={}", self.service_name)],
|
||||
args!["--user", format!("--user-unit={}", self.service_name)],
|
||||
)) as UserServiceError<io::Error>,
|
||||
))
|
||||
}
|
||||
|
|
@ -170,22 +175,18 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, C, R> Symbol for UserService<'a, C, R>
|
||||
where
|
||||
C: Deref<Target = str>,
|
||||
R: CommandRunner,
|
||||
{
|
||||
impl<'a, C: AsRef<str>, R: CommandRunner> Symbol for UserService<'a, C, R> {
|
||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||
if !(self.file.target_reached()?) {
|
||||
if !(self.config.target_reached()?) {
|
||||
return Ok(false);
|
||||
}
|
||||
self.check_if_service()
|
||||
}
|
||||
|
||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||
self.file.execute()?;
|
||||
self.systemctl_wait_for_dbus(&["--user", "enable", self.service_name])?;
|
||||
self.systemctl_wait_for_dbus(&["--user", "restart", self.service_name])?;
|
||||
self.config.execute()?;
|
||||
self.systemctl_wait_for_dbus(args!["--user", "enable", self.service_name])?;
|
||||
self.systemctl_wait_for_dbus(args!["--user", "restart", self.service_name])?;
|
||||
|
||||
if !(self.check_if_service()?) {
|
||||
return Err(Box::new(
|
||||
|
|
@ -193,8 +194,7 @@ where
|
|||
));
|
||||
}
|
||||
|
||||
let file_name = format!("/var/tmp/{}-{}.socket", self.user_name, self.service_name);
|
||||
fs::metadata(&file_name)
|
||||
fs::metadata(&self.socket_path)
|
||||
.map(|_| ())
|
||||
.map_err(|e| Box::new(e) as Box<dyn Error>)
|
||||
}
|
||||
|
|
@ -204,7 +204,7 @@ where
|
|||
"file",
|
||||
format!("/var/lib/systemd/linger/{}", self.user_name),
|
||||
)];
|
||||
r.extend(self.file.get_prerequisites().into_iter());
|
||||
r.extend(self.config.get_prerequisites().into_iter());
|
||||
r
|
||||
}
|
||||
|
||||
|
|
@ -220,11 +220,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, C, R> fmt::Display for UserService<'a, C, R>
|
||||
where
|
||||
C: Deref<Target = str>,
|
||||
R: CommandRunner,
|
||||
{
|
||||
impl<'a, C: AsRef<str>, R: CommandRunner> fmt::Display for UserService<'a, C, R> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(f, "Systemd user service unit for {}", self.service_name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
use std::borrow::{Borrow, Cow};
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::path::PathBuf;
|
||||
|
|
@ -33,13 +32,13 @@ impl<E: Error> fmt::Display for SystemdUserSessionError<E> {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct SystemdUserSession<'a, C: 'a + CommandRunner> {
|
||||
user_name: Cow<'a, str>,
|
||||
pub struct SystemdUserSession<'a, U: AsRef<str>, C: CommandRunner> {
|
||||
user_name: U,
|
||||
command_runner: &'a C,
|
||||
}
|
||||
|
||||
impl<'a, C: CommandRunner> SystemdUserSession<'a, C> {
|
||||
pub fn new(user_name: Cow<'a, str>, command_runner: &'a C) -> Self {
|
||||
impl<'a, U: AsRef<str>, C: CommandRunner> SystemdUserSession<'a, U, C> {
|
||||
pub fn new(user_name: U, command_runner: &'a C) -> Self {
|
||||
SystemdUserSession {
|
||||
user_name,
|
||||
command_runner,
|
||||
|
|
@ -47,10 +46,10 @@ impl<'a, C: CommandRunner> SystemdUserSession<'a, C> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, C: CommandRunner> Symbol for SystemdUserSession<'a, C> {
|
||||
impl<'a, U: AsRef<str>, C: CommandRunner> Symbol for SystemdUserSession<'a, U, C> {
|
||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||
let mut path = PathBuf::from("/var/lib/systemd/linger");
|
||||
path.push(self.user_name.borrow() as &str);
|
||||
path.push(self.user_name.as_ref());
|
||||
Ok(path.exists())
|
||||
// Could also do `loginctl show-user ${self.user_name} | grep -F 'Linger=yes`
|
||||
}
|
||||
|
|
@ -58,7 +57,7 @@ impl<'a, C: CommandRunner> Symbol for SystemdUserSession<'a, C> {
|
|||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||
self
|
||||
.command_runner
|
||||
.run_successfully("loginctl", &["enable-linger", self.user_name.borrow()])
|
||||
.run_successfully("loginctl", args!["enable-linger", self.user_name.as_ref()])
|
||||
}
|
||||
|
||||
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
|
||||
|
|
@ -73,8 +72,8 @@ impl<'a, C: CommandRunner> Symbol for SystemdUserSession<'a, C> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, C: CommandRunner> fmt::Display for SystemdUserSession<'a, C> {
|
||||
impl<'a, U: AsRef<str>, C: CommandRunner> fmt::Display for SystemdUserSession<'a, U, C> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(f, "Systemd user session for {}", self.user_name)
|
||||
write!(f, "Systemd user session for {}", self.user_name.as_ref())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
use std::borrow::Cow;
|
||||
use std::error::Error;
|
||||
use std::ffi::OsStr;
|
||||
use std::fmt;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use command_runner::CommandRunner;
|
||||
use resources::Resource;
|
||||
|
|
@ -20,12 +21,12 @@ impl<'a, C: CommandRunner> TlsCsr<'a, C> {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_key_path(&self) -> String {
|
||||
format!("/etc/ssl/private/{}.key", self.domain)
|
||||
fn get_key_path(&self) -> PathBuf {
|
||||
format!("/etc/ssl/private/{}.key", self.domain).into()
|
||||
}
|
||||
|
||||
fn get_csr_path(&self) -> String {
|
||||
format!("/etc/ssl/local_certs/{}.csr", self.domain)
|
||||
fn get_csr_path(&self) -> PathBuf {
|
||||
format!("/etc/ssl/local_certs/{}.csr", self.domain).into()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -37,13 +38,19 @@ impl<'a, C: CommandRunner> fmt::Display for TlsCsr<'a, C> {
|
|||
|
||||
impl<'a, C: CommandRunner> Symbol for TlsCsr<'a, C> {
|
||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||
if !Path::new(&self.get_csr_path()).exists() {
|
||||
if !self.get_csr_path().exists() {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let output = self.command_runner.get_stderr(
|
||||
"openssl",
|
||||
&["req", "-in", &self.get_csr_path(), "-noout", "-verify"],
|
||||
&[
|
||||
OsStr::new("req"),
|
||||
"-in".as_ref(),
|
||||
self.get_csr_path().as_ref(),
|
||||
"-noout".as_ref(),
|
||||
"-verify".as_ref(),
|
||||
],
|
||||
)?;
|
||||
Ok(output == b"verify OK\n")
|
||||
}
|
||||
|
|
@ -52,22 +59,22 @@ impl<'a, C: CommandRunner> Symbol for TlsCsr<'a, C> {
|
|||
self.command_runner.run_successfully(
|
||||
"openssl",
|
||||
&[
|
||||
"req",
|
||||
"-new",
|
||||
"-sha256",
|
||||
"-key",
|
||||
&self.get_key_path(),
|
||||
"-out",
|
||||
&self.get_csr_path(),
|
||||
"-subj",
|
||||
&format!("/CN={}", self.domain),
|
||||
OsStr::new("req"),
|
||||
"-new".as_ref(),
|
||||
"-sha256".as_ref(),
|
||||
"-key".as_ref(),
|
||||
self.get_key_path().as_ref(),
|
||||
"-out".as_ref(),
|
||||
self.get_csr_path().as_ref(),
|
||||
"-subj".as_ref(),
|
||||
format!("/CN={}", self.domain).as_ref(),
|
||||
],
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_prerequisites(&self) -> Vec<Resource> {
|
||||
vec![Resource::new("file", self.get_key_path())]
|
||||
vec![Resource::new("file", self.get_key_path().to_str().unwrap())]
|
||||
}
|
||||
|
||||
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
use std::borrow::Cow;
|
||||
use std::error::Error;
|
||||
use std::ffi::OsStr;
|
||||
use std::fmt;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use command_runner::CommandRunner;
|
||||
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||
|
|
@ -19,8 +20,10 @@ impl<'a, C: CommandRunner> TlsKey<'a, C> {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_path(&self) -> String {
|
||||
format!("/etc/ssl/private/{}.key", self.domain)
|
||||
fn get_path(&self) -> PathBuf {
|
||||
["/etc/ssl/private", &format!("{}.key", self.domain)]
|
||||
.iter()
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn get_bytes(&self) -> u32 {
|
||||
|
|
@ -36,13 +39,20 @@ impl<'a, C: CommandRunner> fmt::Display for TlsKey<'a, C> {
|
|||
|
||||
impl<'a, C: CommandRunner> Symbol for TlsKey<'a, C> {
|
||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||
if !Path::new(&self.get_path()).exists() {
|
||||
if !self.get_path().exists() {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let output = self.command_runner.get_output(
|
||||
"openssl",
|
||||
&["rsa", "-in", &self.get_path(), "-noout", "-check", "-text"],
|
||||
&[
|
||||
OsStr::new("rsa"),
|
||||
"-in".as_ref(),
|
||||
self.get_path().as_ref(),
|
||||
"-noout".as_ref(),
|
||||
"-check".as_ref(),
|
||||
"-text".as_ref(),
|
||||
],
|
||||
)?;
|
||||
Ok(output.starts_with(&format!("Private-Key: ({} bit)\n", self.get_bytes()).as_bytes()))
|
||||
}
|
||||
|
|
@ -51,10 +61,10 @@ impl<'a, C: CommandRunner> Symbol for TlsKey<'a, C> {
|
|||
self.command_runner.run_successfully(
|
||||
"openssl",
|
||||
&[
|
||||
"genrsa",
|
||||
"-out",
|
||||
&self.get_path(),
|
||||
&self.get_bytes().to_string(),
|
||||
OsStr::new("genrsa"),
|
||||
"-out".as_ref(),
|
||||
self.get_path().as_ref(),
|
||||
self.get_bytes().to_string().as_ref(),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,57 +1,56 @@
|
|||
use std::borrow::Cow;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use command_runner::CommandRunner;
|
||||
use resources::Resource;
|
||||
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||
|
||||
pub struct SelfSignedTlsCert<'a, C: 'a + CommandRunner> {
|
||||
domain: Cow<'a, str>,
|
||||
pub struct SelfSignedTlsCert<'a, D: AsRef<str>, C: CommandRunner> {
|
||||
domain: D,
|
||||
command_runner: &'a C,
|
||||
}
|
||||
|
||||
impl<'a, C: CommandRunner> SelfSignedTlsCert<'a, C> {
|
||||
pub fn new(domain: Cow<'a, str>, command_runner: &'a C) -> Self {
|
||||
impl<'a, D: AsRef<str>, C: CommandRunner> SelfSignedTlsCert<'a, D, C> {
|
||||
pub fn new(domain: D, command_runner: &'a C) -> Self {
|
||||
SelfSignedTlsCert {
|
||||
domain,
|
||||
command_runner,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_key_path(&self) -> String {
|
||||
format!("/etc/ssl/private/{}.key", self.domain)
|
||||
fn get_key_path(&self) -> PathBuf {
|
||||
format!("/etc/ssl/private/{}.key", self.domain.as_ref()).into()
|
||||
}
|
||||
|
||||
fn get_cert_path(&self) -> String {
|
||||
format!("/etc/ssl/local_certs/{}.chained.crt", self.domain)
|
||||
fn get_cert_path(&self) -> PathBuf {
|
||||
format!("/etc/ssl/local_certs/{}.chained.crt", self.domain.as_ref()).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C: CommandRunner> fmt::Display for SelfSignedTlsCert<'a, C> {
|
||||
impl<'a, D: AsRef<str>, C: CommandRunner> fmt::Display for SelfSignedTlsCert<'a, D, C> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "SelfSignedTlsCert {}", self.domain)
|
||||
write!(f, "SelfSignedTlsCert {}", self.domain.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
const DAYS_IN_SECONDS: u32 = 24 * 60 * 60;
|
||||
|
||||
impl<'a, C: CommandRunner> Symbol for SelfSignedTlsCert<'a, C> {
|
||||
impl<'a, D: AsRef<str>, C: CommandRunner> Symbol for SelfSignedTlsCert<'a, D, C> {
|
||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||
if !Path::new(&self.get_cert_path()).exists() {
|
||||
if !self.get_cert_path().exists() {
|
||||
return Ok(false);
|
||||
}
|
||||
let output = self.command_runner.run_with_args(
|
||||
"openssl",
|
||||
&[
|
||||
args![
|
||||
"x509",
|
||||
"-in",
|
||||
&self.get_cert_path(),
|
||||
self.get_cert_path(),
|
||||
"-noout",
|
||||
"-subject",
|
||||
"-checkend",
|
||||
&(30 * DAYS_IN_SECONDS).to_string(),
|
||||
(30 * DAYS_IN_SECONDS).to_string(),
|
||||
],
|
||||
)?;
|
||||
println!("{}", output.status.code().unwrap());
|
||||
|
|
@ -60,13 +59,17 @@ impl<'a, C: CommandRunner> Symbol for SelfSignedTlsCert<'a, C> {
|
|||
output.stdout
|
||||
== format!(
|
||||
"subject=CN = {}\nCertificate will not expire\n",
|
||||
self.domain
|
||||
self.domain.as_ref()
|
||||
)
|
||||
.as_bytes(),
|
||||
),
|
||||
Some(_) => {
|
||||
if output.stdout
|
||||
== format!("subject=CN = {}\nCertificate will expire\n", self.domain).as_bytes()
|
||||
== format!(
|
||||
"subject=CN = {}\nCertificate will expire\n",
|
||||
self.domain.as_ref()
|
||||
)
|
||||
.as_bytes()
|
||||
{
|
||||
Ok(false)
|
||||
} else {
|
||||
|
|
@ -80,24 +83,24 @@ impl<'a, C: CommandRunner> Symbol for SelfSignedTlsCert<'a, C> {
|
|||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||
self.command_runner.run_successfully(
|
||||
"openssl",
|
||||
&[
|
||||
args![
|
||||
"req",
|
||||
"-x509",
|
||||
"-sha256",
|
||||
"-days",
|
||||
"90",
|
||||
"-key",
|
||||
&self.get_key_path(),
|
||||
self.get_key_path(),
|
||||
"-out",
|
||||
&self.get_cert_path(),
|
||||
self.get_cert_path(),
|
||||
"-subj",
|
||||
&format!("/CN={}", self.domain),
|
||||
format!("/CN={}", self.domain.as_ref()),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
fn get_prerequisites(&self) -> Vec<Resource> {
|
||||
vec![Resource::new("file", self.get_key_path())]
|
||||
vec![Resource::new("file", self.get_key_path().to_str().unwrap())]
|
||||
}
|
||||
|
||||
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ impl fmt::Display for UserError {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct User<'a, C: 'a + CommandRunner, A: 'a + UserAdder> {
|
||||
pub struct User<'a, C: CommandRunner, A: UserAdder> {
|
||||
user_name: Cow<'a, str>,
|
||||
command_runner: &'a C,
|
||||
user_adder: &'a A,
|
||||
|
|
@ -95,7 +95,7 @@ impl<'a, C: CommandRunner, A: 'a + UserAdder> Symbol for User<'a, C, A> {
|
|||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||
let output = self
|
||||
.command_runner
|
||||
.run_with_args("getent", &["passwd", &*self.user_name])?;
|
||||
.run_with_args("getent", args!["passwd", self.user_name.as_ref()])?;
|
||||
match output.status.code() {
|
||||
Some(2) => Ok(false),
|
||||
Some(0) => Ok(true),
|
||||
|
|
@ -106,12 +106,12 @@ impl<'a, C: CommandRunner, A: 'a + UserAdder> Symbol for User<'a, C, A> {
|
|||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||
self
|
||||
.user_adder
|
||||
.add_user(&*self.user_name)
|
||||
.add_user(self.user_name.as_ref())
|
||||
.map_err(|e| Box::new(e) as Box<dyn Error>)
|
||||
}
|
||||
|
||||
fn provides(&self) -> Option<Vec<Resource>> {
|
||||
Some(vec![Resource::new("user", self.user_name.to_string())])
|
||||
Some(vec![Resource::new("user", self.user_name.to_owned())])
|
||||
}
|
||||
|
||||
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
|
||||
|
|
@ -140,7 +140,7 @@ impl<'a, C: CommandRunner> UserAdder for SystemUserAdder<'a, C> {
|
|||
fn add_user(&self, user_name: &str) -> Result<(), UserAdderError> {
|
||||
let output = self.command_runner.run_with_args(
|
||||
"adduser",
|
||||
&[
|
||||
args![
|
||||
// "-m", // Necessary for Fedora, not accepted in Debian
|
||||
"--system", user_name,
|
||||
],
|
||||
|
|
|
|||
|
|
@ -4,29 +4,20 @@ use std::fmt;
|
|||
use std::fs::File as FsFile;
|
||||
use std::io;
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::ops::Deref;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use command_runner::CommandRunner;
|
||||
use resources::Resource;
|
||||
use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
|
||||
|
||||
pub struct WordpressPlugin<'a, C, R>
|
||||
where
|
||||
C: Deref<Target = str>,
|
||||
R: 'a + CommandRunner,
|
||||
{
|
||||
base: C,
|
||||
name: C,
|
||||
pub struct WordpressPlugin<'a, P: AsRef<Path>, N: AsRef<str>, R: CommandRunner> {
|
||||
base: P,
|
||||
name: N,
|
||||
command_runner: &'a R,
|
||||
}
|
||||
|
||||
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 {
|
||||
impl<'a, P: AsRef<Path>, N: AsRef<str>, R: CommandRunner> WordpressPlugin<'a, P, N, R> {
|
||||
pub fn new(base: P, name: N, command_runner: &'a R) -> Self {
|
||||
WordpressPlugin {
|
||||
base,
|
||||
name,
|
||||
|
|
@ -35,22 +26,21 @@ where
|
|||
}
|
||||
|
||||
fn get_path(&self) -> PathBuf {
|
||||
Path::new(&*self.base)
|
||||
self
|
||||
.base
|
||||
.as_ref()
|
||||
.join("wp-content/plugins")
|
||||
.join(&*self.name)
|
||||
.join(self.name.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, C, R> Symbol for WordpressPlugin<'a, C, R>
|
||||
where
|
||||
C: Deref<Target = str>,
|
||||
R: CommandRunner,
|
||||
{
|
||||
impl<'a, P: AsRef<Path>, N: AsRef<str>, R: CommandRunner> Symbol for WordpressPlugin<'a, P, N, R> {
|
||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||
if !self.get_path().exists() {
|
||||
let base_path = self.get_path();
|
||||
if !base_path.exists() {
|
||||
return Ok(false);
|
||||
}
|
||||
let path = self.get_path().join(self.name.to_string() + ".php");
|
||||
let path = base_path.join(self.name.as_ref().to_owned() + ".php");
|
||||
let mut version = String::new();
|
||||
let mut plugin_uri = String::new();
|
||||
match FsFile::open(path) {
|
||||
|
|
@ -78,11 +68,13 @@ where
|
|||
}
|
||||
let upstream = self.command_runner.get_output(
|
||||
"curl",
|
||||
&[
|
||||
args![
|
||||
"--form",
|
||||
&format!(
|
||||
format!(
|
||||
r###"plugins={{"plugins":{{"{0}/{0}.php":{{"Version":"{1}", "PluginURI":"{2}"}}}}}}"###,
|
||||
&*self.name, version, plugin_uri
|
||||
self.name.as_ref(),
|
||||
version,
|
||||
plugin_uri
|
||||
),
|
||||
"https://api.wordpress.org/plugins/update-check/1.1/",
|
||||
],
|
||||
|
|
@ -91,23 +83,20 @@ where
|
|||
}
|
||||
|
||||
fn execute(&self) -> Result<(), Box<dyn Error>> {
|
||||
let source = format!("https://downloads.wordpress.org/plugin/{}.zip", &*self.name);
|
||||
let zip = format!("/tmp/{}.zip", &*self.name);
|
||||
let source = format!(
|
||||
"https://downloads.wordpress.org/plugin/{}.zip",
|
||||
self.name.as_ref()
|
||||
);
|
||||
let zip = format!("/tmp/{}.zip", self.name.as_ref());
|
||||
self
|
||||
.command_runner
|
||||
.run_successfully("curl", &[source.as_ref() as &str, "-o", zip.as_ref()])?;
|
||||
.run_successfully("curl", args![source, "-o", zip])?;
|
||||
self
|
||||
.command_runner
|
||||
.run_successfully("rm", &["-rf", &self.get_path().to_string_lossy()])?;
|
||||
.run_successfully("rm", args!["-rf", self.get_path()])?;
|
||||
self.command_runner.run_successfully(
|
||||
"unzip",
|
||||
&[
|
||||
zip.as_ref(),
|
||||
"-d".as_ref(),
|
||||
Path::new(&*self.base)
|
||||
.join("wp-content/plugins")
|
||||
.as_os_str(),
|
||||
],
|
||||
args![zip, "-d", self.base.as_ref().join("wp-content/plugins")],
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -130,12 +119,10 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, C, R> fmt::Display for WordpressPlugin<'a, C, R>
|
||||
where
|
||||
C: Deref<Target = str>,
|
||||
R: CommandRunner,
|
||||
impl<'a, P: AsRef<Path>, N: AsRef<str>, R: CommandRunner> fmt::Display
|
||||
for WordpressPlugin<'a, P, N, R>
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(f, "WordpressPlugin {}", &*self.name)
|
||||
write!(f, "WordpressPlugin {}", self.name.as_ref())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,39 +1,31 @@
|
|||
use regex::Regex;
|
||||
use std::cmp::max;
|
||||
use std::error::Error;
|
||||
|
||||
use std::fmt;
|
||||
use std::fs::File as FsFile;
|
||||
use std::io;
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use command_runner::CommandRunner;
|
||||
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: AsRef<str>, D: AsRef<Path>, R: CommandRunner> {
|
||||
path: D,
|
||||
version: &'a str,
|
||||
locale: C,
|
||||
command_runner: &'a R,
|
||||
}
|
||||
|
||||
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 {
|
||||
impl<'a, C: AsRef<str>, R: CommandRunner> WordpressTranslation<'a, C, PathBuf, R> {
|
||||
pub fn new<D: AsRef<Path>>(path: D, version: &'a str, locale: C, command_runner: &'a R) -> Self {
|
||||
WordpressTranslation {
|
||||
path: Path::new(path.as_ref())
|
||||
.join("wp-content/languages")
|
||||
.to_string_lossy()
|
||||
.to_string(),
|
||||
path: [path.as_ref(), "wp-content/languages".as_ref()]
|
||||
.iter()
|
||||
.collect(),
|
||||
version,
|
||||
locale,
|
||||
command_runner,
|
||||
|
|
@ -41,19 +33,14 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
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)> {
|
||||
impl<'a, C: AsRef<str>, D: AsRef<Path>, R: CommandRunner> WordpressTranslation<'a, C, D, R> {
|
||||
fn get_pairs(&self) -> Vec<(String, PathBuf)> {
|
||||
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 = self.locale.as_ref();
|
||||
let path_locale = if locale == "de_DE" {
|
||||
"de".to_owned()
|
||||
} else {
|
||||
|
|
@ -71,7 +58,7 @@ where
|
|||
for format in ["po", "mo"].iter() {
|
||||
res.push((
|
||||
format!("https://translate.wordpress.org/projects/wp/{}/{}{}/default/export-translations?format={}", version_x, in_slug, path_locale, format),
|
||||
format!("{}/{}{}.{}", self.path.as_ref(), out_slug, self.locale.as_ref(), format)
|
||||
[self.path.as_ref(), format!("{}{}.{}", out_slug, self.locale.as_ref(), format).as_ref()].iter().collect()
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
@ -79,11 +66,8 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, C, D, R> Symbol for WordpressTranslation<'a, C, D, R>
|
||||
where
|
||||
C: AsRef<str>,
|
||||
D: AsRef<str>,
|
||||
R: CommandRunner,
|
||||
impl<'a, C: AsRef<str>, D: AsRef<Path>, R: CommandRunner> Symbol
|
||||
for WordpressTranslation<'a, C, D, R>
|
||||
{
|
||||
fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
|
||||
let mut newest = String::new();
|
||||
|
|
@ -113,7 +97,7 @@ where
|
|||
}
|
||||
let upstream = self.command_runner.get_output(
|
||||
"curl",
|
||||
&[&format!(
|
||||
args![format!(
|
||||
"https://api.wordpress.org/core/version-check/1.7/?version={}&locale={}",
|
||||
self.version,
|
||||
self.locale.as_ref()
|
||||
|
|
@ -131,13 +115,13 @@ where
|
|||
for (source, target) in self.get_pairs() {
|
||||
self
|
||||
.command_runner
|
||||
.run_successfully("curl", &["--compressed", "-o", &target, &source])?;
|
||||
.run_successfully("curl", args!["--compressed", "-o", target, source,])?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_prerequisites(&self) -> Vec<Resource> {
|
||||
vec![Resource::new("dir", self.path.as_ref())]
|
||||
vec![Resource::new("dir", self.path.as_ref().to_str().unwrap())]
|
||||
}
|
||||
|
||||
fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
|
||||
|
|
@ -152,13 +136,10 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
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: AsRef<str>, D: AsRef<Path>, R: CommandRunner> fmt::Display
|
||||
for WordpressTranslation<'a, C, D, R>
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(f, "WordpressTranslation {}", self.path.as_ref())
|
||||
write!(f, "WordpressTranslation {}", self.path.as_ref().display())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue