Skip to content

Commit

Permalink
add basic errata-tool CLI
Browse files Browse the repository at this point in the history
This installs a uility "errata-tool" in the user's $PATH.
  • Loading branch information
ktdreyer committed Apr 2, 2018
1 parent e4778ed commit a3f4f05
Show file tree
Hide file tree
Showing 13 changed files with 511 additions and 0 deletions.
26 changes: 26 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,32 @@ Important, and Critical). See this link for more information:
e.commit()
print(e.url())
errata-tool command-line interface
----------------------------------

The ``errata-tool`` CLI is a thin wrapper around the classes. You can use it to
query information from the Errata Tool or create new releases (releng)::

errata-tool -h

usage: errata-tool [-h] [--stage] [--dry-run] {advisory,product,release} ...

positional arguments:
{advisory,product,release}
advisory Get or create an advisory
product Get a product
release Get or create a release (RCM)

optional arguments:
--stage use staging ET instance
--dry-run show what would happen, but don't do it



More Python Examples
--------------------

Getting an erratum's name:

.. code-block:: python
Expand Down
5 changes: 5 additions & 0 deletions bin/errata-tool
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env python
from errata_tool.cli import main

if __name__ == '__main__':
main.main()
Empty file added errata_tool/cli/__init__.py
Empty file.
92 changes: 92 additions & 0 deletions errata_tool/cli/advisory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
from errata_tool.erratum import Erratum


def add_parser(subparsers):
""" Add our advisory parser to this top-level subparsers object. """
group = subparsers.add_parser('advisory', help='Get or create an advisory')

# advisory-level subcommands:
sub = group.add_subparsers()

# "get"
get_parser = sub.add_parser('get')
get_parser.add_argument('errata_id', help='advisory id, "12345"')
get_parser.set_defaults(func=get)

# "create"
create_parser = sub.add_parser('create')
create_parser.add_argument('--product', required=True,
help='eg. "RHCEPH"')
create_parser.add_argument('--release', required=True,
help='eg. "rhceph-2.5"')
create_parser.add_argument('--type', required=False,
choices=('RHSA', 'RHBA', 'RHEA'),
default='RHBA', help='eg. "RHBA"')
create_parser.add_argument('--security-impact', required=False,
choices=('Low', 'Moderate', 'Important',
'Critical'),
help='only required for RHSA')
create_parser.add_argument('--synopsis', required=True,
help='eg. "Red Hat Product 2.1 bug fix update"')
create_parser.add_argument('--topic', required=True,
help='eg. "An update for Red Hat Product 2.1 is'
' now available."')
create_parser.add_argument('--description', required=True,
help='eg. "This update contains the following'
' fixes ..."')
create_parser.add_argument('--solution', required=True,
help='eg. "Before applying this update..."')
create_parser.add_argument('--qe-email', required=True,
help='eg. "[email protected]"')
create_parser.add_argument('--qe-group', required=True,
help='eg. "RHC (Ceph) QE"')
create_parser.add_argument('--owner-email', required=True,
help='eg. "[email protected]"')
create_parser.add_argument('--manager-email', required=True,
help='eg. "[email protected]"')
create_parser.set_defaults(func=create)

# TODO:
# "new-state" Change erratum state
# "add-bugs" Add bugs to erratum
# "remove-bugs" Provide a list of bugs to remove from erratum
# "add-builds" Add build to erratum (you may specify nvr)


def get(args):
e = Erratum(errata_id=args.errata_id)
print(e)


def create(args):
e = Erratum(product=args.product,
release=args.release,
errata_type=args.type,
synopsis=args.synopsis,
topic=args.topic,
description=args.description,
solution=args.solution,
qe_email=args.qe_email,
qe_group=args.qe_group,
owner_email=args.owner_email,
manager_email=args.manager_email,
)
if args.dry_run:
env = 'prod'
if args.stage:
env = 'stage'
print('DRY RUN: would create new advisory in %s:' % env)
print('Product: %s' % args.product)
print('Release: %s' % args.release)
print('Erratum type: %s' % args.type)
print('Synopsis: %s' % args.synopsis)
print('Topic: %s' % args.topic)
print('Description: %s' % args.description)
print('Solution: %s' % args.solution)
print('QE Email: %s' % args.qe_email)
print('QE Group: %s' % args.qe_group)
print('Owner Email: %s' % args.owner_email)
print('Manager Email: %s' % args.manager_email)
return
e.commit()
print(e)
28 changes: 28 additions & 0 deletions errata_tool/cli/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import argparse
import errata_tool.cli.advisory
import errata_tool.cli.product
import errata_tool.cli.release


def main():
parser = argparse.ArgumentParser()
parser.add_argument('--stage', action='store_true',
help='use staging ET instance')
parser.add_argument('--dry-run', action='store_true',
help="show what would happen, but don't do it")

# top-level subcommands:
subparsers = parser.add_subparsers()

# add arguments for each subcommand:
errata_tool.cli.advisory.add_parser(subparsers)
errata_tool.cli.product.add_parser(subparsers)
errata_tool.cli.release.add_parser(subparsers)

args = parser.parse_args()

if args.stage:
from errata_tool import ErrataConnector
ErrataConnector._url = 'https://errata.stage.engineering.redhat.com'

args.func(args)
22 changes: 22 additions & 0 deletions errata_tool/cli/product.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from errata_tool.product import Product


def add_parser(subparsers):
""" Add our product parser to this top-level subparsers object. """
group = subparsers.add_parser('product', help='Get a product')

# product-level subcommands:
sub = group.add_subparsers()

# "get"
get_parser = sub.add_parser('get')
get_parser.add_argument('name', help='eg. "RHCEPH"')
get_parser.set_defaults(func=get)


def get(args):
p = Product(name=args.name)
print('Name: %s' % p.name)
print('Description: %s' % p.description)
print('Supports PDC: %s' % p.supports_pdc)
print('URL: %s' % p.url)
107 changes: 107 additions & 0 deletions errata_tool/cli/release.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import sys
import posixpath
from errata_tool.connector import ErrataConnector
from errata_tool.release import Release
from errata_tool.release import NoReleaseFoundError


def add_parser(subparsers):
""" Add our release parser to this top-level subparsers object. """
group = subparsers.add_parser('release',
help='Get or create a release (RCM)')

# release-level subcommands:
sub = group.add_subparsers()

# "get"
get_parser = sub.add_parser('get', help='get a release')
get_parser.add_argument('name', help='eg. "rhceph-2.4"')
get_parser.set_defaults(func=get)

# "create"
# There are many arguments to create(), and they might change over time.
# Use named args here for future flexibility.
create_parser = sub.add_parser('create', help='create a new release (RCM)')
create_parser.add_argument('--name', required=True,
help='eg. "rhceph-2.4"')
create_parser.add_argument('--product', required=True,
help='eg. "RHCEPH"')
create_parser.add_argument('--type', required=True,
help='eg. "QuarterlyUpdate"')
create_parser.add_argument('--program_manager', required=True,
help='eg. "anharris"')
create_parser.add_argument('--blocker_flags', required=True,
help='eg. "ceph-2.y"')
create_parser.set_defaults(func=create)

# "list-advisories"
ls_parser = sub.add_parser('list-advisories',
help='list advisories for this release')
ls_parser.add_argument('name', help='eg. "rhceph-2.4"')
ls_parser.set_defaults(func=list_advisories)
ls_parser.add_argument('--status', required=False,
choices=('NEW_FILES', 'QE', 'REL_PREP',
'IN_PUSH', 'SHIPPED_LIVE'),
help='optionally filter by status')


def get(args):
try:
r = Release(name=args.name)
print('Name: %s' % r.name)
print('Description: %s' % r.description)
print('URL: %s' % r.url)
except NoReleaseFoundError:
print('%s release not found' % args.name)
sys.exit(1)


def create(args):
try:
r = Release(name=args.name)
print('%s is already defined at %s' % (r.name, r.url))
sys.exit(1)
except NoReleaseFoundError:
pass
if args.dry_run:
print('DRY RUN: would create new release:')
print('Name: %s' % args.name)
print('Product: %s' % args.product)
print('Type: %s' % args.type)
print('Program manager: %s' % args.program_manager)
print('Blocker flags: %s' % args.blocker_flags)
return
r = Release.create(
name=args.name,
product=args.product,
type=args.type,
program_manager=args.program_manager,
blocker_flags=args.blocker_flags,
)
print('created new %s release' % args.name)
print('visit %s to add PDC associations' % r.edit_url)


def list_advisories(args):
try:
r = Release(name=args.name)
except NoReleaseFoundError:
print('%s release not found' % args.name)
sys.exit(1)
advisories = r.advisories()
if args.status:
advisories = [a for a in advisories if a['status'] == args.status]
if not advisories:
print('no %s advisories found for release %s' % (args.status,
args.name))
else:
if not advisories:
print('no advisories found for release %s' % args.name)
for advisory in advisories:
# hack, avoid initializing the full Erratum class just to get the URL:
url = posixpath.join(ErrataConnector._url, 'errata',
str(advisory['id']))
print('------------------------------')
print('URL: %s' % url)
print('synopsis: %s' % advisory['synopsis'])
print('status: %s' % advisory['status'])
63 changes: 63 additions & 0 deletions errata_tool/tests/cli/test_advisory_cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import sys
import pytest
from errata_tool.cli import main


class FakeErratum(object):
def __init__(self, **kwargs):
pass

def commit(self):
pass


def test_short_help(monkeypatch):
argv = ['errata-tool', 'advisory', '-h']
monkeypatch.setattr(sys, 'argv', argv)
with pytest.raises(SystemExit):
main.main()


def test_help(monkeypatch):
argv = ['errata-tool', 'advisory', '--help']
monkeypatch.setattr(sys, 'argv', argv)
with pytest.raises(SystemExit):
main.main()


def test_get_missing_name(monkeypatch):
argv = ['errata-tool', 'advisory', 'get']
monkeypatch.setattr(sys, 'argv', argv)
with pytest.raises(SystemExit):
main.main()


def test_get(monkeypatch):
argv = ['errata-tool', 'advisory', 'get', '12345']
monkeypatch.setattr(sys, 'argv', argv)
monkeypatch.setattr('errata_tool.cli.advisory.Erratum', FakeErratum)
main.main()


def test_create_missing_args(monkeypatch):
argv = ['errata-tool', 'advisory', 'create']
monkeypatch.setattr(sys, 'argv', argv)
with pytest.raises(SystemExit):
main.main()


def test_create(monkeypatch):
monkeypatch.setattr('errata_tool.cli.advisory.Erratum', FakeErratum)
argv = ['errata-tool', 'advisory', 'create',
'--product', 'RHCEPH',
'--release', 'rhceph-2.1',
'--synopsis', 'Red Hat Product 2.1 bug fix update',
'--topic', 'An update for Red Hat Product 2.1 is now available.',
'--description', 'This update contains the following fixes ...',
'--solution', 'Before applying this update...',
'--qe-email', '[email protected]',
'--qe-group', 'RHC (Ceph) QE',
'--owner-email', '[email protected]',
'--manager-email', '[email protected]']
monkeypatch.setattr(sys, 'argv', argv)
main.main()
Loading

0 comments on commit a3f4f05

Please sign in to comment.