diff options
author | Lars Wirzenius <liw@liw.fi> | 2018-07-29 11:39:05 +0300 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2018-07-29 11:39:05 +0300 |
commit | bf66974141b86bbdcc8c1036bcacb6be3c60933a (patch) | |
tree | 0fc3de160c5ea32c03b25839357d152ec2bdeb88 | |
download | icklint-bf66974141b86bbdcc8c1036bcacb6be3c60933a.tar.gz |
Initial commit
-rw-r--r-- | README | 11 | ||||
-rwxr-xr-x | icklint | 172 |
2 files changed, 183 insertions, 0 deletions
@@ -0,0 +1,11 @@ +icklint +============================================================================= + +icklint checks for problems ("lint") in ick project and pipeline +definitions. + +Use icktool to get project and pipeline definitions from the +controller: + + icktool show /projects > projects.json + icktool show /pipelines > pipelines.json @@ -0,0 +1,172 @@ +#!/usr/bin/env python3 + + +import itertools +import sys + + +import yaml + + +# Define individual checks as functions. A check can check a pipeline +# (name starts with "pipeline_"), a project, or a pair of project and +# one of the pipelines it uses. +# +# Pipeline checks get the pipeline object as the argument, where +# object is the dict from the pipeline spec. +# +# Project checks get the project and a dict-like object with all +# pipelines. +# +# Pair checks get the project and pipeline objects. +# +# Each check should yield all the error messages. An error message can +# use "{project}" and "{pipeline}" to refer to the name of the project +# and pipeline in question. + + +def pipeline_has_actions(pl): + if not pl.get('actions'): + yield '{pipeline}: does not have actions' + + +def pipeline_parameters_spelling(pl): + if 'parameter' in pl: + yield '{pipeline}: has parameter field (note singular)' + + +def project_has_pipelines(pr, pipelines): + if not pr.get('pipelines'): + yield "{project}: doesn't have pipelines" + + +def project_has_parameters(pr, pipelines): + if not pr.get('parameters'): + yield "{project}: doesn't have pipelines" + + +def project_params_wanted(pr, pipelines): + wanted = [] + for plname in pr.get('pipelines', []): + pl = pipelines.get(plname) + if pl is not None: + wanted.extend(pl.get('parameters', [])) + + params = pr.get('parameters', {}) + for param in params: + if param not in wanted: + yield '{project}: %s defined, but not wanted' % param + + +def pair_pipeline_exists(pr, pl): + if pl is None: + yield '{project}: {pipeline}: pipeline does not exist' + + +def pair_params_defined(pr, pl): + params = pr.get('parameters', {}) + for wanted in pl.get('parameters', []): + if wanted not in params: + yield '{project}: {pipeline}: %s wanted, but not defined' % wanted + + +# The rest if icklint infrastructure. + + +class Linter: + + def __init__(self, projects, pipelines): + self._projects = projects + self._pipelines = pipelines + + def check(self): + checks = [ + self.check_pipelines, + self.check_projects, + self.check_pairs, + ] + + for check in checks: + for msg in check(): + ok = False + yield msg + + def find_checkers(self, prefix): + g = globals() + return [ + g[name] + for name in g + if name.startswith(prefix + '_') + ] + + def check_pipelines(self): + checkers = self.find_checkers('pipeline') + for checker in checkers: + for plname, pl in self._pipelines.items(): + for msg in checker(pl): + yield msg.format(pipeline=plname) + + def check_projects(self): + checkers = self.find_checkers('project') + for checker in checkers: + for prname, pr in self._projects.items(): + for msg in checker(pr, self._pipelines): + yield msg.format(project=prname) + + def check_pairs(self): + checkers = self.find_checkers('pair') + for checker in checkers: + for prname, pr in self._projects.items(): + for plname in pr.get('pipelines', []): + pl = self._pipelines.get(plname) + for msg in checker(pr, pl): + yield msg.format(project=prname, pipeline=plname) + + +class Things: + + def __init__(self, items_field, name_field): + self._items_field = items_field + self._name_field = name_field + self._dict = {} + + def from_file(self, filename): + with open(filename) as f: + items = yaml.safe_load(f) + + for item in items[self._items_field]: + name = item[self._name_field] + self[name] = item + + def __contains__(self, name): + return name in self._dict + + def __getitem__(self, name): + return self._dict[name] + + def __setitem__(self, name, value): + self._dict[name] = value + + def get(self, name, default=None): + return self._dict.get(name, default) + + def keys(self): + return list(sorted(self._dict.keys())) + + def items(self): + return self._dict.items() + + +projects = Things('projects', 'project') +projects.from_file(sys.argv[1]) + +pipelines = Things('pipelines', 'pipeline') +pipelines.from_file(sys.argv[2]) + +linter = Linter(projects, pipelines) +ok = True +for msg in linter.check(): + ok = False + print(msg) +if not ok: + sys.exit(1) |