From 532c1171316bd69c3e0140ba43269db61cc9427f Mon Sep 17 00:00:00 2001 From: Chenxiong Qi Date: Jun 06 2018 07:21:02 +0000 Subject: Add new command for creating override in Bodhi To create a buildroot override, run inside a package repo: fedpkg override create --duration 5 It also accepts a specified build: fedpkg override create --duration 5 rpkg-1.54-2.fc27 Fixes #92 Signed-off-by: Chenxiong Qi --- diff --git a/fedpkg/__init__.py b/fedpkg/__init__.py index 683a1a8..7f09097 100644 --- a/fedpkg/__init__.py +++ b/fedpkg/__init__.py @@ -15,6 +15,8 @@ import git import re import platform +from datetime import datetime + from . import cli # noqa from .lookaside import FedoraLookasideCache from pyrpkg.utils import cached_property @@ -234,6 +236,41 @@ class Commands(pyrpkg.Commands): cmd.append(self.nvr) self._run_command(cmd, shell=True) + def create_buildroot_override(self, bodhi_config, build, duration, + notes=''): + from bodhi.client.bindings import BodhiClient + + bodhi = BodhiClient(staging=bodhi_config['staging']) + result = bodhi.list_overrides(builds=build) + if result['total'] == 0: + try: + self.log.debug( + 'Create override in %s: nvr=%s, duration=%s, notes="%s"', + 'staging Bodhi' if bodhi_config['staging'] else 'Bodhi', + build, duration, notes) + override = bodhi.save_override( + nvr=build, duration=duration, notes=notes) + except Exception as e: + self.log.error(str(e)) + raise pyrpkg.rpkgError('Cannot create override.') + else: + self.log.info('Override is created.') + self.log.info('Expiration date: %s', + override['expiration_date']) + self.log.info('Notes: %s', override['notes']) + else: + override = result['overrides'][0] + expiration_date = datetime.strptime(override['expiration_date'], + '%Y-%m-%d %H:%M:%S') + if expiration_date < datetime.now(): + self.log.info( + 'Buildroot override for %s exists and is expired. Consider' + ' using command `override extend` to extend duration.', + build) + else: + self.log.info('Buildroot override for %s already exists and ' + 'not expired.', build) + if __name__ == "__main__": from fedpkg.__main__ import main diff --git a/fedpkg/cli.py b/fedpkg/cli.py index 2b21719..d55259a 100644 --- a/fedpkg/cli.py +++ b/fedpkg/cli.py @@ -40,7 +40,7 @@ def check_bodhi_version(): dist = pkg_resources.get_distribution('bodhi_client') except pkg_resources.DistributionNotFound: raise rpkgError('bodhi-client < 2.0 is not supported.') - major = int(dist.version.split('.', 1)) + major = int(dist.version.split('.', 1)[0]) if major >= 4: raise rpkgError( 'This system has bodhi v{0}, which is unsupported.'.format(major)) @@ -72,6 +72,7 @@ class fedpkgClient(cliClient): self.register_request_repo() self.register_request_tests_repo() self.register_request_branch() + self.register_override() # Target registry goes here def register_retire(self): @@ -229,6 +230,62 @@ and created: ) request_branch_parser.set_defaults(command=self.request_branch) + def register_override(self): + """Register command line parser for subcommand override""" + + def validate_duration(value): + try: + duration = int(value) + except ValueError: + raise argparse.ArgumentTypeError('duration must be an integer.') + if duration > 0: + return duration + raise argparse.ArgumentTypeError( + 'override should have 1 day to exist at least.') + + override_parser = self.subparsers.add_parser( + 'override', + help='Manage buildroot overrides') + override_subparser = override_parser.add_subparsers( + description='Commands on override') + + create_parser = override_subparser.add_parser( + 'create', + help='Create buildroot override from build', + formatter_class=argparse.RawDescriptionHelpFormatter, + description='''\ +Create a buildroot override from build guessed from current release branch or +specified explicitly. + +Examples: + +Create a buildroot override from build guessed from release branch. Note that, +command must run inside a package repository. + + fedpkg switch-branch f28 + fedpkg override create --duration 5 + +Create for a specified build: + + fedpkg override create --duration 5 package-1.0-1.fc28 +''') + create_parser.add_argument( + '--duration', + type=validate_duration, + default=7, + help='Number of days the override should exist. If omitted, ' + 'default to 7 days.') + create_parser.add_argument( + '--notes', + default='No explanation given...', + help='Optional notes on why this override is in place.') + create_parser.add_argument( + 'NVR', + nargs='?', + help='Create override from this build. If omitted, build will be' + ' guessed from current release branch.') + create_parser.set_defaults(command=self.create_buildroot_override) + # Target functions go here def retire(self): # Skip if package is already retired... @@ -254,20 +311,21 @@ and created: log.append('#') return lines[0], "\n".join(log) - def update(self): - check_bodhi_version() - + def _get_bodhi_config(self): try: section = '%s.bodhi' % self.name - bodhi_config = { + return { 'staging': self.config.getboolean(section, 'staging'), - } + } except (ValueError, NoOptionError, NoSectionError) as e: self.log.error(str(e)) raise rpkgError('Could not get bodhi options. It seems configuration is changed. ' 'Please try to reinstall %s or consult developers to see what ' 'is wrong with it.' % self.name) + def update(self): + check_bodhi_version() + bodhi_config = self._get_bodhi_config() template = """\ [ %(nvr)s ] @@ -630,3 +688,13 @@ suggest_reboot=False name=name, config=config, ) + + def create_buildroot_override(self): + """Create a buildroot override in Bodhi""" + check_bodhi_version() + bodhi_config = self._get_bodhi_config() + self.cmd.create_buildroot_override( + bodhi_config, + build=self.args.NVR or self.cmd.nvr, + duration=self.args.duration, + notes=self.args.notes) diff --git a/test/test_cli.py b/test/test_cli.py index 3de4bd5..a71c6ba 100644 --- a/test/test_cli.py +++ b/test/test_cli.py @@ -15,7 +15,16 @@ import os import sys import json import pkg_resources -import unittest + +try: + import unittest2 as unittest +except ImportError: + import unittest + +try: + import bodhi +except ImportError: + bodhi = None from datetime import datetime, timedelta from tempfile import mkdtemp @@ -1122,3 +1131,126 @@ class TestCheckBodhiVersion(unittest.TestCase): six.assertRaisesRegex( self, rpkgError, r'bodhi-client < 2\.0 is not supported\.', check_bodhi_version) + + +@unittest.skipUnless(bodhi, 'Skip if no supported bodhi-client is available') +class TestBodhiOverride(CliTestCase): + """Test command override""" + + @patch('fedpkg.cli.check_bodhi_version') + @patch('bodhi.client.bindings.BodhiClient') + def test_create_for_given_build(self, BodhiClient, check_bodhi_version): + bodhi_client = BodhiClient.return_value + bodhi_client.list_overrides.return_value = {'total': 0} + expiration_date = datetime.now() + timedelta(days=7) + new_override = { + 'expiration_date': expiration_date.strftime('%Y-%m-%d %H:%M:%S'), + 'notes': 'build for fedpkg' + } + bodhi_client.save_override.return_value = new_override + + cli_cmd = [ + 'fedpkg', '--path', self.cloned_repo_path, + 'override', 'create', + '--duration', '7', '--notes', 'build for fedpkg', + 'rpkg-1.54-1.fc28' + ] + + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + with patch.object(cli.cmd, 'log') as log: + cli.create_buildroot_override() + + log.info.assert_any_call('Expiration date: %s', + new_override['expiration_date']) + log.info.assert_any_call('Notes: %s', new_override['notes']) + + bodhi_client.save_override.assert_called_once_with( + nvr='rpkg-1.54-1.fc28', + duration=7, + notes='build for fedpkg') + + @patch('fedpkg.cli.check_bodhi_version') + @patch('bodhi.client.bindings.BodhiClient') + @patch('fedpkg.Commands.nvr', new_callable=PropertyMock) + def test_create_from_current_branch( + self, nvr, BodhiClient, check_bodhi_version): + nvr.return_value = 'rpkg-1.54-2.fc28' + bodhi_client = BodhiClient.return_value + bodhi_client.list_overrides.return_value = {'total': 0} + + cli_cmd = [ + 'fedpkg', '--path', self.cloned_repo_path, + 'override', 'create', + '--duration', '7', '--notes', 'build for fedpkg', + ] + + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + cli.create_buildroot_override() + + bodhi_client.save_override.assert_called_once_with( + nvr='rpkg-1.54-2.fc28', + duration=7, + notes='build for fedpkg') + + @patch('fedpkg.cli.check_bodhi_version') + @patch('bodhi.client.bindings.BodhiClient') + @patch('fedpkg.Commands.nvr', new_callable=PropertyMock) + def test_override_already_exists_but_expired( + self, nvr, BodhiClient, check_bodhi_version): + nvr.return_value = 'rpkg-1.54-2.fc28' + today = datetime.today() + fake_expiration_date = today - timedelta(days=10) + BodhiClient.return_value.list_overrides.return_value = { + 'total': 1, + 'overrides': [{ + 'expiration_date': fake_expiration_date.strftime( + '%Y-%m-%d %H:%M:%S') + }] + } + + cli_cmd = [ + 'fedpkg', '--path', self.cloned_repo_path, + 'override', 'create', + '--duration', '7', '--notes', 'build for fedpkg', + ] + + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + with patch.object(cli.cmd, 'log') as log: + cli.create_buildroot_override() + log.info.assert_any_call( + 'Buildroot override for %s exists and is expired. Consider' + ' using command `override extend` to extend duration.', + 'rpkg-1.54-2.fc28') + + @patch('fedpkg.cli.check_bodhi_version') + @patch('bodhi.client.bindings.BodhiClient') + @patch('fedpkg.Commands.nvr', new_callable=PropertyMock) + def test_override_already_exists_but_not_expired( + self, nvr, BodhiClient, check_bodhi_version): + nvr.return_value = 'rpkg-1.54-2.fc28' + today = datetime.today() + fake_expiration_date = today + timedelta(days=10) + BodhiClient.return_value.list_overrides.return_value = { + 'total': 1, + 'overrides': [{ + 'expiration_date': fake_expiration_date.strftime( + '%Y-%m-%d %H:%M:%S') + }] + } + + cli_cmd = [ + 'fedpkg', '--path', self.cloned_repo_path, + 'override', 'create', + '--duration', '7', '--notes', 'build for fedpkg', + ] + + with patch('sys.argv', new=cli_cmd): + cli = self.new_cli() + with patch.object(cli.cmd, 'log') as log: + cli.create_buildroot_override() + log.info.assert_any_call( + 'Buildroot override for %s already exists and not ' + 'expired.', 'rpkg-1.54-2.fc28') diff --git a/tox.ini b/tox.ini index fdb9934..a0e1b30 100644 --- a/tox.ini +++ b/tox.ini @@ -2,6 +2,7 @@ envlist = py26,py27,py36,flake8 [testenv] +sitepackages=True deps = -r{toxinidir}/requirements.txt -r{toxinidir}/tests-requirements.txt