From 9b36474e485c1ad798bbdfbd088d2ee0c1200012 Mon Sep 17 00:00:00 2001 From: Jeroen De Meerleer Date: Tue, 1 Jun 2021 17:41:10 +0200 Subject: [PATCH] NEW FEATURE: manual runs --- config/routes.yaml | 7 +++++ src/Controller/JobController.php | 11 +++++++ src/Repository/Job.php | 50 +++++++++++++++++++++++--------- src/Repository/Run.php | 14 +++++++++ 4 files changed, 69 insertions(+), 13 deletions(-) diff --git a/config/routes.yaml b/config/routes.yaml index 1c6d8ed..a58c21b 100644 --- a/config/routes.yaml +++ b/config/routes.yaml @@ -49,6 +49,13 @@ job_edit: requirements: id: \d+ +job_runnow: + path: '/job/{id}/runnow' + defaults: + _controller: JeroenED\Webcron\Controller\JobController::runnowAction + requirements: + id: \d+ + job_add: path: '/job/add' defaults: diff --git a/src/Controller/JobController.php b/src/Controller/JobController.php index c4eb48c..e8980a8 100644 --- a/src/Controller/JobController.php +++ b/src/Controller/JobController.php @@ -4,6 +4,7 @@ namespace JeroenED\Webcron\Controller; use JeroenED\Framework\Controller; +use JeroenED\Framework\Repository; use JeroenED\Webcron\Repository\Job; use JeroenED\Webcron\Repository\Run; use Symfony\Component\HttpFoundation\JsonResponse; @@ -88,4 +89,14 @@ class JobController extends Controller return new Response('Not implemented yet', Response::HTTP_TOO_EARLY); } } + + public function runNowAction(int $id) { + if(!isset($_SESSION['isAuthenticated']) || !$_SESSION['isAuthenticated']) { + return new RedirectResponse($this->generateRoute('login')); + } + if($this->getRequest()->getMethod() == 'GET') { + $jobRepo = new Job($this->getDbCon()); + return new JsonResponse($jobRepo->runNow($id)); + } + } } \ No newline at end of file diff --git a/src/Repository/Job.php b/src/Repository/Job.php index 459c0d0..d6a72f7 100644 --- a/src/Repository/Job.php +++ b/src/Repository/Job.php @@ -232,6 +232,28 @@ class Job extends Repository } } + public function runNow($job) { + $job = $this->getJob($job, true); + $runRepo = new Run($this->dbcon); + + if($runRepo->isSlowJob($job['id']) && $job['data']['crontype'] !== 'reboot') { + $jobsSql = "UPDATE job SET running = :status WHERE id = :id AND running IN (0,1,2)"; + $jobsStmt = $this->dbcon->prepare($jobsSql); + $jobsStmt->executeQuery([':id' => $job['id'], ':status' => 2]); + return ['success' => true, 'message' => 'Job was scheduled to be run. You will find the output soon in the job details']; + } else { + $this->runJob($job['id'], true); + $output = $runRepo->getLastRun($job['id']); + return [ + 'output' => $output['output'], + 'exitcode' => $output['exitcode'], + 'runtime' => $output['runtime'], + 'success' => !str_contains($output['flags'], Run::FAILED) + ]; + } + + } + private function prepareDockerCommand(string $command, string $service, string|NULL $user): string { $prepend = 'docker exec '; @@ -244,11 +266,11 @@ class Job extends Repository { $starttime = microtime(true); $job = $this->getJob($job, true); - if($job['data']['crontype'] == 'http') { + if ($job['data']['crontype'] == 'http') { $result = $this->runHttpJob($job); - } elseif($job['data']['crontype'] == 'command') { + } elseif ($job['data']['crontype'] == 'command') { $result = $this->runCommandJob($job); - } elseif($job['data']['crontype'] == 'reboot') { + } elseif ($job['data']['crontype'] == 'reboot') { $result = $this->runRebootJob($job, $starttime, $manual); } $endtime = microtime(true); @@ -256,28 +278,30 @@ class Job extends Repository // setting flags $flags = []; - if($result['failed'] === true) { + if ($result['failed'] === true) { $flags[] = Run::FAILED; } else { $flags[] = Run::SUCCESS; } - if($manual === true) { + if ($manual === true) { $flags[] = Run::MANUAL; } // saving to database $runRepo = new Run($this->dbcon); $runRepo->addRun($job['id'], $result['exitcode'], floor($starttime), $runtime, $result['output'], $flags); - // setting nextrun to next run - $nextrun = $job['nextrun']; - do { - $nextrun = $nextrun + $job['interval']; - } while ($nextrun < time()); + if (!$manual){ + // setting nextrun to next run + $nextrun = $job['nextrun']; + do { + $nextrun = $nextrun + $job['interval']; + } while ($nextrun < time()); - $addRunSql = 'UPDATE job SET nextrun = :nextrun WHERE id = :id'; - $addRunStmt = $this->dbcon->prepare($addRunSql); - $addRunStmt->executeQuery([':id' => $job['id'], ':nextrun' => $nextrun]); + $addRunSql = 'UPDATE job SET nextrun = :nextrun WHERE id = :id'; + $addRunStmt = $this->dbcon->prepare($addRunSql); + $addRunStmt->executeQuery([':id' => $job['id'], ':nextrun' => $nextrun]); + } } public function unlockJob(int $id = 0): void diff --git a/src/Repository/Run.php b/src/Repository/Run.php index e1dc02d..f3ebae1 100644 --- a/src/Repository/Run.php +++ b/src/Repository/Run.php @@ -33,4 +33,18 @@ class Run extends Repository $addRunStmt = $this->dbcon->prepare($addRunSql); $addRunStmt->executeQuery([':job_id' => $jobid, ':exitcode' => $exitcode, 'output' => $output, 'runtime' => $runtime, ':timestamp' => $starttime, ':flags' => implode("", $flags)]); } + + public function getLastRun(int $jobid): array + { + $lastRunSql = 'SELECT * FROM run WHERE job_id = :jobid ORDER BY timestamp DESC LIMIT 1'; + $lastRun = $this->dbcon->prepare($lastRunSql)->executeQuery([':jobid' => $jobid])->fetchAssociative(); + return $lastRun; + } + + public function isSlowJob(int $jobid, int $timelimit = 5): bool + { + $slowJobSql = 'SELECT AVG(runtime) as average FROM run WHERE job_id = :jobid LIMIT 5'; + $slowJob = $this->dbcon->prepare($slowJobSql)->executeQuery([':jobid' => $jobid])->fetchAssociative(); + return $slowJob['average'] > $timelimit; + } } \ No newline at end of file