From 9d4f0df115484126e59a40913fac6aa2f3911990 Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Mon, 15 Mar 2021 14:15:16 +0200 Subject: fix: request muck scopes as well --- icktool | 250 +++++++++++++++++++++++++++------------------------------------- 1 file changed, 104 insertions(+), 146 deletions(-) diff --git a/icktool b/icktool index 64b0793..c676b24 100755 --- a/icktool +++ b/icktool @@ -30,11 +30,11 @@ import ick2 def scopes(base): patterns = [ - 'uapi_{}_get', - 'uapi_{}_post', - 'uapi_{}_id_get', - 'uapi_{}_id_put', - 'uapi_{}_id_delete', + "uapi_{}_get", + "uapi_{}_post", + "uapi_{}_id_get", + "uapi_{}_id_put", + "uapi_{}_id_delete", ] return [x.format(base) for x in patterns] @@ -46,71 +46,64 @@ def scopes_for_types(typelist): return result -types = [ - 'projects', - 'pipelines', - 'workers', - 'work', - 'builds', - 'logs', -] +types = ["projects", "pipelines", "workers", "work", "builds", "logs"] class Icktool(cliapp.Application): _default_scopes = [ - 'uapi_version_get', - 'uapi_work_post', - 'uapi_status_get', - 'uapi_projects_id_status_get', - 'uapi_projects_id_status_put', - 'uapi_blobs_id_get', - 'uapi_blobs_id_put', - 'uapi_notify_post', + "uapi_version_get", + "uapi_work_post", + "uapi_status_get", + "uapi_projects_id_status_get", + "uapi_projects_id_status_put", + "uapi_blobs_id_get", + "uapi_blobs_id_put", + "uapi_notify_post", + "create", + "update", + "show", + "delete", ] + scopes_for_types(types) def add_settings(self): self.settings.string( - ['controller', 'c'], - 'use URL as the controller base URL', - metavar='URL', + ["controller", "c"], "use URL as the controller base URL", metavar="URL" ) self.settings.string( - ['secrets'], - 'use FILE for credentials for authentication server', - metavar='FILE', - default=os.path.expanduser('~/.config/icktool/credentials.conf') + ["secrets"], + "use FILE for credentials for authentication server", + metavar="FILE", + default=os.path.expanduser("~/.config/icktool/credentials.conf"), ) self.settings.boolean( - ['verify-tls'], - 'verify API provider TLS certificate ' - '(default is verify, use --no-verify-tls)', + ["verify-tls"], + "verify API provider TLS certificate " + "(default is verify, use --no-verify-tls)", default=True, ) self.settings.string( - ['token'], - 'use TOKEN instead of generating a new one', - metavar='TOKEN', + ["token"], "use TOKEN instead of generating a new one", metavar="TOKEN" ) self.settings.string_list( - ['scope'], - 'add SCOPE to the list of scope in requested token', - metavar='SCOPE', + ["scope"], + "add SCOPE to the list of scope in requested token", + metavar="SCOPE", default=self._default_scopes, ) def cmd_scopes(self, args): - for scope in self.settings['scope']: - self.output.write('{}\n'.format(scope)) + for scope in self.settings["scope"]: + self.output.write("{}\n".format(scope)) def cmd_token(self, args): api = self._new_api() token = self._new_token(api) - self.output.write('{}\n'.format(token)) + self.output.write("{}\n".format(token)) def cmd_version(self, args): cmd = self._command(VersionCommand) @@ -159,13 +152,13 @@ class Icktool(cliapp.Application): return klass(api, self.output) def _new_api(self): - if not self.settings['verify-tls']: + if not self.settings["verify-tls"]: urllib3.disable_warnings() logging.captureWarnings(True) api = ick2.ControllerClient() - api.set_verify_tls(self.settings['verify-tls']) - api.set_controller_url(self.settings['controller']) + api.set_verify_tls(self.settings["verify-tls"]) + api.set_controller_url(self.settings["controller"]) return api def _new_auth(self, api): @@ -180,36 +173,35 @@ class Icktool(cliapp.Application): return ac def _new_token(self, api): - if self.settings['token']: - return self.settings['token'] + if self.settings["token"]: + return self.settings["token"] ac = self._new_auth(api) - scopes = ' '.join(self.settings['scope']) + scopes = " ".join(self.settings["scope"]) try: return ac.get_token(scopes) except ick2.HttpError as e: - sys.stderr.write('Error getting token: %s\n' % str(e)) + sys.stderr.write("Error getting token: %s\n" % str(e)) sys.exit(1) def _get_client_creds(self, controller_url, auth_url): cp = configparser.ConfigParser() - cp.read(self.settings['secrets']) + cp.read(self.settings["secrets"]) if cp.has_section(controller_url): section = controller_url else: section = auth_url - client_id = cp.get(section, 'client_id') - client_secret = cp.get(section, 'client_secret') + client_id = cp.get(section, "client_id") + client_secret = cp.get(section, "client_secret") return client_id, client_secret def _prettyson(self, obj): json.dump(obj, self.output, indent=4, sort_keys=True) - self.output.write('\n') + self.output.write("\n") class Table: - def __init__(self): self._column_names = None self._rows = [] @@ -222,41 +214,28 @@ class Table: def format(self): assert self._column_names is not None - headings = { - key: key - for key in self._column_names - } + 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 - } + 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) + return "".join("{}\n".format(line) for line in lines) def _format_headings(self, widths): - row = { - key: key - for key in self._column_names - } + 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 + 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) + return "%*s" % (width, value) def _column_widths(self): - return { - key: self._width(key) - for key in self._column_names - } + return {key: self._width(key) for key in self._column_names} def _width(self, column_name): return max(len(str(row[column_name])) for row in self._rows) @@ -270,142 +249,132 @@ def _latest_build(project_name, builds): def _find_builds(project_name, builds): - return [b for b in builds['builds'] if b['project'] == project_name] + return [b for b in builds["builds"] if b["project"] == project_name] def _find_build(builds, build_id): for build in builds: - if build['build_id'] == build_id: + if build["build_id"] == build_id: return build return None class Command: - def __init__(self, api, output): self.api = api self.output = output def _check_for_leading_slash(self, args): for arg in args: - if not arg.startswith('/'): - raise Exception( - 'Argument should start with slash: {}'.format(arg)) + if not arg.startswith("/"): + raise Exception("Argument should start with slash: {}".format(arg)) def _prettyson(self, obj): json.dump(obj, self.output, indent=4, sort_keys=True) - self.output.write('\n') + self.output.write("\n") def execute(self, args): raise NotImplementedError() class VersionCommand(Command): - def execute(self, args): version = self.api.get_version() self._prettyson(version) class StatusCommand(Command): - def execute(self, args): table = Table() - table.set_columns('project', 'build_status', 'log_id') + table.set_columns("project", "build_status", "log_id") - projects = self.api.show('/projects') - builds = self.api.show('/builds') + projects = self.api.show("/projects") + builds = self.api.show("/builds") projects = self._sort_projects(projects) for project in projects: - project_name = project['project'] + project_name = project["project"] - row = { - 'project': project_name, - 'build_status': 'n/a', - 'log_id': 'n/a' - } + row = {"project": project_name, "build_status": "n/a", "log_id": "n/a"} build = _latest_build(project_name, builds) if build: - bs = build['status'] + bs = build["status"] if bs == 0: - bs = 'OK' + bs = "OK" elif isinstance(bs, int): - bs = 'FAILED ({})'.format(bs) - row['build_status'] = bs - row['log_id'] = build['log'] + bs = "FAILED ({})".format(bs) + row["build_status"] = bs + row["log_id"] = build["log"] table.append_row(**row) self.output.write(table.format()) def _sort_projects(self, projects): - return list(sorted(projects['projects'], key=lambda p: p['project'])) + return list(sorted(projects["projects"], key=lambda p: p["project"])) class BuildGraphCommand(Command): - def execute(self, args): project_name = args[0] if len(args) > 1: build_id = args[1] else: - build_id = 'latest' + build_id = "latest" - builds = self.api.show('/builds') + builds = self.api.show("/builds") builds = _find_builds(project_name, builds) if not builds: - sys.exit('No such build %s' % build_id) + sys.exit("No such build %s" % build_id) - if build_id == 'latest': + if build_id == "latest": build = builds[-1] else: build = _find_build(builds, build_id) - actions = build['actions'] - current = build['current_action'] - status = build['status'] + 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) if i > 0: - f.write('a{} -> a{}\n'.format(i-1, i)) - f.write('}\n') + f.write("a{} -> a{}\n".format(i - 1, i)) + f.write("}\n") def _describe_node(self, f, i, current, action): styles = { - 'done': ('rectangle', '#ffffff'), - 'building': ('ellipse', '#00ff00'), - 'blocked': ('ellipse', '#bbbbbb'), + "done": ("rectangle", "#ffffff"), + "building": ("ellipse", "#00ff00"), + "blocked": ("ellipse", "#bbbbbb"), } if current is None: - shape, color = styles['done'] + shape, color = styles["done"] elif i < current: - shape, color = styles['done'] + shape, color = styles["done"] elif i == current: - shape, color = styles['building'] + shape, color = styles["building"] elif i > current: - shape, color = styles['blocked'] + 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']: + for key in ["action", "archive"]: if key in action: - return '{}: {}'.format(key, action[key]) - for key in ['debootstrap', 'shell', 'python']: + return "{}: {}".format(key, action[key]) + for key in ["debootstrap", "shell", "python"]: if key in action: return key return str(action) class MakeItSoCommand(Command): - def execute(self, args): if not args: obj = self._read_object(sys.stdin) @@ -420,38 +389,31 @@ class MakeItSoCommand(Command): return yaml.load(f) def _make_it_so(self, obj): - self._create_resources( - '/projects', 'project', obj.get('projects', [])) - self._create_resources( - '/pipelines', 'pipeline', obj.get('pipelines', [])) + self._create_resources("/projects", "project", obj.get("projects", [])) + self._create_resources("/pipelines", "pipeline", obj.get("pipelines", [])) def _create_resources(self, path, name_field, objs): for obj in objs: try: self.api.create(path, obj) except ick2.HttpError: - update_path = '{}/{}'.format(path, obj[name_field]) + update_path = "{}/{}".format(path, obj[name_field]) self.api.update(update_path, obj) class TriggerCommand(Command): - def execute(self, args): for project in args: try: self._prettyson(self.api.trigger(project)) except ick2.HttpError as e: - sys.stderr.write('ERROR: {}: {}\n'.format(project, str(e))) + sys.stderr.write("ERROR: {}: {}\n".format(project, str(e))) class ShowCommand(Command): - def execute(self, args): if not args: - args = [ - '/projects', - '/pipelines', - ] + args = ["/projects", "/pipelines"] else: self._check_for_leading_slash(args) @@ -461,7 +423,6 @@ class ShowCommand(Command): class DeleteCommand(Command): - def execute(self, args): self._check_for_leading_slash(args) for what in args: @@ -469,48 +430,45 @@ class DeleteCommand(Command): class ShowLogCommand(Command): - def execute(self, args): self._check_for_leading_slash(args) for log_id in args: log = self.api.show_blob(log_id) - log = log.decode('UTF-8') + log = log.decode("UTF-8") self.output.write(log) - if not log.endswith('\n'): - self.output.write('\n') + if not log.endswith("\n"): + self.output.write("\n") class ShowLatestLogCommand(Command): - def execute(self, args): - builds = self.api.show('/builds') + builds = self.api.show("/builds") if builds: - builds = builds['builds'] + builds = builds["builds"] for project in args: latest = self._get_latest_build(builds, project) if latest: - log_id = latest['log'] + log_id = latest["log"] log = self.api.show_blob(log_id) - log = log.decode('UTF-8') + log = log.decode("UTF-8") self.output.write(log) - if not log.endswith('\n'): - self.output.write('\n') + if not log.endswith("\n"): + self.output.write("\n") def _get_latest_build(self, builds, project): - builds = [b for b in builds if b['project'] == project] + builds = [b for b in builds if b["project"] == project] if builds: return builds[-1] return None class GetArtifactCommand(Command): - def execute(self, args): if not args: - raise Exception('Must give blob id as argument') + raise Exception("Must give blob id as argument") blob_id = args[0] - path = '/blobs/{}'.format(blob_id) + path = "/blobs/{}".format(blob_id) blob = self.api.show_blob(path) sys.stdout.buffer.write(blob) -- cgit v1.2.1