ENHANCEMENT: Using MOAR symfony

This commit is contained in:
Jeroen De Meerleer 2022-05-17 16:06:46 +02:00
parent 92e74f0797
commit 0fef3a275e
Signed by: JeroenED
GPG Key ID: 28CCCB8F62BFADD6
6 changed files with 222 additions and 164 deletions

View File

@ -33,13 +33,15 @@ class DaemonCommand extends Command
$this
->setDescription('The deamon slayer of webcron')
->setHelp('This command is the daemon process of webcron, enabling webcron to actually run jobs on time')
->addOption('time-limit', 't', InputOption::VALUE_REQUIRED, 'Time limit in seconds before stopping the daemon.');
->addOption('time-limit', 't', InputOption::VALUE_REQUIRED, 'Time limit in seconds before stopping the daemon.')
->addOption('async', 'a', InputOption::VALUE_NEGATABLE, 'Time limit in seconds before stopping the daemon.');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$jobRepo = $this->doctrine->getRepository(Job::class);
$timelimit = $input->getOption('time-limit') ?? false;
$async = $input->getOption('async') ?? function_exists('pcntl_fork');
if ($timelimit === false) {
$endofscript = false;
} elseif(is_numeric($timelimit)) {
@ -55,27 +57,29 @@ class DaemonCommand extends Command
$jobsToRun = $jobRepo->getJobsDue();
if(!empty($jobsToRun)) {
foreach($jobsToRun as $job) {
$jobObj = $jobRepo->getJob($job['id']);
if($jobObj['data']['crontype'] == 'reboot') {
if($job->getData('crontype') == 'reboot') {
$str = @file_get_contents('/proc/uptime');
$num = floatval($str);
$rebootedself = ($num < $jobObj['data']['reboot-duration'] * 60);
$consolerun = $jobRepo->getTempVar($job['id'], 'consolerun', false);
$rebootedself = ($num < $job->getData('reboot-duration') * 60);
$consolerun = $jobRepo->getTempVar($job->getId(), 'consolerun', false);
if($consolerun && !$rebootedself) continue;
}
$jobRepo->setJobRunning($job['id'], true);
$output->writeln('Running Job ' . $job['id']);
declare(ticks = 1);
pcntl_signal(SIGCHLD, SIG_IGN);
$pid = pcntl_fork();
$this->doctrine->getConnection()->close();
$jobRepo = $this->doctrine->getRepository(Job::class);
if($pid == -1) {
$jobRepo->RunJob($job['id'], $job['running'] == 2);
$jobRepo->setJobRunning($job['id'], false);
$jobRepo->setJobRunning($job->getId(), true);
$output->writeln('Running Job ' . $job->getId());
if($async) {
declare(ticks = 1);
pcntl_signal(SIGCHLD, SIG_IGN);
$pid = pcntl_fork();
$this->doctrine->getConnection()->close();
$jobRepo = $this->doctrine->getRepository(Job::class);
}
if(!$async || $pid == -1) {
$jobRepo->RunJob($job->getId(), $job->getRunning() == 2);
$jobRepo->setJobRunning($job->getId(), false);
} elseif ($pid == 0) {
$jobRepo->RunJob($job['id'], $job['running'] == 2);
$jobRepo->setJobRunning($job['id'], false);
$jobRepo->RunJob($job->getId(), $job->getRunning() == 2);
$jobRepo->setJobRunning($job->getId(), false);
exit;
}
}

View File

@ -42,7 +42,7 @@ class JobController extends AbstractController
if($request->getMethod() == 'GET') {
$jobRepo = $doctrine->getRepository(Job::class);
$job = $jobRepo->getJob($id, true);
return $this->render('job/edit.html.twig', $job);
return $this->render('job/edit.html.twig', ['job' => $job]);
} elseif($request->getMethod() == 'POST') {
$allValues = $request->request->all();
$jobRepo = $doctrine->getRepository(Job::class);

View File

@ -5,6 +5,7 @@ namespace App\Entity;
use App\Repository\JobRepository;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use InvalidArgumentException;
#[ORM\Entity(repositoryClass: JobRepository::class)]
class Job
@ -100,33 +101,78 @@ class Job
return $this;
}
/**
* @return array
*/
public function getData(): array
public function getData(?string $name = ''): mixed
{
return json_decode($this->data, true);
$data = json_decode($this->data, true);
if(!empty($name)) {
$names = explode('.', $name);
foreach($names as $item) {
if(!isset($data[$item])) {
return NULL;
}
$data = $data[$item];
}
}
return $data;
}
/**
* @param array $data
* @return Job
*/
public function setData(array $data): Job
{
$this->data = json_encode($data);
return $this;
}
public function addData(string $name, mixed $value): Job
public function addData(string $name, mixed $value): mixed
{
$data = json_decode($this->data, true);
$data[$name] = $value;
if (!empty($name)) {
$this->addDataItem($data, $name, $value);
}
$this->data = json_encode($data);
return $this;
}
private function addDataItem(array &$data, array|string $name, mixed $value): bool
{
$names = is_array($name) ? $name : explode('.', $name);
$current = $names[0];
if(isset($data[$current]) && is_array($data[$current])) {
unset($names[0]);
$this->addDataItem($data[$current], array_values($names), $value);
} else {
$data[$names[0]] = $value;
}
return true;
}
public function removeData(?string $name = ''): mixed
{
$data = json_decode($this->data, true);
if (!empty($name)) {
$this->removeDataItem($data, $name);
}
return $this;
}
private function removeDataItem(array &$data, array|string $name): bool
{
$names = is_array($name) ? $name : explode('.', $name);
$current = $names[0];
if(is_array($data[$current])) {
unset($names[0]);
$this->removeDataItem($data[$current], array_values($names));
} elseif(!isset($data[$current])) {
return false;
} else {
unset($names[0]);
}
return true;
}
public function hasData($name): bool
{
return !empty($this->getData($name));
}
/**
* @return int
*/

View File

@ -25,7 +25,7 @@ class JobRepository extends EntityRepository
$return = [];
foreach($jobs as $job) {
if($job->getData()['needschecking']) {
if($job->getData('needschecking')) {
$return[] = $job;
}
}
@ -35,13 +35,14 @@ class JobRepository extends EntityRepository
public function getRunningJobs(bool $idiskey = false): array
{
$qb = $this->createQueryBuilder('job');
return $qb->where('job.running != 0')->getQuery()->getResult();
return $qb
->where('job.running != 0')
->getQuery()->getResult();
}
public function getAllJobs(bool $idiskey = false)
{
$qb = $this->createQueryBuilder('job');
/** @var Job[] $jobs */
$jobs = $qb
->orderBy('job.name')
@ -79,19 +80,29 @@ class JobRepository extends EntityRepository
public function getJobsDue()
{
$jobsSql = "SELECT id, running
FROM job
WHERE (
nextrun <= :timestamp
AND (lastrun IS NULL OR lastrun > :timestamplastrun)
AND running IN (0,2)
)
OR (running NOT IN (0,1,2) AND running < :timestamprun)
OR (running = 2)";
$jobsStmt = $this->getEntityManager()->getConnection()->prepare($jobsSql);
$jobsRslt = $jobsStmt->executeQuery([':timestamp' => time(), ':timestamplastrun' => time(), ':timestamprun' => time()]);
$jobs = $jobsRslt->fetchAllAssociative();
return $jobs;
$qb = $this->createQueryBuilder('job');
return $qb
->where(
$qb->expr()->andX(
$qb->expr()->lte('job.nextrun', ':timestamp'),
$qb->expr()->orX(
$qb->expr()->isNull('job.lastrun'),
$qb->expr()->gt('job.lastrun', ':timestamp')
),
$qb->expr()->in('job.running', [0,2])
)
)
->orWhere(
$qb->expr()->andX(
$qb->expr()->notIn('job.running', [0,1,2]),
$qb->expr()->lt('job.running', ':timestamp')
)
)
->orWhere('job.running = 2')
->orderBy('job.running', 'DESC')
->addOrderBy('job.nextrun', 'ASC')
->setParameter(':timestamp', time())
->getQuery()->getResult();
}
public function getTimeOfNextRun()
@ -186,25 +197,25 @@ class JobRepository extends EntityRepository
return $result['temp_vars'][$name] ?? $default;
}
private function runHttpJob(array $job): array
private function runHttpJob(Job $job): array
{
$client = new Client();
if(!empty($job['data']['vars'])) {
foreach($job['data']['vars'] as $key => $var) {
if (!empty($job['data']['basicauth-username'])) $job['data']['basicauth-username'] = str_replace('{' . $key . '}', $var['value'], $job['data']['basicauth-username']);
$job['data']['url'] = str_replace('{' . $key . '}', $var['value'], $job['data']['url']);
if(!empty($job->getData('vars'))) {
foreach($job->getData('vars') as $key => $var) {
if (!empty($job->getData('basicauth-username'))) $job->addData('basicauth-username', str_replace('{' . $key . '}', $var['value'], $job->getData('basicauth-username')));
$job->addData('url', str_replace('{' . $key . '}', $var['value'], $job->getData('url')));
}
}
$url = $job['data']['url'];
$url = $job->getData('url');
$options['http_errors'] = false;
$options['auth'] = !empty($job['data']['basicauth-username']) ? [$job['data']['basicauth-username'], $job['data']['basicauth-password']] : NULL;
$options['auth'] = !empty($job->getData('basicauth-username')) ? [$job->getData('basicauth-username'), $job->getData('basicauth-password')] : NULL;
try {
$res = $client->request('GET', $url, $options);
$return['exitcode'] = $res->getStatusCode();
$return['output'] = $res->getBody();
$return['failed'] = !in_array($return['exitcode'], $job['data']['http-status']);
$return['failed'] = !in_array($return['exitcode'], $job->getData('http-status'));
} catch(GuzzleException $exception) {
$return['exitcode'] = $exception->getCode();
$return['output'] = $exception->getMessage();
@ -214,25 +225,25 @@ class JobRepository extends EntityRepository
return $return;
}
private function runCommandJob(array $job): array
private function runCommandJob(Job $job): array
{
if(!empty($job['data']['vars'])) {
foreach ($job['data']['vars'] as $key => $var) {
$job['data']['command'] = str_replace('{' . $key . '}', $var['value'], $job['data']['command']);
if(!empty($job->getData('vars'))) {
foreach ($job->getData('vars') as $key => $var) {
$job->addData('command', str_replace('{' . $key . '}', $var['value'], $job->getData('command')));
}
}
$command = $job['data']['command'];
if ($job['data']['containertype'] == 'docker') {
$command = $this->prepareDockerCommand($command, $job['data']['service'], $job['data']['container-user']);
$command = $job->getData('command');
if ($job->getData('containertype') == 'docker') {
$command = $this->prepareDockerCommand($command, $job->getData('service'), $job->getData('container-user'));
}
try {
if($job['data']['hosttype'] == 'local') {
if($job->getData('hosttype') == 'local') {
$return = $this->runLocalCommand($command);
} elseif($job['data']['hosttype'] == 'ssh') {
$return = $this->runSshCommand($command, $job['data']['host'], $job['data']['user'], $job['data']['ssh-privkey'], $job['data']['privkey-password']);
} elseif($job->getData('hosttype') == 'ssh') {
$return = $this->runSshCommand($command, $job->getData('host'), $job->getData('user'), $job->getData('ssh-privkey'), $job->getData('privkey-password'));
}
$return['failed'] = !in_array($return['exitcode'], $job['data']['response']);
$return['failed'] = !in_array($return['exitcode'], $job->getData('response'));
} catch (\RuntimeException $exception) {
$return['exitcode'] = $exception->getCode();
$return['output'] = $exception->getMessage();
@ -253,7 +264,7 @@ class JobRepository extends EntityRepository
return $return;
}
private function runSshCommand(string $command, string $host, string $user, string $privkey, string $password): array
private function runSshCommand(string $command, string $host, string $user, ?string $privkey, ?string $password): array
{
$ssh = new SSH2($host);
$key = null;
@ -378,14 +389,13 @@ class JobRepository extends EntityRepository
public function runJob(int $job, bool $manual): array
{
global $kernel;
$starttime = microtime(true);
$job = $this->getJob($job, true);
if ($job['data']['crontype'] == 'http') {
if ($job->getData('crontype') == 'http') {
$result = $this->runHttpJob($job);
} elseif ($job['data']['crontype'] == 'command') {
} elseif ($job->getData('crontype') == 'command') {
$result = $this->runCommandJob($job);
} elseif ($job['data']['crontype'] == 'reboot') {
} elseif ($job->getData('crontype') == 'reboot') {
$result = $this->runRebootJob($job, $starttime, $manual);
if(isset($result['status']) && $result['status'] == 'deferred') return $result;
}
@ -405,8 +415,8 @@ class JobRepository extends EntityRepository
}
// Remove secrets from output
if(!empty($job['data']['vars'])) {
foreach($job['data']['vars'] as $key => $var) {
if(!empty($job->getData('vars'))) {
foreach($job->getData('vars') as $key => $var) {
if ($var['issecret']) {
$result['output'] = str_replace($var['value'], '{'.$key.'}', $result['output']);
}
@ -415,20 +425,20 @@ class JobRepository extends EntityRepository
// saving to database
$this->getEntityManager()->getConnection()->close();
$runRepo = $this->getEntityManager()->getRepository(Run::class);
$runRepo->addRun($job['id'], $result['exitcode'], floor($starttime), $runtime, $result['output'], $flags);
$runRepo->addRun($job->getId(), $result['exitcode'], floor($starttime), $runtime, $result['output'], $flags);
if (!$manual){
// setting nextrun to next run
$nextrun = $job['nextrun'];
$nextrun = $job->getNextrun();
do {
$nextrun = $nextrun + $job['interval'];
$nextrun = $nextrun + $job->getInterval();
} while ($nextrun < time());
$addRunSql = 'UPDATE job SET nextrun = :nextrun WHERE id = :id';
$addRunStmt = $this->getEntityManager()->getConnection()->prepare($addRunSql);
$addRunStmt->executeQuery([':id' => $job['id'], ':nextrun' => $nextrun]);
$addRunStmt->executeQuery([':id' => $job->getId(), ':nextrun' => $nextrun]);
}
return ['job_id' => $job['id'], 'exitcode' => $result['exitcode'], 'timestamp' =>floor($starttime), 'runtime' => $runtime, 'output' => (string)$result['output'], 'flags' => implode("", $flags)];
return ['job_id' => $job->getId(), 'exitcode' => $result['exitcode'], 'timestamp' =>floor($starttime), 'runtime' => $runtime, 'output' => (string)$result['output'], 'flags' => implode("", $flags)];
}
public function unlockJob(int $id = 0): void
@ -618,50 +628,47 @@ class JobRepository extends EntityRepository
}
public function getJob(int $id, bool $withSecrets = false) {
$jobSql = "SELECT * FROM job WHERE id = :id";
$jobStmt = $this->getEntityManager()->getConnection()->prepare($jobSql);
$jobRslt = $jobStmt->executeQuery([':id' => $id])->fetchAssociative();
$job = $this->find($id);
$jobRslt['data'] = json_decode($jobRslt['data'], true);
if(!empty($jobRslt['data']['vars'])) {
foreach ($jobRslt['data']['vars'] as $key => &$value) {
if(!empty($job->getData('vars'))) {
foreach ($job->getData('vars') as $key => &$value) {
if ($value['issecret']) {
$value['value'] = ($withSecrets) ? Secret::decrypt(base64_decode($value['value'])) : '';
$job->addData('vars.' . $key . '.value', ($withSecrets) ? Secret::decrypt(base64_decode($value['value'])) : '');
}
}
}
switch($jobRslt['data']['crontype']) {
switch($job->getData('crontype')) {
case 'http':
if(isset($jobRslt['data']['vars']['basicauth-password']['value'])) {
$jobRslt['data']['basicauth-password'] = $jobRslt['data']['vars']['basicauth-password']['value'];
unset($jobRslt['data']['vars']['basicauth-password']);
if($job->hasData('vars.basicauth-password.value')) {
$job->addData('basicauth-password', $job->getData('vars.basicauth-password.value'));
$job->removeData('vars.basicauth-password');
}
break;
case 'reboot':
$jobRslt['data']['reboot-delay'] = $jobRslt['data']['vars']['reboot-delay']['value'];
$jobRslt['data']['reboot-delay-secs'] = $jobRslt['data']['vars']['reboot-delay-secs']['value'];
unset($jobRslt['data']['vars']['reboot-delay']);
unset($jobRslt['data']['vars']['reboot-delay-secs']);
$job->addData('reboot-delay', $job->getData('vars.reboot-delay.value'));
$job->addData('reboot-delay-secs', $job->getData('vars.reboot-delay-secs.value'));
$job->removeData('vars.reboot-delay');
$job->removeData('vars.reboot-delay-secs');
break;
}
switch($jobRslt['data']['hosttype']) {
switch($job->getData('hosttype')) {
case 'ssh':
if(isset($jobRslt['data']['vars']['ssh-privkey']['value'])) {
$jobRslt['data']['ssh-privkey'] = $jobRslt['data']['vars']['ssh-privkey']['value'];
unset($jobRslt['data']['vars']['ssh-privkey']);
if($job->hasData('vars.ssh-privkey.value')) {
$job->addData('ssh-privkey', $job->getData('vars.ssh-privkey.value'));
$job->removeData('vars.ssh-privkey');
}
if(isset($jobRslt['data']['vars']['privkey-password']['value'])) {
$jobRslt['data']['privkey-password'] = $jobRslt['data']['vars']['privkey-password']['value'];
unset($jobRslt['data']['vars']['privkey-password']);
if($job->hasData('vars.privkey-password.value')) {
$job->addData('privkey-password', $job->getData('vars.privkey-password.value'));
$job->removeData('vars.privkey-password');
}
break;
}
if($jobRslt['data']['crontype'] == 'http') {
if($job->getData('crontype') == 'http') {
}
return $jobRslt;
return $job;
}
public function deleteJob(int $id)

View File

@ -16,20 +16,20 @@ class RunRepository extends EntityRepository
public function getRunsForJob(int $id, bool $onlyfailed = false, int $maxage = NULL, bool $ordered = true): array
{
$runsSql = "SELECT * FROM run WHERE job_id = :job";
$params = [':job' => $id];
$qb = $this->createQueryBuilder('run');
$job = $this->getEntityManager()->getRepository(Job::class)->find($id);
$runs = $qb
->where('run.job = :job')
->setParameter(':job', $job);
if ($onlyfailed) {
$runsSql .= ' AND flags LIKE "%' . RunRepository::FAILED . '%"';
$runs = $runs->andWhere('run.flags LIKE :flags')->setParameter(':flags', '%' . RunRepository::FAILED . '%');
}
if($maxage !== NULL) {
$runsSql .= ' AND timestamp > :timestamp';
$params[':timestamp'] = time() - ($maxage * 24 * 60 * 60);
$runs = $runs->andWhere('run.timestamp > :timestamp')->setParameter(':timestamp', time() - ($maxage * 24 * 60 * 60));
}
if ($ordered) $runsSql .= ' ORDER by timestamp DESC';
$runsStmt = $this->getEntityManager()->getConnection()->prepare($runsSql);
$runsRslt = $runsStmt->executeQuery($params);
$runs = $runsRslt->fetchAllAssociative();
return $runs;
if ($ordered) $runs->orderBy('run.timestamp', 'DESC');
return $runs->getQuery()->getResult();
}
public function addRun(int $jobid, string $exitcode, int $starttime, float $runtime, string $output, array $flags): void

View File

@ -2,12 +2,12 @@
{% block title %}Add job{% endblock %}
{% block content %}
<h2>Add a cronjob</h2>
<form method="post" class="form-horizontal" enctype="multipart/form-data" action="{{ path('job_edit', { id : id }) }}">
<form method="post" class="form-horizontal" enctype="multipart/form-data" action="{{ path('job_edit', { id : job.id }) }}">
<h3>General info</h3>
<div class="mb-3">
<label for="name">Name</label>
<input type="text" name="name" class="form-control" id="name" placeholder="System update" value="{{ name }}">
<input type="text" name="name" class="form-control" id="name" placeholder="System update" value="{{ job.name }}">
<small id="name-help" class="form-text text-muted">You can create colored tags by using [tag]</small>
</div>
<div class="mb-3">
@ -23,54 +23,54 @@
<li><a class="dropdown-item intervalpattern-item" href="#" data-time="604800">Every week</a></li>
<li><a class="dropdown-item intervalpattern-item" href="#" data-time="2419200">Every 4 weeks</a></li>
</ul>
<input type="number" class="form-control" id="interval" name="interval" value="{{ interval }}">
<input type="number" class="form-control" id="interval" name="interval" value="{{ job.interval }}">
</div>
</div>
<div class="mb-3">
<label for="nextrun">Next run</label>
<input type="text" autocomplete="off" pattern="[0-9]{2}\/[0-9]{2}\/[0-9]{4} [0-9]{2}:[0-9]{2}:[0-9]{2}" placeholder="{{ date() | date("d/m/Y H:i:s")}}" id="nextrunselector" class="form-control datetimepicker-input" data-target="#nextrunselector" data-bs-toggle="datetimepicker" name="nextrun" value="{{ nextrun | date("d/m/Y H:i:s")}}">
<input type="text" autocomplete="off" pattern="[0-9]{2}\/[0-9]{2}\/[0-9]{4} [0-9]{2}:[0-9]{2}:[0-9]{2}" placeholder="{{ date() | date("d/m/Y H:i:s")}}" id="nextrunselector" class="form-control datetimepicker-input" data-target="#nextrunselector" data-bs-toggle="datetimepicker" name="nextrun" value="{{ job.nextrun | date("d/m/Y H:i:s")}}">
</div>
<div class="mb-3">
<label for="lastrun">Last run</label>
<div class="input-group">
<div class="input-group-text border-end-0">
<input type="checkbox" name="lastrun-eternal" class="lastrun-eternal" placeholder="value" value="true"{% if lastrun is not defined or lastrun is empty %} checked{% endif %}>
<input type="checkbox" name="lastrun-eternal" class="lastrun-eternal" placeholder="value" value="true"{% if job.lastrun is not defined or job.lastrun is empty %} checked{% endif %}>
</div>
<span class="input-group-text border-start-0">Eternal</span>
<input type="text" autocomplete="off" pattern="[0-9]{2}\/[0-9]{2}\/[0-9]{4} [0-9]{2}:[0-9]{2}:[0-9]{2}" data-placeholder="{{ date() | date("d/m/Y H:i:s")}}" id="lastrunselector" class="form-control datetimepicker-input" data-target="#lastrunselector" data-bs-toggle="datetimepicker" name="lastrun"{% if lastrun is not defined or lastrun is empty %} disabled{% else %} value="{{ lastrun | date("d/m/Y H:i:s")}}"{% endif %}>
<input type="text" autocomplete="off" pattern="[0-9]{2}\/[0-9]{2}\/[0-9]{4} [0-9]{2}:[0-9]{2}:[0-9]{2}" data-placeholder="{{ date() | date("d/m/Y H:i:s")}}" id="lastrunselector" class="form-control datetimepicker-input" data-target="#lastrunselector" data-bs-toggle="datetimepicker" name="lastrun"{% if job.lastrun is not defined or job.lastrun is empty %} disabled{% else %} value="{{ job.lastrun | date("d/m/Y H:i:s")}}"{% endif %}>
</div>
</div>
<div class="mb-3">
<label for="retention">Retention (in days)</label>
<input type="number" name="retention" class="form-control" id="retention" placeholder="7" value="{% if attribute(data, 'retention') is defined %}{{ attribute(data, 'retention') }}{% endif %}">
<input type="number" name="retention" class="form-control" id="retention" placeholder="7" value="{% if attribute(job.data, 'retention') is defined %}{{ attribute(job.data, 'retention') }}{% endif %}">
<small id="retention-help" class="form-text text-muted">How many days (at least) to keep runs of this job in the database</small>
</div>
<div class="mb-3">
<label for="fail-pct">Max fail percentage</label>
<div class="input-group d-flex">
<div class="range-value range-value-fail-pct pe-1">{% if attribute(data, 'fail-pct') is defined %}{{ attribute(data, 'fail-pct') }}{% else %}50{% endif %}%</div>
<div class="range-value range-value-fail-pct pe-1">{% if attribute(job.data, 'fail-pct') is defined %}{{ attribute(job.data, 'fail-pct') }}{% else %}50{% endif %}%</div>
<div class="range-input ps-1 flex-grow-1">
<input type="range" name="fail-pct" class="form-range range-input-fail-pct" id="fail-pct" max="100" step="5" value="{% if attribute(data, 'fail-pct') is defined %}{{ attribute(data, 'fail-pct') }}{% else %}50{% endif %}">
<input type="range" name="fail-pct" class="form-range range-input-fail-pct" id="fail-pct" max="100" step="5" value="{% if attribute(job.data, 'fail-pct') is defined %}{{ attribute(job.data, 'fail-pct') }}{% else %}50{% endif %}">
</div>
</div>
</div>
<div class="mb-3">
<label for="fail-days">Number of days calculated for fail percentage</label>
<input type="number" name="fail-days" class="form-control" id="fail-days" placeholder="7" value="{% if attribute(data, 'fail-days') is defined %}{{ attribute(data, 'fail-days') }}{% endif %}">
<input type="number" name="fail-days" class="form-control" id="fail-days" placeholder="7" value="{% if attribute(job.data, 'fail-days') is defined %}{{ attribute(job.data, 'fail-days') }}{% endif %}">
</div>
<h3>Job details</h3>
<div class="mb-3 btn-group croncategory-selector">
<div class="dropdown croncategory-group crontype-group{% if data.crontype != 'http' %} btn-group{% endif %}">
<div class="dropdown croncategory-group crontype-group{% if job.data.crontype != 'http' %} btn-group{% endif %}">
<button class="btn btn-outline-primary dropdown-toggle" type="button" id="crontypeButton" data-default-text="Job type" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{% if data.crontype == 'command' %}
{% if job.data.crontype == 'command' %}
Command
{% elseif data.crontype == 'reboot' %}
{% elseif job.data.crontype == 'reboot' %}
Reboot
{% elseif data.crontype == 'http' %}
{% elseif job.data.crontype == 'http' %}
Http request
{% else %}
Job type
@ -83,11 +83,11 @@
</div>
</div>
<div class="dropdown croncategory-group hosttype-group{% if data.crontype != 'http' %} btn-group{% else %} d-none{% endif %}">
<div class="dropdown croncategory-group hosttype-group{% if job.data.crontype != 'http' %} btn-group{% else %} d-none{% endif %}">
<button class="btn btn-outline-primary dropdown-toggle" type="button" id="hosttypeButton" data-default-text="Host type" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{% if data.hosttype == 'local' %}
{% if job.data.hosttype == 'local' %}
Local
{% elseif data.hosttype == 'ssh' %}
{% elseif job.data.hosttype == 'ssh' %}
SSH
{% else %}
Host type
@ -99,15 +99,15 @@
</div>
</div>
{% if data.crontype == 'reboot' %}
{% if job.data.crontype == 'reboot' %}
</div>
<div id="btn-group-discriminator" class="d-none">
{% endif %}
<div class="dropdown croncategory-group containertype-group{% if data.crontype != 'http' %} btn-group{% else %} d-none{% endif %}">
<div class="dropdown croncategory-group containertype-group{% if job.data.crontype != 'http' %} btn-group{% else %} d-none{% endif %}">
<button class="btn btn-outline-primary dropdown-toggle" type="button" id="containertypeButton" data-default-text="Container" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{% if data.containertype == 'none' or data.containertype == '' %}
{% if job.data.containertype == 'none' or job.data.containertype == '' %}
None
{% elseif data.containertype == 'docker' %}
{% elseif job.data.containertype == 'docker' %}
Docker
{% else %}
Container
@ -120,99 +120,99 @@
</div>
</div>
<div class="crontype-command crontype-inputs croncategory-inputs{% if data.crontype != 'command' %} d-none{% endif %}">
<div class="crontype-command crontype-inputs croncategory-inputs{% if job.data.crontype != 'command' %} d-none{% endif %}">
<h4>Command details</h4>
<div class="mb-3">
<label for="command">Command</label>
<input type="text" name="command" class="form-control" id="command" placeholder="sudo apt update" value="{% if data.command is defined %}{{ data.command }}{% endif %}">
<input type="text" name="command" class="form-control" id="command" placeholder="sudo apt update" value="{% if job.data.command is defined %}{{ job.data.command }}{% endif %}">
</div>
<div class="mb-3">
<label for="response">Expected exit code</label>
<input type="text" name="response" class="form-control" id="response" placeholder="0" value="{% if data.response is defined %}{{ data.response | join(',') }}{% endif %}">
<input type="text" name="response" class="form-control" id="response" placeholder="0" value="{% if job.data.response is defined %}{{ job.data.response | join(',') }}{% endif %}">
</div>
</div>
<div class="crontype-reboot crontype-inputs croncategory-inputs{% if data.crontype != 'reboot' %} d-none{% endif %}">
<div class="crontype-reboot crontype-inputs croncategory-inputs{% if job.data.crontype != 'reboot' %} d-none{% endif %}">
<h4>Reboot job details</h4>
<div class="mb-3">
<label for="reboot-command">Reboot command</label>
<input type="text" name="reboot-command" class="form-control" id="reboot-command" placeholder="systemctl reboot" value="
{% if attribute(data, 'reboot-command') is defined %}
{{ attribute(data, 'reboot-command') }}
{% if attribute(job.data, 'reboot-command') is defined %}
{{ attribute(job.data, 'reboot-command') }}
{% endif %}">
<small id="reboot-command-help" class="form-text text-muted">Use {reboot-delay} or {reboot-delay-secs} to add the delay in your command</small>
</div>
<div class="mb-3">
<label for="getservices-command">Get services command</label>
<input type="text" name="getservices-command" class="form-control" id="getservices)command" placeholder="systemctl list-units" value="{% if attribute(data, 'getservices-command') is defined %}{{ attribute(data, 'getservices-command') }}{% endif %}">
<input type="text" name="getservices-command" class="form-control" id="getservices)command" placeholder="systemctl list-units" value="{% if attribute(job.data, 'getservices-command') is defined %}{{ attribute(job.data, 'getservices-command') }}{% endif %}">
</div>
<div class="mb-3">
<label for="getservices-response">Get services command exit code</label>
<input type="text" name="getservices-response" class="form-control" id="getservices-response" placeholder="0" value="{% if attribute(data, 'getservices-response') is defined %}{{ attribute(data, 'getservices-response') | join(',') }}{% endif %}">
<input type="text" name="getservices-response" class="form-control" id="getservices-response" placeholder="0" value="{% if attribute(job.data, 'getservices-response') is defined %}{{ attribute(job.data, 'getservices-response') | join(',') }}{% endif %}">
</div>
<div class="mb-3">
<label for="reboot-delay">Reboot delay (in minutes)</label>
<input type="number" name="reboot-delay" class="form-control" placeholder="5" value="{% if attribute(data, 'reboot-delay') is defined %}{{ attribute(data, 'reboot-delay') }}{% endif %}">
<input type="number" name="reboot-delay" class="form-control" placeholder="5" value="{% if attribute(job.data, 'reboot-delay') is defined %}{{ attribute(job.data, 'reboot-delay') }}{% endif %}">
<small id="reboot-delay-help" class="form-text text-muted">Delay between triggering reboot and actual reboot</small>
</div>
<div class="mb-3">
<label for="reboot-duration">Reboot duration (in minutes)</label>
<input type="number" name="reboot-duration" class="form-control" placeholder="10" value="{% if attribute(data, 'reboot-duration') is defined %}{{ attribute(data, 'reboot-duration') }}{% endif %}">
<input type="number" name="reboot-duration" class="form-control" placeholder="10" value="{% if attribute(job.data, 'reboot-duration') is defined %}{{ attribute(job.data, 'reboot-duration') }}{% endif %}">
<small id="reboot-duration-help" class="form-text text-muted">The amount of time the system takes to actually reboot</small>
</div>
</div>
<div class="crontype-http crontype-inputs croncategory-inputs{% if data.crontype != 'http' %} d-none{% endif %}">
<div class="crontype-http crontype-inputs croncategory-inputs{% if job.data.crontype != 'http' %} d-none{% endif %}">
<h4>HTTP request details</h4>
<div class="mb-3">
<label for="url">Url</label>
<input type="text" name="url" class="form-control" id="url" placeholder="https://scripts.example.com/" value="{% if data.url is defined %}{{ data.url }}{% endif %}">
<input type="text" name="url" class="form-control" id="url" placeholder="https://scripts.example.com/" value="{% if job.data.url is defined %}{{ job.data.url }}{% endif %}">
</div>
<div class="mb-3">
<label for="basicauth-username">Username for Basic-Auth</label>
<input type="text" name="basicauth-username" class="form-control" id="basicauth-username" placeholder="www-data" value="{% if attribute(data, 'basicauth-username') is defined %}{{ attribute(data, 'basicauth-username') }}{% endif %}">
<input type="text" name="basicauth-username" class="form-control" id="basicauth-username" placeholder="www-data" value="{% if attribute(job.data, 'basicauth-username') is defined %}{{ attribute(job.data, 'basicauth-username') }}{% endif %}">
</div>
<div class="mb-3">
<label for="basicauth-password">Password for Basic-Auth</label>
<input type="password" name="basicauth-password" class="form-control" placeholder="correct horse battery staple" value="{% if attribute(data, 'basicauth-password') is defined %}{{ attribute(data, 'basicauth-password') }}{% endif %}">
<input type="password" name="basicauth-password" class="form-control" placeholder="correct horse battery staple" value="{% if attribute(job.data, 'basicauth-password') is defined %}{{ attribute(job.data, 'basicauth-password') }}{% endif %}">
<small id="basicauth-password-help" class="form-text text-muted">This field is being saved as a secret</small>
</div>
<div class="mb-3">
<label for="http-status">Expected http status code</label>
<input type="text" name="http-status" class="form-control" id="http-status" placeholder="200" value="{% if attribute(data, 'http-status') is defined %}{{ attribute(data, 'http-status') | join(',')}}{% endif %}">
<input type="text" name="http-status" class="form-control" id="http-status" placeholder="200" value="{% if attribute(job.data, 'http-status') is defined %}{{ attribute(job.data, 'http-status') | join(',')}}{% endif %}">
</div>
</div>
<div class="hosttype-local hosttype-inputs croncategory-inputs{% if data.hosttype != 'local' %} d-none{% endif %}">
<div class="hosttype-local hosttype-inputs croncategory-inputs{% if job.data.hosttype != 'local' %} d-none{% endif %}">
<h4>Localhost details</h4>
<h5>No options</h5>
</div>
<div class="hosttype-ssh hosttype-inputs croncategory-inputs{% if data.hosttype != 'ssh' %} d-none{% endif %}">
<div class="hosttype-ssh hosttype-inputs croncategory-inputs{% if job.data.hosttype != 'ssh' %} d-none{% endif %}">
<h4>SSH host details</h4>
<div class="mb-3">
<label for="host">Hostname</label>
<input type="text" name="host" class="form-control" id="host" placeholder="ssh.abc.xyz" value="{% if data.host is defined %}{{ data.host }}{% endif %}">
<input type="text" name="host" class="form-control" id="host" placeholder="ssh.abc.xyz" value="{% if job.data.host is defined %}{{ job.data.host }}{% endif %}">
</div>
<div class="mb-3">
<label for="user">Username</label>
<input type="text" name="user" class="form-control" id="user" placeholder="larry" value="{% if data.user is defined %}{{ data.user }}{% endif %}">
<input type="text" name="user" class="form-control" id="user" placeholder="larry" value="{% if job.data.user is defined %}{{ job.data.user }}{% endif %}">
</div>
<div class="mb-3">
<label for="privkey">Private key</label>
<div class="input-group">
<span class=" input-group-text border-end-0">
<input type="checkbox" name="privkey-keep" class="privkey-keep" value="true" data-privkey="{% if attribute(data, 'ssh-privkey') is defined %}{{ attribute(data, 'ssh-privkey') }}{% endif %}" checked>
<input type="checkbox" name="privkey-keep" class="privkey-keep" value="true" data-privkey="{% if attribute(job.data, 'ssh-privkey') is defined %}{{ attribute(job.data, 'ssh-privkey') }}{% endif %}" checked>
</span>
<input type="hidden" name="privkey-orig" class="privkey-orig" value="{% if attribute(data, 'ssh-privkey') is defined %}{{ attribute(data, 'ssh-privkey') }}{% endif %}">
<input type="hidden" name="privkey-orig" class="privkey-orig" value="{% if attribute(job.data, 'ssh-privkey') is defined %}{{ attribute(job.data, 'ssh-privkey') }}{% endif %}">
<span class="input-group-text border-start-0">Keep</span>
<input type="file" id="privkey" name="privkey" class="form-control " disabled>
</div>
@ -221,25 +221,25 @@
<div class="mb-3">
<label for="privkey-password">Password for private key</label>
<input type="password" name="privkey-password" class="form-control" placeholder="correct horse battery staple" value="{% if attribute(data, 'privkey-password') is defined %}{{ attribute(data, 'privkey-password') }}{% endif %}">
<input type="password" name="privkey-password" class="form-control" placeholder="correct horse battery staple" value="{% if attribute(job.data, 'privkey-password') is defined %}{{ attribute(job.data, 'privkey-password') }}{% endif %}">
<small id="privkey-password-help" class="form-text text-muted">If private key is empty this field is being used as ssh-password</small>
<small id="privkey-password-help-2" class="form-text text-muted">This field is being saved as a secret</small>
</div>
</div>
<div class="containertype-none containertype-inputs croncategory-inputs{% if data.containertype != 'none' %} d-none{% endif %}">
<div class="containertype-none containertype-inputs croncategory-inputs{% if job.data.containertype != 'none' %} d-none{% endif %}">
</div>
<div class="containertype-docker containertype-inputs croncategory-inputs{% if data.containertype != 'docker' %} d-none{% endif %}">
<div class="containertype-docker containertype-inputs croncategory-inputs{% if job.data.containertype != 'docker' %} d-none{% endif %}">
<h4>Docker container details</h4>
<div class="mb-3">
<label for="service">Service</label>
<input type="text" name="service" class="form-control" id="service" placeholder="mysql" value="{% if attribute(data, 'service') is defined %}{{ attribute(data, 'service') }}{% endif %}">
<input type="text" name="service" class="form-control" id="service" placeholder="mysql" value="{% if attribute(job.data, 'service') is defined %}{{ attribute(job.data, 'service') }}{% endif %}">
</div>
<div class="mb-3">
<label for="container-user">Username</label>
<input type="text" name="container-user" class="form-control" id="container-user" placeholder="larry" value="{% if attribute(data, 'container-user') is defined %}{{ attribute(data, 'container-user') }}{% endif %}">
<input type="text" name="container-user" class="form-control" id="container-user" placeholder="larry" value="{% if attribute(job.data, 'container-user') is defined %}{{ attribute(job.data, 'container-user') }}{% endif %}">
</div>
</div>
@ -255,8 +255,9 @@
<input type="text" name="var-value[0]" class="form-control var-value" placeholder="value">
</div>
{% set key = 1 %}
{% if data.vars is defined %}
{% for id,var in data.vars %}
{% if job.data.vars is defined %}
Im defined!
{% for id,var in job.data.vars %}
<div class="input-group var-group">
<div class="input-group-text border-end-0">
<input type="checkbox" name="var-issecret[{{ key }}]" class="var-issecret" placeholder="value" value="true"{% if var.issecret %} checked{% endif %}>
@ -278,9 +279,9 @@
<a href="#" class="btn btn-outline-primary addvar-btn">Add variable</a>
</div>
<input type="hidden" name="crontype" class="crontype" value="{{ data.crontype }}">
<input type="hidden" name="hosttype" class="hosttype" value="{{ data.hosttype }}">
<input type="hidden" name="containertype" class="containertype" value="{{ data.containertype }}">
<input type="hidden" name="crontype" class="crontype" value="{{ job.data.crontype }}">
<input type="hidden" name="hosttype" class="hosttype" value="{{ job.data.hosttype }}">
<input type="hidden" name="containertype" class="containertype" value="{{ job.data.containertype }}">
<button type="submit" class="btn btn-outline-primary">Submit</button>
</form>