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/')