diff options
| -rw-r--r-- | copr-backend.spec | 15 | ||||
| -rw-r--r-- | euler_msgbus.patch | 4 | ||||
| -rw-r--r-- | fail_to_build_in_oe2403.patch | 14 | ||||
| -rw-r--r-- | helpers.patch | 35 | ||||
| -rw-r--r-- | print_queues.patch | 12 | ||||
| -rw-r--r-- | sources | 4 | ||||
| -rw-r--r-- | support_signatrust_backend.patch | 505 | 
7 files changed, 440 insertions, 149 deletions
diff --git a/copr-backend.spec b/copr-backend.spec index 511f185..ae9f5f4 100644 --- a/copr-backend.spec +++ b/copr-backend.spec @@ -1,11 +1,11 @@  %global prunerepo_version 1.20 -%global tests_version 2 +%global tests_version 5  %global tests_tar test-data-copr-backend -%global copr_common_version 0.19 +%global copr_common_version 0.20.1.dev1  Name:       copr-backend -Version:    1.168 +Version:    1.173  Release:    1%{?dist}  Summary:    Backend for Copr @@ -17,9 +17,9 @@ URL:        https://github.com/fedora-copr/copr  # tito build --tgz --tag %%name-%%version-%%release  Source0:    %{name}-%{version}.tar.gz  Source1:    %{tests_tar}-%{tests_version}.tar.gz -Patch1:     helpers.patch +Patch1:     fail_to_build_in_oe2403.patch  Patch2:     euler_msgbus.patch -Patch3:     print_queues.patch +#Patch3:     print_queues.patch  #Patch4:     redis_helpers.patch # should patch to copr common  Patch5:     support_signatrust_backend.patch  Patch6:     signatrust_bin.patch @@ -39,6 +39,7 @@ BuildRequires: python3-devel  BuildRequires: python3-setuptools  BuildRequires: python3-copr +BuildRequires: python3-kafka-python  BuildRequires: python3-copr-common = %copr_common_version  BuildRequires: python3-daemon  BuildRequires: python3-dateutil @@ -103,6 +104,7 @@ Requires:   rsync  Requires:   modulemd-tools >= 0.6  Recommends: util-linux-core  Requires:   zstd +Requires: python3-kafka-python  Requires(post): systemd  Requires(preun): systemd @@ -177,7 +179,6 @@ install -d %{buildroot}%{_sysconfdir}/logstash.d  install -d %{buildroot}%{_datadir}/logstash/patterns/  cp -a conf/logstash/lighttpd.pattern %{buildroot}%{_datadir}/logstash/patterns/lighttpd.pattern -cp -a conf/playbooks %{buildroot}%{_pkgdocdir}/  install -d %{buildroot}%{_pkgdocdir}/examples/%{_sysconfdir}/logstash.d  cp -a conf/logstash/copr_backend.conf %{buildroot}%{_pkgdocdir}/examples/%{_sysconfdir}/logstash.d/copr_backend.conf @@ -221,7 +222,6 @@ useradd -r -g copr -G lighttpd -s /bin/bash -c "COPR user" copr  %config(noreplace) %{_sysconfdir}/logrotate.d/copr-backend  %dir %{_pkgdocdir}  %doc %{_pkgdocdir}/lighttpd -%doc %{_pkgdocdir}/playbooks  %dir %{_sysconfdir}/copr  %config(noreplace) %attr(0640, root, copr) %{_sysconfdir}/copr/copr-be.conf  %{_unitdir}/*.service @@ -242,7 +242,6 @@ useradd -r -g copr -G lighttpd -s /bin/bash -c "COPR user" copr  %doc  %{_pkgdocdir}/  %exclude %{_pkgdocdir}/lighttpd -%exclude %{_pkgdocdir}/playbooks  %changelog  * Wed Mar 12 2025 lichaoran <pkwarcraft@hotmail.com> 1.168-1 diff --git a/euler_msgbus.patch b/euler_msgbus.patch index 8973ce1..5eca90a 100644 --- a/euler_msgbus.patch +++ b/euler_msgbus.patch @@ -1,8 +1,8 @@ -diff --git a/backend/copr_backend/euler_msgbus.py b/backend/copr_backend/euler_msgbus.py +diff --git a/copr_backend/euler_msgbus.py b/copr_backend/euler_msgbus.py  new file mode 100644  index 000000000..1395249be  --- /dev/null -+++ b/backend/copr_backend/euler_msgbus.py ++++ b/copr_backend/euler_msgbus.py  @@ -0,0 +1,77 @@  +import socket  + diff --git a/fail_to_build_in_oe2403.patch b/fail_to_build_in_oe2403.patch new file mode 100644 index 0000000..be32aee --- /dev/null +++ b/fail_to_build_in_oe2403.patch @@ -0,0 +1,14 @@ +diff --git a/tests/test_modifyrepo.py b/backend/tests/test_modifyrepo.py +index 330ff68f7..2c1bd0a66 100644 +--- a/tests/test_modifyrepo.py ++++ b/tests/test_modifyrepo.py +@@ -431,7 +431,8 @@ def test_copr_repo_run_createrepo(self, popen, do_stat, chroot, database_option) +              "--workers", "8", "--general-compress-type=gz", "--update"] + additional_args +  +     @pytest.mark.skipif( +-        distro.id() == 'fedora' and int(distro.version()) >= 36, ++        (distro.id() == 'fedora' and int(distro.version()) >= 36) or ++        (distro.id() == 'openeuler' and float(distro.version()) >= 24.03), +         reason="createrepo_c dropped md5 checksum support" +     ) +     def test_copr_repo_el5(self, f_third_build): diff --git a/helpers.patch b/helpers.patch deleted file mode 100644 index 33be500..0000000 --- a/helpers.patch +++ /dev/null @@ -1,35 +0,0 @@ -diff --git a/copr_backend/helpers.py b/copr_backend/helpers.py -index df2819e0..c85c7396 100644 ---- a/copr_backend/helpers.py -+++ b/copr_backend/helpers.py -@@ -288,6 +288,9 @@ class BackendConfigReader(object): -         opts.redis_port = _get_conf( -             cp, "backend", "redis_port", "6379") - -+        opts.redis_pwd = _get_conf( -+            cp, "backend", "redis_password", None) -+ -         opts.redis_db = _get_conf( -             cp, "backend", "redis_db", "0") - - -@@ -416,6 +416,19 @@ class BackendConfigReader(object): -         opts.aws_cloudfront_distribution = _get_conf( -             cp, "backend", "aws_cloudfront_distribution", None) - -+        opts.message = Munch() -+        opts.message.bootstrap_servers = _get_conf( -+            cp, "message", "bootstrap_servers", None) -+ -+        opts.message.user_name = _get_conf( -+            cp, "message", "user_name", None) -+ -+        opts.message.password = _get_conf( -+            cp, "message", "password", None) -+ -+        opts.message.topic = _get_conf( -+            cp, "message", "topic", None) -+ -         # ssh options -         opts.ssh = Munch() -         opts.ssh.builder_config = _get_conf( diff --git a/print_queues.patch b/print_queues.patch deleted file mode 100644 index 83d7fc8..0000000 --- a/print_queues.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/run/print_queues.py b/run/print_queues.py -index 849f8f6a..f48d1157 100755 ---- a/run/print_queues.py -+++ b/run/print_queues.py -@@ -14,6 +14,7 @@ redis_config = { -     'host': opts['redis_host'], -     'port': opts['redis_port'], -     'db': opts['redis_db'], -+    'password': opts['redis_pwd'], - } -  - for i in range(0, NUM_QUEUES): @@ -1,2 +1,2 @@ -8dacd18f46475296d0199365db977649  copr-backend-1.168.tar.gz -a35697a68640fb4c0bcfef02a93eac63  test-data-copr-backend-2.tar.gz +5ed3646f3887a4ff1656c1f7a05d78eb  copr-backend-1.173.tar.gz +c94d55e21a4935b3e62f673fbae65af2  test-data-copr-backend-5.tar.gz diff --git a/support_signatrust_backend.patch b/support_signatrust_backend.patch index 9e1fde5..d803d26 100644 --- a/support_signatrust_backend.patch +++ b/support_signatrust_backend.patch @@ -1,5 +1,5 @@ -diff --git a/copr_backend/actions.py b/copr_backend/actions.py -index 39722b84..6da2dfb6 100644 +diff --git a/copr_backend/actions.py b/backend/copr_backend/actions.py +index 39722b843..6da2dfb65 100644  --- a/copr_backend/actions.py  +++ b/copr_backend/actions.py  @@ -21,13 +21,13 @@ from copr_common.worker_manager import WorkerManager @@ -57,8 +57,8 @@ index 39722b84..6da2dfb6 100644                                            chroot, opts=self.opts, log=self.log)                       self.log.info("Forked build %s as %s", src_path, dst_path) -diff --git a/copr_backend/background_worker.py b/copr_backend/background_worker.py -index 4b5f1313..bc05edbd 100644 +diff --git a/copr_backend/background_worker.py b/backend/copr_backend/background_worker.py +index 4b5f13135..bc05edbd7 100644  --- a/copr_backend/background_worker.py  +++ b/copr_backend/background_worker.py  @@ -9,7 +9,7 @@ import logging @@ -78,21 +78,22 @@ index 4b5f1313..bc05edbd 100644       def _switch_logger_to_redis(self):           logger_name = '{}.{}.pid-{}'.format( -diff --git a/copr_backend/background_worker_build.py b/copr_backend/background_worker_build.py -index 233549b4..afc948e8 100644 +diff --git a/copr_backend/background_worker_build.py b/backend/copr_backend/background_worker_build.py +index 0eee39ab8..c12d4921d 100644  --- a/copr_backend/background_worker_build.py  +++ b/copr_backend/background_worker_build.py -@@ -27,7 +27,7 @@ from copr_backend.helpers import ( +@@ -27,8 +27,8 @@ from copr_backend.helpers import ( +     call_copr_repo, run_cmd, register_build_result, format_evr,   )   from copr_backend.job import BuildJob  -from copr_backend.msgbus import MessageSender -+from copr_backend.euler_msgbus import MessageSender  -from copr_backend.sign import sign_rpms_in_dir, get_pubkey ++from copr_backend.euler_msgbus import MessageSender  +from copr_backend.sign import new_signer   from copr_backend.sshcmd import SSHConnection, SSHConnectionError   from copr_backend.vm_alloc import ResallocHostFactory -@@ -599,7 +599,7 @@ class BuildBackgroundWorker(BackendBackgroundWorker): +@@ -622,7 +622,7 @@ class BuildBackgroundWorker(BackendBackgroundWorker):           self.log.info("Going to sign pkgs from source: %s in chroot: %s",                         self.job.task_id, self.job.chroot_dir) @@ -101,7 +102,7 @@ index 233549b4..afc948e8 100644               self.job.project_owner,               self.job.project_name,               os.path.join(self.job.chroot_dir, self.job.target_dir_name), -@@ -712,7 +712,7 @@ class BuildBackgroundWorker(BackendBackgroundWorker): +@@ -736,7 +736,7 @@ class BuildBackgroundWorker(BackendBackgroundWorker):           # TODO: uncomment this when key revoke/change will be implemented           # if os.path.exists(pubkey_path):           #    return @@ -110,8 +111,8 @@ index 233549b4..afc948e8 100644           self.log.info("Added pubkey for user %s project %s into: %s",                         user, project, pubkey_path) -diff --git a/copr_backend/constants.py b/copr_backend/constants.py -index a529be28..83bcb8fb 100644 +diff --git a/copr_backend/constants.py b/backend/copr_backend/constants.py +index a529be28a..83bcb8fbe 100644  --- a/copr_backend/constants.py  +++ b/copr_backend/constants.py  @@ -13,6 +13,7 @@ DEF_BUILD_USER = "mockbuilder" @@ -122,8 +123,8 @@ index a529be28..83bcb8fb 100644   DEF_CONSECUTIVE_FAILURE_THRESHOLD = 10 -diff --git a/copr_backend/exceptions.py b/copr_backend/exceptions.py -index 21afb14c..0865fcc8 100644 +diff --git a/copr_backend/exceptions.py b/backend/copr_backend/exceptions.py +index 21afb14c6..0865fcc8c 100644  --- a/copr_backend/exceptions.py  +++ b/copr_backend/exceptions.py  @@ -48,8 +48,8 @@ class CoprKeygenRequestError(Exception): @@ -137,8 +138,8 @@ index 21afb14c..0865fcc8 100644               out += "status code: {}\n" "response content: {}\n" \                   .format(self.response.status_code, self.response.content)           return out -diff --git a/copr_backend/helpers.py b/copr_backend/helpers.py -index 05348366..db4af7f4 100644 +diff --git a/copr_backend/helpers.py b/backend/copr_backend/helpers.py +index 75fa5e62d..291228912 100644  --- a/copr_backend/helpers.py  +++ b/copr_backend/helpers.py  @@ -31,7 +31,7 @@ from munch import Munch @@ -147,7 +148,7 @@ index 05348366..db4af7f4 100644   from copr_backend.constants import DEF_BUILD_USER, DEF_BUILD_TIMEOUT, DEF_CONSECUTIVE_FAILURE_THRESHOLD, \  -    CONSECUTIVE_FAILURE_REDIS_KEY, default_log_format  +    CONSECUTIVE_FAILURE_REDIS_KEY, default_log_format, DEF_SIGN_BACKEND - from copr_backend.exceptions import CoprBackendError, CoprBackendSrpmError + from copr_backend.exceptions import CoprBackendError   from . import constants  @@ -309,6 +309,18 @@ class BackendConfigReader(object): @@ -169,32 +170,11 @@ index 05348366..db4af7f4 100644           opts.build_groups = []           for group_id in range(opts.build_groups_count):               archs = _get_conf(cp, "backend", -diff --git a/copr_backend/sign.py b/copr_backend/sign.py -index 6655fb98..31af3c8a 100644 +diff --git a/copr_backend/sign.py b/backend/copr_backend/sign.py +index e21653e78..95f674255 100644  --- a/copr_backend/sign.py  +++ b/copr_backend/sign.py -@@ -4,8 +4,11 @@ - Wrapper for /bin/sign from obs-sign package - """ -  -+from datetime import datetime, timedelta, timezone - from subprocess import Popen, PIPE, SubprocessError - import os -+import re -+import requests - import time -  - from packaging import version -@@ -16,8 +19,6 @@ from .exceptions import CoprSignError, CoprSignNoKeyError, \ -     CoprKeygenRequestError -  -  --SIGN_BINARY = "/bin/sign" -- - def create_gpg_email(username, projectname, domain): -     """ -     Creates canonical name_email to identify gpg key -@@ -25,78 +26,6 @@ def create_gpg_email(username, projectname, domain): +@@ -25,78 +25,6 @@ def create_gpg_email(username, projectname, domain):       return "{}#{}@copr.{}".format(username, projectname, domain) @@ -273,56 +253,63 @@ index 6655fb98..31af3c8a 100644   def gpg_hashtype_for_chroot(chroot, opts):       """       Given the chroot name (in "mock format", like "fedora-rawhide-x86_64") -@@ -135,133 +64,438 @@ def gpg_hashtype_for_chroot(chroot, opts): -     # fallback to sha256 +@@ -136,133 +64,436 @@ def gpg_hashtype_for_chroot(chroot, opts):       return "sha256" +  +-def sign_rpms_in_dir(username, projectname, path, chroot, opts, log): +-    """ +-    Signs rpms using obs-signd.  +# a sign interface  +class Signer(object):  +    @classmethod  +    def get_pubkey(cls, username, projectname, log, sign_domain, outfile=None):  +        """get public key"""  +        raise NotImplementedError -+ +  +-    If some some pkgs failed to sign, entire build marked as failed, +-    but we continue to try sign other pkgs.  +    @classmethod  +    def sign_rpms_in_dir(cls, username, projectname, path, chroot, opts, log):  +        """batch sign rpms"""  +        raise NotImplementedError -+ +  +-    :param username: copr username +-    :param projectname: copr projectname +-    :param path: directory with rpms to be signed +-    :param chroot: chroot name where we sign packages, affects the hash type +-    :param Munch opts: backend config  +    @classmethod -+    def create_user_keys(cls, username, projectname, opts): ++    def create_user_keys(username, projectname, opts, try_indefinitely=False):  +        """create user key pair"""  +        raise NotImplementedError -+ +  +-    :type log: logging.Logger  +    @classmethod  +    def _sign_one(cls, path, email, hashtype, log):  +        """sign one rpm"""  +        raise NotImplementedError -+ +  +-    :raises: :py:class:`backend.exceptions.CoprSignError` failed to sign at least one package +-    """  +    @classmethod  +    def _unsign_one(cls, path):  +        # Requires rpm-sign package  +        cmd = ["/usr/bin/rpm", "--delsign", path]  +        handle = Popen(cmd, stdout=PIPE, stderr=PIPE, encoding="utf-8")  +        stdout, stderr = handle.communicate() -  --def sign_rpms_in_dir(username, projectname, path, chroot, opts, log): --    """ --    Signs rpms using obs-signd. -- --    If some some pkgs failed to sign, entire build marked as failed, --    but we continue to try sign other pkgs. ++  +        if handle.returncode != 0:  +            err = CoprSignError(  +                msg="Failed to unsign {}".format(path),  +                return_code=handle.returncode,  +                cmd=cmd, stdout=stdout, stderr=stderr) --    :param username: copr username --    :param projectname: copr projectname --    :param path: directory with rpms to be signed --    :param chroot: chroot name where we sign packages, affects the hash type --    :param Munch opts: backend config +-    rpm_list = [ +-        os.path.join(path, filename) +-        for filename in os.listdir(path) +-        if filename.endswith(".rpm") +-    ]  +            raise err  +  +        return stdout, stderr @@ -377,25 +364,22 @@ index 6655fb98..31af3c8a 100644  +            if filename.endswith(".rpm")  +        ] --    :type log: logging.Logger +-    if not rpm_list: +-        return  +        if not rpm_list:  +            return --    :raises: :py:class:`backend.exceptions.CoprSignError` failed to sign at least one package --    """ --    rpm_list = [ --        os.path.join(path, filename) --        for filename in os.listdir(path) --        if filename.endswith(".rpm") --    ] +-    hashtype = gpg_hashtype_for_chroot(chroot, opts)  +        hashtype = gpg_hashtype_for_chroot(chroot, opts) --    if not rpm_list: --        return +-    try: +-        get_pubkey(username, projectname, log, opts.sign_domain) +-    except CoprSignNoKeyError: +-        create_user_keys(username, projectname, opts, try_indefinitely=True)  +        try:  +            cls.get_pubkey(username, projectname, log, opts.sign_domain)  +        except CoprSignNoKeyError: -+            cls.create_user_keys(username, projectname, opts) ++            cls.create_user_keys(username, projectname, opts, try_indefinitely=True)  +  +        errors = []  # tuples (rpm_filepath, exception)  +        for rpm in rpm_list: @@ -444,14 +428,12 @@ index 6655fb98..31af3c8a 100644  +                                .format([err[0] for err in errors]))  +  +def new_signer(opts): -+    if opts.sign_backend == "obs-signd": -+        return ObsSign -+    elif opts.sign_backend == "signatrust": ++    if opts.sign_backend == "signatrust":  +        Signatrust.signatrust_token = opts.signatrust_token  +        Signatrust.signatrust_host = opts.signatrust_host  +        return Signatrust -+    else: -+        raise CoprSignError("invalid signer type: {}".format(opts.signer_type)) ++    else: # keep obs-signd as default backend ++        return ObsSign  +  +class ObsSign(Signer):  +    sign_cmd = "/bin/sign" @@ -484,19 +466,14 @@ index 6655fb98..31af3c8a 100644  +                return_code=returncode,  +                cmd=cmd, stdout=stdout, stderr=stderr) --    hashtype = gpg_hashtype_for_chroot(chroot, opts) +-    errors = []  # tuples (rpm_filepath, exception) +-    for rpm in rpm_list:  +        if outfile:  +            with open(outfile, "w") as handle:  +                handle.write(stdout) -  --    try: --        get_pubkey(username, projectname, log, opts.sign_domain) --    except CoprSignNoKeyError: --        create_user_keys(username, projectname, opts) ++  +        return stdout -  --    errors = []  # tuples (rpm_filepath, exception) --    for rpm in rpm_list: ++  +    @classmethod  +    def _sign_one(cls, path, email, hashtype, log):  +        cmd = [cls.sign_cmd, "-4", "-h", hashtype, "-u", email, "-r", path] @@ -509,7 +486,7 @@ index 6655fb98..31af3c8a 100644  +        return stdout, stderr  +  +    @classmethod -+    def create_user_keys(cls, username, projectname, opts): ++    def create_user_keys(cls, username, projectname, opts, try_indefinitely=False):  +        """  +        Generate a new key-pair at sign host  + @@ -528,7 +505,7 @@ index 6655fb98..31af3c8a 100644  +        keygen_url = "http://{}/gen_key".format(opts.keygen_host)  +        query = dict(url=keygen_url, data=data, method="post")  +        try: -+            request = SafeRequest(log=log) ++            request = SafeRequest(log=log, try_indefinitely=try_indefinitely)  +            response = request.send(**query)  +        except Exception as e:  +            raise CoprKeygenRequestError( @@ -673,7 +650,7 @@ index 6655fb98..31af3c8a 100644  +        """  +            check keyname existence --def create_user_keys(username, projectname, opts): +-def create_user_keys(username, projectname, opts, try_indefinitely=False):  -    """  -    Generate a new key-pair at sign host  +            HEAD /api/v1/keys/ @@ -697,7 +674,7 @@ index 6655fb98..31af3c8a 100644  -    keygen_url = "http://{}/gen_key".format(opts.keygen_host)  -    query = dict(url=keygen_url, data=data, method="post")  -    try: --        request = SafeRequest(log=log) +-        request = SafeRequest(log=log, try_indefinitely=try_indefinitely)  -        response = request.send(**query)  -    except Exception as e:  -        raise CoprKeygenRequestError( @@ -778,7 +755,7 @@ index 6655fb98..31af3c8a 100644  +                msg="Failed to check key existence", request="/api/v1/keys/name_identical", response=res)  +  +    @classmethod -+    def create_user_keys(cls, username, projectname, opts): ++    def create_user_keys(cls, username, projectname, opts, try_indefinitely=False):  +        """  +            create user key pair  + @@ -823,3 +800,351 @@ index 6655fb98..31af3c8a 100644  +        if res.status_code >= 400:  +            raise CoprKeygenRequestError(  +                msg="Failed to create user payload: {}".format(data), request="/api/v1/keys/", response=res) +diff --git a/tests/daemons/test_log.py b/backend/tests/daemons/test_log.py +index 73b1a777a..c68b7d918 100644 +--- a/tests/daemons/test_log.py ++++ b/tests/daemons/test_log.py +@@ -45,7 +45,8 @@ class TestLog(object): +         self.log_file = os.path.join(self.log_dir, "copr.log") +         self.opts = Munch( +             verbose=False, +-            log_dir=self.log_dir ++            log_dir=self.log_dir, ++            sign_backend = "obs-signd" +         ) +         print("\n log dir: {}".format(self.log_dir)) +         self.queue = MagicMock() +diff --git a/tests/daemons/unused_test_job_grab.py b/backend/tests/daemons/unused_test_job_grab.py +index be3e64c56..79f6472b7 100644 +--- a/tests/daemons/unused_test_job_grab.py ++++ b/tests/daemons/unused_test_job_grab.py +@@ -87,6 +87,7 @@ class TestJobGrab(object): +             redis_host="127.0.0.1", +             redis_port=6379, +             redis_db=0, ++            sign_backend = "obs-signd" +         ) +  +         self.queue = MagicMock() +diff --git a/tests/run/test_copr_prune_results.py b/backend/tests/run/test_copr_prune_results.py +index 6620f01b1..8c9ef2369 100644 +--- a/tests/run/test_copr_prune_results.py ++++ b/tests/run/test_copr_prune_results.py +@@ -59,7 +59,8 @@ class TestPruneResults(object): +         self.opts = Munch( +             prune_days=14, +             frontend_base_url = '<frontend_url>', +-            destdir=self.testresults_dir ++            destdir=self.testresults_dir, ++            sign_backend = "obs-signd" +         ) +  +     def teardown_method(self, method): +diff --git a/tests/test_action.py b/backend/tests/test_action.py +index 7da3ab09c..4c935eb53 100644 +--- a/tests/test_action.py ++++ b/tests/test_action.py +@@ -57,6 +57,7 @@ class TestAction(object): +             results_baseurl=RESULTS_ROOT_URL, +  +             do_sign=False, ++            sign_backend = "obs-signd", +  +             keygen_host="example.com" +         ) +@@ -136,7 +137,7 @@ class TestAction(object): +     @mock.patch("copr_backend.actions.os.makedirs") +     @mock.patch("copr_backend.actions.copy_tree") +     @mock.patch("copr_backend.actions.os.path.exists") +-    @mock.patch("copr_backend.actions.unsign_rpms_in_dir") ++    @mock.patch("copr_backend.sign.ObsSign.unsign_rpms_in_dir") +     @mock.patch("copr_backend.helpers.subprocess.Popen") +     def test_action_handle_forks(self, mc_popen, mc_unsign_rpms_in_dir, +                                  mc_exists, mc_copy_tree, _mc_os_makedirs, +diff --git a/tests/test_background_worker_build.py b/backend/tests/test_background_worker_build.py +index 1dfe19563..258564067 100644 +--- a/tests/test_background_worker_build.py ++++ b/tests/test_background_worker_build.py +@@ -77,6 +77,7 @@ def _reset_build_worker(): +     # Don't waste time with mocking.  We don't want to log anywhere, and we want +     # to let BuildBackgroundWorker adjust the handlers. +     worker.log.handlers = [] ++    worker.opts.sign_backend = "obs-signd" +     return worker +  + def _fake_host(): +diff --git a/tests/test_frontend.py b/backend/tests/test_frontend.py +index eeb8dc8f4..06a6e605b 100644 +--- a/tests/test_frontend.py ++++ b/tests/test_frontend.py +@@ -48,6 +48,7 @@ class TestFrontendClient(object): +         self.opts = Munch( +             frontend_base_url="http://example.com/", +             frontend_auth="12345678", ++            sign_backend = "obs-signd" +         ) +         self.fc = FrontendClient(self.opts) +  +diff --git a/tests/test_helpers.py b/backend/tests/test_helpers.py +index 8da70269d..e23565054 100644 +--- a/tests/test_helpers.py ++++ b/tests/test_helpers.py +@@ -31,6 +31,7 @@ class TestHelpers(object): +         self.opts = Munch( +             redis_db=9, +             redis_port=7777, ++            sign_backend = "obs-signd" +         ) +  +         self.rc = get_redis_connection(self.opts) +diff --git a/tests/test_sign.py b/backend/tests/test_sign.py +index bf2dd1b8c..ebb8f2be3 100644 +--- a/tests/test_sign.py ++++ b/tests/test_sign.py +@@ -10,10 +10,10 @@ import pytest +  + from copr_backend.exceptions import CoprSignError, CoprSignNoKeyError, CoprKeygenRequestError + from copr_backend.sign import ( +-    get_pubkey, _sign_one, sign_rpms_in_dir, create_user_keys, ++    new_signer, +     gpg_hashtype_for_chroot, +-    call_sign_bin, + ) ++from copr_backend.constants import DEF_SIGN_BACKEND +  + STDOUT = "stdout" + STDERR = "stderr" +@@ -33,6 +33,8 @@ class TestSign(object): +         self.opts = Munch(keygen_host="example.com") +         self.opts.gently_gpg_sha256 = False +         self.opts.sign_domain = "fedorahosted.org" ++        self.opts.sign_backend = DEF_SIGN_BACKEND ++        self.signer = new_signer(self.opts) +  +     def teardown_method(self, method): +         if self.tmp_dir_path: +@@ -60,7 +62,7 @@ class TestSign(object): +         mc_handle.returncode = 0 +         mc_popen.return_value = mc_handle +  +-        result = get_pubkey(self.username, self.projectname, MagicMock(), self.opts.sign_domain) ++        result = self.signer.get_pubkey(self.username, self.projectname, MagicMock(), self.opts.sign_domain) +         assert result == STDOUT +         assert mc_popen.call_args[0][0] == ['/bin/sign', '-u', self.usermail, '-p'] +  +@@ -70,7 +72,7 @@ class TestSign(object): +         mc_popen.side_effect = IOError(STDERR) +  +         with pytest.raises(CoprSignError): +-            get_pubkey(self.username, self.projectname, MagicMock(), self.opts.sign_domain) ++            self.signer.get_pubkey(self.username, self.projectname, MagicMock(), self.opts.sign_domain) +  +  +     @mock.patch("copr_backend.sign.time.sleep") +@@ -82,7 +84,7 @@ class TestSign(object): +         mc_popen.return_value = mc_handle +  +         with pytest.raises(CoprSignNoKeyError) as err: +-            get_pubkey(self.username, self.projectname, MagicMock(), self.opts.sign_domain) ++            self.signer.get_pubkey(self.username, self.projectname, MagicMock(), self.opts.sign_domain) +  +         assert "There are no gpg keys for user foo in keyring" in str(err) +  +@@ -95,7 +97,7 @@ class TestSign(object): +         mc_popen.return_value = mc_handle +  +         with pytest.raises(CoprSignError) as err: +-            get_pubkey(self.username, self.projectname, MagicMock(), self.opts.sign_domain) ++            self.signer.get_pubkey(self.username, self.projectname, MagicMock(), self.opts.sign_domain) +  +         assert "Failed to get user pubkey" in str(err) +  +@@ -108,7 +110,7 @@ class TestSign(object): +  +         outfile_path = os.path.join(self.tmp_dir_path, "out.pub") +         assert not os.path.exists(outfile_path) +-        result = get_pubkey(self.username, self.projectname, MagicMock(), ++        result = self.signer.get_pubkey(self.username, self.projectname, MagicMock(), +                             self.opts.sign_domain, outfile_path) +         assert result == STDOUT +         assert os.path.exists(outfile_path) +@@ -124,7 +126,7 @@ class TestSign(object): +         mc_popen.return_value = mc_handle +  +         fake_path = "/tmp/pkg.rpm" +-        result = _sign_one(fake_path, self.usermail, "sha1", MagicMock()) ++        result = self.signer._sign_one(fake_path, self.usermail, "sha1", MagicMock()) +         assert STDOUT, STDERR == result +  +         expected_cmd = ['/bin/sign', "-4", "-h", "sha1", "-u", self.usermail, +@@ -137,7 +139,7 @@ class TestSign(object): +  +         fake_path = "/tmp/pkg.rpm" +         with pytest.raises(CoprSignError): +-            _sign_one(fake_path, self.usermail, "sha256", MagicMock()) ++            self.signer._sign_one(fake_path, self.usermail, "sha256", MagicMock()) +  +     @mock.patch("copr_backend.sign.time.sleep") +     @mock.patch("copr_backend.sign.Popen") +@@ -149,12 +151,11 @@ class TestSign(object): +  +         fake_path = "/tmp/pkg.rpm" +         with pytest.raises(CoprSignError): +-            _sign_one(fake_path, self.usermail, "sha256", MagicMock()) ++            self.signer._sign_one(fake_path, self.usermail, "sha256", MagicMock()) +  +-    @staticmethod +     @mock.patch("copr_backend.sign.time.sleep") +     @mock.patch("copr_backend.sign.Popen") +-    def test_call_sign_bin_repeatedly(mc_popen, _sleep): ++    def test_call_sign_bin_repeatedly(self, mc_popen, _sleep): +         """ +         Test that we attempt to run /bin/sign multiple times if it returns +         non-zero exit status +@@ -163,13 +164,13 @@ class TestSign(object): +         mc_handle.communicate.return_value = (STDOUT, STDERR) +         mc_handle.returncode = 1 +         mc_popen.return_value = mc_handle +-        call_sign_bin(cmd=[], log=MagicMock()) ++        self.signer.call_sign_bin(cmd=[], log=MagicMock()) +         assert mc_popen.call_count == 3 +  +     @mock.patch("copr_backend.sign.SafeRequest.send") +     def test_create_user_keys(self, mc_request): +         mc_request.return_value.status_code = 200 +-        create_user_keys(self.username, self.projectname, self.opts) ++        self.signer.create_user_keys(self.username, self.projectname, self.opts) +  +         assert mc_request.called +         expected_call = mock.call( +@@ -183,7 +184,7 @@ class TestSign(object): +     def test_create_user_keys_error_1(self, mc_request): +         mc_request.side_effect = IOError() +         with pytest.raises(CoprKeygenRequestError) as err: +-            create_user_keys(self.username, self.projectname, self.opts) ++            self.signer.create_user_keys(self.username, self.projectname, self.opts) +  +         assert "Failed to create key-pair" in str(err) +  +@@ -195,16 +196,16 @@ class TestSign(object): +             mc_request.return_value.content = "error: {}".format(code) +  +             with pytest.raises(CoprKeygenRequestError) as err: +-                create_user_keys(self.username, self.projectname, self.opts) ++                self.signer.create_user_keys(self.username, self.projectname, self.opts) +             assert "Failed to create key-pair for user: foo, project:bar" in str(err) +  +-    @mock.patch("copr_backend.sign._sign_one") +-    @mock.patch("copr_backend.sign.create_user_keys") +-    @mock.patch("copr_backend.sign.get_pubkey") ++    @mock.patch("copr_backend.sign.ObsSign._sign_one") ++    @mock.patch("copr_backend.sign.ObsSign.create_user_keys") ++    @mock.patch("copr_backend.sign.ObsSign.get_pubkey") +     def test_sign_rpms_id_dir_nothing(self, mc_gp, mc_cuk, mc_so, +                                       tmp_dir): +         # empty target dir doesn't produce error +-        sign_rpms_in_dir(self.username, self.projectname, ++        self.signer.sign_rpms_in_dir(self.username, self.projectname, +                          self.tmp_dir_path, "epel-8-x86_64", self.opts, +                          log=MagicMock()) +  +@@ -212,13 +213,13 @@ class TestSign(object): +         assert not mc_cuk.called +         assert not mc_so.called +  +-    @mock.patch("copr_backend.sign._sign_one") +-    @mock.patch("copr_backend.sign.create_user_keys") +-    @mock.patch("copr_backend.sign.get_pubkey") ++    @mock.patch("copr_backend.sign.ObsSign._sign_one") ++    @mock.patch("copr_backend.sign.ObsSign.create_user_keys") ++    @mock.patch("copr_backend.sign.ObsSign.get_pubkey") +     def test_sign_rpms_id_dir_ok(self, mc_gp, mc_cuk, mc_so, +                                       tmp_dir, tmp_files): +  +-        sign_rpms_in_dir(self.username, self.projectname, ++        self.signer.sign_rpms_in_dir(self.username, self.projectname, +                          self.tmp_dir_path, "fedora-rawhide-x86_64", +                          self.opts, log=MagicMock()) +  +@@ -234,15 +235,15 @@ class TestSign(object): +                 assert os.path.join(self.tmp_dir_path, name) in pathes +         assert len(pathes) == count +  +-    @mock.patch("copr_backend.sign._sign_one") +-    @mock.patch("copr_backend.sign.create_user_keys") +-    @mock.patch("copr_backend.sign.get_pubkey") ++    @mock.patch("copr_backend.sign.ObsSign._sign_one") ++    @mock.patch("copr_backend.sign.ObsSign.create_user_keys") ++    @mock.patch("copr_backend.sign.ObsSign.get_pubkey") +     def test_sign_rpms_id_dir_error_on_pubkey( +             self, mc_gp, mc_cuk, mc_so, tmp_dir, tmp_files): +  +         mc_gp.side_effect = CoprSignError("foobar") +         with pytest.raises(CoprSignError): +-            sign_rpms_in_dir(self.username, self.projectname, ++            self.signer.sign_rpms_in_dir(self.username, self.projectname, +                              self.tmp_dir_path, "epel-7-x86_64", self.opts, +                              log=MagicMock()) +  +@@ -250,15 +251,15 @@ class TestSign(object): +         assert not mc_cuk.called +         assert not mc_so.called +  +-    @mock.patch("copr_backend.sign._sign_one") +-    @mock.patch("copr_backend.sign.create_user_keys") +-    @mock.patch("copr_backend.sign.get_pubkey") ++    @mock.patch("copr_backend.sign.ObsSign._sign_one") ++    @mock.patch("copr_backend.sign.ObsSign.create_user_keys") ++    @mock.patch("copr_backend.sign.ObsSign.get_pubkey") +     def test_sign_rpms_id_dir_no_pub_key( +             self, mc_gp, mc_cuk, mc_so, tmp_dir, tmp_files): +  +         mc_gp.side_effect = CoprSignNoKeyError("foobar") +  +-        sign_rpms_in_dir(self.username, self.projectname, ++        self.signer.sign_rpms_in_dir(self.username, self.projectname, +                          self.tmp_dir_path, "rhel-7-x86_64", self.opts, +                          log=MagicMock()) +  +@@ -266,9 +267,9 @@ class TestSign(object): +         assert mc_cuk.called +         assert mc_so.called +  +-    @mock.patch("copr_backend.sign._sign_one") +-    @mock.patch("copr_backend.sign.create_user_keys") +-    @mock.patch("copr_backend.sign.get_pubkey") ++    @mock.patch("copr_backend.sign.ObsSign._sign_one") ++    @mock.patch("copr_backend.sign.ObsSign.create_user_keys") ++    @mock.patch("copr_backend.sign.ObsSign.get_pubkey") +     def test_sign_rpms_id_dir_sign_error_one( +             self, mc_gp, mc_cuk, mc_so, tmp_dir, tmp_files): +  +@@ -276,7 +277,7 @@ class TestSign(object): +             None, CoprSignError("foobar"), None +         ] +         with pytest.raises(CoprSignError): +-            sign_rpms_in_dir(self.username, self.projectname, ++            self.signer.sign_rpms_in_dir(self.username, self.projectname, +                              self.tmp_dir_path, "fedora-36-x86_64", self.opts, +                              log=MagicMock()) +  +@@ -285,15 +286,15 @@ class TestSign(object): +  +         assert mc_so.called +  +-    @mock.patch("copr_backend.sign._sign_one") +-    @mock.patch("copr_backend.sign.create_user_keys") +-    @mock.patch("copr_backend.sign.get_pubkey") ++    @mock.patch("copr_backend.sign.ObsSign._sign_one") ++    @mock.patch("copr_backend.sign.ObsSign.create_user_keys") ++    @mock.patch("copr_backend.sign.ObsSign.get_pubkey") +     def test_sign_rpms_id_dir_sign_error_all( +             self, mc_gp, mc_cuk, mc_so, tmp_dir, tmp_files): +  +         mc_so.side_effect = CoprSignError("foobar") +         with pytest.raises(CoprSignError): +-            sign_rpms_in_dir(self.username, self.projectname, ++            self.signer.sign_rpms_in_dir(self.username, self.projectname, +                              self.tmp_dir_path, "fedora-36-i386", self.opts, +                              log=MagicMock()) +   | 
