summaryrefslogtreecommitdiff
path: root/ickweb
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2018-08-03 15:11:03 +0300
committerLars Wirzenius <liw@liw.fi>2018-08-03 15:11:03 +0300
commita46a46858072db5f5ca5a5ae3df203de545c26d0 (patch)
treee3f8ef800821903dcdf8d58418e7cfa0c363134e /ickweb
parent3038292daee114f22417495bfac4dcd105ee955a (diff)
downloadickweb-a46a46858072db5f5ca5a5ae3df203de545c26d0.tar.gz
Add: basic functionality
This does NOT work yet, lacks authentication, but that requires having a Qvisqve that supports that.
Diffstat (limited to 'ickweb')
-rw-r--r--ickweb/app.py120
1 files changed, 119 insertions, 1 deletions
diff --git a/ickweb/app.py b/ickweb/app.py
index f420098..42504e3 100644
--- a/ickweb/app.py
+++ b/ickweb/app.py
@@ -1,22 +1,140 @@
+import json
+
import bottle
+import requests
+import yaml
+
+API_URL = 'https://ci-prod-controller.vm.liw.fi'
COOKIE = 'ickweb1'
+TOKEN = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzUxMiJ9.eyJpc3MiOiJodHRwczovL2NpLXByb2QtY29udHJvbGxlci52bS5saXcuZmkiLCJhdWQiOiJhZG1pbiIsInN1YiI6IiIsInNjb3BlIjoidWFwaV92ZXJzaW9uX2dldCB1YXBpX3N0YXR1c19nZXQgdWFwaV9wcm9qZWN0c19pZF9zdGF0dXNfZ2V0IHVhcGlfcHJvamVjdHNfaWRfc3RhdHVzX3B1dCB1YXBpX2Jsb2JzX2lkX2dldCB1YXBpX25vdGlmeV9wb3N0IHVhcGlfcHJvamVjdHNfZ2V0IHVhcGlfcHJvamVjdHNfcG9zdCB1YXBpX3Byb2plY3RzX2lkX2dldCB1YXBpX3Byb2plY3RzX2lkX3B1dCB1YXBpX3Byb2plY3RzX2lkX2RlbGV0ZSB1YXBpX3BpcGVsaW5lc19nZXQgdWFwaV9waXBlbGluZXNfcG9zdCB1YXBpX3BpcGVsaW5lc19pZF9nZXQgdWFwaV9waXBlbGluZXNfaWRfcHV0IHVhcGlfcGlwZWxpbmVzX2lkX2RlbGV0ZSB1YXBpX3dvcmtlcnNfZ2V0IHVhcGlfd29ya2Vyc19pZF9nZXQgdWFwaV9idWlsZHNfZ2V0IHVhcGlfYnVpbGRzX2lkX2dldCB1YXBpX2xvZ3NfZ2V0IHVhcGlfbG9nc19pZF9nZXQiLCJleHAiOjE1MzMyOTkzMTIuNTgxNDMxfQ.MNd3eP6zmet7vLLGE_XXnfDZ1_flRb7qRw87qdKtZdx496NK3j-Aco0DuA5lKAeSFcK1yx01pnIH9maUO1uU5CTkOaVk2jlq1rr9LyRBaqyF1VI1qrTiWQ5QfLLCUXsH4zl4PC2g33BhcgLY74ua22P7QA3Rwnkp1xLP32VA6zN_Y4nrJDJhTFpRJXQk1Lo59hyOeiXnc2s26wXDASWMmqqNFJHf9i6uTvJQDrxjhiLjI7w3s_DxtBINJ6eWaZqbVvkYvQYVGBjqbDd3N1VOg4thoi_XXsF12Jrwo9LORT8pNygxlR40Lb-n5gDN9olTj0fVWZ9UMP-G1DQVrxZPf3rlMhRiPSmEvfLJJn7IF9fqwSnsseZYwvzjh6DafdFctuL9cNrCQt_UrlH7l0KPHC65GR1P4ejCVM5E5FC0Lx3KWhh6eagcnd47LNtQCxCMvl5Ffig58VBlRer4J3vTtpECkEryIC6rVurwUETlOGqai1LTJO_KOM3h9eP_RbAy0fogj8sE_UjIiEbp9IuodEaRwp3hGlX_qBkLZwKDMMhlhtZGX3dOyb4CiEoDxKnYQm7sE3Hu0KAE88QrpS8Lwt36q1SeD9srI5yfo31YXW__vBQ2AYlXF9efSpm0XuYIWHEaGX-7DF1ANAV0vMmyDxYsf32K4MQUEsF5nQ_fvao'
+
def create_app():
app = bottle.Bottle()
+ api = API(API_URL)
+ api.set_token(TOKEN)
+
+ @app.route('/favicon.ico')
+ def favicon():
+ raise bottle.HTTPError(status=404)
@app.route('/')
def root():
- if bottle.request.get_cookie(COOKIE) == 'yes':
+ if is_logged_in():
return bottle.template('index')
else:
return bottle.template('login')
+ def is_logged_in():
+ return True # FIXME
+
+ @app.route('/projects')
+ def projects():
+ projects = api.get_projects()
+ return bottle.template('projects', projects=projects)
+
+ @app.route('/projects/<name>')
+ def show_project(name):
+ project = api.get_project(name)
+ params = {
+ 'project': project,
+ 'as_yaml': as_yaml(project),
+ }
+ return bottle.template('project', **params)
+
+ @app.route('/builds')
+ def builds():
+ builds = api.get_builds()
+ return bottle.template('builds', builds=builds)
+
+ @app.route('/logs/<buildid:path>')
+ def show_log(buildid):
+ log = api.get_log(buildid)
+ return bottle.template('log', buildid=buildid, log=log)
+
@app.route('/callback')
def callback():
+ assert 0
+ print('/callback called')
+ print('URL args:', dict(bottle.request.query))
+
bottle.response.set_cookie(ICKWEB, 'yes')
bottle.redirect('/')
return app
+
+
+def as_yaml(obj):
+ return yaml.safe_dump(obj, indent=4, default_flow_style=False)
+
+
+def string_representer(dumper, data):
+ style = None
+ if '\n' in data:
+ style = '|'
+ return dumper.represent_scalar('tag:yaml.org,2002:str', data, style=style)
+
+
+yaml.add_representer(str, string_representer, Dumper=yaml.SafeDumper)
+
+
+class API:
+
+ def __init__(self, api_url):
+ self._url = api_url
+ self.set_token(None)
+
+ def set_token(self, token):
+ self._token = token
+
+ def get_token(self):
+ return self._token
+
+ def get_projects(self):
+ url = self.url('/projects')
+ headers = {
+ 'Authorization': 'Bearer {}'.format(self.get_token())
+ }
+ r = requests.get(url, headers=headers)
+ if not r.ok:
+ raise bottle.HTTPError(status=r.status_code)
+ obj = r.json()
+ return list(sorted(obj['projects'], key=lambda p: p['project']))
+
+ def get_project(self, name):
+ url = self.url('/projects', name)
+ print('get_project: url:', url)
+ headers = {
+ 'Authorization': 'Bearer {}'.format(self.get_token())
+ }
+ r = requests.get(url, headers=headers)
+ if not r.ok:
+ raise bottle.HTTPError(status=r.status_code)
+ return r.json()
+
+ def get_builds(self):
+ url = self.url('/builds')
+ headers = {
+ 'Authorization': 'Bearer {}'.format(self.get_token())
+ }
+ r = requests.get(url, headers=headers)
+ if not r.ok:
+ raise bottle.HTTPError(status=r.status_code)
+ obj = r.json()
+ return list(sorted(obj['builds'], key=lambda p: p['build_id']))
+
+ def get_log(self, buildid):
+ url = self.url('/logs', buildid)
+ headers = {
+ 'Authorization': 'Bearer {}'.format(self.get_token())
+ }
+ r = requests.get(url, headers=headers)
+ if not r.ok:
+ raise bottle.HTTPError(status=r.status_code)
+ return r.text
+
+ def url(self, *parts):
+ return '{}{}'.format(self._url, '/'.join(parts))