diff --git a/src/symbols/dir.rs b/src/symbols/dir.rs index b5188b7..3624738 100644 --- a/src/symbols/dir.rs +++ b/src/symbols/dir.rs @@ -1,11 +1,7 @@ -use std::borrow::Cow; use std::error::Error; use std::fmt; use std::fs; use std::io; -use std::io::{Read, Write}; -use std::ops::Deref; -use std::path::Path; use symbols::Symbol; diff --git a/src/symbols/dir_for.rs b/src/symbols/dir_for.rs new file mode 100644 index 0000000..40d3ccf --- /dev/null +++ b/src/symbols/dir_for.rs @@ -0,0 +1,49 @@ +use std::error::Error; +use std::fmt; +use std::fs; +use std::os::unix::fs::MetadataExt; + +use users::get_user_by_name; + +use symbols::Symbol; +use symbols::dir::Dir; +use command_runner::CommandRunner; + + +pub struct DirFor<'a, D> where D: AsRef + fmt::Display { + dir: Dir, + path: D, + user_name: &'a str, + command_runner: &'a CommandRunner +} + +impl<'a, D> DirFor<'a, D> where D: AsRef + fmt::Display + Clone { + pub fn new(path: D, user_name: &'a str, command_runner: &'a CommandRunner) -> Self { + DirFor { dir: Dir::new(path.clone()), path: path, user_name: user_name, command_runner: command_runner } + } +} + +impl<'a, D> Symbol for DirFor<'a, D> where D: AsRef + fmt::Display { + fn target_reached(&self) -> Result> { + match self.dir.target_reached() { + Ok(true) => { + let actual_uid = fs::metadata(self.path.as_ref()).unwrap().uid(); + let target_uid = get_user_by_name(self.user_name).unwrap().uid(); + Ok(actual_uid == target_uid) + }, + res => res + } + } + + fn execute(&self) -> Result<(), Box> { + try!(self.dir.execute()); + try!(self.command_runner.run_with_args("chown", &[self.user_name, self.path.as_ref()])); + Ok(()) + } +} + +impl<'a, D> fmt::Display for DirFor<'a, D> where D: AsRef + fmt::Display { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>{ + write!(f, "Dir {} for {}", self.path, self.user_name) + } +} diff --git a/src/symbols/file.rs b/src/symbols/file.rs index b5b29ae..3be43a9 100644 --- a/src/symbols/file.rs +++ b/src/symbols/file.rs @@ -1,4 +1,3 @@ -use std::borrow::Cow; use std::error::Error; use std::fmt; use std::fs::File as FsFile; diff --git a/src/symbols/git.rs b/src/symbols/git/checkout.rs similarity index 100% rename from src/symbols/git.rs rename to src/symbols/git/checkout.rs diff --git a/src/symbols/git/mod.rs b/src/symbols/git/mod.rs new file mode 100644 index 0000000..d3e2df1 --- /dev/null +++ b/src/symbols/git/mod.rs @@ -0,0 +1,2 @@ +pub mod checkout; +pub mod submodules; diff --git a/src/symbols/git/submodules.rs b/src/symbols/git/submodules.rs new file mode 100644 index 0000000..99eba64 --- /dev/null +++ b/src/symbols/git/submodules.rs @@ -0,0 +1,50 @@ +use std::error::Error; +use std::fmt; +use std::io; + +use command_runner::CommandRunner; +use symbols::Symbol; + +pub struct GitSubmodules<'a> { + target: &'a str, + command_runner: &'a CommandRunner +} + +impl<'a> GitSubmodules<'a> { + pub fn new(target: &'a str, command_runner: &'a CommandRunner) -> GitSubmodules<'a> { + GitSubmodules { + target: target, + command_runner: command_runner + } + } +} + +impl<'a> fmt::Display for GitSubmodules<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Submodules for {}", self.target) + } +} + +impl<'a> GitSubmodules<'a> { + fn _run_in_target_repo(&self, args: &[&str]) -> Result, io::Error> { + let mut new_args = vec!["-C", self.target]; + new_args.extend_from_slice(args); + self.command_runner.run_with_args("git", &new_args).map(|res| res.stdout) + } +} + +impl<'a> Symbol for GitSubmodules<'a> { + fn target_reached(&self) -> Result> { + let output = try!(self._run_in_target_repo(&["submodule", "status"])); + Ok(String::from_utf8(output).unwrap().lines().all(|line| line.len() == 0 || line.starts_with(' '))) + } + + fn execute(&self) -> Result<(), Box> { + try!(self._run_in_target_repo(&["submodule", "update", "--init"])); + Ok(()) + } +} + +#[cfg(test)] +mod test { +} diff --git a/src/symbols/list.rs b/src/symbols/list.rs new file mode 100644 index 0000000..1a2f471 --- /dev/null +++ b/src/symbols/list.rs @@ -0,0 +1,117 @@ +use std::error::Error; +use std::fmt; + +use symbols::Symbol; + +pub struct List<'a> { + symbols: Vec> +} + +impl<'a> List<'a> { + pub fn new(symbols: Vec>) -> Self { + List { symbols: symbols } + } +} + +impl<'a> Symbol for List<'a> { + fn target_reached(&self) -> Result> { + for symbol in &self.symbols { + match symbol.target_reached() { + Ok(false) => return Ok(false), + Err(e) => return Err(e), + Ok(true) => {} + } + } + Ok(true) + } + + fn execute(&self) -> Result<(), Box> { + for symbol in &self.symbols { + try!(symbol.execute()); + } + Ok(()) + } +} + +impl<'a> fmt::Display for List<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>{ + try!(write!(f, "List [ ")); + for symbol in &self.symbols { + try!(write!(f, "{} ", symbol)); + } + write!(f, "]") + } +} + +/* +#[cfg(test)] +mod test { + use std::error::Error; + use std::fmt; + + use symbols::Symbol; + use symbols::hook::List; + + struct ErrSymbol(String); + impl Symbol for ErrSymbol { + fn target_reached(&self) -> Result> { Err(self.0.clone().into()) } + fn execute(&self) -> Result<(), Box> { Err(self.0.clone().into()) } + } + impl fmt::Display for ErrSymbol { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>{ write!(f, "") } } + + struct OkSymbol(bool); + impl Symbol for OkSymbol { + fn target_reached(&self) -> Result> { Ok(self.0) } + fn execute(&self) -> Result<(), Box> { Ok(()) } + } + impl fmt::Display for OkSymbol { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>{ write!(f, "") } } + + #[test] + fn first_target_reached_fails() { + let res = List::new(ErrSymbol("first".into()), ErrSymbol("second".into())).target_reached(); + assert_eq!(res.unwrap_err().description(), "first"); + } + + #[test] + fn first_target_not_reached() { + let res = List::new(OkSymbol(false), ErrSymbol("second".into())).target_reached(); + assert_eq!(res.unwrap(), false); + } + + #[test] + fn second_target_reached_fails() { + let res = List::new(OkSymbol(true), ErrSymbol("second".into())).target_reached(); + assert_eq!(res.unwrap_err().description(), "second"); + } + + #[test] + fn second_target_not_reached() { + let res = List::new(OkSymbol(true), OkSymbol(false)).target_reached(); + assert_eq!(res.unwrap(), false); + } + + #[test] + fn everything_reached() { + let res = List::new(OkSymbol(true), OkSymbol(true)).target_reached(); + assert_eq!(res.unwrap(), true); + } + + #[test] + fn first_execute_fails() { + let res = List::new(ErrSymbol("first".into()), ErrSymbol("second".into())).execute(); + assert_eq!(res.unwrap_err().description(), "first"); + } + + #[test] + fn second_execute_fails() { + let res = List::new(OkSymbol(true), ErrSymbol("second".into())).execute(); + assert_eq!(res.unwrap_err().description(), "second"); + } + + #[test] + fn everything_executes() { + let res = List::new(OkSymbol(true), OkSymbol(true)).execute(); + assert_eq!(res.unwrap(), ()); + } +} +*/ diff --git a/src/symbols/mod.rs b/src/symbols/mod.rs index 0f5f66b..11115d7 100644 --- a/src/symbols/mod.rs +++ b/src/symbols/mod.rs @@ -1,4 +1,3 @@ -use std::borrow::Cow; use std::error::Error; use std::fmt::Display; use resources::Resource; @@ -12,6 +11,7 @@ pub trait Symbol: Display { } pub mod dir; +pub mod dir_for; pub mod file; pub mod git; pub mod npm; @@ -19,3 +19,4 @@ pub mod user; pub mod systemd; pub mod nginx; pub mod hook; +pub mod list; diff --git a/src/symbols/nginx/server.rs b/src/symbols/nginx/server.rs index 2e0e3e7..d6fb8c8 100644 --- a/src/symbols/nginx/server.rs +++ b/src/symbols/nginx/server.rs @@ -57,9 +57,9 @@ impl<'a> NginxServer<'a, String> { server_name {}; return 302 $scheme://{}$request_uri; - }} +}} - ", redir_domain, domain)).fold(String::new(), |s, v| s + &v); +", redir_domain, domain)).fold(String::new(), |s, v| s + &v); let proxy_content = if let Some(socket) = socket_path { format!("location / {{ @@ -71,7 +71,7 @@ location @proxy {{ proxy_pass http://unix:{}:; proxy_redirect off; }}", socket) - } else { "".to_string() }; + } else { "\ntry_files $uri $uri/ $uri.html =404;".to_string() }; // FIXME: This is a crude hack let content = String::from(redir_content) + &format!("server {{ listen 80; @@ -82,7 +82,8 @@ location @proxy {{ root {}; {} -}}", domain, static_path, proxy_content); +}} +", domain, static_path, proxy_content); NginxServer { command_runner: command_runner, file: FileSymbol::new(file_path, content) diff --git a/src/symbols/systemd/node_js_user_service.rs b/src/symbols/systemd/node_js_user_service.rs index 574c467..67215a4 100644 --- a/src/symbols/systemd/node_js_user_service.rs +++ b/src/symbols/systemd/node_js_user_service.rs @@ -68,6 +68,8 @@ impl<'a> NodeJsSystemdUserService<'a, Cow<'a, str>, String> { let content = format!("[Service] ExecStartPre=rm /var/tmp/{1}-{2}.socket +# This only works if the path is a directory +WorkingDirectory={0} ExecStart=/usr/bin/nodejs {0} Restart=always Environment=NODE_ENV=production @@ -138,12 +140,27 @@ impl<'a, P, C> Symbol for NodeJsSystemdUserService<'a, P, C> where P: AsRef let file_name = format!("/var/tmp/{}-{}.socket", self.user_name, self.name); // try!(self.command_runner.run_with_args("chmod", &["666", &file_name])); - sleep(Duration::from_millis(500)); - let metadata = try!(fs::metadata(file_name.clone())); - let mut perms = metadata.permissions(); - perms.set_mode(0o666); - try!(fs::set_permissions(file_name, perms)); - + let mut tries = 5; + loop { + let metadata = fs::metadata(file_name.clone()); + match metadata { + Ok(metadata) => { + let mut perms = metadata.permissions(); + perms.set_mode(0o666); + try!(fs::set_permissions(file_name, perms)); + break; + }, + Err(e) => { + if e.kind() == io::ErrorKind::NotFound { + tries -= 1; + if tries == 0 { return Err("Gave up waiting for socket to appear".to_string().into()); } + sleep(Duration::from_millis(500)); + } else { + return Err(Box::new(e)); + } + } + } + } Ok(()) }