diff options
author | Lars Wirzenius <liw@liw.fi> | 2018-04-20 19:23:00 +0300 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2018-04-20 19:23:00 +0300 |
commit | 4bbb2ccbcdbdb055ffe0799361f3abd3cb5aeae7 (patch) | |
tree | 3119c70c36b784d3b9dc4b36e7ab4c7ce829c913 /icktool | |
parent | ef7c00019943b693955c78fd182ac41c925687a9 (diff) | |
parent | 186ec3f89806fc3d5532b171c7ae34b06059c491 (diff) | |
download | ick2-4bbb2ccbcdbdb055ffe0799361f3abd3cb5aeae7.tar.gz |
Merge: icktool improvements
Diffstat (limited to 'icktool')
-rwxr-xr-x | icktool | 201 |
1 files changed, 175 insertions, 26 deletions
@@ -18,6 +18,7 @@ import configparser import json import logging +import os import sys import cliapp @@ -73,15 +74,10 @@ class Icktool(cliapp.Application): ) self.settings.string( - ['auth-url'], - 'use URL as the authentication URL', - metavar='URL', - ) - - self.settings.string( ['secrets'], - 'use URL as the controller base URL', - metavar='URL', + 'use FILE for credentials for authentication server', + metavar='FILE', + default=os.path.expanduser('~/.config/icktool/credentials.conf') ) self.settings.boolean( @@ -113,21 +109,128 @@ class Icktool(cliapp.Application): self.output.write('{}\n'.format(scope)) def cmd_token(self, args): - token = self._new_token() + api = self._new_api() + token = self._new_token(api) self.output.write('{}\n'.format(token)) def cmd_version(self, args): - token = self._new_token() api = self._new_api() + token = self._new_token(api) api.set_token(token) version = api.get_version() self._prettyson(version) + def cmd_status(self, args): + table = Table() + table.set_columns('project', 'status', 'build_status', 'log_id') + + api = self._new_api() + token = self._new_token(api) + api.set_token(token) + + projects = api.show('/projects') + builds = api.show('/builds') + + for project in projects['projects']: + project_name = project['project'] + project_status = api.show( + '/projects/{}/status'.format(project_name)) + + row = { + 'project': project_name, + 'status': project_status['status'], + 'build_status': 'n/a', + 'log_id': 'n/a' + } + + build = self._latest_build(project_name, builds) + if build: + row['build_status'] = build['status'] + row['log_id'] = build['log'] + + table.append_row(**row) + + self.output.write(table.format()) + + def _latest_build(self, project_name, builds): + builds = self._find_builds(project_name, builds) + if builds: + return builds[-1] + return None + + def _find_builds(self, project_name, builds): + return [b for b in builds['builds'] if b['project'] == project_name] + + def _find_build(self, builds, build_id): + for build in builds: + if build['build_id'] == build_id: + return build + return None + + def cmd_build_graph(self, args): + api = self._new_api() + token = self._new_token(api) + api.set_token(token) + + project_name = args[0] + if len(args) > 1: + build_id = args[1] + else: + build_id = 'latest' + + builds = api.show('/builds') + builds = self._find_builds(project_name, builds) + if not builds: + sys.exit('No such build %s' % build_id) + + if build_id == 'latest': + build = builds[-1] + else: + build = self._find_build(builds, build_id) + + actions = build['actions'] + current = build['current_action'] + status = build['status'] + + f = self.output + f.write('digraph "build_graph" {\n') + for i, action in enumerate(actions): + self._describe_node(f, i, current, action) + f.write('}\n') + + def _describe_node(self, f, i, current, action): + styles = { + 'done': ('rectangle', '#ffffff'), + 'building': ('ellipse', '#00ff00'), + 'blocked': ('ellipse', '#bbbbbb'), + } + + if current is None: + shape, color = styles['done'] + elif i < current: + shape, color = styles['done'] + elif i == current: + shape, color = styles['building'] + elif i > current: + shape, color = styles['blocked'] + + tmpl = 'a{} [label="{}" shape={} style=filled fillcolor="{}"]\n' + f.write(tmpl.format(i, self._describe_action(action), shape, color)) + + def _describe_action(self, action): + for key in ['action', 'archive']: + if key in action: + return '{}: {}'.format(key, action[key]) + for key in ['debootstrap', 'shell', 'python']: + if key in action: + return key + return str(action) + def cmd_make_it_so(self, argv): obj = self._read_object() - token = self._new_token() api = self._new_api() + token = self._new_token(api) api.set_token(token) self._create_resources(api, '/projects', obj.get('projects', [])) @@ -140,9 +243,16 @@ class Icktool(cliapp.Application): for obj in objs: api.create(path, obj) + def cmd_trigger(self, args): + project = args[0] + api = self._new_api() + token = self._new_token(api) + api.set_token(token) + self._prettyson(api.trigger(project)) + def cmd_show(self, args): - token = self._new_token() api = self._new_api() + token = self._new_token(api) api.set_token(token) if not args: args = [ @@ -160,8 +270,8 @@ class Icktool(cliapp.Application): api.set_controller_url(self.settings['controller']) return api - def _new_auth(self): - url = self.settings['auth-url'] + def _new_auth(self, api): + url = api.get_auth_url() client_id, client_secret = self._get_client_creds(url) ac = ick2.AuthClient() @@ -169,10 +279,10 @@ class Icktool(cliapp.Application): ac.set_client_creds(client_id, client_secret) return ac - def _new_token(self): + def _new_token(self, api): if self.settings['token']: return self.settings['token'] - ac = self._new_auth() + ac = self._new_auth(api) scopes = ' '.join(self.settings['scope']) return ac.get_token(scopes) @@ -188,19 +298,58 @@ class Icktool(cliapp.Application): self.output.write('\n') -class Command: - - def __init__(self, api): - self._api = api - - def execute(self): - raise NotImplementedError() +class Table: + + def __init__(self): + self._column_names = None + self._rows = [] + + def set_columns(self, *column_names): + self._column_names = column_names + + def append_row(self, **kwargs): + self._rows.append(kwargs) + + def format(self): + assert self._column_names is not None + headings = { + key: key + for key in self._column_names + } + self._rows.insert(0, headings) + widths = self._column_widths() + underlines = { + key: '-' * widths[key] + for key in self._column_names + } + self._rows.insert(1, underlines) + lines = [self._format_row(widths, row) for row in self._rows] + return ''.join('{}\n'.format(line) for line in lines) + + def _format_headings(self, widths): + row = { + key: key + for key in self._column_names + } + return self._format_row(widths, row) + + def _format_row(self, widths, row): + return ' | '.join( + self._format_cell(widths[x], row[x]) + for x in self._column_names + ) + def _format_cell(self, width, value): + return '%*s' % (width, value) -class VersionCommand(Command): + def _column_widths(self): + return { + key: self._width(key) + for key in self._column_names + } - def execute(self): - self._api.get_version() + def _width(self, column_name): + return max(len(str(row[column_name])) for row in self._rows) Icktool(version=ick2.__version__).run() |