Add builds subcommand

Allow a user to search builds according to filtering criteria.

Change-Id: Ibde3730d00f623df7ff6716a034af88de89f4835
This commit is contained in:
Matthieu Huin 2020-09-10 16:05:41 +02:00
parent da8e50862e
commit dcef301d79
5 changed files with 173 additions and 0 deletions

View File

@ -55,6 +55,15 @@ Example::
zuul-client autohold-list --tenant openstack
Builds
^^^^^^
.. program-output:: zuul-client builds --help
Examples::
zuul-client --use-conf sfio builds --tenant mytenant --result NODE_FAILURE
zuul-client --use-conf opendev builds --tenant zuul --project zuul/zuul-client --limit 10
Dequeue
^^^^^^^

View File

@ -0,0 +1,5 @@
---
features:
- |
Add the **builds** subcommand to zuul-client, allowing users to search through
builds using filters.

View File

@ -468,3 +468,32 @@ class TestCmd(BaseTestCase):
self.assertEqual(secret, f.read())
os.unlink(infile.name)
os.unlink(outfile.name)
def test_builds(self):
"""Test builds subcommand"""
ZC = ZuulClient()
with self.assertRaisesRegex(Exception,
'--voting and --non-voting are '
'mutually exclusive'):
exit_code = ZC._main(
['--zuul-url', 'https://fake.zuul',
'builds', '--tenant', 'tenant1', '--voting', '--non-voting'])
with patch('requests.Session') as mock_sesh:
session = mock_sesh.return_value
session.post = MagicMock(
return_value=FakeRequestResponse(200, {}))
exit_code = ZC._main(
['--zuul-url', 'https://fake.zuul', 'builds',
'--pipeline', 'gate',
'--tenant', 'tenant1',
'--change', '1234', '--job', 'job1', '--held'])
session.get.assert_called_with(
'https://fake.zuul/api/tenant/tenant1/builds',
params={'pipeline': 'gate',
'change': '1234',
'job_name': 'job1',
'held': True,
'skip': 0,
'limit': 50}
)
self.assertEqual(0, exit_code)

View File

@ -226,3 +226,24 @@ class ZuulRESTClient(object):
req = self.session.get(url)
self._check_request_status(req)
return req.text
def builds(self, tenant, **kwargs):
# check kwargs
allowed_args = {'project', 'pipeline', 'change', 'branch', 'patchset',
'ref', 'newrev', 'uuid', 'job_name', 'voting',
'node_name', 'result', 'final', 'held',
'limit', 'skip'}
if not set(kwargs.keys()).issubset(allowed_args):
raise Exception(
'Allowed arguments are %s' % ', '.join(allowed_args))
params = kwargs
if 'limit' not in params:
params['limit'] = 50
if 'skip' not in params:
params['skip'] = 0
url = urllib.parse.urljoin(
self.base_url,
'tenant/%s/builds' % tenant)
req = self.session.get(url, params=kwargs)
self._check_request_status(req)
return req.json()

View File

@ -87,6 +87,8 @@ class ZuulClient():
self.add_dequeue_subparser(subparsers)
self.add_promote_subparser(subparsers)
self.add_encrypt_subparser(subparsers)
self.add_builds_list_subparser(subparsers)
return subparsers
def parseArguments(self, args=None):
@ -530,6 +532,113 @@ class ZuulClient():
os.unlink(pubkey_file.name)
return return_code
def add_builds_list_subparser(self, subparsers):
cmd_builds = subparsers.add_parser(
'builds', help='List builds matching search criteria')
cmd_builds.add_argument(
'--tenant', help='tenant name', required=True)
cmd_builds.add_argument(
'--project', help='project name')
cmd_builds.add_argument(
'--pipeline', help='pipeline name')
cmd_builds.add_argument(
'--change', help='change reference')
cmd_builds.add_argument(
'--branch', help='branch name')
cmd_builds.add_argument(
'--patchset', help='patchset number')
cmd_builds.add_argument(
'--ref', help='ref name')
cmd_builds.add_argument(
'--newrev', help='the applied revision')
cmd_builds.add_argument(
'--job', help='job name')
cmd_builds.add_argument(
'--voting', help='show voting builds only',
action='store_true', default=False)
cmd_builds.add_argument(
'--non-voting', help='show non-voting builds only',
action='store_true', default=False)
cmd_builds.add_argument(
'--node', help='node name')
cmd_builds.add_argument(
'--result', help='build result')
cmd_builds.add_argument(
'--final', help='show final builds only',
action='store_true', default=False)
cmd_builds.add_argument(
'--held', help='show held builds only',
action='store_true', default=False)
cmd_builds.add_argument(
'--limit', help='maximum amount of results to return',
default=50, type=int)
cmd_builds.add_argument(
'--skip', help='how many results to skip',
default=0, type=int)
cmd_builds.set_defaults(func=self.builds)
def builds(self):
if self.args.voting and self.args.non_voting:
raise Exception('--voting and --non-voting are mutually exclusive')
self.log.info('Showing the last {} matches.'.format(self.args.limit))
filters = {'limit': self.args.limit,
'skip': self.args.skip}
if self.args.project:
filters['project'] = self.args.project
if self.args.pipeline:
filters['pipeline'] = self.args.pipeline
if self.args.change:
filters['change'] = self.args.change
if self.args.branch:
filters['branch'] = self.args.branch
if self.args.patchset:
filters['patchset'] = self.args.patchset
if self.args.ref:
filters['ref'] = self.args.ref
if self.args.newrev:
filters['newrev'] = self.args.newrev
if self.args.job:
filters['job_name'] = self.args.job
if self.args.voting:
filters['voting'] = True
if self.args.non_voting:
filters['voting'] = False
if self.args.node:
filters['node'] = self.args.node
if self.args.result:
filters['result'] = self.args.result
if self.args.final:
filters['final'] = True
if self.args.held:
filters['held'] = True
client = self.get_client()
builds = client.builds(tenant=self.args.tenant, **filters)
table = prettytable.PrettyTable(
field_names=[
'ID', 'Job', 'Project', 'Branch', 'Pipeline', 'Change or Ref',
'Duration (s)', 'Start time', 'Result', 'Event ID'
]
)
for build in builds:
if build['change'] and build['patchset']:
change = str(build['change']) + ',' + str(build['patchset'])
else:
change = build['ref']
table.add_row([
build.get('uuid') or 'N/A',
build['job_name'],
build['project'],
build['branch'],
build['pipeline'],
change,
build['duration'],
build['start_time'],
build['result'],
build.get('event_id') or 'N/A'
])
print(table)
return True
def main():
ZuulClient().main()