summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2021-03-15 14:15:16 +0200
committerLars Wirzenius <liw@liw.fi>2021-03-15 14:15:16 +0200
commit9d4f0df115484126e59a40913fac6aa2f3911990 (patch)
treed9f374b4d1a385d06e34bab48902c6150c4996f9
parent7e674fb46c1e42506c15b3a79c120956b3ff3336 (diff)
downloadick2-9d4f0df115484126e59a40913fac6aa2f3911990.tar.gz
fix: request muck scopes as well
-rwxr-xr-xicktool250
1 files 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)