AsyncJobsServer.php 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. <?php
  2. Lib::loadClass('Request');
  3. /**
  4. * @usage: Core_AsyncJobsServer::startAsyncJob(execPath, name, [ 'out.log', 'error.log', 'cwd', 'args' ])
  5. * @usage: Core_AsyncJobsServer::isInstalled()
  6. */
  7. class Core_AsyncJobsServer {
  8. static function path_nodeJS() { return "/usr/local/bin/node"; }
  9. // npm_path="/usr/local/bin/npm"
  10. static function path_pm2() { return "/usr/local/bin/pm2"; }
  11. static function path_pm2UserWww() { return "/Library/WebServer/.pm2/"; }
  12. static function startAsyncJob($execPath, $name, $props = [], &$out) {
  13. if (!$execPath) throw new Exception("Missing job exec path");
  14. if (!$name) throw new Exception("Missing job name");
  15. $pathLog = V::get('out.log', '', $props);
  16. $pathError = V::get('error.log', '', $props);
  17. // for Ant:
  18. // index.php?_route=UrlAction_Ant&_task=ant&path=default_db.in7_dziennik_koresp/etykieta&typeName=default_db:IN7_DZIENNIK_KORESP&primaryKey=66263&primaryKeyField=ID
  19. // index.php?_route=UrlAction_Ant
  20. // & _task=ant
  21. // & path=default_db.in7_dziennik_koresp/test-bash
  22. // & typeName=default_db:IN7_DZIENNIK_KORESP
  23. // & primaryKey=66263
  24. // & primaryKeyField=ID
  25. // index.php?_route=UrlAction_Ant
  26. // & _task=ant
  27. // & path=default_db.in7_dziennik_koresp/test-bash
  28. // & template=test-loop
  29. // & typeName=default_db:IN7_DZIENNIK_KORESP
  30. // & primaryKey=66263
  31. // & primaryKeyField=ID
  32. // ant=default_db:IN7_DZIENNIK_KORESP/test-bash & task=test-loop & ns=default_db:IN7_DZIENNIK_KORESP & pk=66263
  33. if (empty($pathLog)) throw new Exception("Missing job log path");
  34. if (empty($pathError)) throw new Exception("Missing job error path");
  35. $execArg = V::get('args', '', $props); // execArg for script
  36. $cmd = implode(" ", [
  37. "pm2 start '{$execPath}'",
  38. (!empty($props['cwd'])) ? "--cwd=\"{$props['cwd']}\"" : "",
  39. "--name '{$name}'",
  40. "--no-autorestart",
  41. "--output '{$pathLog}'",
  42. "--error '{$pathError}'",
  43. "--time", // prefix time to log entry
  44. (!empty(trim($execArg))) ? "-- {$execArg}" : "",
  45. ]);
  46. // return V::shell_exec($cmd);
  47. V::exec($cmd . " 2>&1", $out, $ret);
  48. // echo "cmd: <code>{$cmd}</code><br>RETURN CODE: '{$ret}'<br><pre>OUTPUT:\n" . implode("\n", $out). "</pre>";
  49. if (0 !== $ret) {
  50. // [PM2][ERROR] Script already launched, add -f option to force re-execution
  51. $firstLine = reset($out);
  52. if (0 === strpos($firstLine, '[PM2][ERROR] Script already launched')) {
  53. $out = [ "[PM2][ERROR] Script already launched" ];
  54. }
  55. // throw new Exception("Nie uruchomiono zadania. " . implode(" ", $out));
  56. } else {
  57. // [PM2] Applying action restartProcessId on app [biall-backup-db](ids: [ 0 ])
  58. // [PM2] [biall-backup-db](0) ✓
  59. // [PM2] Process successfully started
  60. $line0 = reset($out);
  61. $line1 = next($out);
  62. $line2 = next($out);
  63. if (0 === strpos($line0, '[PM2] Applying action')
  64. && 0 === strpos($line1, '[PM2] [')
  65. && 0 === strpos($line2, '[PM2] Process successfully started')
  66. ) {
  67. $out = [ "[PM2] Process successfully started" ];
  68. return 0;
  69. }
  70. // [PM2] Starting /opt/local/pl.procesy5/async_jobs/2020-02-26/3/start.sh in fork_mode (1 instance) [PM2] Done.
  71. if (0 === strpos($line0, '[PM2] Starting ')
  72. && 0 === strpos($line1, '[PM2] Done')
  73. ) {
  74. $out = [ "[PM2] Process successfully started" ];
  75. return 0;
  76. }
  77. // throw new AlertInfoException("Uruchomiono zadanie. " . implode(" ", $out));
  78. }
  79. return $ret;
  80. }
  81. static function runPm2Script($props, &$out) { // TODO: mv to AsyncJobs / @return bool $ret from exec
  82. if (empty($props['jobName'])) throw new Exception("Missing jobName");
  83. if (empty($props['path'])) throw new Exception("Missing path");
  84. // cwd (string) “/var/www/” the directory from which your app will be launched
  85. $jobName = $props['jobName'];
  86. $outLogPath = "/tmp/p5-async-jobs--{$jobName}--out.log";
  87. $errLogPath = "/tmp/p5-async-jobs--{$jobName}--err.log";
  88. $path = $props['path'];
  89. $args = trim(V::get('args', '', $props));
  90. $cmd = implode(" ", [
  91. "pm2 start '{$path}'",
  92. (!empty($props['cwd'])) ? "--cwd=\"{$props['cwd']}\"" : "",
  93. "--name '{$jobName}'",
  94. "--no-autorestart",
  95. "--output '{$outLogPath}'",
  96. "--error '{$errLogPath}'",
  97. "--time", // prefix time to log entry
  98. (!empty($args)) ? "-- {$args}" : "",
  99. ]);
  100. V::exec($cmd . " 2>&1", $out, $ret);
  101. // echo "cmd: <code>{$cmd}</code><br>RETURN CODE: '{$ret}'<br><pre>OUTPUT:\n" . implode("\n", $out). "</pre>";
  102. if (0 !== $ret) {
  103. // [PM2][ERROR] Script already launched, add -f option to force re-execution
  104. $firstLine = reset($out);
  105. if (0 === strpos($firstLine, '[PM2][ERROR] Script already launched')) {
  106. $out = [ "[PM2][ERROR] Script already launched" ];
  107. }
  108. // throw new Exception("Nie uruchomiono backupu. " . implode(" ", $out));
  109. } else {
  110. // [PM2] Applying action restartProcessId on app [biall-backup-db](ids: [ 0 ])
  111. // [PM2] [biall-backup-db](0) ✓
  112. // [PM2] Process successfully started
  113. $line0 = reset($out);
  114. $line1 = next($out);
  115. $line2 = next($out);
  116. if (0 === strpos($line0, '[PM2] Applying action')
  117. && 0 === strpos($line1, '[PM2] [')
  118. && 0 === strpos($line2, '[PM2] Process successfully started')
  119. ) {
  120. $out = [ "[PM2] Process successfully started" ];
  121. }
  122. // throw new AlertInfoException("Uruchomiono backup. " . implode(" ", $out));
  123. }
  124. return $ret;
  125. }
  126. static function isInstalled() {
  127. // V::exec("/usr/local/bin/pm2 --version 2>&1", $out, $ret);
  128. V::exec("/usr/local/bin/pm2 ping 2>&1", $out, $ret); // expected "{ msg: 'pong' }"
  129. // echo UI::h('pre', [], "ret({$ret}):\n" . implode("\n", $out));
  130. if ($ret === 0) {
  131. // [PM2] Spawning PM2 daemon with pm2_home=/Library/WebServer/.pm2
  132. // [PM2] PM2 Successfully daemonized
  133. // { msg: 'pong' }
  134. return true;
  135. }
  136. if ($ret !== 0) {
  137. if (!file_exists(self::path_nodeJS())) throw new Exception("pm2 not installed");
  138. // if [ ! -f "$node_path" ]; then
  139. // echo "$node_path not exists"
  140. // wget https://nodejs.org/dist/v12.15.0/node-v12.15.0.pkg
  141. // sudo installer -verbose -pkg node-v12.15.0.pkg -target /
  142. // fi
  143. // # node -v # expected v12.15.0
  144. if (!file_exists(self::path_pm2())) throw new Exception("pm2 not installed");
  145. // npm install -g pm2
  146. // sudo npm install -g pm2
  147. // pm2 -version # expected 4.2.3
  148. if (!file_exists(self::path_pm2UserWww())) throw new Exception("pm2 user folder not exists");
  149. // FIX for Mac OS:
  150. // $ sudo mkdir /Library/WebServer/.pm2/
  151. // $ sudo chown _www /Library/WebServer/.pm2/
  152. throw new Exception("Error pm2"); // unknown error
  153. }
  154. }
  155. }