A library for writing host-specific, single-binary configuration management and deployment tools
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

248 lines
5.6 KiB

7 years ago
5 years ago
7 years ago
5 years ago
7 years ago
7 years ago
5 years ago
5 years ago
7 years ago
5 years ago
7 years ago
5 years ago
5 years ago
7 years ago
5 years ago
6 years ago
7 years ago
5 years ago
7 years ago
5 years ago
5 years ago
7 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
7 years ago
5 years ago
5 years ago
5 years ago
5 years ago
7 years ago
5 years ago
7 years ago
5 years ago
5 years ago
5 years ago
5 years ago
7 years ago
5 years ago
5 years ago
6 years ago
7 years ago
5 years ago
5 years ago
7 years ago
5 years ago
5 years ago
5 years ago
7 years ago
5 years ago
5 years ago
5 years ago
7 years ago
7 years ago
5 years ago
7 years ago
5 years ago
7 years ago
7 years ago
5 years ago
7 years ago
  1. use std::error::Error;
  2. use std::fmt;
  3. use std::io;
  4. use std::ops::Deref;
  5. use command_runner::CommandRunner;
  6. use resources::Resource;
  7. use symbols::file::File as FileSymbol;
  8. use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner};
  9. #[derive(Debug)]
  10. pub enum NginxServerError<E: Error> {
  11. ExecError(E),
  12. GenericError,
  13. }
  14. impl From<io::Error> for NginxServerError<io::Error> {
  15. fn from(err: io::Error) -> NginxServerError<io::Error> {
  16. NginxServerError::ExecError(err)
  17. }
  18. }
  19. impl<E: Error> Error for NginxServerError<E> {
  20. fn description(&self) -> &str {
  21. match self {
  22. NginxServerError::ExecError(ref e) => e.description(),
  23. NginxServerError::GenericError => "Generic error",
  24. }
  25. }
  26. fn cause(&self) -> Option<&dyn Error> {
  27. match self {
  28. NginxServerError::ExecError(ref e) => Some(e),
  29. _ => None,
  30. }
  31. }
  32. }
  33. impl<E: Error> fmt::Display for NginxServerError<E> {
  34. fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
  35. write!(f, "{}", self.description())
  36. }
  37. }
  38. pub struct NginxServer<'a, C: 'a + CommandRunner, T>
  39. where
  40. T: Deref<Target = str>,
  41. {
  42. command_runner: &'a C,
  43. file: FileSymbol<T, String>,
  44. }
  45. use std::borrow::Cow;
  46. pub fn server_config(domain: &str, content: &str) -> String {
  47. format!(
  48. "server {{
  49. listen 443 ssl http2;
  50. server_name {0};
  51. include \"snippets/acme-challenge.conf\";
  52. ssl_certificate /etc/ssl/local_certs/{0}.chained.crt;
  53. ssl_certificate_key /etc/ssl/private/{0}.key;
  54. add_header Strict-Transport-Security \"max-age=31536000\";
  55. {1}
  56. }}
  57. # Redirect all HTTP links to the matching HTTPS page
  58. server {{
  59. listen 80;
  60. server_name {0};
  61. include \"snippets/acme-challenge.conf\";
  62. location / {{
  63. return 301 https://$host$request_uri;
  64. }}
  65. }}
  66. ",
  67. domain, content
  68. )
  69. }
  70. pub fn php_server_config_snippet<'a>(
  71. socket_path: Cow<'a, str>,
  72. static_path: Cow<'a, str>,
  73. ) -> String {
  74. format!(
  75. "
  76. root {};
  77. index index.html index.php;
  78. location ~ [^/]\\.php(/|$) {{
  79. fastcgi_pass unix:{};
  80. include \"snippets/fastcgi-php.conf\";
  81. }}",
  82. static_path, socket_path
  83. )
  84. }
  85. pub trait SocketSpec {
  86. fn to_nginx(&self) -> String;
  87. }
  88. impl SocketSpec for &str {
  89. fn to_nginx(&self) -> String {
  90. format!("unix:{}:", self)
  91. }
  92. }
  93. pub struct LocalTcpSocket(usize);
  94. impl LocalTcpSocket {
  95. pub fn new(x: usize) -> Self {
  96. LocalTcpSocket(x)
  97. }
  98. }
  99. impl SocketSpec for LocalTcpSocket {
  100. fn to_nginx(&self) -> String {
  101. format!("localhost:{}", self.0)
  102. }
  103. }
  104. impl<'a, C: CommandRunner> NginxServer<'a, C, String> {
  105. pub fn new_redir(domain: &'a str, target: &'a str, command_runner: &'a C) -> Self {
  106. let content = server_config(
  107. domain,
  108. &format!(
  109. "location / {{
  110. return 301 $scheme://{}$request_uri;
  111. }}",
  112. target
  113. ),
  114. );
  115. NginxServer::new(domain, content, command_runner)
  116. }
  117. pub fn new_proxy<S: SocketSpec>(
  118. domain: &'a str,
  119. socket_path: S,
  120. static_path: &'a str,
  121. command_runner: &'a C,
  122. ) -> Self {
  123. let proxy_content = format!(
  124. "location / {{
  125. try_files $uri @proxy;
  126. }}
  127. location @proxy {{
  128. include fastcgi_params;
  129. proxy_pass http://{};
  130. proxy_redirect off;
  131. }}",
  132. socket_path.to_nginx()
  133. );
  134. let content = server_config(
  135. domain,
  136. &format!(
  137. "
  138. root {};
  139. {}
  140. ",
  141. static_path, proxy_content
  142. ),
  143. );
  144. NginxServer::new(domain, content, command_runner)
  145. }
  146. pub fn new_php(
  147. domain: &'a str,
  148. socket_path: Cow<'a, str>,
  149. static_path: Cow<'a, str>,
  150. command_runner: &'a C,
  151. additional_config: &'a str,
  152. ) -> Self {
  153. let content = server_config(
  154. domain,
  155. &(php_server_config_snippet(socket_path, static_path) + additional_config),
  156. );
  157. NginxServer::new(domain, content, command_runner)
  158. }
  159. pub fn new_static(domain: &'a str, static_path: &'a str, command_runner: &'a C) -> Self {
  160. let content = server_config(
  161. domain,
  162. &format!(
  163. "
  164. root {};
  165. try_files $uri $uri/ $uri.html =404;
  166. ",
  167. static_path
  168. ),
  169. );
  170. NginxServer::new(domain, content, command_runner)
  171. }
  172. pub fn new(domain: &'a str, content: String, command_runner: &'a C) -> Self {
  173. let file_path = String::from("/etc/nginx/sites-enabled/") + domain;
  174. NginxServer {
  175. command_runner,
  176. file: FileSymbol::new(file_path, content),
  177. }
  178. }
  179. }
  180. impl<'a, C: CommandRunner, T> Symbol for NginxServer<'a, C, T>
  181. where
  182. T: Deref<Target = str>,
  183. {
  184. fn target_reached(&self) -> Result<bool, Box<dyn Error>> {
  185. if !self.file.target_reached()? {
  186. return Ok(false);
  187. }
  188. // TODO: Could try to find out if the server is in the live config
  189. Ok(true)
  190. }
  191. fn execute(&self) -> Result<(), Box<dyn Error>> {
  192. self.file.execute()?;
  193. self
  194. .command_runner
  195. .run_successfully("systemctl", &["reload-or-restart", "nginx"])
  196. }
  197. fn get_prerequisites(&self) -> Vec<Resource> {
  198. self.file.get_prerequisites()
  199. }
  200. fn as_action<'b>(&'b self, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b> {
  201. Box::new(SymbolAction::new(runner, self))
  202. }
  203. fn into_action<'b>(self: Box<Self>, runner: &'b dyn SymbolRunner) -> Box<dyn Action + 'b>
  204. where
  205. Self: 'b,
  206. {
  207. Box::new(OwnedSymbolAction::new(runner, *self))
  208. }
  209. }
  210. impl<'a, C: CommandRunner, T> fmt::Display for NginxServer<'a, C, T>
  211. where
  212. T: Deref<Target = str>,
  213. {
  214. fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
  215. write!(f, "Nginx server config")
  216. }
  217. }