Failed runs are now saved per run instead of based on global response code

This commit is contained in:
Jeroen De Meerleer 2021-06-01 13:45:57 +02:00
parent 4eec21b3dd
commit 65aa04a69a
Signed by: JeroenED
GPG Key ID: 28CCCB8F62BFADD6
6 changed files with 56 additions and 34 deletions

View File

@ -49,20 +49,20 @@ class DaemonCommand extends Command
$jobsToRun = $jobRepo->getJobsDue(); $jobsToRun = $jobRepo->getJobsDue();
if(!empty($jobsToRun)) { if(!empty($jobsToRun)) {
foreach($jobsToRun as $job) { foreach($jobsToRun as $job) {
$jobRepo->setJobRunning($job, true); $jobRepo->setJobRunning($job['id'], true);
$output->writeln('Running Job ' . $job); $output->writeln('Running Job ' . $job['id']);
declare(ticks = 1); declare(ticks = 1);
pcntl_signal(SIGCHLD, SIG_IGN); pcntl_signal(SIGCHLD, SIG_IGN);
$pid = pcntl_fork(); $pid = pcntl_fork();
if($pid == -1) { if($pid == -1) {
$jobRepo->RunJob($job); $jobRepo->RunJob($job['id'], $job['running'] == 2);
$jobRepo->setJobRunning($job, false); $jobRepo->setJobRunning($job['id'], false);
} elseif ($pid == 0) { } elseif ($pid == 0) {
$dbcon = $this->kernel->getDbCon(); $dbcon = $this->kernel->getDbCon();
$dbcon->close(); $dbcon->close();
$dbcon->connect(); $dbcon->connect();
$jobRepo->RunJob($job); $jobRepo->RunJob($job['id'], $job['running'] == 2);
$jobRepo->setJobRunning($job, false); $jobRepo->setJobRunning($job['id'], false);
exit; exit;
} }

View File

@ -32,7 +32,7 @@ class JobController extends Controller
if($this->getRequest()->getMethod() == 'GET') { if($this->getRequest()->getMethod() == 'GET') {
$job = $jobRepo->getJob($id); $job = $jobRepo->getJob($id);
$runs = $runRepo->getRunsForJob($id, $all != 'all' ? array_merge($job['data']['response'] ?? [], $job['data']['http-status'] ?? []) : []); $runs = $runRepo->getRunsForJob($id, $all != 'all');
return $this->render('job/view.html.twig', ['job' => $job, 'runs' => $runs, 'allruns' => $all == 'all']); return $this->render('job/view.html.twig', ['job' => $job, 'runs' => $runs, 'allruns' => $all == 'all']);
} elseif($this->getRequest()->getMethod() == 'DELETE') { } elseif($this->getRequest()->getMethod() == 'DELETE') {
$success = $jobRepo->deleteJob($id); $success = $jobRepo->deleteJob($id);

View File

@ -38,7 +38,7 @@ class Job extends Repository
public function getJobsDue() public function getJobsDue()
{ {
$jobsSql = "SELECT id $jobsSql = "SELECT id, running
FROM job FROM job
WHERE ( WHERE (
nextrun <= :timestamp nextrun <= :timestamp
@ -49,11 +49,7 @@ class Job extends Repository
$jobsStmt = $this->dbcon->prepare($jobsSql); $jobsStmt = $this->dbcon->prepare($jobsSql);
$jobsRslt = $jobsStmt->executeQuery([':timestamp' => time(), ':timestamplastrun' => time(), ':timestamprun' => time()]); $jobsRslt = $jobsStmt->executeQuery([':timestamp' => time(), ':timestamplastrun' => time(), ':timestamprun' => time()]);
$jobs = $jobsRslt->fetchAllAssociative(); $jobs = $jobsRslt->fetchAllAssociative();
$return = []; return $jobs;
foreach ($jobs as $job) {
$return[] = $job['id'];
}
return $return;
} }
public function setJobRunning(int $job, bool $status): void public function setJobRunning(int $job, bool $status): void
@ -117,9 +113,9 @@ class Job extends Repository
$options['http_errors'] = false; $options['http_errors'] = false;
$options['auth'] = !empty($job['data']['basicauth-username']) ? [$job['data']['basicauth-username'], $job['data']['basicauth-password']] : NULL; $options['auth'] = !empty($job['data']['basicauth-username']) ? [$job['data']['basicauth-username'], $job['data']['basicauth-password']] : NULL;
$res = $client->request('GET', $url, $options); $res = $client->request('GET', $url, $options);
$return['exitcode'] = $res->getStatusCode(); $return['exitcode'] = $res->getStatusCode();
$return['output'] = $res->getBody(); $return['output'] = $res->getBody();
$return['failed'] = !in_array($return['exitcode'], $job['data']['http-status']);
return $return; return $return;
} }
@ -136,9 +132,13 @@ class Job extends Repository
$command = $this->prepareDockerCommand($command, $job['data']['service'], $job['data']['container-user']); $command = $this->prepareDockerCommand($command, $job['data']['service'], $job['data']['container-user']);
} }
if($job['data']['hosttype'] == 'local') { if($job['data']['hosttype'] == 'local') {
return $this->runLocalCommand($command); $return = $this->runLocalCommand($command);
$return['failed'] = !in_array($return['exitcode'], $job['data']['http-status']);
return $return;
} elseif($job['data']['hosttype'] == 'ssh') { } elseif($job['data']['hosttype'] == 'ssh') {
return $this->runSshCommand($command, $job['data']['host'], $job['data']['user'], $job['data']['ssh-privkey'], $job['data']['privkey-password']); $return = $this->runSshCommand($command, $job['data']['host'], $job['data']['user'], $job['data']['ssh-privkey'], $job['data']['privkey-password']);
$return['failed'] = !in_array($return['exitcode'], $job['data']['http-status']);
return $return;
} }
} }
@ -178,10 +178,11 @@ class Job extends Repository
return $return; return $return;
} }
private function runRebootJob(array $job, float &$starttime): array private function runRebootJob(array $job, float &$starttime, bool &$manual): array
{ {
if($job['running'] == 1) { if($job['running'] == 1) {
$this->setTempVar($job['id'], 'starttime', $starttime); $this->setTempVar($job['id'], 'starttime', $starttime);
$this->setTempVar($job['id'], 'manual', $manual);
$job['data']['reboot-command'] = str_replace('{reboot-delay}', $job['data']['reboot-delay'], $job['data']['reboot-command']); $job['data']['reboot-command'] = str_replace('{reboot-delay}', $job['data']['reboot-delay'], $job['data']['reboot-command']);
$job['data']['reboot-command'] = str_replace('{reboot-delay-secs}', $job['data']['reboot-delay-secs'], $job['data']['reboot-command']); $job['data']['reboot-command'] = str_replace('{reboot-delay-secs}', $job['data']['reboot-delay-secs'], $job['data']['reboot-command']);
@ -209,6 +210,8 @@ class Job extends Repository
} }
$starttime = (float)$this->getTempVar($job['id'], 'starttime'); $starttime = (float)$this->getTempVar($job['id'], 'starttime');
$this->deleteTempVar($job['id'], 'starttime'); $this->deleteTempVar($job['id'], 'starttime');
$manual = (float)$this->getTempVar($job['id'], 'manual');
$this->deleteTempVar($job['id'], 'manual');
$jobsSql = "UPDATE job SET running = :status WHERE id = :id"; $jobsSql = "UPDATE job SET running = :status WHERE id = :id";
$jobsStmt = $this->dbcon->prepare($jobsSql); $jobsStmt = $this->dbcon->prepare($jobsSql);
@ -237,7 +240,7 @@ class Job extends Repository
return $prepend . $command; return $prepend . $command;
} }
public function runJob(int $job): void public function runJob(int $job, bool $manual): void
{ {
$starttime = microtime(true); $starttime = microtime(true);
$job = $this->getJob($job, true); $job = $this->getJob($job, true);
@ -246,16 +249,25 @@ class Job extends Repository
} elseif($job['data']['crontype'] == 'command') { } elseif($job['data']['crontype'] == 'command') {
$result = $this->runCommandJob($job); $result = $this->runCommandJob($job);
} elseif($job['data']['crontype'] == 'reboot') { } elseif($job['data']['crontype'] == 'reboot') {
$result = $this->runRebootJob($job, $starttime); $result = $this->runRebootJob($job, $starttime, $manual);
} }
$endtime = microtime(true); $endtime = microtime(true);
$runtime = $endtime - $starttime; $runtime = $endtime - $starttime;
// handling of response // setting flags
$addRunSql = 'INSERT INTO run(job_id, exitcode, output, runtime, timestamp) VALUES (:job_id, :exitcode, :output, :runtime, :timestamp)'; $flags = [];
$addRunStmt = $this->dbcon->prepare($addRunSql); if($result['failed'] === true) {
$addRunStmt->executeQuery([':job_id' => $job['id'], ':exitcode' => $result['exitcode'], ':output' => $result['output'], ':runtime' => $runtime, ':timestamp' => floor($starttime)]); $flags[] = Run::FAILED;
} else {
$flags[] = Run::SUCCESS;
}
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 // setting nextrun to next run
$nextrun = $job['nextrun']; $nextrun = $job['nextrun'];
do { do {
@ -270,7 +282,7 @@ class Job extends Repository
public function unlockJob(int $id = 0): void public function unlockJob(int $id = 0): void
{ {
$jobsSql = "UPDATE job SET running = :status WHERE running IN (0,1,2)"; $jobsSql = "UPDATE job SET running = :status WHERE running = 1";
$params = [':status' => 0]; $params = [':status' => 0];
if($id != 0) { if($id != 0) {

View File

@ -8,18 +8,16 @@ use JeroenED\Framework\Repository;
class Run extends Repository class Run extends Repository
{ {
public function getRunsForJob(int $id, $excludedexitcodes = [], $ordered = true): array const FAILED = 'F';
const SUCCESS = 'S';
const MANUAL = 'M';
public function getRunsForJob(int $id, $failed = true, $ordered = true): array
{ {
$runsSql = "SELECT * FROM run WHERE job_id = :job"; $runsSql = "SELECT * FROM run WHERE job_id = :job";
$params = [':job' => $id]; $params = [':job' => $id];
if (!empty($excludedexitcodes)) { if ($failed) {
$runsSql .= ' AND exitcode NOT in '; $runsSql .= ' AND flags LIKE "%' . Run::FAILED . '%"';
$exitcodes = [];
foreach($excludedexitcodes as $key => $exitcode) {
$exitcodes[] = ':code' . $key;
$params[':code' . $key] = $exitcode;
}
$runsSql .= '(' . implode(',', $exitcodes) . ')';
} }
if ($ordered) $runsSql .= ' ORDER by timestamp DESC'; if ($ordered) $runsSql .= ' ORDER by timestamp DESC';
$runsStmt = $this->dbcon->prepare($runsSql); $runsStmt = $this->dbcon->prepare($runsSql);
@ -27,4 +25,12 @@ class Run extends Repository
$runs = $runsRslt->fetchAllAssociative(); $runs = $runsRslt->fetchAllAssociative();
return $runs; return $runs;
} }
public function addRun(int $jobid, string $exitcode, int $starttime, float $runtime, string $output, array $flags): void
{
// handling of response
$addRunSql = 'INSERT INTO run(job_id, exitcode, output, runtime, timestamp,flags) VALUES (:job_id, :exitcode, :output, :runtime, :timestamp, :flags)';
$addRunStmt = $this->dbcon->prepare($addRunSql);
$addRunStmt->executeQuery([':job_id' => $jobid, ':exitcode' => $exitcode, 'output' => $output, 'runtime' => $runtime, ':timestamp' => $starttime, ':flags' => implode("", $flags)]);
}
} }

View File

@ -24,5 +24,6 @@ CREATE TABLE run (
exitcode TEXT NOT NULL, exitcode TEXT NOT NULL,
output TEXT NOT NULL, output TEXT NOT NULL,
runtime REAL NOT NULL, runtime REAL NOT NULL,
timestamp INTEGER NOT NULL timestamp INTEGER NOT NULL,
flags TEXT NOT NULL
); );

View File

@ -17,6 +17,9 @@
<div> <div>
<div class="d-md-inline d-block text-left">{{ run.timestamp | date("d/m/Y H:i:s") }}</div> <div class="d-md-inline d-block text-left">{{ run.timestamp | date("d/m/Y H:i:s") }}</div>
<div class="d-md-inline d-block text-left">(runtime: {{ run.runtime | format_number({fraction_digit: 3}) }})</div> <div class="d-md-inline d-block text-left">(runtime: {{ run.runtime | format_number({fraction_digit: 3}) }})</div>
{% if 'M' in run.flags %}
<div class="d-md-inline d-block text-left">Manual Run</div>
{% endif %}
</div> </div>
<div>{{ run.exitcode }}</div> <div>{{ run.exitcode }}</div>
</button> </button>