From e690d4032263f906a5fc38d1e3d708670f56db02 Mon Sep 17 00:00:00 2001
From: Aurélien Bompard <aurelien@bompard.org>
Date: Mar 24 2022 10:40:22 +0000
Subject: [PATCH 1/2] Use Bodhi Client >= 6.0.0


Signed-off-by: Aurélien Bompard <aurelien@bompard.org>

---

diff --git a/fedpkg/__init__.py b/fedpkg/__init__.py
index a52c70d..5779cac 100644
--- a/fedpkg/__init__.py
+++ b/fedpkg/__init__.py
@@ -22,10 +22,8 @@ from . import cli  # noqa
 from .lookaside import FedoraLookasideCache
 from pyrpkg.utils import cached_property
 
-try:
-    from bodhi.client.bindings import BodhiClient as _BodhiClient
-except ImportError:
-    _BodhiClient = None
+from bodhi.client.bindings import BodhiClient
+from bodhi.client.constants import UPDATE_TYPES, REQUEST_TYPES
 
 try:
     from distro import linux_distribution  # noqa
@@ -33,57 +31,6 @@ except ImportError:
     from platform import linux_distribution  # noqa
 
 
-if _BodhiClient is not None:
-    from fedora.client import AuthError
-
-    def clear_csrf_and_retry(func):
-        """Clear csrf token and retry
-
-        fedpkg uses Bodhi Python binding API list_overrides first before other
-        save and extend APIs. That causes a readonly csrf token is received,
-        which will be got again when next time to construct request data to
-        modify updates. That is not expected and AuthError will be raised.
-
-        So, the solution is to capture the AuthError error, clear the token and
-        try to modify update again by requesting another token with user's
-        credential.
-        """
-        def _decorator(self, *args, **kwargs):
-            try:
-                return func(self, *args, **kwargs)
-            except AuthError:
-                self._session.cookies.clear()
-                self.csrf_token = None
-                return func(self, *args, **kwargs)
-        return _decorator
-
-    class BodhiClient(_BodhiClient):
-        """Customized BodhiClient for fedpkg"""
-
-        UPDATE_TYPES = ['bugfix', 'security', 'enhancement', 'newpackage']
-        REQUEST_TYPES = ['testing', 'stable']
-
-        @clear_csrf_and_retry
-        def save(self, *args, **kwargs):
-            return super(BodhiClient, self).save(*args, **kwargs)
-
-        @clear_csrf_and_retry
-        def save_override(self, *args, **kwargs):
-            return super(BodhiClient, self).save_override(*args, **kwargs)
-
-        @clear_csrf_and_retry
-        def extend_override(self, override, expiration_date):
-            data = dict(
-                nvr=override['nvr'],
-                notes=override['notes'],
-                expiration_date=expiration_date,
-                edited=override['nvr'],
-                csrf_token=self.csrf(),
-            )
-            return self.send_request(
-                'overrides/', verb='POST', auth=True, data=data)
-
-
 class Commands(pyrpkg.Commands):
 
     def __init__(self, *args, **kwargs):
@@ -344,8 +291,7 @@ class Commands(pyrpkg.Commands):
 
     def update(self, bodhi_config, template='bodhi.template', bugs=[]):
         """Submit an update to bodhi using the provided template."""
-        bodhi = BodhiClient(username=self.user,
-                            staging=bodhi_config['staging'])
+        bodhi = BodhiClient(staging=bodhi_config['staging'])
 
         update_details = bodhi.parse_file(template)
 
@@ -353,10 +299,10 @@ class Commands(pyrpkg.Commands):
             if not detail['type']:
                 raise ValueError(
                     'Missing update type, which is required to create update.')
-            if detail['type'] not in BodhiClient.UPDATE_TYPES:
+            if detail['type'] not in UPDATE_TYPES:
                 raise ValueError(
                     'Incorrect update type {0}'.format(detail['type']))
-            if detail['request'] not in BodhiClient.REQUEST_TYPES:
+            if detail['request'] not in REQUEST_TYPES:
                 raise ValueError(
                     'Incorrect request type {0}'.format(detail['request']))
 
@@ -368,8 +314,7 @@ class Commands(pyrpkg.Commands):
 
     def create_buildroot_override(self, bodhi_config, build, duration,
                                   notes=''):
-        bodhi = BodhiClient(username=self.user,
-                            staging=bodhi_config['staging'])
+        bodhi = BodhiClient(staging=bodhi_config['staging'])
         result = bodhi.list_overrides(builds=build)
         if result['total'] == 0:
             try:
@@ -398,8 +343,7 @@ class Commands(pyrpkg.Commands):
                               'not expired.', build)
 
     def extend_buildroot_override(self, bodhi_config, build, duration):
-        bodhi = BodhiClient(username=self.user,
-                            staging=bodhi_config['staging'])
+        bodhi = BodhiClient(staging=bodhi_config['staging'])
         result = bodhi.list_overrides(builds=build)
 
         if result['total'] == 0:
@@ -448,7 +392,11 @@ class Commands(pyrpkg.Commands):
         try:
             self.log.debug('Extend override expiration date to %s',
                            new_expiration_date)
-            override = bodhi.extend_override(override, new_expiration_date)
+            override = bodhi.save_override(
+                nvr=override["nvr"],
+                notes=override["notes"],
+                expiration_date=new_expiration_date
+            )
         except Exception as e:
             self.log.error('Cannot extend override expiration.')
             raise pyrpkg.rpkgError(str(e))
diff --git a/requirements.txt b/requirements.txt
index 7e52a2c..20b00b7 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,5 +1,5 @@
 argcomplete
-bodhi-client
+bodhi-client>=6.0.0
 openidc-client
 python-bugzilla
 rpkg
diff --git a/test/test_cli.py b/test/test_cli.py
index 7bc5aef..5f1ec66 100644
--- a/test/test_cli.py
+++ b/test/test_cli.py
@@ -161,9 +161,7 @@ class TestUpdate(CliTestCase):
 
     @patch('bodhi.client.bindings.BodhiClient.csrf')
     @patch('bodhi.client.bindings.BodhiClient.send_request')
-    # Do not operate OpenIDC session file with lock
-    @patch('fedora.client.OpenIdBaseClient._load_cookies')
-    def assert_bodhi_update(self, cli, _load_cookies, send_request, csrf,
+    def assert_bodhi_update(self, cli, send_request, csrf,
                             update_type=None, request_type=None, notes=None,
                             stable_karma=None, unstable_karma=None):
         csrf.return_value = '123456'
@@ -1581,10 +1579,8 @@ class TestBodhiOverride(CliTestCase):
     @patch('bodhi.client.bindings.BodhiClient.list_overrides')
     @patch('bodhi.client.bindings.BodhiClient.save_override')
     @patch('bodhi.client.bindings.BodhiClient.override_str')
-    # Do not acquire lock for local openidc cache file
-    @patch('fedora.client.OpenIdBaseClient._load_cookies')
     def test_create_for_given_build(
-            self, _load_cookies, override_str, save_override, list_overrides):
+            self, override_str, save_override, list_overrides):
         list_overrides.return_value = {'total': 0}
         expiration_date = datetime.now() + timedelta(days=7)
         new_override = {
@@ -1695,46 +1691,6 @@ class TestBodhiOverride(CliTestCase):
                     'Buildroot override for %s already exists and not '
                     'expired.', 'rpkg-1.54-2.fc28')
 
-    @patch('fedora.client.OpenIdBaseClient._load_cookies')
-    @patch('bodhi.client.bindings.BodhiClient.list_overrides')
-    @patch('bodhi.client.bindings.BodhiClient.save_override')
-    @patch('fedpkg.Commands.nvr', new_callable=PropertyMock)
-    def test_retry_create(
-            self, nvr, save_override, list_overrides, _load_cookies):
-        nvr.return_value = 'rpkg-1.54-2.fc28'
-        list_overrides.return_value = {'total': 0}
-
-        # For save_override raises AuthError twice.
-        from fedora.client import AuthError
-        save_override.side_effect = AuthError
-
-        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()
-
-            # This is raised when call save_override in the second time due to
-            # the AuthError is raised again. This is expected for running this
-            # test.
-            six.assertRaisesRegex(self, rpkgError, 'Cannot create override',
-                                  cli.create_buildroot_override)
-
-        # First call to save_override should raise AuthError, and it must be
-        # called twice.
-        self.assertEqual(2, save_override.call_count)
-        save_override.assert_has_calls([
-            call(nvr='rpkg-1.54-2.fc28',
-                 duration=7,
-                 notes='build for fedpkg'),
-            call(nvr='rpkg-1.54-2.fc28',
-                 duration=7,
-                 notes='build for fedpkg')
-        ])
-
     def test_invalid_duration_option(self):
         cli_cmds = (
             (
@@ -1780,15 +1736,10 @@ class TestBodhiOverrideExtend(CliTestCase):
         self.anon_kojisession_m = self.anon_kojisession_p.start()
         self.kojisession = self.anon_kojisession_m.return_value
 
-        self.load_cookies_p = patch(
-            'fedora.client.OpenIdBaseClient._load_cookies')
-        self.mock_load_cookies = self.load_cookies_p.start()
-
         # Fake build returned from Koji for the specified build NVR in tests
         self.kojisession.getBuild.return_value = {'build_id': 1}
 
     def tearDown(self):
-        self.load_cookies_p.stop()
         self.anon_kojisession_p.stop()
         self.cbv_p.stop()
         super(TestBodhiOverrideExtend, self).tearDown()
@@ -1871,7 +1822,6 @@ class TestBodhiOverrideExtend(CliTestCase):
             'expiration_date': expected_expiration_date,
             'nvr': build_nvr,
             'notes': build_override['notes'],
-            'edited': build_nvr,
             'csrf_token': csrf.return_value,
         }
         send_request.assert_called_once_with(
@@ -1928,7 +1878,6 @@ class TestBodhiOverrideExtend(CliTestCase):
             'expiration_date': expected_expiration_date,
             'nvr': build_nvr,
             'notes': build_override['notes'],
-            'edited': build_nvr,
             'csrf_token': csrf.return_value,
         }
         send_request.assert_called_once_with(
@@ -1986,7 +1935,6 @@ class TestBodhiOverrideExtend(CliTestCase):
             'expiration_date': expected_expiration_date,
             'nvr': build_nvr,
             'notes': build_override['notes'],
-            'edited': build_nvr,
             'csrf_token': csrf.return_value,
         }
         send_request.assert_called_once_with(
@@ -1994,10 +1942,10 @@ class TestBodhiOverrideExtend(CliTestCase):
 
     @patch('fedpkg.BodhiClient.list_overrides')
     @patch('fedpkg.BodhiClient.csrf')
-    @patch('fedpkg.BodhiClient.extend_override')
+    @patch('fedpkg.BodhiClient.save_override')
     def test_error_handled_properly_when_fail_to_request(
-            self, extend_override, csrf, list_overrides):
-        extend_override.side_effect = Exception
+            self, save_override, csrf, list_overrides):
+        save_override.side_effect = Exception
 
         utcnow = datetime.utcnow()
         override_expiration_date = utcnow + timedelta(days=7)
@@ -2025,72 +1973,6 @@ class TestBodhiOverrideExtend(CliTestCase):
             six.assertRaisesRegex(self, rpkgError, '',
                                   cli.extend_buildroot_override)
 
-    @patch('fedpkg.BodhiClient.list_overrides')
-    @patch('fedpkg.BodhiClient.csrf')
-    @patch('fedpkg.BodhiClient.send_request')
-    def test_retry_to_extend_override_by_days(
-            self, send_request, csrf, list_overrides):
-        utcnow = datetime.utcnow()
-        override_expiration_date = utcnow + timedelta(days=7)
-
-        from fedora.client import AuthError
-        send_request.side_effect = AuthError
-
-        csrf.side_effect = ['123456', '678901']
-
-        build_nvr = 'somepkg-1.54-2.fc28'
-        build_override = {
-            'expiration_date': override_expiration_date.strftime('%Y-%m-%d %H:%M:%S'),
-            'nvr': build_nvr,
-            'notes': 'build for other package',
-            'build': {'nvr': build_nvr},
-            'submitter': {'name': 'someone'},
-            'expired_date': utcnow - timedelta(days=20)
-        }
-
-        list_overrides.return_value = {
-            'total': 1,
-            'overrides': [build_override]
-        }
-        edited_override = build_override.copy()
-        expected_expiration_date = override_expiration_date + timedelta(days=2)
-        edited_override['expiration_date'] = \
-            expected_expiration_date.strftime('%Y-%m-%d %H:%M:%S')
-        send_request.return_value = edited_override
-
-        cli_cmd = [
-            'fedpkg', '--path', self.cloned_repo_path,
-            'override', 'extend', '2', build_nvr
-        ]
-
-        with patch('sys.argv', new=cli_cmd):
-            cli = self.new_cli()
-
-            # This error is expected due to the design for this test. See also
-            # above explanation to test of `override create`.
-            six.assertRaisesRegex(
-                self, rpkgError, '', cli.extend_buildroot_override)
-
-        # Ensure no microsecond is included in the expected expiration data
-        new_date = override_expiration_date + timedelta(days=2)
-        expected_expiration_date = datetime(year=new_date.year,
-                                            month=new_date.month,
-                                            day=new_date.day,
-                                            hour=new_date.hour,
-                                            minute=new_date.minute,
-                                            second=new_date.second)
-
-        send_request.assert_has_calls([
-            call('overrides/', verb='POST', auth=True, data={
-                'expiration_date': expected_expiration_date,
-                'nvr': build_nvr,
-                'notes': build_override['notes'],
-                'edited': build_nvr,
-                'csrf_token': token,
-            })
-            for token in csrf.side_effect
-        ])
-
     @freeze_time('2018-07-22')
     @patch('fedpkg.BodhiClient.list_overrides')
     def test_raise_error_if_duration_less_than_today(self, list_overrides):
diff --git a/tox.ini b/tox.ini
index 009f3ae..1be6026 100644
--- a/tox.ini
+++ b/tox.ini
@@ -18,6 +18,7 @@ commands =
     python -m pytest {posargs}
 setenv=
     PYCURL_SSL_LIBRARY=openssl
+    HOME={envtmpdir}/home
 
 [testenv:py27]
 deps =

From 456a492b5dc82c7eccbcfae5ed93f5001a05fed9 Mon Sep 17 00:00:00 2001
From: Aurélien Bompard <aurelien@bompard.org>
Date: Mar 24 2022 10:40:22 +0000
Subject: [PATCH 2/2] Drop support for Python 2


Since Bodhi Client >= 6 is not compatible with Python 2.

Signed-off-by: Aurélien Bompard <aurelien@bompard.org>

---

diff --git a/Makefile b/Makefile
index f0008b3..2c23f7a 100644
--- a/Makefile
+++ b/Makefile
@@ -5,5 +5,5 @@ test: $(default_targets)
 tox:
 	@python3 -m venv .env
 	@.env/bin/pip install tox
-	@.env/bin/tox -e py27,py36,py39,flake8,flake8python2 --parallel=auto ${TOX_POSARGS}
+	@.env/bin/tox -e py36,py39,flake8 --parallel=auto ${TOX_POSARGS}
 .PHONY: tox
diff --git a/README.rst b/README.rst
index 51ad961..9615e5f 100644
--- a/README.rst
+++ b/README.rst
@@ -3,7 +3,7 @@ Introduction
 
 This is the fedpkg project, which mostly is a subclass of the rpkg project.
 
-rpkg works with Python 2.7, 3.6, 3.9 and 3.10.
+rpkg works with Python 3.6, 3.9 and 3.10.
 
 License
 =======
@@ -17,8 +17,7 @@ Install from distribution packages
 ----------------------------------
 
 fedpkg is available in Fedora and EPEL repositories. It can be installed with
-package manager command. There is Python 3 package for Fedora and EPEL8;
-and Python 2 package in EPEL7.
+package manager command. There is a Python 3 package for Fedora and EPEL8.
 
 Install in a Fedora system::
 
@@ -35,7 +34,7 @@ Contribution
 ============
 
 You are welcome to write patches to fix or improve rpkg. All code should work
-with Python 2.7 and 3. Before you create a PR to propose your changes,
+with Python 3. Before you create a PR to propose your changes,
 make sure
 
 Sign-off commit
@@ -54,7 +53,7 @@ Before make a pull request, ensure local changes pass all test cases.
 
 Before run tests, install these packages::
 
-    sudo dnf install python27 python36 python37 git make gcc rpm-build \
+    sudo dnf install python36 python37 git make gcc rpm-build \
     libcurl-devel krb5-devel openssl-devel
 
 To run tests simply, ``make test``.
diff --git a/jenkins_test_py2.dockerfile b/jenkins_test_py2.dockerfile
deleted file mode 100644
index 75c2e22..0000000
--- a/jenkins_test_py2.dockerfile
+++ /dev/null
@@ -1,32 +0,0 @@
-FROM centos:7
-LABEL \
-    name="fedpkg test for Python 2" \
-    description="Run tests using tox with Python 2" \
-    vendor="fedpkg developers" \
-    license="MIT"
-
-RUN yum -y update && yum install -y epel-release
-RUN yum -y install \
-        pytest \
-        python2-six \
-        python2-mock \
-        git \
-        GitPython \
-        python-requests \
-        PyYAML \
-        python2-cccolutils \
-        python2-koji \
-        python2-bugzilla \
-        python2-freezegun \
-        bodhi-client \
-        python-flake8 \
-        python2-fedora \
-        python2-distro \
-        rpm-build
-RUN yum clean all
-
-WORKDIR /src
-
-ENV PYTHONPATH "${PYTHONPATH}:./rpkg"
-
-CMD ["python", "-m", "pytest" ,"test"]
diff --git a/requirements-py2.txt b/requirements-py2.txt
deleted file mode 100644
index e1de6c2..0000000
--- a/requirements-py2.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-argcomplete
-openidc-client
-python-bugzilla < 3.0.0
-rpkg
-six
diff --git a/setup.py b/setup.py
index c7bef6d..6c9566d 100755
--- a/setup.py
+++ b/setup.py
@@ -63,8 +63,6 @@ setup(
         'Intended Audience :: Developers',
         'Topic :: Software Development :: Build Tools',
         'License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)',
-        'Programming Language :: Python :: 2',
-        'Programming Language :: Python :: 2.7',
         'Programming Language :: Python :: 3',
         'Programming Language :: Python :: 3.6',
         'Programming Language :: Python :: 3.9',
diff --git a/tox.ini b/tox.ini
index 1be6026..d44425d 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,15 +1,13 @@
 [tox]
-envlist = py27,py36,py39,py310,flake8
+envlist = py36,py39,py310,flake8
 
 [testenv]
 sitepackages=false
 basepython=
-    py27: {env:TOXPYTHON:python2.7}
     py36: {env:TOXPYTHON:python3.6}
     py39: {env:TOXPYTHON:python3.9}
     py310: {env:TOXPYTHON:python3.10}
     flake8: {env:TOXPYTHON:python3.6}
-    flake8python2: {env:TOXPYTHON:python2.7}
 deps =
     -r{toxinidir}/requirements.txt
     -r{toxinidir}/tests-requirements.txt
@@ -20,15 +18,6 @@ setenv=
     PYCURL_SSL_LIBRARY=openssl
     HOME={envtmpdir}/home
 
-[testenv:py27]
-deps =
-    -r{toxinidir}/requirements-py2.txt
-    -r{toxinidir}/tests-requirements.txt
-
 [testenv:flake8]
 deps = flake8
 commands = python -m flake8 fedpkg/ test/
-
-[testenv:flake8python2]
-deps = flake8
-commands = python -m flake8 fedpkg/ test/

