From a46a46858072db5f5ca5a5ae3df203de545c26d0 Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Fri, 3 Aug 2018 15:11:03 +0300 Subject: Add: basic functionality This does NOT work yet, lacks authentication, but that requires having a Qvisqve that supports that. --- ickweb/app.py | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++++- views/builds.tpl | 19 +++++++++ views/index.tpl | 10 +++-- views/log.tpl | 13 ++++++ views/login.tpl | 6 +-- views/project.tpl | 13 ++++++ views/projects.tpl | 19 +++++++++ 7 files changed, 192 insertions(+), 8 deletions(-) create mode 100644 views/builds.tpl create mode 100644 views/log.tpl create mode 100644 views/project.tpl create mode 100644 views/projects.tpl 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/') + 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/') + 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)) diff --git a/views/builds.tpl b/views/builds.tpl new file mode 100644 index 0000000..2fd2d71 --- /dev/null +++ b/views/builds.tpl @@ -0,0 +1,19 @@ + + + + + + + Ick builds + + +

Builds:

+
    + % for build in builds: +
  1. + {{ build['log'] }} +
  2. + % end +
+ + diff --git a/views/index.tpl b/views/index.tpl index 20ba140..01868ec 100644 --- a/views/index.tpl +++ b/views/index.tpl @@ -2,11 +2,13 @@ - - - Facade content + + + Ick -

You ARE logged in. Well done!

+

You ARE logged in. Well done!

+

Projects

+

Builds

diff --git a/views/log.tpl b/views/log.tpl new file mode 100644 index 0000000..14c5235 --- /dev/null +++ b/views/log.tpl @@ -0,0 +1,13 @@ + + + + + + + Ick build log {{ buildid }} + + +

Build log {{buildid }}:

+

{{ log }}

+ + diff --git a/views/login.tpl b/views/login.tpl index f983ac0..66cc02e 100644 --- a/views/login.tpl +++ b/views/login.tpl @@ -2,12 +2,12 @@ - - + + Facade login

You are NOT logged in.

-

Login

+

Login

diff --git a/views/project.tpl b/views/project.tpl new file mode 100644 index 0000000..a4428aa --- /dev/null +++ b/views/project.tpl @@ -0,0 +1,13 @@ + + + + + + + Ick project: {{ project['project'] }} + + +

Project: {{ project['project'] }}

+

{{ as_yaml }}

+ + diff --git a/views/projects.tpl b/views/projects.tpl new file mode 100644 index 0000000..66feecf --- /dev/null +++ b/views/projects.tpl @@ -0,0 +1,19 @@ + + + + + + + Ick projects + + +

Projects:

+
    + % for p in projects: +
  1. + {{ p['project'] }} +
  2. + % end +
+ + -- cgit v1.2.1