mirror of
https://github.com/YunoHost/yunorunner.git
synced 2024-09-03 20:05:52 +02:00
[enh] uses uuid in job urls instead
This commit is contained in:
parent
f1dad89c83
commit
79cc2cad2f
6 changed files with 41 additions and 25 deletions
|
@ -22,6 +22,7 @@ class Repo(peewee.Model):
|
|||
|
||||
class Job(peewee.Model):
|
||||
name = peewee.CharField()
|
||||
uuid = peewee.CharField(unique=True)
|
||||
url_or_path = peewee.CharField()
|
||||
|
||||
state = peewee.CharField(choices=(
|
||||
|
|
50
run.py
50
run.py
|
@ -4,6 +4,7 @@
|
|||
import os
|
||||
import sys
|
||||
import argh
|
||||
import string
|
||||
import random
|
||||
import logging
|
||||
import asyncio
|
||||
|
@ -106,6 +107,10 @@ subscriptions = defaultdict(list)
|
|||
jobs_in_memory_state = {}
|
||||
|
||||
|
||||
def generate_random_uuid():
|
||||
return ''.join(random.SystemRandom().choice(string.ascii_letters + string.digits) for n in range(10))
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def wait_closed(self):
|
||||
"""
|
||||
|
@ -134,7 +139,7 @@ def reset_busy_workers():
|
|||
|
||||
|
||||
def merge_jobs_on_startup():
|
||||
task_logger.info(f"looks for jobs to merge on startup")
|
||||
task_logger.info("looks for jobs to merge on startup")
|
||||
|
||||
query = Job.select().where(Job.state == "scheduled").order_by(Job.name, -Job.id)
|
||||
|
||||
|
@ -174,6 +179,7 @@ async def create_job(app_id, repo_url, job_comment=""):
|
|||
|
||||
job = Job.create(
|
||||
name=job_name,
|
||||
uuid=generate_random_uuid(),
|
||||
url_or_path=repo_url,
|
||||
state="scheduled",
|
||||
)
|
||||
|
@ -525,6 +531,7 @@ async def ws_index(request, websocket):
|
|||
selected_fields = (
|
||||
Job.id,
|
||||
Job.name,
|
||||
Job.uuid,
|
||||
Job.url_or_path,
|
||||
Job.state,
|
||||
Job.created_time,
|
||||
|
@ -558,6 +565,8 @@ async def ws_index(request, websocket):
|
|||
|
||||
first_chunck = next(data)
|
||||
|
||||
print(first_chunck)
|
||||
|
||||
await websocket.send(ujson.dumps({
|
||||
"action": "init_jobs",
|
||||
"data": first_chunck, # send first chunk
|
||||
|
@ -572,10 +581,10 @@ async def ws_index(request, websocket):
|
|||
await websocket.wait_closed()
|
||||
|
||||
|
||||
@app.websocket('/job-<job_id>-ws')
|
||||
@app.websocket('/job-<job_uuid>-ws')
|
||||
@clean_websocket
|
||||
async def ws_job(request, websocket, job_id):
|
||||
job = Job.select().where(Job.id == job_id)
|
||||
async def ws_job(request, websocket, job_uuid):
|
||||
job = Job.select().where(Job.uuid == job_uuid)
|
||||
|
||||
if job.count == 0:
|
||||
raise NotFound()
|
||||
|
@ -611,6 +620,7 @@ async def ws_apps(request, websocket):
|
|||
"random_job_day",
|
||||
"job_id",
|
||||
"job_name",
|
||||
"job_uuid",
|
||||
"job_state",
|
||||
"created_time",
|
||||
"started_time",
|
||||
|
@ -621,6 +631,7 @@ async def ws_apps(request, websocket):
|
|||
SELECT
|
||||
"t1"."id" as "job_id",
|
||||
"t1"."name" as "job_name",
|
||||
"t1"."uuid" as "job_uuid",
|
||||
"t1"."url_or_path",
|
||||
"t1"."state" as "job_state",
|
||||
"t1"."created_time",
|
||||
|
@ -659,6 +670,7 @@ async def ws_apps(request, websocket):
|
|||
"random_job_day": x.random_job_day,
|
||||
"job_id": x.job_id,
|
||||
"job_name": x.job_name,
|
||||
"job_uuid": x.job_uuid,
|
||||
"job_state": x.job_state,
|
||||
"created_time": datetime.strptime(x.created_time.split(".")[0], '%Y-%m-%d %H:%M:%S') if x.created_time else None,
|
||||
"started_time": datetime.strptime(x.started_time.split(".")[0], '%Y-%m-%d %H:%M:%S') if x.started_time else None,
|
||||
|
@ -679,6 +691,7 @@ async def ws_apps(request, websocket):
|
|||
"random_job_day": repo.random_job_day,
|
||||
"job_id": None,
|
||||
"job_name": None,
|
||||
"job_uuid": None,
|
||||
"job_state": None,
|
||||
"created_time": None,
|
||||
"started_time": None,
|
||||
|
@ -736,9 +749,9 @@ def require_token():
|
|||
token = request.headers["X-Token"].strip()
|
||||
|
||||
if token not in tokens:
|
||||
api_logger.warning(f"someone tried to access the API using "
|
||||
"the {token} but it's not a valid token in "
|
||||
"the 'tokens' file")
|
||||
api_logger.warning(f"someone tried to access the API using \
|
||||
the {token} but it's not a valid token in \
|
||||
the 'tokens' file")
|
||||
return response.json({'status': 'invalide token'}, 403)
|
||||
|
||||
result = await f(request, *args, **kwargs)
|
||||
|
@ -752,11 +765,12 @@ def require_token():
|
|||
async def api_new_job(request):
|
||||
job = Job.create(
|
||||
name=request.json["name"],
|
||||
uuid=generate_random_uuid(),
|
||||
url_or_path=request.json["url_or_path"],
|
||||
created_time=datetime.now(),
|
||||
)
|
||||
|
||||
api_logger.info(f"Request to add new job '{job.name}' [{job.id}]")
|
||||
api_logger.info(f"Request to add new job '{job.name}' [{job.id}/{job.uuid}]")
|
||||
|
||||
await broadcast({
|
||||
"action": "new_job",
|
||||
|
@ -795,7 +809,7 @@ async def api_delete_job(request, job_id):
|
|||
# no need to check if job exist, api_stop_job will do it for us
|
||||
job = Job.select().where(Job.id == job_id)[0]
|
||||
|
||||
api_logger.info(f"Request to delete job '{job.name}' [{job.id}]")
|
||||
api_logger.info(f"Request to delete job '{job.name}' [{job.id}/{job.uuid}]")
|
||||
|
||||
data = model_to_dict(job)
|
||||
job.delete_instance()
|
||||
|
@ -910,10 +924,10 @@ async def api_badge_job(request, job_id):
|
|||
})
|
||||
|
||||
|
||||
@app.route('/job/<job_id>')
|
||||
@app.route('/job/<job_uuid>')
|
||||
@jinja.template('job.html')
|
||||
async def html_job(request, job_id):
|
||||
job = Job.select().where(Job.id == job_id)
|
||||
async def html_job(request, job_uuid):
|
||||
job = Job.select().where(Job.uuid == job_uuid)
|
||||
|
||||
if job.count == 0:
|
||||
raise NotFound()
|
||||
|
@ -989,7 +1003,7 @@ async def github(request):
|
|||
# (which also allows to only enable this feature if
|
||||
# we define the webhook secret)
|
||||
if not os.path.exists("./github_webhook_secret") or not os.path.exists("./github_bot_token"):
|
||||
api_logger.info(f"Received a webhook but no ./github_webhook_secret or ./github_bot_token file exists ... ignoring")
|
||||
api_logger.info("Received a webhook but no ./github_webhook_secret or ./github_bot_token file exists ... ignoring")
|
||||
abort(403)
|
||||
|
||||
# Only SHA1 is supported
|
||||
|
@ -1008,7 +1022,7 @@ async def github(request):
|
|||
mac = hmac.new(secret.encode(), msg=request.body, digestmod=hashlib.sha1)
|
||||
|
||||
if not hmac.compare_digest(str(mac.hexdigest()), str(signature)):
|
||||
api_logger.info(f"Received a webhook but signature authentication failed (is the secret properly configured?)")
|
||||
api_logger.info("Received a webhook but signature authentication failed (is the secret properly configured?)")
|
||||
abort(403, "Bad signature ?!")
|
||||
|
||||
hook_type = request.headers.get("X-Github-Event")
|
||||
|
@ -1018,9 +1032,9 @@ async def github(request):
|
|||
# - *New* comments
|
||||
# - On issue/PRs which are still open
|
||||
if hook_type != "issue_comment" \
|
||||
or hook_infos["action"] != "created" \
|
||||
or hook_infos["issue"]["state"] != "open" \
|
||||
or "pull_request" not in hook_infos["issue"]:
|
||||
or hook_infos["action"] != "created" \
|
||||
or hook_infos["issue"]["state"] != "open" \
|
||||
or "pull_request" not in hook_infos["issue"]:
|
||||
# Nothing to do but success anyway (204 = No content)
|
||||
abort(204, "Nothing to do")
|
||||
|
||||
|
@ -1071,7 +1085,7 @@ async def github(request):
|
|||
async with session.post(comments_url, data=ujson.dumps({"body": body})) as resp:
|
||||
api_logger.info("Added comment %s" % resp.json()["html_url"])
|
||||
|
||||
catchphrases = ["Alrighty!", "Fingers crossed!", "May the CI gods be with you!", ":carousel_horse:", ":rocket:", ":sunflower:", "Meow :cat2:", ":v:", ":stuck_out_tongue_winking_eye:" ]
|
||||
catchphrases = ["Alrighty!", "Fingers crossed!", "May the CI gods be with you!", ":carousel_horse:", ":rocket:", ":sunflower:", "Meow :cat2:", ":v:", ":stuck_out_tongue_winking_eye:"]
|
||||
catchphrase = random.choice(catchphrases)
|
||||
# Dirty hack with base_url passed from cmd argument because we can't use request.url_for because Sanic < 20.x
|
||||
job_url = app.config.base_url + app.url_for("html_job", job_id=job.id)
|
||||
|
|
|
@ -13,8 +13,8 @@
|
|||
<th>Started time</th>
|
||||
<th>End time</th>
|
||||
</thead>
|
||||
<tr v-for="(job, index) in jobs" :id="job.id" v-bind:class="[{deleted: job.deleted}, job.state + 'Job']">
|
||||
<td><a v-if="!job.deleted" v-bind:href="'<{ relative_path_to_root }>job/' + job.id">#{{job.id}}</a><span v-if="job.deleted">#{{job.id}} (deleted)</span></td>
|
||||
<tr v-for="(job, index) in jobs" :id="job.uuid" v-bind:class="[{deleted: job.deleted}, job.state + 'Job']">
|
||||
<td><a v-if="!job.deleted" v-bind:href="'<{ relative_path_to_root }>job/' + job.uuid">#{{job.uuid}}</a><span v-if="job.deleted">#{{job.id}} (deleted)</span></td>
|
||||
<td>{{job.state}}</td>
|
||||
<td>{{timestampToDate(job.created_time)}}</td>
|
||||
<td>{{timestampToDate(job.started_time)}}</td>
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
<template v-if="apps.length > 0">
|
||||
<tr v-for="(app, index) in apps" :id="app.id" v-bind:class="[app.job_state + 'Job']">
|
||||
<td><a v-bind:href="app.name + '/'">{{app.name}}</a></td>
|
||||
<td><a v-bind:href="'<{ relative_path_to_root }>job/' + app.job_id">#{{app.job_id}}</a> <span v-bind:class="['tag', jobStateTagClass(app.job_state)]">{{app.job_state}}</span></td>
|
||||
<td><a v-bind:href="'<{ relative_path_to_root }>job/' + app.job_uuid">#{{app.job_uuid}}</a> <span v-bind:class="['tag', jobStateTagClass(app.job_state)]">{{app.job_state}}</span></td>
|
||||
<td>{{timestampToDate(app.created_time)}}</td>
|
||||
<td>{{timestampToDate(app.started_time)}}</td>
|
||||
<td>{{timestampToDate(app.end_time)}}</td>
|
||||
|
@ -140,6 +140,7 @@
|
|||
if (app.apps[i].url == data.url_or_path) {
|
||||
Vue.set(app.apps[i], 'job_id', data.id);
|
||||
Vue.set(app.apps[i], 'job_name', data.name);
|
||||
Vue.set(app.apps[i], 'job_uuid', data.uuid);
|
||||
Vue.set(app.apps[i], 'job_state', data.state);
|
||||
Vue.set(app.apps[i], 'created_time', data.created_time);
|
||||
Vue.set(app.apps[i], 'started_time', data.started_time);
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
<th>End time</th>
|
||||
</thead>
|
||||
<template v-if="jobs.length > 0">
|
||||
<tr v-for="(job, index) in jobs" :id="job.id" v-bind:class="[{deleted: job.deleted}, job.state + 'Job']">
|
||||
<td><a v-if="!job.deleted" v-bind:href="'job/' + job.id">{{job.name}}</a><span v-if="job.deleted">{{job.name}} (deleted)</span> <small title="job's id">#{{job.id}} </small></td>
|
||||
<tr v-for="(job, index) in jobs" :id="job.uuid" v-bind:class="[{deleted: job.deleted}, job.state + 'Job']">
|
||||
<td><a v-if="!job.deleted" v-bind:href="'job/' + job.uuid">{{job.name}}</a><span v-if="job.deleted">{{job.name}} (deleted)</span> <small title="job's uuid">#{{job.uuid}} </small></td>
|
||||
<td>{{job.state}}</td>
|
||||
<td>{{timestampToDate(job.created_time)}}</td>
|
||||
<td>{{timestampToDate(job.started_time)}}</td>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<div class="container" v-bind:class="{deleted: job.deleted}">
|
||||
<h1 class="title">
|
||||
<span v-if="job.deleted">(DELETED)</span>
|
||||
Job #{{job.id}}
|
||||
Job #{{job.uuid}}
|
||||
<% if app %><a href="<{ relative_path_to_root }>apps/<{ app.name }>/">{{ job.name }} </a>
|
||||
<% else %>{{ job.name }}
|
||||
<% endif %>
|
||||
|
@ -63,7 +63,7 @@
|
|||
}
|
||||
})
|
||||
|
||||
ws = new ReconnectingWebSocket(websocketPrefix() + '://' + document.domain + ':' + location.port + websocketRelativePath('<{ path }>') + '/job-<{ job.id }>-ws');
|
||||
ws = new ReconnectingWebSocket(websocketPrefix() + '://' + document.domain + ':' + location.port + websocketRelativePath('<{ path }>') + '/job-<{ job.uuid }>-ws');
|
||||
|
||||
ws.onmessage = function (event) {
|
||||
var message = JSON.parse(event.data);
|
||||
|
|
Loading…
Add table
Reference in a new issue