diff --git a/index.html b/index.html index 90c857b..0e888c4 100644 --- a/index.html +++ b/index.html @@ -219,9 +219,11 @@ function showJobMenu(event, jobId) { const st = schedule[jobId].state; if (st == 'SUCCESSFUL' || st == 'FAILED') { menuView.appendChild(makeEntry('↺ Retry', () => restartJob(jobId))); + } + if (st == 'SUCCESSFUL' || st == 'FAILED' || st == 'SCHEDULED') { menuView.appendChild(makeEntry('🗑️ Delete', () => deleteJob(jobId))); } - if (st == 'RUNNING' || st == 'SCHEDULED') { + if (st == 'RUNNING') { menuView.appendChild(makeEntry('🗴 Cancel', () => cancelJob(jobId))); } menuView.appendChild(makeEntry('🗎 View Logs', () => viewJobLogs(jobId))); diff --git a/test-dude.py b/test-dude.py index e85cdea..c21d9e7 100644 --- a/test-dude.py +++ b/test-dude.py @@ -64,6 +64,8 @@ class ScheduledTest: if k not in ScheduledTest._unserialized_slots: if k != 'id': setattr(a, k, dict[k]) + if a.state == 'RUNNING': + a.state = 'SCHEDULED' return a @@ -81,8 +83,13 @@ class Runner(threading.Thread): self.program = program self.process = None self.job = None + + # Event used for stopping the runner self.stop_event = threading.Event() + # Event used to abort only the current job + self.abort_event = threading.Event() + threading.Thread.__init__(self) self.name += "-%s" % template self.start() @@ -96,8 +103,12 @@ class Runner(threading.Thread): } def stop(self): + self.abort_event.set() self.stop_event.set() + def abort_current_job(self): + self.abort_event.set() + def lock_new_job(self): my_queue = [ s for s in schedule @@ -124,6 +135,7 @@ class Runner(threading.Thread): def do_job(self): self.job.time_started = int(time.time()) + self.abort_event.clear() cmd = [] cmd += self.program @@ -139,7 +151,7 @@ class Runner(threading.Thread): stderr=err_fd ) while self.process.poll() is None: - if self.stop_event.wait(timeout=1): + if self.abort_event.wait(timeout=1): self.process.send_signal(signal.SIGINT) try: self.process.wait(timeout=1) @@ -201,6 +213,27 @@ class DeleteJob(Resource): return 'Job is already running', 400 +class CancelJob(Resource): + def get(self, job_id): + job = [job for job in schedule if job.id == job_id] + job = job[0] if len(job) > 0 else None + if job is None: + return 'Job not found', 404 + + with job.lock: + if job.state == 'RUNNING': + r = [r for r in runners if r if r.job == job] + if len(r) == 0: + return 'Job runner not found', 404 + process = r[0].process + if process is None: + return 'Job runner process not found', 404 + process.send_signal(signal.SIGINT) + return jsonify({'success': True}) + else: + return 'Job is not running', 400 + + class RestartJob(Resource): def get(self, job_id): job = [job for job in schedule if job.id == job_id] @@ -238,6 +271,7 @@ class ScheduleJob(Resource): api.add_resource(Status, '/status') api.add_resource(ScheduleJob, '/schedule_test') +api.add_resource(CancelJob, '/cancel_test/') api.add_resource(RestartJob, '/restart_test/') api.add_resource(DeleteJob, '/delete_test/')