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.

124 lines
4.0 KiB

7 years ago
  1. use std::error::Error;
  2. use std::fmt;
  3. use std::io;
  4. use std::path::Path;
  5. use std::thread::sleep;
  6. use std::time::Duration;
  7. use std::ops::Deref;
  8. use command_runner::CommandRunner;
  9. use symbols::Symbol;
  10. use symbols::file::File as FileSymbol;
  11. #[derive(Debug)]
  12. pub enum NodeJsSystemdUserServiceError<E: Error> {
  13. ExecError(E),
  14. GenericError
  15. }
  16. impl From<io::Error> for NodeJsSystemdUserServiceError<io::Error> {
  17. fn from(err: io::Error) -> NodeJsSystemdUserServiceError<io::Error> {
  18. NodeJsSystemdUserServiceError::ExecError(err)
  19. }
  20. }
  21. impl<E: Error> Error for NodeJsSystemdUserServiceError<E> {
  22. fn description(&self) -> &str {
  23. match self {
  24. &NodeJsSystemdUserServiceError::ExecError(ref e) => e.description(),
  25. &NodeJsSystemdUserServiceError::GenericError => "Generic error"
  26. }
  27. }
  28. fn cause(&self) -> Option<&Error> {
  29. match self {
  30. &NodeJsSystemdUserServiceError::ExecError(ref e) => Some(e),
  31. _ => None
  32. }
  33. }
  34. }
  35. impl<E: Error> fmt::Display for NodeJsSystemdUserServiceError<E> {
  36. fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
  37. write!(f, "{}", self.description())
  38. }
  39. }
  40. pub struct NodeJsSystemdUserService<'a, P, C> where P: AsRef<str> + fmt::Display, C: Deref<Target=str> {
  41. name: &'a str,
  42. path: P,
  43. command_runner: &'a CommandRunner,
  44. file: FileSymbol<C, Cow<'a, str>>
  45. }
  46. use std::borrow::Cow;
  47. impl<'a> NodeJsSystemdUserService<'a, Cow<'a, str>, String> {
  48. pub fn new(name: &'a str, path: &'a str, command_runner: &'a CommandRunner) -> Self {
  49. let home = String::from_utf8(command_runner.run_with_args("sh", &["-c", "echo \"$HOME\""]).unwrap().stdout).unwrap();
  50. let file_path: Cow<str> = Cow::from(String::from(home.trim_right()) + "/.config/systemd/user/" + name + ".service");
  51. let content = format!("[Service]
  52. ExecStart=/usr/bin/nodejs {}
  53. Restart=always
  54. Environment=NODE_ENV=production
  55. Environment=PORT={}/var/service.socket
  56. [Install]
  57. WantedBy=default.target
  58. ", path, home);
  59. NodeJsSystemdUserService {
  60. name: name,
  61. path: file_path.clone(),
  62. command_runner: command_runner,
  63. file: FileSymbol::new(file_path, content)
  64. }
  65. }
  66. }
  67. impl<'a, P, C> Symbol for NodeJsSystemdUserService<'a, P, C> where P: AsRef<str> + fmt::Display, C: Deref<Target=str> {
  68. type Error = NodeJsSystemdUserServiceError<io::Error>;
  69. fn target_reached(&self) -> Result<bool, Self::Error> {
  70. match self.file.target_reached() {
  71. Ok(false) => return Ok(false),
  72. Ok(true) => {},
  73. Err(e) => return Err(NodeJsSystemdUserServiceError::GenericError)
  74. }
  75. /*
  76. if !(try!(self.file.target_reached())) {
  77. return Ok(false)
  78. }
  79. */
  80. loop {
  81. // Check if service is registered
  82. let active_state = try!(self.command_runner.run_with_args("systemctl", &["--user", "show", "--property", "ActiveState", self.name]));
  83. if !active_state.status.success() {
  84. return Ok(false);
  85. }
  86. // Check if service is running
  87. match String::from_utf8(active_state.stdout).unwrap().trim_right() {
  88. "ActiveState=activating" => sleep(Duration::from_millis(500)),
  89. "ActiveState=active" => return Ok(true),
  90. _ => return Ok(false)
  91. }
  92. }
  93. }
  94. fn execute(&self) -> Result<(), Self::Error> {
  95. try!(self.command_runner.run_with_args("mkdir", &["-p", Path::new(self.path.as_ref()).parent().unwrap().to_str().unwrap()]));
  96. // FIXME: Permissions
  97. // try!(create_dir_all(Path::new(&path).parent().unwrap()));
  98. match self.file.execute() {
  99. Ok(_) => {},
  100. Err(e) => return Err(NodeJsSystemdUserServiceError::GenericError)
  101. }
  102. try!(self.command_runner.run_with_args("systemctl", &["--user", "enable", self.name]));
  103. try!(self.command_runner.run_with_args("systemctl", &["--user", "start", self.name]));
  104. Ok(())
  105. }
  106. }
  107. impl<'a, P, C> fmt::Display for NodeJsSystemdUserService<'a, P, C> where P: AsRef<str> + fmt::Display, C: Deref<Target=str> {
  108. fn fmt(&self, f: &mut fmt::Formatter) -> Result<(),fmt::Error>{
  109. write!(f, "Systemd Node.js user service unit for {}", self.path)
  110. }
  111. }