summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCoprDistGit <infra@openeuler.org>2025-03-21 17:37:41 +0000
committerCoprDistGit <infra@openeuler.org>2025-03-21 17:37:41 +0000
commitbed3ab1ed2c9a92b20bae1d0f11f17d9051bfff9 (patch)
tree76779ef5ca00cab259eb516c4c44eef0b3048cc9
parent2132b310609fdedcd5427676a98f1174869b3a82 (diff)
automatic import of copr-backend
-rw-r--r--copr-backend.spec1472
-rw-r--r--euler_msgbus.patch83
-rw-r--r--fail_to_build_in_oe2403.patch14
-rw-r--r--signatrust_bin.patch105
-rw-r--r--sources2
-rw-r--r--support_signatrust_backend.patch1150
6 files changed, 1371 insertions, 1455 deletions
diff --git a/copr-backend.spec b/copr-backend.spec
index 86a223c..ae9f5f4 100644
--- a/copr-backend.spec
+++ b/copr-backend.spec
@@ -1,7 +1,3 @@
-%if 0%{?rhel} <= 7 && 0%{?rhel} > 0
-%global _pkgdocdir %{_docdir}/%{name}-%{version}
-%endif
-
%global prunerepo_version 1.20
%global tests_version 5
%global tests_tar test-data-copr-backend
@@ -10,7 +6,7 @@
Name: copr-backend
Version: 1.173
-Release: 2%{?dist}
+Release: 1%{?dist}
Summary: Backend for Copr
License: GPL-2.0-or-later
@@ -20,7 +16,13 @@ URL: https://github.com/fedora-copr/copr
# git clone %%url && cd copr
# tito build --tgz --tag %%name-%%version-%%release
Source0: %{name}-%{version}.tar.gz
-Source1: https://github.com/fedora-copr/%{tests_tar}/archive/v%{tests_version}/%{tests_tar}-%{tests_version}.tar.gz
+Source1: %{tests_tar}-%{tests_version}.tar.gz
+Patch1: fail_to_build_in_oe2403.patch
+Patch2: euler_msgbus.patch
+#Patch3: print_queues.patch
+#Patch4: redis_helpers.patch # should patch to copr common
+Patch5: support_signatrust_backend.patch
+Patch6: signatrust_bin.patch
BuildArch: noarch
BuildRequires: asciidoc
@@ -37,7 +39,8 @@ BuildRequires: python3-devel
BuildRequires: python3-setuptools
BuildRequires: python3-copr
-BuildRequires: python3-copr-common >= %copr_common_version
+BuildRequires: python3-kafka-python
+BuildRequires: python3-copr-common = %copr_common_version
BuildRequires: python3-daemon
BuildRequires: python3-dateutil
BuildRequires: python3-distro
@@ -56,10 +59,10 @@ BuildRequires: python3-retask
BuildRequires: python3-setproctitle
BuildRequires: python3-sphinx
BuildRequires: python3-tabulate
-BuildRequires: python3-zstandard
BuildRequires: modulemd-tools >= 0.6
BuildRequires: prunerepo >= %prunerepo_version
BuildRequires: dnf
+BuildRequires: rpmdevtools
Requires: (copr-selinux if selinux-policy-targeted)
Requires: ansible
@@ -75,7 +78,7 @@ Requires: obs-signd
Requires: openssh-clients
Requires: prunerepo >= %prunerepo_version
Requires: python3-copr
-Requires: python3-copr-common >= %copr_common_version
+Requires: python3-copr-common = %copr_common_version
Recommends: python3-copr-messaging
Requires: python3-daemon
Requires: python3-dateutil
@@ -101,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
@@ -124,7 +128,8 @@ only.
%prep
-%setup -q -a 1
+%autosetup -a1 -p1
+
%build
@@ -174,6 +179,7 @@ install -d %{buildroot}%{_sysconfdir}/logstash.d
install -d %{buildroot}%{_datadir}/logstash/patterns/
cp -a conf/logstash/lighttpd.pattern %{buildroot}%{_datadir}/logstash/patterns/lighttpd.pattern
+
install -d %{buildroot}%{_pkgdocdir}/examples/%{_sysconfdir}/logstash.d
cp -a conf/logstash/copr_backend.conf %{buildroot}%{_pkgdocdir}/examples/%{_sysconfdir}/logstash.d/copr_backend.conf
@@ -238,1447 +244,5 @@ useradd -r -g copr -G lighttpd -s /bin/bash -c "COPR user" copr
%exclude %{_pkgdocdir}/lighttpd
%changelog
-* Fri Feb 21 2025 mywaaagh_admin <pkwarcraft@hotmail.com> 1.173-2
-- fix for openeuler
-
-* Thu Nov 23 2023 Pavel Raiskup <praiskup@redhat.com> 1.173-1
-- enforce createrepo_c gzip compression (f39+ switched to zstd)
-- self-identify the resalloc resource in logs
-- dropping the documentary playbooks from copr-backend payload
-- nicer unknown-resalloc-tickets output
-- worker to not call keygen for source builds at all
-- don't sign products of srpm-build
-- longer timeout for fallback generating GPG keys after build
-- recreate missing repodata so that prunerepo doesn't traceback
-- use the rename HashWorkerLimit instead of GroupWorkerLimit
-- provide per-arch & per-owner worker limit implemented
-- collect and compress fedora-review logs after run
-- react on staled SSH connections in some cases
-
-* Tue Aug 15 2023 Pavel Raiskup <praiskup@redhat.com> 1.172-1
-- dump the /update/ payload to worker.log
-- don't run external command(s) to collect built packages
-- don't eat the "build detail collecting" traceback
-- fixes in the unknown-resalloc-tickets.py helper
-- more careful format_evr() method
-- fix tests for zst compression on F39+
-- log task dict in case of error returned from redis
-- skip builds for ExcludeArch and "not" ExclusiveArch
-- offload NEVRA (s)rpm parsing to copr-rpmbuild
-- redis authentication support added
-
-* Tue Jun 06 2023 Pavel Raiskup <praiskup@redhat.com> 1.171-1
-- copr_prune_results.py: work-around the arg_max problem
-
-* Mon Jun 05 2023 Pavel Raiskup <praiskup@redhat.com> 1.170-1
-- copr_prune_results.py: don't enforce appstream-builder, ask FE
-- copr_prune_results.py: just one API call to FE per one repo
-
-* Tue May 23 2023 Jakub Kadlcik <frostyx@email.cz> 1.169-1
-- Forking: better handle FileExistsError
-- Run the copr-rpmbuild command with task URL, not build ID
-
-* Wed Apr 05 2023 Jiri Kyjovsky <j1.kyjovsky@gmail.com> 1.168-1
-- Bump version for release mess
-
-* Tue Apr 04 2023 Jiri Kyjovsky <j1.kyjovsky@gmail.com> 1.167-1
-- Run createrepo without --database
-- Make copr_messaging optional
-
-* Wed Mar 22 2023 Jiri Kyjovsky <j1.kyjovsky@gmail.com> 1.166-1
-- Don't include package name into srpm result dir name
-- Remove libmodulemd1 dependency
-- Hardlink RPMs while doing rawhide_to_release
-- Make sign key domain name configurable
-* Wed Jan 25 2023 Jakub Kadlcik <frostyx@email.cz> 1.165-1
-- Skip the test_run_prunerepo test because of Koji
-
-* Tue Jan 24 2023 Jakub Kadlcik <frostyx@email.cz> 1.164-1
-- Fix chroot version parsing with new python-packaging
-- Fix traceback for non-existing tasks
-- Python: drop the unneeded marshmallow dep
-- Log general exceptions to find issues more easily
-- Proper log argument formatting instead of .format
-- Use spdx license
-
-* Wed Nov 30 2022 Pavel Raiskup <praiskup@redhat.com> 1.163-1
-- start systemd services after the redis.service
-- build worker - list the built RPMs with rpm --nosignature
-
-* Sat Nov 26 2022 Jakub Kadlcik <frostyx@email.cz> 1.162-1
-- use OpenPGP v4 signatures
-- migrate from pipes to shlex
-- require redis.service to be started
-- move to GitHub home page
-- add resultdir cleaner
-- move dispatcher and background workers to copr-common
-- de-prio IO for the analyze-results script
-- don't traceback when there are no files in the S3 storage
-- allow devel instance to remove access files
-- send non-CDN hitcounter hits in chunks
-- copr-backend-unknown-resalloc-tickets script
-- work with multiple CDN hostnames per instance
-- move setup_script_logger to copr-common
-
-* Mon Sep 26 2022 Pavel Raiskup <praiskup@redhat.com> 1.161-1
-- sign everything EPEL-5+ with sha256 hashalgo
-
-* Tue Sep 20 2022 Jakub Kadlcik <frostyx@email.cz> 1.160-1
-- aws-hitcounter: remove temporary files as soon as possible
-- aws-hitcounter: ignore downloaded SRPM files
-- aws-hitcounter: decode special characters from URLs
-
-* Tue Aug 16 2022 Pavel Raiskup <praiskup@redhat.com> 1.159-1
-- count only hits from an appropriate CDN hostname
-- add option for infinite number of attempts to the hitcounter script
-- print more reasonable output from AWS hitcounter script
-
-* Tue Aug 16 2022 Jiri Kyjovsky <j1.kyjovsky@gmail.com> 1.158-1
-- log every request that is sent to frontend
-
-* Tue Jul 26 2022 Jakub Kadlcik <frostyx@email.cz> 1.157-1
-- Don't use --keep-all-metadata
-- Search for comps.xml in chroot dir
-
-* Tue Jun 21 2022 Jakub Kadlcik <frostyx@email.cz> 1.156-1
-- Consolidate the two hitcounter scripts
-- Dump Resalloc ticket ID and hostname to backend.log
-- Automatically restart services in %%post
-- Don't count RPMs downloaded from Mock
-- Attempt to sign multiple times
-- Try multiple attempts of creating GPG keys
-
-* Mon Apr 04 2022 Pavel Raiskup <praiskup@redhat.com> 1.155-1
-- fix slow priority queue filling
-- speedup the background-process spawner
-
-* Fri Mar 18 2022 Pavel Raiskup <praiskup@redhat.com> 1.154-1
-- copr_fix_gpg: automatically refresh CDN cache
-- copr_fix_gpg: don't use --skip-stat for copr-repo when RPMs are re-signed
-- action processor: properly return failures of the fork action
-- sign EL8+ with sha256 hash algorithm
-- copr_fix_gpg: add a new --chroot option
-- copr_fix_gpg: skip non-chroot directories
-- add hitcounter script for AWS CDN
-- backend: tasks in concurrent sandboxes reprioritized to be more fair
-
-* Wed Feb 02 2022 Silvie Chlupova <schlupov@redhat.com> 1.153-1
-- less aggressive final_prunerepo setting
-- analyze-results: dump data for projects' chroots
-- basic build tagging
-- better "regenerate repo" instructions
-- limit RubyGems and PyPI package names length
-- Disable coverage analysis during RPM build
-- python code for removing unused tarballs on dist-git server
-
-* Wed Nov 10 2021 Silvie Chlupova <schlupov@redhat.com> 1.152-1
-- Fixup ACR handling
-- Drop the unused pid file from the specfile
-* Thu Sep 30 2021 Silvie Chlupova 1.151-1
-- backend: use lock(timeout=5) to work-around fair-locks
-
-* Wed Aug 25 2021 Pavel Raiskup <praiskup@redhat.com> 1.150-1
-- request arch_noarch resalloc tag for source RPM builds
-- re-try /bin/sign call upon connection timeout
-- drop 'check_consecutive_build_fails' script
-
-* Tue Jun 15 2021 Pavel Raiskup <praiskup@redhat.com> 1.149-1
-- new weekly cron-job for analyzing storage use (graphs, statistics)
-- added some convenience globals into copr_backend.setup module
-- keep the max batch size really on the MAX_IN_BATCH limit
-- require up2date rpmbuild version
-- new action for removing CoprDirs (triggered by cron on frontend)
-- fix the CompsUpdate action
-- users now can disable appstream metadata generation themselves (without admins asistance)
-- handle results.json given by builder, and provide it to frontend (served as APIv3 later)
-- pruner: allow pruning also the finalized chroots on demand
-- invent FE-BE API version, so backend politely waits for an updated copr-frontend version
-
-* Thu May 13 2021 Pavel Raiskup <praiskup@redhat.com> 1.148-1
-- work with builders also over ipv6
-
-* Sun May 02 2021 Pavel Raiskup <praiskup@redhat.com> 1.147-1
-- fix logging traceback for module builds
-- call creatrepo_c with --update if possible
-- don't do full createrpeo with --rpms-to-remove
-
-* Fri Apr 30 2021 Pavel Raiskup <praiskup@redhat.com> 1.146-1
-- better fixes for the appstream-builder generated files
-- new helper script named /bin/copr-assure-permissions
-- do not rsync-copy permissions from the builder
-- log the partial "prunerepo" effects into resultdir
-- better, more verbose call_copr_repo logging
-
-* Tue Apr 27 2021 Jakub Kadlcik <frostyx@email.cz> 1.145-1
-- backend: make the walk_limited test not dependend on its output order, pt2
-
-* Tue Apr 27 2021 Jakub Kadlcik <frostyx@email.cz> 1.144-1
-- backend: make the walk_limited test not dependend on its output order
-
-* Tue Apr 27 2021 Jakub Kadlcik <frostyx@email.cz> 1.143-1
-- backend: fix copr_prune_results logging once more
-- backend: better logging in prunerepo
-- backend: prunerepo: don't re-createrepo when no rpm is removed
-- backend: catch correct client exceptions in copr_prune_results
-- test: backend: change prunerepo logic, use get_rpms_to_remove from prunerepo
-- backend: new fixture for testing prunerepo
-- backend: use safe defaults if APIv3 result doesn't contain what it should
-- backend: migrate to APIv3
-- backend: better how-to-redirect logs comment
-- backend, frontend, keygen, distgit: keep cca 3 months of logs
-- backend: don't createrepo in srpm-builds on delete
-- backend: test walk_limited function from helpers
-
-* Tue Mar 16 2021 Pavel Raiskup <praiskup@redhat.com> 1.142-1
-- prepare for the centos-stream-8 rename
-- add script to prune srpm-build directories
-- pruner: correctly deliver the final prunerepo stamp to frontend
-- pruner: logging through RedisLogHandler
-- pruner: better parallelization
-- pruner: re-try be-fe communication upon failures
-- require up2date modulemd-tools
-
-* Wed Jan 20 2021 Pavel Raiskup <praiskup@redhat.com> 1.141-1
-- run prunerepo in parallel
-- add one-shot copr_find_wrong_chroot_artifacts.py script
-- support modulemd v2
-
-* Tue Dec 01 2020 Pavel Raiskup <praiskup@redhat.com> 1.140-1
-- fix frontend-client post arguments
-
-* Mon Nov 30 2020 Pavel Raiskup <praiskup@redhat.com> 1.139-1
-- require appropriate common version
-- use common for repeatedly sends requests to frontend
-
-* Mon Nov 30 2020 Pavel Raiskup <praiskup@redhat.com> 1.138-1
-- get back to using standard createrepo_c command from createrepo_mod
-- drop call to stomp's conn.start() (it was dropped)
-
-* Mon Nov 09 2020 Jakub Kadlcik <frostyx@email.cz> 1.137-1
-- backend: test action for deleting chroot
-- backend: fix testsuite for the new createrepo_c
-- frontend: don't set ended_on for canceled builds
-- all: run pytest with -vv in package build
-- common, cli, python, rpmbuild, frontend, backend: DistGit source method
-- backend: use createrepo_mod tool for generating module repodata
-
-* Wed Aug 12 2020 Pavel Raiskup <praiskup@redhat.com> 1.136-1
-- testsuite: give more time to the slow Koji builders
-
-* Mon Aug 10 2020 Pavel Raiskup <praiskup@redhat.com> 1.135-1
-- prioritize all non-background jobs
-- fix up libmodulemd dependency
-
-* Fri Jun 19 2020 Pavel Raiskup <praiskup@redhat.com> 1.134-1
-- fix copr-repo to work with absolute paths
-- automatically batch the createrepo requests
-- scheduler is now fair, and ordered
-- indefinitely retry workers' talk to frontend
-- allow canceling also "starting" builds
-- more verbose delete action in logs
-- cleanup the example configuration
-- use FileHandler for backend.log, fixes traceback
-
-* Tue Jun 09 2020 Pavel Raiskup <praiskup@redhat.com> 1.133-1
-- better build task priority processing
-- dump attempt to send message to backend.log
-- drop the VMM concept, replaced with resalloc
-- delegate more work to the builder code
-- external blob tarball for unittests
-- buggy error handler in pkg_name_evr()
-- basic build task priority
-- the reschedule-all builds idiom removed
-- fix the build cancelation
-- drop duplicate BuildRequire on python-requests
-- require the newest version of copr-common
-- minimalize the transfered amount of information about actions from FE
-- process actions in regard to their priority
-- move backend's code to standard PYTHONPATH
-- move ActionResult to copr_common.enums
-- actions/builds use the same WorkerManager logic
-- more verbose rawhide to release action processing
-
-* Wed Feb 05 2020 Pavel Raiskup <praiskup@redhat.com> 1.132-1
-- better handle invalid options in copr-repo --add/--delete
-- copr-repo: optimize-out useless createrepo_c runs
-- move initial createrepo check from dispatcher to worker
-- don't send messages on bus N-times
-- /bin/copr-repo now respects .disable-appstream files
-- drop unused build_deleting_without_createrepo option
-
-* Wed Jan 15 2020 Tomas Hrnciar <thrnciar@redhat.com> 1.131-1
-- put build-ID.log file to resultdir
-- call call_copr_repo if initial createrepo failed
-- Build Dispatcher does not wait forever till repo is created,
- it creates it manually
-- properly delete logs for old builds
-- delete build-ID.log files again
-- edit repositories only by new 'copr-repo' tool
-- fix multi-build delete
-- fix for not saving end time of actions
-- lower traffic in build_dispatcher log
-- more resilient redis logging
-- attempt to publish on msgbus N-times
-- log service: move RequiredBy to [Install]
-- keep worker ID in proc title
-
-* Fri Dec 06 2019 Pavel Raiskup <praiskup@redhat.com> 1.130-1
-- backend: execute actions with sane umask=0022
-
-* Wed Dec 04 2019 Pavel Raiskup <praiskup@redhat.com> 1.129-1
-- do not start a build if copr_base is not available yet
-- systemd services' restart re-ordering
-- de-duplicate frontend_.update() call when reattaching to existing build
-- allow specifying timeout for spawn/terminate playbooks
-- removing dependecy on euca2ools in spec
-- send `uses_devel_repo' as a part of task info
-- correctly configure logrotate
-- get_redis_logger: skip log entries bellow log_level
-- delete leftover action workers from redis
-
-* Fri Oct 11 2019 Pavel Raiskup <praiskup@redhat.com> 1.128-1
-- restart copr-backend sub-services on failure
-- don't kill action processors by 'systemctl restart'
-- lower the log traffic in build_dispatcher.log
-
-* Thu Oct 03 2019 Pavel Raiskup <praiskup@redhat.com> 1.127-1
-- fix testsuite for slow Koji builders
-
-* Thu Oct 03 2019 Pavel Raiskup <praiskup@redhat.com> 1.126-1
-- more reliable communication with frontend (#1021)
-- only ask for auto_createrepo once per project
-- parallel handling of actions (#1007)
-- don't provide builder-live.log once the build ended, and
- add 'copr-compress-live-logs' helper (#985)
-- less exceptions in logs
-- project forking fixes
-- depend on copr-messaging, not fedora-messaging
-- fixes for copr_print_results_to_delete.py script
-
-* Wed Aug 28 2019 Dominik Turecek <dturecek@redhat.com> 1.125-1
-- minimize redis traffic for looping over pending-jobs (issue#902)
-- batch delete builds into a single action (issue#688)
-- admin opt-out createrepo after build-deleting
-- fix wrong message validation class
-- refine cleanup_vm_nova.py
-- depend on copr-messaging
-
-* Mon Jul 29 2019 Pavel Raiskup <praiskup@redhat.com> 1.124-1
-- run createrepo immediately, don't wait for build (issue#833)
-- compress backend-live.log by calling gzip (issue#86)
-- use copr-messaging module for validating outgoing messages
-- don't run appstream-builder for PR dirs
-- don't run createrepo for srpm directories
-- skip VMs with failing live-check in scheduler
-- sandbox builds per user/submitter/project
-- drop unused compat code for droped /bin/copr-builder
-- do not call appstream builder with --max-threads (issue#717)
-- added copr_print_results_to_delete.py script, should help
- us with removal of orphaned resources on backend storage (issue#712)
-- allow disabling appstream builder per project (issue#738)
-- tabular output from copr_get_vm_info.py
-
-* Wed Apr 24 2019 Jakub Kadlčík <frostyx@email.cz> 1.123-1
-- clean data for failed builds; fix #619
-- replace runnecessary regex with str.endswith
-- move clean_copr from prunerepo to our codebase
-- cleanup_vm_nova.py: use yaml.safe_load
-- don't rely on createrepo from prunerepo
-- simplify logging through redis
-- run sign command without sudo to fix #636
-- encode 'msg' in LogRecord sooner
-- fix charset warnings on redis-py v3
-- fix default arguments in redis scripts
-- don't prunerepo too old directories
-- LogHandler: don't drop exc_info from LogRecord
-- use the correct data for rawhide_to_release createrepo
-- make copr_prune_results skip already pruned outdated chroots
-- require libmodulemd in at least 1.7.0
-- remove dependency on python3-configparser
-
-* Mon Feb 11 2019 Jakub Kadlčík <frostyx@email.cz> 1.122-1
-- Add requires python3-novaclient
-- Set the architecture for which the module has been built
-- Generate module artifacts in the correct format
-- Compress the modules.yaml file
-
-* Fri Jan 11 2019 Miroslav Suchý <msuchy@redhat.com> 1.121-1
-- remove data from outdated chroots
-
-* Thu Jan 10 2019 Miroslav Suchý <msuchy@redhat.com> 1.120-1
-- update list of copr services
-- Use oslo_concurrency for craeterepo locking
-- use run_cmd() in pkg_name_evr()
-- drop "downloading" state
-- allow blacklisting packages from chroots
-
-* Fri Oct 19 2018 Miroslav Suchý <msuchy@redhat.com> 1.119-1
-- optimize copr_log_hitcounter.py for speed a bit
-- move selinux rules to copr-selinux
-- fix traceback for non-serializable log message
-- fix tracebacks in copr-backend-log
-- more robust run_tests.sh
-- remove unused imports
-- py3 compat for msgbus support
-- use git_dir_archive instead of git_dir_pack
-- migrate from deprecated python3-modulemd to libmodulemd
-- doc: remove warning that _static directory does not exists
-- doc: the undeline need to be at least as long as the title
-
-* Thu Aug 23 2018 clime <clime@redhat.com> 1.118-1
-- fix logging exception
-- send proper arguments for rawhide_to_release
-- packaging: Python 2/3, RHEL/Fedora fixes
-
-* Mon Aug 06 2018 clime <clime@redhat.com> 1.117-1
-- None task protection
-- pagure integration
-- use manual .pyc file generation
-- remove unused imports, ad. pr#327
-- resolving pylint warnings
-- for py3 use unittest.mock
-- fix msgbus ContentType to application/json
-
-* Fri May 18 2018 clime <clime@redhat.com> 1.116-1
-- fix #291 forks are incomplete
-- log more information about incoming actions
-- preparation for opensuse-leap-15.0-x86_64
-
-* Thu Apr 26 2018 Dominik Turecek <dturecek@redhat.com> 1.115-1
-- rpkg deployment into COPR - containers + releng continuation
-- fix pagure bugs #269, #273, #221 and #268
-- cleanup in test_helpers, one test added
-- change order of args in StrictRedis call
-- add comment about expected usage of the copr_log_hitcounter script
-- try to send hit data to frontend several times from
-copr_log_hitcounter
-
-* Mon Feb 26 2018 clime <clime@redhat.com> 1.114-1
-- add possibility for copr_log_hitcounter to ignore multiple subnets
-
-* Fri Feb 23 2018 clime <clime@redhat.com> 1.113-1
-- original builder deprecation
-- remove Group tag
-
-* Mon Feb 19 2018 clime <clime@redhat.com> 1.112-1
-- Shebangs cleanup
-- escapes in changelogs
-
-* Sun Feb 18 2018 clime <clime@redhat.com> 1.111-1
-- use netaddr instead of IPy module
-- sleep after each load_jobs iteration
-- python3 conversion
-- UMB: adding content type
-- add source_status field for Builds
-- generate module artifacts rpms
-- the rsync log is actually renderred directly into result dir now
-- mockchain.log renamed to backend.log
-- pg#192 missing records in mockchain.log
-- enable running tests in spec file
-- enable and update vmmamanger tests, fix three minor bugs in the
- manager
-- frontend now presents the whole job queue state to
- backend
-- copy only module builds into the repo directory
-
-* Wed Dec 20 2017 clime <clime@redhat.com> 1.110-1
-- exception handling for hit counting when IP address cannot be parsed
-
-* Mon Dec 18 2017 Dominik Turecek <dturecek@redhat.com> 1.109-1
-- terminate also 'in_use' builders if health checks have failed
-- make --detached the last arg for copr-rpmbuild
-- update copr_log_hitcounter to check ip against ignored pattern
-- new msg bus options
-- disable DNF makecache timer/service
-- fix message duplication for multi-bus scenario
-
-* Thu Nov 16 2017 Miroslav Suchý <msuchy@redhat.com> 1.108-1
-- optimize createrepo_c
-- Revert "[backend] remove --ignore-lock from createrepo_c"
-
-* Thu Nov 09 2017 clime <clime@redhat.com> 1.107-1
-- kill all processes in copr-rpmbuild's process group
-- add --drop-resultdir switch to copr-rpmbuild call
-- release_vm immediately after VM is no longer needed
-- remove --ignore-lock from createrepo_c
-
-* Wed Oct 18 2017 clime <clime@redhat.com> 1.106-1
-- run copr-rpmbuild with --verbose option
-
-* Wed Sep 27 2017 clime <clime@redhat.com> 1.105-1
-- remove uneeded yum dep
-
-* Tue Sep 26 2017 clime <clime@redhat.com> 1.104-1
-- update copr-rpmbuild command for the new options
-- change arguments to build_id and chroot
-- #128 AppStream data collection vetoes addons
-- fix rpm download stats collection
-- module-stuff update
-
-* Fri Sep 15 2017 clime <clime@redhat.com> 1.103-1
-- update fedora image version to 26
-- fixes for recent code
-
-* Thu Sep 07 2017 clime <clime@redhat.com> 1.102-1
-- srpms are now being built from upstream on builders
-
-* Wed Jun 14 2017 clime <clime@redhat.com> 1.101-1
-- remove unused helpers.run_ssh + function spacing fixup
-- cancel-build action fix
-
-* Fri Jun 09 2017 clime <clime@redhat.com> 1.100-1
-- extend check for a builder package present on a builder machine
-- arbitrary dist-git branching
-- remove --add-cache-id from appstream-builder call, see Bug 1426166
-- change to using a standalone builder package
-
-* Wed May 03 2017 clime <clime@redhat.com> 1.99-1
-- missing on_success_build call added back to sign packages and recreate repo after each build
-
-* Mon Apr 24 2017 clime <clime@redhat.com> 1.98-1
-- Bug 1444804 - Logs are not present for failed builds
-
-* Wed Apr 19 2017 clime <clime@redhat.com> 1.97-1
-- do not condrestart optional logstash service
-- standalone builder option
-- build reattaching after copr-backend(-build) service restart
-- live mockchain log
-- use openssh instead of paramiko
-- update cleanup_vm_nova script
-- remove buggy logging
-- removed Sphinx as a dependency...
-- verbose log everything we have about failed playbook
-- replace fedorahosted links
-- make systemd services out of ActionDispatcher and BuildDispatcher
-
-* Thu Jan 26 2017 clime <clime@redhat.com> 1.96-1
-- Fixes for building COPR Backend and Dist-git on EL7
-- simplified/improved logging of exceptions mainly
-- don't use sha256 checksum for rhel-5* repos, too
-- drop mentions of the max_builds_per_vm optoin
-- switched usage of deprecated ansible Runner for python-paramiko module
-- os_nova filter plugin fixed for python-novaclient 3
-- support for STOMP msg buses
-- fix Bug 1402689 regarding job cancellation
-- jobgrab service is no more
-- respect 'do_sign' option when forking
-- fix buildroot_cmd for rhel mock profiles
-
-* Thu Dec 01 2016 clime <clime@redhat.com> 1.95-1
-- use buildroot_pkgs substitution type according to job.chroot
-- use timeout command to respect timeout param coming from frontend
-- don't ship unitfiles in %%bindir
-- move createrepo to the end of the rawhide_to_release handler
-- modulemd 1.0.2 compatibility
-- Bug 1397119 - Error reading SSH protocol banner
-- added auto-prune project's option
-- Bug 1086139 - [RFE] provide UI to cancel a build
-- Fix misleading debug statement
-- fix exception logging in ensure_dir_exists helper
-- Fix chroot_setup_cmd regex for custom chroot
-
-* Mon Sep 19 2016 clime <clime@redhat.com> 1.94-1
-- also provide default version and release for generated modules.json
-
-* Mon Sep 19 2016 clime <clime@redhat.com> 1.93-1
-- fix NameError: global name 'result' is not defined
-- fix exception logging
-- Modularity support
-- Bug 1357564 - RFE: allow downloading of mock profiles (reproducible builds)
-- "safer" exception handling for actions
-
-* Mon Aug 15 2016 clime <clime@redhat.com> 1.92-1
-- wrap feedback about actions to frontend into try-except
-- log even the traceback from forking
-- use makedirs instead of mkpath in fork action
-- if anything bad happens, log exception in generate_gpg_key action
-- also restart copr-backend-vmm and copr-backend-log when (re)installing
-- Bug 1361344 - RFE: Allow denial of build deletion and resubmitting at project or group level
-- catch errors in fork action
-- set action result for comps.xml and module_md.yaml file deletion
-- backend fork action now takes care of new gpg-key generation instead of frontend
-- removed no longer supported --api-version=0.8 arg from appstream-builder command line
-- specify module_md as module type
-- fix saving comps.xml and module_md.yaml into empty copr (with no build)
-- module_md.yaml is added to repodata now similarly to appstream.xml
-- support for generation of module dist tags
-- module_md.yaml uploading for a chroot
-- simplified build and action task workflow
-- use copy of the mock (chroot) config, not the original in /etc/mock/
-
-* Wed Jun 22 2016 Miroslav Suchý <msuchy@redhat.com> 1.91-1
-- configure more packages to run pylint
-- terminate machine which was only partially spawned
-- [copr-prune-results] do not sys.exit if prunerepo returns non-zero status,
- just raise an exception
-- more of log file migration
-- claim /var/log/copr-backend in %%files
-- adjust log path in runtime files
-- update conf file log path directives
-- change logdir to /var/log/copr-backend/
-
-* Fri May 27 2016 Miroslav Suchý <msuchy@redhat.com> 1.90-1
-- do not use --log-dir in appstream-builder
-
-* Tue May 24 2016 Miroslav Suchý <miroslav@suchy.cz> 1.89-1
-- use correct conditional in requires
-
-* Mon May 23 2016 Miroslav Suchý <msuchy@redhat.com> 1.88-1
-- backend: change logstash requires to soft requires
-- 1336360 - allow custom chroots
-
-* Fri May 13 2016 Miroslav Suchý <msuchy@redhat.com> 1.87-1
-- workaround for BZ 1334200
-- more info in logs by default
-- print seconds just as int
-- unsign gpg from forked packages before signing them with new key
-- sign forked packages @TODO We need to delsign them first
-
-* Fri May 06 2016 Miroslav Suchý <msuchy@redhat.com> 1.86-1
-- more info in logs by default
-- unsign gpg from forked packages before signing them with new key
-
-* Thu May 05 2016 Miroslav Suchý <msuchy@redhat.com> 1.85-1
-- also be tolerant about sign/unsign failures on particular rpm
-- just log errors (exception) during particular copr fixing, do not
- interrupt the whole process
-- added additional check on copr path existence into copr_fix_gpg.py
-- allow sudo /usr/bin/rpm for `copr` user
-- look into build dirs (subdirs of a chroot) for rpms to be re-signed
-- on F24+ use just ansible
-- Run rpm-sign with sudo when unsigning
-- script to fix gpg keys & rpm signatures
-- define functions for deleting gpg signatures from packages
-- removed temporary mock workaround from Dockerfile (no
- longer needed)
-
-* Thu Apr 28 2016 Miroslav Suchý <msuchy@redhat.com> 1.84-1
-- Bug 1327996 - config_opts['use_host_resolv'] is not set back to
- True if it was False before
-
-* Fri Apr 22 2016 Miroslav Suchý <msuchy@redhat.com> 1.83-1
-- run createrepo on forked project (RhBug: 1329076)
-- Bug 1327852 - /usr/bin/check_consecutive_build_fails.py errors
-- we need to stick to ansible1.9
-- more escaping
-- prunning down testresults :)
-- a few unittests for copr_prune_results.py script
-- unit test "fixes"
-- fix error when forking into existing project
-- (mockremote): improve chroot_setup_cmd replacement for EL-5
-- copr_prune_results.py - python path fix
-- Bug 1324514 - copr createrepo error messages - fix for errors of
- type one
-- Bug 1324514 - copr createrepo error messages - fix for errors of
- type 2
-
-* Thu Mar 24 2016 Jakub Kadlčík <jkadlcik@redhat.com> 1.82-1
-- use timeout variable from config
-
-* Mon Mar 14 2016 Jakub Kadlčík <jkadlcik@redhat.com> 1.81-1
-- support project forking
-- support building from PyPI
-- support for redis_host, redis_port, redis_db config options
-- dockerized-backend project moved under backend/docker
-- run createrepo in rawhide_to_release
-- specify rawhide name when calling rawhide_to_release
-
-* Fri Jan 29 2016 Miroslav Suchý <msuchy@redhat.com> 1.80-1
-- do not fail when when you receive job with architecture which does not have
- queue
-- fix 1260780 - Build fails after successful package generation -
- just add a log error message pointing to an rsync log
-- jobgrabcontrol.py/retask misuse fix
-- "localhost-targeted" spawn and terminate playbooks added for testing
-- [frontend]implement rawhide to release feature First create new
- chroots: python manage.py create_chroot fedora-24-i386 fedora-24-x86_64
-- abstraction above [BE <-> JG <-> Builders] channels
-- don't traceback backend if frontend is not yet up&running
-- do not preserve user and group when rsyncing
-
-* Wed Dec 23 2015 Miroslav Suchý <msuchy@redhat.com> 1.79-1
-- fix packaging issues in epel-7+
-
-* Mon Nov 16 2015 Miroslav Suchý <miroslav@suchy.cz> 1.78-1
-- handle_generate_gpg_key skips key creation when signing is disabled
-- Added test_handle_generate_gpg_key
-- fixed failing tests
-- show when createrepo is waiting for lock
-- do not block builds when processing too much actions
-
-* Fri Nov 06 2015 Miroslav Suchý <msuchy@redhat.com> 1.77-1
-- we need to have recent python-copr
-- create copr-backend-service script to handle all copr services
-- [backend] fix not starting job_grab
-
-* Tue Oct 13 2015 Miroslav Suchý <msuchy@redhat.com> 1.76-1
-- createrepo action run infinitely when applied to
- deleted project
-
-* Mon Sep 21 2015 Valentin Gologuzov <vgologuz@redhat.com> 1.75-1
-- [backend] run copr-backend-log service before other components
-
-* Mon Sep 21 2015 Valentin Gologuzov <vgologuz@redhat.com> 1.74-1
-- [backend] add executable bit to run/copr_run_job_grab.py
-
-* Mon Sep 21 2015 Valentin Gologuzov <vgologuz@redhat.com> 1.73-1
-- added context manager `local_file_logger`
-- eliminated global multiprocessing.Lock
-- split backend daemon: extracted RedisLogHandler, JobGrab, VMM
-- replace python-bunch with python-munch
-- added comps.xml support
-
-* Tue Aug 04 2015 Valentin Gologuzov <vgologuz@redhat.com> 1.72-1
-- support new results naming in the build deletion action
-- fix BuildJob.results_dir; eliminated MockRemote.pkg_dest_path
-- using package name and versiong given in the build task; cleanup;
-- handle error's caused by failure to obtain srpm from dist-git
-- repairing test for newest changes
-- rsync update + several fixes
-- building from dist git
-- fix vm spawn check: spawner count child processes per build group;
-
-* Wed Jul 01 2015 Valentin Gologuzov <vgologuz@redhat.com> 1.71-1
-- add small script to print queues
-- AppData supproted
-- copy mockchain and rsync logs to resdir (RhBug:1221519)
-- note which modules still stops us from migrating to python3
-
-* Mon Jun 15 2015 Miroslav Suchý <msuchy@redhat.com> 1.70-1
-- alter vm_name= regexp
-- polishing Bug 1195867 - Move or delete logs when rebuilding failed
- build.
-- backup only info and log files
-- have just one backup directory per results directory
-- clean results from previous build
-- alter IP= regexp
-- disabled appdata until fixed
-- unable appdata in createrepo
-- more safe VmMaster.check_one_vm_for_dead_builder function
-- adding support for AppData
-- new requirement form AppData support
-- createrepo_unsafe now returns only STDOUT and raise exception on
- errors
-
-* Mon Jun 01 2015 Valentin Gologuzov <vgologuz@redhat.com> 1.69-1
-- removed creation of symlinks for log.gz
-- catch exception during Worker.can_start_job
-- config cleanup
-
-* Thu May 28 2015 Valentin Gologuzov <vgologuz@redhat.com> 1.68-1
-- [backend] add config option for VM health check timeout
-- [backend] moved config parameters from Threshold class into the backend
- config file
-
-* Thu May 21 2015 Valentin Gologuzov <vgologuz@redhat.com> 1.67-1
-- [backend] Handle unexpected exception VmMaster::check_one_vm_for_dead_builder
-
-* Thu May 21 2015 Valentin Gologuzov <vgologuz@redhat.com> 1.66-1
-- [backend] fix race condition in check for dead worker
-
-* Wed May 20 2015 Valentin Gologuzov <vgologuz@redhat.com> 1.65-1
-- [backend] Rescheduling unfinished builds before stop
-- fix indentation
-- [backend] request frontend to reschedule old unfinished builds at startup
-- [backend] update sytemd unit: removed obsolete
- EnviromentFile=/home/copr/cloud/ec2rc.variable directive
-
-* Tue May 19 2015 Valentin Gologuzov <vgologuz@redhat.com> 1.64-1
-- [backend] check for aux process state and restart if needed
-
-* Fri May 15 2015 Miroslav Suchý <msuchy@redhat.com> 1.63-1
-- BR python-sphinx
-
-* Fri May 15 2015 Miroslav Suchý <msuchy@redhat.com> 1.62-1
-- [backend] small cleanup, need more tests
-
-* Fri May 15 2015 Miroslav Suchý <msuchy@redhat.com> 1.61-1
-- [backend] notify job_grab to remove job from added when start_job failed
-- [backend] [vmm] terminate VM with state IN_USE only when builder process is
- missed
-- [backend] bugfix VMM.get_all_vm_in_group : VM could be removed before load
- occures
-- [backend] job_grab: postpone build is we already serving more builds
- than`max_vm_per_user` option
-- [backend] fix build logging
-- [backend] defer sending job to worker if job owner acquired too much VMs
-- [backend] replaced Thresholds.dirty_vm_terminating_timeout with config option
- vm_dirty_terminating_timeout
-- [backend] Thread's doesn't have a pid
-- [backend] vm manage: user threading instead of multiprocessing
-- [backend] VMM aware cleunup_vm_nova
-- [backend] moving to nginx to serve results. lighttpd couldnt server pre-
- compressed properly
-- [backend] script to clean up erred and forgotten VM's using python-novaclient
-- [backend] new documentation
-- [backend] repaired unittests
-- [backend] updated builder playbooks
-- [backend] updated example spawn playbook
-- [backend] don't provide logstash config directly, add only example to
- documentation
-- [backend] do logging from multiply processes through redis pubsub; some fixes
- to VM-management
-- [image_builder] initial release, due to OS bug, we cannot create snapshot
- after provision through API, need to do it manually in the WebUI.
-- [backend] tests cleanup
-- [backend] ensure that prune script running under the copr user; simpler
- `copr_find_obsolete_builds`
-- [backend] safer copr_prune_results script, unittests
-- [backend][frontend] Send for delete action only `src_pkg_name` instead of
- original URL.
-- [backend] returned script to call createrepo from cli
-- [copr] don't allow acquire VMs that was last checked before server restart.
-- [backend] Added limit to acquire_vm based on VMs used by the same username at
- the current moment.
-- New python dependencies
-- [backend] run tmp redis-server for tests
-- [backend] Dedicated and more complex management for builder machines.
- [frontend] Now builds failed due to VM errors reschedulted faster.
-
-* Fri Mar 20 2015 Valentin Gologuzov <vgologuz@redhat.com> 1.60-1
-- [backend][spec] start/stop redis server during package build tests
-
-* Fri Mar 20 2015 Valentin Gologuzov <vgologuz@redhat.com> 1.59-1
-- [backend][hotfix] 1203753 : don't process delete action if src_pkg is
- mallformed
-
-* Mon Mar 02 2015 Valentin Gologuzov <vgologuz@redhat.com> 1.58-1
-- [rhbz:#1185959] - RFE: Present statistics about project
- popularity. A few more counters for downloads from backend's result
- directory.
-- [backend] [rhbz:#1191037] RFE: Include package name and version in fedmsg
- notification
-- [rhbz:#1091640] RFE: Release specific additional repos
-- [rhbz:#1119300] [RFE] allow easy add copr repos in using
- repository lis
-- [backend][frontend] removing code related to multiply source rpms in build.
- Build.pkgs now expected to have exactly one src.rpm.
-- [copr] backend: script fixes, dropped create_repo cli script
-- more file descriptors on builder
-- [rhbz:#1171796] copr sometimes doesn't delete build from repository
-- [rhbz:#1073333] Record consecutive builds fails to redis. Added
- script to produce warnings for nagios check from failures recorded to redis.
-- correctly print job representation
-
-* Fri Jan 23 2015 Valentin Gologuzov <vgologuz@redhat.com> 1.57-1
-- call correct Worker method on backend termination
-- put gpg pubkey to the project results root dir (one level up from
- the chroot dir)
-- don't kill Worker from errors during job build
-- [rhbz:#1169782] RFE - Show package "version-release" instead of
- just "version"
-- [rhbz:#1117446] add a build id tagfile into the package directory
-- Updated unittests to reflect latest changes.
-- builder: use only one log file for rsync per build
-- dispatcher: run terminate_instance safely
-- cleanup example config
-- cleanup mockremote.builder
-- Builder.download don't use Popen+PIPE.communicate with rsync,
- output redirected to the files.
-- disable networking only when required; python style exception
- handling in mockremote*; removed run/copr_mockremote
-- test build with disabled networking
-- simplified mockremote.builder.Builder.check_for_ans_error; new
- method mockremote.builder.Builder.run_ansible_with_check
-- daemons.dispatched.Worker: don't fail when wrong group_id was
- provided
-- add vm_ip to worker process title (rhbz: 1182637)
-
-* Wed Jan 14 2015 Valentin Gologuzov <vgologuz@redhat.com> 1.56-1
-- [backend] [.spec] fix %%files section
-
-* Wed Jan 14 2015 Valentin Gologuzov <vgologuz@redhat.com> 1.55-1
-- [backend] [bugfix] set pythonpath in systemd unit to run /usr/bin/copr_be.py
-- [backend] [RHBZ:#1182106] JobGrabber dies when action raises an exception.
-- [backend] Moved scripts into /usr/bin/ Renamed copr{-,_}be.py.
-
-* Wed Jan 07 2015 Miroslav Suchý <msuchy@redhat.com> 1.54-1
-- 1179713 - workaround for 1179806
-- run script unbufferred otherwise log is written after full block
-- express that it is n-th projects
-- fix permissions on prune script
-
-* Mon Jan 05 2015 Valentin Gologuzov <vgologuz@redhat.com> 1.53-1
-- [backend, frontend] [RHBZ:#1176364] Wrong value for the build timeout.
-
-* Mon Dec 15 2014 Valentin Gologuzov <vgologuz@redhat.com> 1.52-1
-- fixed config option `results_baseurl` usage, in mockremote
-
-* Fri Dec 12 2014 Valentin Gologuzov <vgologuz@redhat.com> 1.51-1
-- updated BuildRequires; cleanup imports
-- package sign: generate gpg usermail with special symbol
-- bugfix: when dispatcher has vm_ip it shouldn't start new VM;
-- run tests during rpm build
-- minor docstring fix
-
-* Wed Dec 10 2014 Valentin Gologuzov <vgologuz@redhat.com> 1.50-1
-- [backend] added option to control ansible ssh transport, changed by default
- to `paramiko` [frontend] bugfix api create new
-- [backend] removed spawn_vars options, to be able to spawn VMs in advance
-- [backend] unittest for backend.daemons.log
-- [backend] massive refactoring and unittest coverage
-- [backend] backend.sign: discover `keygen_host` from backend config file
-
-* Tue Nov 25 2014 Valentin Gologuzov <vgologuz@redhat.com> 1.49-1
-- [backend] small bug in dispatcher
-
-* Tue Nov 25 2014 Valentin Gologuzov <vgologuz@redhat.com> 1.48-1
-- bugfixes, disabled debug prints, fixed PEP8 violations
-
-* Thu Nov 20 2014 Valentin Gologuzov <vgologuz@redhat.com> 1.47-1
-- refactored mockremote, added explicit BuildJob class
-- allow to spawn builder in advance
-- copr-prune-repo respects auto_createrepo option
-- bugfix: repeated config reads produced constantly growing lists
-
-* Fri Oct 24 2014 Valentin Gologuzov <vgologuz@redhat.com> 1.46-1
-- [backend] added handling of new action type: "createrepo"
-- [backend] added dependency on `python-copr`
-- [backend] added to mockchroot -a /devel/repodata subfolder
-- [backend] new config option to define the public frontend api endpoint
-- [backend] conditional execution of createrepo_c
-- [backend] unittest for Action and minor refactoring
-- [backend] rotate backend.log as well
-
-* Thu Sep 18 2014 Miroslav Suchý <msuchy@redhat.com> 1.45-1
-- [backend][keygen] minor fixes/typos
-
-* Thu Sep 18 2014 Valentin Gologuzov <vgologuz@redhat.com> 1.44-1
-- [backend] type fix
-
-* Thu Sep 18 2014 Valentin Gologuzov <vgologuz@redhat.com> 1.43-1
-- [backend] config parsing: convert fields to proper data type.
-- [backend] added option to disable package signing.
-- [keygen] new component for copr: gpg key generation for package sign
-- [backend] broadcast both submitter and owner to fedmsg
-- [backend] example backend config: changes url protocol to HTTPS.
-
-* Mon Aug 25 2014 Adam Samalik <asamalik@redhat.com> 1.42-1
-- [backend] [RHBZ:1128606 ] For rhel-5 builds pass "--checksum md5" to
- `createrepo_c` command.
-- [backend] fix of builder test
-- [backend] test builder instance after spawning
-- [backend] never give up while spawning an OpenStack VM
-- [backend] worker's log filename correction
-- [backend] task id in worker process' name
-- [backend] async build playbooks
-
-* Thu Aug 14 2014 Miroslav Suchý <miroslav@suchy.cz> 1.41-1
-- [backend] fix of fix
-- [backend] couple of fixes
-
-* Wed Aug 13 2014 Miroslav Suchý <msuchy@redhat.com> 1.40-1
-- [backend] queue cleaning
-- [backend] experimental build groups for more architectures
-- [backend] fix of a strange beaviour of retask
-- [backend] fedmsg shows submitter instead of project owner
-- [backend] new task queue for workers using retask
-- epel-7 comps workaround is need no more, since CENTOS7 have been released
-
-* Tue Jul 22 2014 Miroslav Suchý <msuchy@redhat.com> 1.39-1
-- FrontendCallback prettified
-- Starting state implemented, cancelling fixed
-- [backend] faster skipping
-
-* Tue Jul 15 2014 Miroslav Suchý <msuchy@redhat.com> 1.38-1
-- [backend] built pkgs fix
-
-* Tue Jul 15 2014 Miroslav Suchý <msuchy@redhat.com> 1.37-1
-- [backend] shell command uses pipes.quote
-- Return the chroot that finished when sending build.end
-- better and safer deleting of builds
-- [backend] separate playbooks for each architecture
-- [backend] built pkgs - include subpackages
-- [backend] skipped status and package details implemented
-- document vm_name option
-
-* Thu Jun 19 2014 Miroslav Suchý <msuchy@redhat.com> 1.36-1
-- backend: migrate to nova ansible module
-- backend: make sure that exit() exit whole script not just sub-shell
-- backend: allow passing additional info to playbooks
-- handle {spawn,terminate}_instance equally
-- backend: stop if you could not change to directory
-- W:310, 8: Attribute 'abort' defined outside __init__ (attribute-defined-
- outside-init)
-- W:139, 0: Dangerous default value [] as argument (dangerous-default-value)
- W:139, 0: Dangerous default value [0] as argument (dangerous-default-value)
- W:139, 0: Dangerous default value ['stdout', 'stderr'] as argument
- (dangerous-default-value)
-- W:543, 4: Dangerous default value DEF_MACROS ({}) as argument (dangerous-
- default-value)
-- W:543, 4: Dangerous default value DEF_REPOS ([]) as argument (dangerous-
- default-value)
-- W:677,24: Unused variable 'out' (unused-variable) W:677,20: Unused variable
- 'rc' (unused-variable)
-- W:297,12: Unused variable 'hn' (unused-variable)
-- C:116, 0: Unnecessary parens after 'print' keyword (superfluous-parens)
-- W: 72,28: Unused variable 'out' (unused-variable) W: 72,24: Unused variable
- 'rc' (unused-variable)
-- fix typo in exception message printing
-- 1102788 - Increase number of file descriptors on the build machine
-
-* Fri May 30 2014 Miroslav Suchý <msuchy@redhat.com> 1.35-1
-- follow selinux packaging draft
-- [backend] epel 5 repo fix (sha256 -> sha)
-
-* Thu Apr 24 2014 Miroslav Suchý <msuchy@redhat.com> 1.34-1
-- if directory does not exist, do not try to delete it
-
-* Tue Apr 15 2014 Miroslav Suchý <miroslav@suchy.cz> 1.33-1
-- do not publish copr.worker messages
-- better count workers
-
-* Thu Apr 10 2014 Miroslav Suchý <msuchy@redhat.com> 1.32-1
-- include ec2rc in service unit file
-
-* Wed Apr 09 2014 Miroslav Suchý <msuchy@redhat.com> 1.31-1
-- 1077791 - set perm of cronfile to 755
-- 1077791 - add LICENSE to -doc subpackage
-- 1077791 - remove make as BR
-
-* Tue Mar 18 2014 Miroslav Suchý <msuchy@redhat.com> 1.30-1
-- [backend] exclude files which are part of main package
-- copr-backend.src:113: W: mixed-use-of-spaces-and-tabs (spaces: line 5, tab:
- line 113)
-
-* Tue Mar 18 2014 Miroslav Suchý <msuchy@redhat.com> 1.29-1
-- move backend into separate package
-
-* Thu Feb 27 2014 Miroslav Suchý <msuchy@redhat.com> 1.28-1
-- [backend] - pass lock to Actions
-
-* Wed Feb 26 2014 Miroslav Suchý <msuchy@redhat.com> 1.27-1
-- [frontend] update to jquery 1.11.0
-- [fronted] link username to fas
-- [cli] allow to build into projects of other users
-- [backend] do not create repo in destdir
-- [backend] ensure that only one createrepo is running at the same time
-- [cli] allow to get data from sent build
-- temporary workaround for BZ 1065251
-- Chroot details API now uses GET instead of POST
-- when deleting/canceling task, go to same page
-- add copr modification to web api
-- 1063311 - admin should be able to delete task
-- [frontend] Stray end tag h4.
-- [frontend] another s/coprs/projects/ rename
-- [frontend] provide info about last successful build
-- [spec] rhel5 needs group definition even in subpackage
-- [frontend] move 'you agree' text to dd
-- [frontend] add margin to chroots-set
-- [frontend] add margin to field label
-- [frontend] put disclaimer to paragraph tags
-- [frontend] use black font color
-- [frontend] use default filter instead of *_not_filled
-- [frontend] use markdown template filter
-- [frontend] use isdigit instead of is_int
-- [frontend] move Serializer to helpers
-- [frontend] fix coding style and py3 compatibility
-- [cli] fix coding style and py3 compatibility
-- [backend] fix coding style and py3 compatibility
-
-* Tue Jan 28 2014 Miroslav Suchý <miroslav@suchy.cz> 1.26-1
-- lower testing date
-- move localized_time into filters
-- [frontend] update user data after login
-- [frontend] use iso-8601 date
-
-* Mon Jan 27 2014 Miroslav Suchý <msuchy@redhat.com> 1.25-1
-- 1044085 - move timezone modification out of template and make it actually
- work
-- clean up temp data if any
-- [db] timezone can be nullable
-- [frontend] actually save the timezone to model
-- fix colision of revision id
-- 1044085 - frontend: display time in user timezone
-- [frontend] rebuild stuck task
-- disable test on i386
-- use experimental createrepo_c to get rid of lock on temp files
-- [frontend] - do not throw ISE when build_id is malformed
-- [tests] add test for BuildLogic.add
-- [tests] add test for build resubmission
-- [frontend] permission checking is done in BuildLogic.add
-- [frontend] remove BuildLogic.new, use BL.add only
-- [api] fix validation error handling
-- [cli] fix initial_pkgs and repos not sent to backend
-- [frontend] fix BuildsLogic.new not assigning copr to build
-- [frontend] allow resubmitting builds from monitor
-- [frontend] allow GET on repeat_build
-- [frontend] 1050904 - monitor shows not submitted chroots
-- [frontend] rename active_mock_chroots to active_chroots
-- [frontend] rename MockChroot.chroot_name to .name
-- [frontend] 1054474 - drop Copr.build_count nonsense
-- [tests] fix https and repo generation
-- [tests] return exit code from manage.py test
-- 1054472 - Fix deleting multiple SRPMs
-- [spec] tighten acl on copr-be.conf
-- [backend] - add missing import
-- 1054082 - general: encode to utf8 if err in mimetext
-- [backend] lock log file before writing
-- 1055594 - mockremote: always unquote pkg url
-- 1054086 - change vendor tag
-- mockremote: rawhide instead of $releasever in repos when in rawhide chroot
-- 1055499 - do not replace version with $releasever on rawhide
-- 1055119 - do not propagate https until it is properly signed
-- fix spellings on chroot edit page
-- 1054341 - be more verbose about allowed licenses
-- 1054594 - temporary disable https in repo file
-
-* Thu Jan 16 2014 Miroslav Suchý <msuchy@redhat.com> 1.24-1
-- add BR python-markdown
-- [fronted] don't add description to .repo files
-- [spec] fix with_tests conditional
-- add build deletion
-- 1044158 - do not require fas username prior to login
-- replace http with https in copr-cli and in generated repo file
-- [cli] UX changes - explicitly state that pkgs is URL
-- 1053142 - only build copr-cli on el6
-- [frontend] correctly handle mangled chroot
-- [frontend] do not traceback when user malform url
-- [frontend] change default description and instructions to sound more
- dangerously
-- 1052075 - do not set chroots on repeated build
-- 1052071 - do not throw ISE when copr does not exist
-
-* Mon Jan 13 2014 Miroslav Suchý <msuchy@redhat.com> 1.23-1
-- [backend] rhel7-beta do not have comps
-- 1052073 - correctly parse malformed chroot
-
-* Fri Jan 10 2014 Miroslav Suchý <msuchy@redhat.com> 1.22-1
-- [backend] if we could not spawn VM, wait a moment and try again
-- [backend] use createrepo_c instead of createrepo
-- 1050952 - check if copr_url exist in config
-- [frontend] replace newlines in description by space in repo file
-
-* Wed Jan 08 2014 Miroslav Suchý <msuchy@redhat.com> 1.21-1
-- 1049460 - correct error message
-- [cron] manually clean /var/tmp after createrepo
-
-* Wed Jan 08 2014 Miroslav Suchý <msuchy@redhat.com> 1.20-1
-- [cli] no need to set const with action=store_true
-- [cli] code cleanup
-- 1049460 - print nice error when projects does not exist
-- 1049392 - require python-setuptools
-- [backend] add --verbose to log to stderr
-- [backend] handle KeyboardInterrupt without tons of tracebacks
-- 1048508 - fix links at projects lists
-- [backend] in case of error the output is in e.output
-- [selinux] allow httpd to search
-- [backend] set number of worker in name of process
-- [logrotate] rotate every week unconditionally
-- [backend] do not traceback if jobfile is mangled
-- [backend] print error messages to stderr
-- [cli] do not require additional arguments for --nowait
-- [backend] replace procname with setproctitle
-- [cli] use copr.fedoraproject.org as default url
-- [frontend] show monitor even if last build have been canceled
-- [backend] call correct function
-- [cli] print errors to stderr
-- 1044136 - do not print TB if config in mangled
-- 1044165 - Provide login and token information in the same form as entered to
- ~/.config-copr
-- [frontend] code cleanup
-- [frontend] move rendering of .repo file to helpers
-- 1043649 - in case of Fedora use $releasever in repo file
-- [frontend] condition should be in reverse
-
-* Mon Dec 16 2013 Miroslav Suchý <msuchy@redhat.com> 1.19-1
-- [backend] log real cause if ansible crash
-- [frontend] try again if whoosh does not get lock
-- [backend] if frontend does not respond, repeat
-- print yum repos nicely
-- Bump the copr-cli release to 0.2.0 with all the changes made
-- Refer to the man page for more information about the configuration file for
- copr-cli
-- Rework the layout of the list command
-- Fix parsing the copr_url from the configuration file
-- [backend] run createrepo as copr user
-- 1040615 - wrap lines with long URL
-
-* Wed Dec 11 2013 Miroslav Suchý <msuchy@redhat.com> 1.18-1
-- [frontend] inicialize variable
-
-* Wed Dec 11 2013 Miroslav Suchý <msuchy@redhat.com> 1.17-1
-- [frontend] fix latest build variable overwrite
-
-* Wed Dec 11 2013 Miroslav Suchý <msuchy@redhat.com> 1.16-1
-- [backend] store jobs in id-chroot.json file
-- [frontend] handle unknown build/chroot status
-- use newstyle ansible variables
-
-* Tue Dec 10 2013 Miroslav Suchý <msuchy@redhat.com> 1.15-1
-- [frontend] smarter package name parsing
-- [frontend] extend range to allow 0
-- handle default timeout on backend
-- initial support for SCL
-- [backend] create word readable files in result directory
-- [backend] print tracebacks
-- [frontend] monitor: display only pkg name w/o version
-- [doc] update api docs
-- [doc] update copr-cli manpage
-- [cli] list only name, description and instructions
-- [cli] add support for build status & build monitor
-- [frontend] add build status to API
-- [playbook] do not overwrite mockchain
-- [backend] add spece between options
-- [backend] pass mock options correctly
-- [frontend] support markdown in description and instructions
-- [backend] Add macros to mockchain define arguments
-- [backend] Pass copr username and project name to MockRemote
-- [backend] Handle additional macro specification in MockRemote
-- [frontend] monitor: show results per package
-- [frontend] add favicon
-- [backend] quote strings before passing to mockchain
-- send chroots with via callback to frontend
-- [cli] change cli to new api call
-- enhance API documentation
-- add yum_repos to coprs/user API call
-- [frontend] provide link to description of allowed content
-- [backend] we pass just one chroot
-- [backend] - variable play is not defined
-- if createrepo fail, run it again
-- [cron] fix syntax error
-- [man] state that --chroot for create command is required
-- [spec] enable tests
-- [howto] add note about upgrading db schema
-- [frontend]: add copr monitor
-- [tests]: replace test_allowed_one
-- [tests]: fix for BuildChroots & new backend view
-- [frontend] rewrite backend view to use Build <-> Chroot relation
-- [frontend] add Build <-> Chroot relation
-- 1030493 - [cli] check that at least one chroot is entered
-- [frontend] typo
-- fixup! [tests]: fix test_build_logic to handle BuildChroot
-- fixup! [frontend] add ActionsLogic
-- [tests]: fix test_build_logic to handle BuildChroot
-- [spec] enable/disable test using variable
-- add migration script - add table build_chroot
-- [frontend] skip legal-flag actions when dumping waiting actions
-- [frontend] rewrite backend view to use Build <-> Chroot relation
-- [frontend] add ActionsLogic
-- [frontend] create BuildChroot objects on new build
-- [frontend] add Build <-> Chroot relation
-- [frontend] add StatusEnum
-- [frontend] fix name -> coprname typo
-- [frontend] remove unused imports
-- [frontend] add missing json import
-- [backend] rework ip address extraction
-- ownership of /etc/copr should be just normal
-- [backend] - wrap up returning action in "action" blok
-- [backend] rename backend api url
-- [backend] handle "rename" action
-- [backend] handle "delete" action
-- base handling of actions
-- move callback to frontend to separate object
-- secure waiting_actions with password
-- pick only individual builds
-- make address, where we send legal flags, configurable
-- send email to root after legal flag have been raised
-
-* Fri Nov 08 2013 Miroslav Suchý <msuchy@redhat.com> 1.14-1
-- 1028235 - add disclaimer about repos
-- fix pagination
-- fix one failing test
-
-* Wed Nov 06 2013 Miroslav Suchý <msuchy@redhat.com> 1.13-1
-- suggest correct name of repo file
-- we could not use releasever macro
-- no need to capitalize Projects
-- another s/copr/project
-- add link to header for sign-in
-- fix failing tests
-- UX - let textarea will full widht of box
-- UX - make background of hovered builds darker
-- generate yum repo for each chroot of copr
-- align table header same way as ordinary rows
-- enable resulting repo and disable gpgchecks
-
-* Mon Nov 04 2013 Miroslav Suchý <msuchy@redhat.com> 1.12-1
-- do not send parameters when we neither need them nor use them
-- authenticate using api login, not using username
-- disable editing name of project
-- Add commented out WTF_CSRF_ENABLED = True to configs
-- Use new session for each test
-- fix test_coprs_general failures
-- fix test_coprs_builds failures
-- Add WTF_CSRF_ENABLED = False to unit test config
-- PEP8 fixes
-- Fix compatibility with wtforms 0.9
-- typo s/submited/submitted/
-- UX - show details of build only after click
-- add link to FAQ to footer
-- UX - add placeholders
-- UX - add asterisk to required fields
-- dynamicly generate url for home
-- add footer
-
-* Sat Oct 26 2013 Miroslav Suchý <msuchy@redhat.com> 1.11-1
-- catch IOError from libravatar if there is no network
-
-* Fri Oct 25 2013 Miroslav Suchý <msuchy@redhat.com> 1.10-1
-- do not normalize url
-- specify full prefix of http
-- execute playbook using /usr/bin/ansible-playbook
-- use ssh transport
-- check after connection is made
-- add notes about debuging mockremote
-- clean up instance even when worker fails
-- normalize paths before using
-- do not use exception variable
-- operator should be preceded and followed by space
-- remove trailing whitespace
-- convert comment to docstring
-- use ssh transport
-- do not create new ansible connection, reuse self.conn
-- run copr-be.py as copr
-- s/Copr/Project/ where we use copr in meaning of projects
-- number will link to those coprs, to which it refers
-- run log and jobgrab as copr user
-- log event to log file
-- convert comment into docstring
-- use unbufferred output for copr-be.py
-- hint how to set ec2 variables
-- document sleeptime
-- document copr_url for copr-cli
-- document how to set api key for copr-cli
-- do not create list of list
-- document SECRET_KEY variable
-- make note how to become admin
-- instruct people to install selinux with frontend
-
-* Thu Oct 03 2013 Miroslav Suchý <msuchy@redhat.com> 1.9-1
-- prune old builds
-- require python-decorator
-- remove requirements.txt
-- move TODO-backend to our wiki
-- create pid file in /var/run/copr-backend
-- add backend service file for systemd
-- remove daemonize option in config
-- use python logging
-- create pid file in /var/run by default
-- do not create destdir
-- use daemon module instead of home brew function
-- fix default location of copr-be.conf
-- 2 tests fixed, one still failing
-- fix failing test test_fail_on_missing_dash
-- fixing test_fail_on_nonexistent_copr test
-- run frontend unit tests when building package
-- Adjust URLs in the unit-tests to their new structure
-- Adjust the CLI to call the adjuste endpoint of the API
-- Adjust API endpoint to reflects the UI endpoints in their url structure
-- First pass at adding fedmsg hooks.
-
-* Tue Sep 24 2013 Miroslav Suchý <msuchy@redhat.com> 1.8-1
-- 1008532 - require python2-devel
-- add note about ssh keys to copr-setup.txt
-- set home of copr user to system default
-
-* Mon Sep 23 2013 Miroslav Suchý <msuchy@redhat.com> 1.7-1
-- 1008532 - backend should own _pkgdocdir
-- 1008532 - backend should owns /etc/copr as well
-- 1008532 - require logrotate
-- 1008532 - do not distribute empty copr.if
-- 1008532 - use %%{?_smp_mflags} macro with make
-- move jobsdir to /var/lib/copr/jobs
-- correct playbooks path
-- selinux with enforce can be used for frontend
-
-* Wed Sep 18 2013 Miroslav Suchý <msuchy@redhat.com> 1.6-1
-- add BR python-devel
-- generate selinux type for /var/lib/copr and /var/log/copr
-- clean up backend setup instructions
-- initial selinux subpackage
-
-* Mon Sep 16 2013 Miroslav Suchý <msuchy@redhat.com> 1.5-1
-- 1008532 - use __python2 instead of __python
-- 1008532 - do not mark man page as doc
-- 1008532 - preserve timestamp
-
-* Mon Sep 16 2013 Miroslav Suchý <msuchy@redhat.com> 1.4-1
-- add logrotate file
-
-* Mon Sep 16 2013 Miroslav Suchý <msuchy@redhat.com> 1.3-1
-- be clear how we create tgz
-
-* Mon Sep 16 2013 Miroslav Suchý <msuchy@redhat.com> 1.2-1
-- fix typo
-- move frontend data into /var/lib/copr
-- no need to own /usr/share/copr by copr-fe
-- mark application as executable
-- coprs_frontend does not need to be owned by copr-fe
-- add executable attribute to copr-be.py
-- remove shebang from dispatcher.py
-- squeeze description into 80 chars
-- fix typo
-- frontend need argparse too
-- move results into /var/lib/copr/public_html
-- name of dir is just copr-%%version
-- Remove un-necessary quote that breaks the tests
-- Adjust unit-tests to the new urls
-- Update the URL to be based upon a /user/copr/<action> structure
-- comment config copr-be.conf and add defaults
-- put examples of builderpb.yml and terminatepb.yml into doc dir
-- more detailed description of copr-be.conf
-- move files in config directory not directory itself
-- include copr-be.conf
-- include copr-be.py
-- create copr with lighttpd group
-- edit backend part of copr-setup.txt
-- remove fedora16 and add 19 and 20
-- create -doc subpackage with python documentation
-- add generated documentation on gitignore list
-- add script to generate python documentation
-- copr-setup.txt change to for mock
-- rhel6 do not know _pkgdocdir macro
-- make instruction clear
-- require recent whoosh
-- add support for libravatar
-- include backend in rpm
-- add notes about lighttpd config files and how to deploy them
-- do not list file twice
-- move log file to /var/log
-- change destdir in copr-be.conf.example
-- lightweight is the word and buildsystem has more meaning than 'koji'.
-- restart apache after upgrade of frontend
-- own directory where backend put results
-- removal of hidden-file-or-dir
- /usr/share/copr/coprs_frontend/coprs/logic/.coprs_logic.py.swo
-- copr-backend.noarch: W: spelling-error %%description -l en_US latests ->
- latest, latest's, la tests
-- simplify configuration - introduce /etc/copr/copr*.conf
-- Replace "with" statements with @TransactionDecorator decorator
-- add python-flexmock to deps of frontend
-- remove sentence which does not have meaning
-- change api token expiration to 120 days and make it configurable
-- create_chroot must be run as copr-fe user
-- add note that you have to add chroots to db
-- mark config.py as config so it is not overwritten during upgrade
-- own directory data/whooshee/copr_user_whoosheer
-- gcc is not needed
-- sqlite db must be owned by copr-fe user
-- copr does not work with selinux
-- create subdirs under data/openid_store
-- suggest to install frontend as package from copr repository
-- on el6 add python-argparse to BR
-- add python-requests to BR
-- add python-setuptools to BR
-- maintain apache configuration on one place only
-- apache 2.4 changed access control
-- require python-psycopg2
-- postgresql server is not needed
-- document how to create db
-- add to HOWTO how to create db
-- require python-alembic
-- add python-flask-script and python-flask-whooshee to requirements
-- change user in coprs.conf.example to copr-fe
-- fix paths in coprs.conf.example
-- copr is noarch package
-- add note where to configure frontend
-- move frontend to /usr/share/copr/coprs_frontend
-- put production placeholders in coprs_frontend/coprs/config.py
-- put frontend into copr.spec
-- web application should be put in /usr/share/%%{name}
-
-* Mon Jun 17 2013 Miroslav Suchý <msuchy@redhat.com> 1.1-1
-- new package built with tito
-
-
+* Wed Mar 12 2025 lichaoran <pkwarcraft@hotmail.com> 1.168-1
+- Init package
diff --git a/euler_msgbus.patch b/euler_msgbus.patch
new file mode 100644
index 0000000..5eca90a
--- /dev/null
+++ b/euler_msgbus.patch
@@ -0,0 +1,83 @@
+diff --git a/copr_backend/euler_msgbus.py b/copr_backend/euler_msgbus.py
+new file mode 100644
+index 000000000..1395249be
+--- /dev/null
++++ b/copr_backend/euler_msgbus.py
+@@ -0,0 +1,77 @@
++import socket
++
++from kafka import KafkaProducer
++import os
++import datetime
++import uuid
++import json
++import ssl
++
++
++def message_from_worker_job(topic, job, who, ip, pid):
++ message = {}
++ content = {
++ 'user': job.submitter,
++ 'copr': job.project_name,
++ 'owner': job.project_owner,
++ 'pkg': job.package_name,
++ 'build': job.build_id,
++ 'chroot': job.chroot,
++ 'version': job.package_version,
++ 'status': job.status,
++ }
++ content.update({'ip': ip, 'who': who, 'pid': pid})
++ message_types = {
++ 'build.start': {
++ 'what': "build start: user:{user} copr:{copr}" \
++ " pkg:{pkg} build:{build} ip:{ip} pid:{pid}",
++ },
++ 'chroot.start': {
++ 'what': "chroot start: chroot:{chroot} user:{user}" \
++ " copr:{copr} pkg:{pkg} build:{build} ip:{ip} pid:{pid}",
++ },
++ 'build.end': {
++ 'what': "build end: user:{user} copr:{copr} build:{build}" \
++ " pkg:{pkg} version:{version} ip:{ip} pid:{pid} status:{status}",
++ },
++ }
++ content['what'] = message_types[topic]['what'].format(**content)
++ message['body'] = content
++ now = datetime.datetime.now().isoformat()
++ headers = {
++ "openEuler_messaging_schema": "eur." + topic,
++ "sent-at": now,
++ }
++ message['headers'] = headers
++ message['id'] = str(uuid.uuid4())
++ message['topic'] = "org.openEuler.prod.eur." + topic
++ return message
++
++
++class MessageSender:
++ def __init__(self, backend_opts, name, log):
++ self.log = log
++ self.name = name
++ self.pid = os.getpid()
++ self.opts = backend_opts
++
++ def announce(self, topic, job, host):
++ msg = message_from_worker_job(topic, job, self.name, host, self.pid)
++ self.send_message(msg)
++
++ def send_message(self, msg):
++ """ Send message to kafka """
++
++ context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
++ context.verify_mode = ssl.CERT_REQUIRED
++ context.load_verify_locations("etc/copr/kafka.crt")
++ producer = KafkaProducer(bootstrap_servers=self.opts.message.bootstrap_servers,
++ api_version=(3, 5, 0),
++ sasl_mechanism="PLAIN",
++ ssl_context=context,
++ security_protocol='SASL_SSL',
++ sasl_plain_username=self.opts.message.user_name,
++ sasl_plain_password=self.opts.message.password,
++ value_serializer=lambda v: json.dumps(v).encode('utf-8'))
++ producer.send(str(self.opts.message.topic), msg)
++ producer.flush()
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/signatrust_bin.patch b/signatrust_bin.patch
new file mode 100644
index 0000000..fcba220
--- /dev/null
+++ b/signatrust_bin.patch
@@ -0,0 +1,105 @@
+diff --git a/run/copr_fix_gpg.py b/run/copr_fix_gpg.py
+index 24edf74e..bfa70c05 100755
+--- a/run/copr_fix_gpg.py
++++ b/run/copr_fix_gpg.py
+@@ -9,7 +9,7 @@ from urllib.parse import urlparse
+ import pwd
+
+ from copr_backend.helpers import BackendConfigReader, call_copr_repo, run_cmd
+-from copr_backend.sign import get_pubkey, unsign_rpms_in_dir, sign_rpms_in_dir, create_user_keys, create_gpg_email
++from copr_backend.sign import new_signer, create_gpg_email
+
+ logging.basicConfig(
+ filename="/var/log/copr-backend/fix_gpg.log",
+@@ -69,12 +69,14 @@ def fix_copr(args, opts, copr_full_name):
+ log.info('Ignoring %s. Directory does not exist.', copr_path)
+ return
+
++ signer = new_signer(opts)
++
+ log.info("Generate key-pair on copr-keygen (if not generated) for email %s",
+ create_gpg_email(owner, coprname, opts.sign_domain))
+- create_user_keys(owner, coprname, opts)
++ signer.create_user_keys(owner, coprname, opts)
+
+ log.info("Regenerate pubkey.gpg in copr %s", copr_path)
+- get_pubkey(owner, coprname, log, opts.sign_domain, os.path.join(copr_path, 'pubkey.gpg'))
++ signer.get_pubkey(owner, coprname, log, opts.sign_domain, os.path.join(copr_path, 'pubkey.gpg'))
+
+ # Match the "00001231-anycharacer" directory names. Compile once, use many.
+ builddir_matcher = re.compile(r"\d{8,}-")
+@@ -111,8 +113,8 @@ def fix_copr(args, opts, copr_full_name):
+
+ log.info("Processing rpms in builddir %s", builddir_path)
+ try:
+- unsign_rpms_in_dir(builddir_path, opts, log) # first we need to unsign by using rpm-sign before we sign with obs-sign
+- sign_rpms_in_dir(owner, coprname, builddir_path, chroot, opts, log)
++ signer.unsign_rpms_in_dir(builddir_path, opts, log) # first we need to unsign by using rpm-sign before we sign with obs-sign
++ signer.sign_rpms_in_dir(owner, coprname, builddir_path, chroot, opts, log)
+ except Exception as e:
+ log.exception(str(e))
+ continue
+diff --git a/run/copr_sign_unsigned.py b/run/copr_sign_unsigned.py
+index 33eaeae3..e17e65bb 100755
+--- a/run/copr_sign_unsigned.py
++++ b/run/copr_sign_unsigned.py
+@@ -12,7 +12,7 @@ import pwd
+
+ from copr_backend.helpers import (BackendConfigReader, create_file_logger,
+ uses_devel_repo, call_copr_repo)
+-from copr_backend.sign import get_pubkey, sign_rpms_in_dir, create_user_keys
++from copr_backend.sign import new_signer
+ from copr_backend.exceptions import CoprSignNoKeyError
+
+
+@@ -26,10 +26,11 @@ log = logging.getLogger(__name__)
+ def check_signed_rpms_in_pkg_dir(pkg_dir, user, project, opts, chroot_dir, devel):
+ success = True
+
++ signer = new_signer(opts)
+ logger = create_file_logger("run.check_signed_rpms_in_pkg_dir",
+ "/tmp/copr_check_signed_rpms.log")
+ try:
+- sign_rpms_in_dir(user, project, pkg_dir, chroot_dir, opts, log=logger)
++ signer.sign_rpms_in_dir(user, project, pkg_dir, chroot_dir, opts, log=logger)
+ log.info("running createrepo for %s", pkg_dir)
+ call_copr_repo(directory=chroot_dir, devel=devel, logger=log)
+ except Exception as err:
+@@ -80,9 +81,10 @@ def check_pubkey(pubkey_path, user, project, opts):
+ log.info("Pubkey for %s/%s exists: %s", user, project, pubkey_path)
+ return True
+ else:
++ signer = new_signer(opts)
+ log.info("Missing pubkey for %s/%s", user, project)
+ try:
+- get_pubkey(user, project, log, opts.sign_domain, pubkey_path)
++ signer.get_pubkey(user, project, log, opts.sign_domain, pubkey_path)
+ return True
+ except Exception as err:
+ log.exception(err)
+@@ -102,6 +104,8 @@ def main():
+ opts = BackendConfigReader().read()
+ log.info("Starting pubkey fill, destdir: %s", opts.destdir)
+
++ signer = new_signer(opts)
++
+ log.debug("list dir: %s", os.listdir(opts.destdir))
+ for user_name in os.listdir(opts.destdir):
+ if user_name in users_done_old:
+@@ -116,13 +120,13 @@ def main():
+ log.info("Checking project dir: %s", project_name)
+
+ try:
+- get_pubkey(user_name, project_name, log, opts.sign_domain)
++ signer.get_pubkey(user_name, project_name, log, opts.sign_domain)
+ log.info("Key-pair exists for %s/%s", user_name, project_name)
+ except CoprSignNoKeyError:
+- create_user_keys(user_name, project_name, opts)
++ signer.create_user_keys(user_name, project_name, opts)
+ log.info("Created new key-pair for %s/%s", user_name, project_name)
+ except Exception as err:
+- log.error("Failed to get pubkey for {}/{}, mark as failed, skipping")
++ log.error("Failed to get pubkey for {}/{}, mark as failed, skipping".format(user_name, project_name))
+ log.exception(err)
+ failed = True
+ continue
diff --git a/sources b/sources
index 3009c44..adc3d34 100644
--- a/sources
+++ b/sources
@@ -1,2 +1,2 @@
-22717aa0348a40c6e80c746cc6c818ca copr-backend-1.173.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
new file mode 100644
index 0000000..d803d26
--- /dev/null
+++ b/support_signatrust_backend.patch
@@ -0,0 +1,1150 @@
+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
+
+ from copr_backend.worker_manager import BackendQueueTask
+
+-from .sign import create_user_keys, CoprKeygenRequestError
++from .sign import CoprKeygenRequestError
+ from .exceptions import CreateRepoError, CoprSignError, FrontendClientException
+ from .helpers import (get_redis_logger, silent_remove, ensure_dir_exists,
+ get_chroot_arch, format_filename,
+ uses_devel_repo, call_copr_repo, build_chroot_log_name,
+ copy2_but_hardlink_rpms)
+-from .sign import sign_rpms_in_dir, unsign_rpms_in_dir, get_pubkey
++from .sign import new_signer
+
+
+ class Action(object):
+@@ -92,6 +92,8 @@ class Action(object):
+
+ self.log = log if log else get_redis_logger(self.opts, "backend.actions", "actions")
+
++ self.signer = new_signer(opts)
++
+ def __str__(self):
+ return "<{}(Action): {}>".format(self.__class__.__name__, self.data)
+
+@@ -147,7 +149,7 @@ class GPGMixin(object):
+ # skip key creation, most probably sign component is unused
+ return True
+ try:
+- create_user_keys(ownername, projectname, self.opts)
++ self.signer.create_user_keys(ownername, projectname, self.opts)
+ return True
+ except CoprKeygenRequestError as e:
+ self.log.exception(e)
+@@ -176,7 +178,7 @@ class Fork(Action, GPGMixin):
+ # Generate brand new gpg key.
+ self.generate_gpg_key(data["user"], data["copr"])
+ # Put the new public key into forked build directory.
+- get_pubkey(data["user"], data["copr"], self.log, self.opts.sign_domain, pubkey_path)
++ self.signer.get_pubkey(data["user"], data["copr"], self.log, self.opts.sign_domain, pubkey_path)
+
+ chroot_paths = set()
+ for chroot, src_dst_dir in builds_map.items():
+@@ -206,9 +208,9 @@ class Fork(Action, GPGMixin):
+ continue
+
+ # Drop old signatures coming from original repo and re-sign.
+- unsign_rpms_in_dir(dst_path, opts=self.opts, log=self.log)
++ self.signer.unsign_rpms_in_dir(dst_path, opts=self.opts, log=self.log)
+ if sign:
+- sign_rpms_in_dir(data["user"], data["copr"], dst_path,
++ self.signer.sign_rpms_in_dir(data["user"], data["copr"], dst_path,
+ 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/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
+ from copr_common.background_worker import BackgroundWorker
+ from copr_backend.frontend import FrontendClient
+ from copr_backend.helpers import (BackendConfigReader, get_redis_logger)
+-
++from copr_backend.sign import new_signer
+
+ class BackendBackgroundWorker(BackgroundWorker):
+ """
+@@ -28,6 +28,7 @@ class BackendBackgroundWorker(BackgroundWorker):
+
+ self.frontend_client = FrontendClient(self.opts, self.log,
+ try_indefinitely=True)
++ self.signer = new_signer(self.opts)
+
+ def _switch_logger_to_redis(self):
+ logger_name = '{}.{}.pid-{}'.format(
+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,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.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
+
+@@ -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)
+
+- sign_rpms_in_dir(
++ self.signer.sign_rpms_in_dir(
+ self.job.project_owner,
+ self.job.project_name,
+ os.path.join(self.job.chroot_dir, self.job.target_dir_name),
+@@ -736,7 +736,7 @@ class BuildBackgroundWorker(BackendBackgroundWorker):
+ # TODO: uncomment this when key revoke/change will be implemented
+ # if os.path.exists(pubkey_path):
+ # return
+- get_pubkey(user, project, self.log, self.opts.sign_domain, pubkey_path)
++ self.signer.get_pubkey(user, project, self.log, self.opts.sign_domain, pubkey_path)
+ self.log.info("Added pubkey for user %s project %s into: %s",
+ user, project, pubkey_path)
+
+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"
+ DEF_DESTDIR = os.getcwd()
+ DEF_MACROS = {}
+ DEF_BUILDROOT_PKGS = ""
++DEF_SIGN_BACKEND = "obs-signd"
+
+
+ DEF_CONSECUTIVE_FAILURE_THRESHOLD = 10
+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):
+
+ def __str__(self):
+ out = super(CoprKeygenRequestError, self).__str__()
+- out += "\nrequest to copr-keygen: {}\n".format(self.request)
+- if self.response:
++ out += "\nrequest to key_backend: {}\n".format(self.request)
++ if self.response is not None:
+ 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/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
+ from copr_common.redis_helpers import get_redis_connection
+ from copr.v3 import Client
+ 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
+
+ from . import constants
+@@ -309,6 +309,18 @@ class BackendConfigReader(object):
+ opts.sign_domain = _get_conf(
+ cp, "backend", "sign_domain", DOMAIN)
+
++ opts.sign_backend = _get_conf(
++ cp, "backend", "sign_backend", DEF_SIGN_BACKEND)
++
++ opts.signatrust_host = _get_conf(
++ cp, "backend", "signatrust_host", "")
++
++ opts.signatrust_token = _get_conf(
++ cp, "backend", "signatrust_token", "")
++
++ opts.signatrust_key_expire = _get_conf(
++ cp, "backend", "signatrust_key_expire", 1825, mode="int")
++
+ 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/backend/copr_backend/sign.py
+index e21653e78..95f674255 100644
+--- a/copr_backend/sign.py
++++ b/copr_backend/sign.py
+@@ -25,78 +25,6 @@ def create_gpg_email(username, projectname, domain):
+
+ return "{}#{}@copr.{}".format(username, projectname, domain)
+
+-
+-def call_sign_bin(cmd, log):
+- """
+- Call /bin/sign and return (rc, stdout, stderr). Re-try the call
+- automatically upon certain failures (if that makes sense).
+- """
+- cmd_pretty = ' '.join(cmd)
+- for attempt in [1, 2, 3]:
+- log.info("Calling '%s' (attempt #%s)", cmd_pretty, attempt)
+- try:
+- handle = Popen(cmd, stdout=PIPE, stderr=PIPE, encoding="utf-8")
+- stdout, stderr = handle.communicate()
+- except (SubprocessError, OSError) as err:
+- new_err = CoprSignError("Failed to invoke '{}'".format(cmd_pretty))
+- raise new_err from err
+-
+- if handle.returncode != 0:
+- log.warning("Command '%s' failed with: %s",
+- cmd_pretty, stderr.rstrip())
+- sleeptime = 20
+- log.warning("Going to sleep %ss and re-try.", sleeptime)
+- time.sleep(sleeptime)
+- continue
+- break
+- return handle.returncode, stdout, stderr
+-
+-
+-def get_pubkey(username, projectname, log, sign_domain, outfile=None):
+- """
+- Retrieves public key for user/project from signer host.
+-
+- :param sign_domain: the domain name of the sign key
+- :param outfile: [optional] file to write obtained key
+- :return: public keys
+-
+- :raises CoprSignError: failed to retrieve key, see error message
+- :raises CoprSignNoKeyError: if there are no such user in keyring
+- """
+- usermail = create_gpg_email(username, projectname, sign_domain)
+- cmd = [SIGN_BINARY, "-u", usermail, "-p"]
+-
+- returncode, stdout, stderr = call_sign_bin(cmd, log)
+- if returncode != 0:
+- if "unknown key:" in stderr:
+- raise CoprSignNoKeyError(
+- "There are no gpg keys for user {} in keyring".format(username),
+- return_code=returncode,
+- cmd=cmd, stdout=stdout, stderr=stderr)
+- raise CoprSignError(
+- msg="Failed to get user pubkey\n"
+- "sign stdout: {}\n sign stderr: {}\n".format(stdout, stderr),
+- return_code=returncode,
+- cmd=cmd, stdout=stdout, stderr=stderr)
+-
+- if outfile:
+- with open(outfile, "w") as handle:
+- handle.write(stdout)
+-
+- return stdout
+-
+-
+-def _sign_one(path, email, hashtype, log):
+- cmd = [SIGN_BINARY, "-4", "-h", hashtype, "-u", email, "-r", path]
+- returncode, stdout, stderr = call_sign_bin(cmd, log)
+- if returncode != 0:
+- raise CoprSignError(
+- msg="Failed to sign {} by user {}".format(path, email),
+- return_code=returncode,
+- cmd=cmd, stdout=stdout, stderr=stderr)
+- return stdout, stderr
+-
+-
+ def gpg_hashtype_for_chroot(chroot, opts):
+ """
+ Given the chroot name (in "mock format", like "fedora-rawhide-x86_64")
+@@ -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(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()
++
++ if handle.returncode != 0:
++ err = CoprSignError(
++ msg="Failed to unsign {}".format(path),
++ return_code=handle.returncode,
++ cmd=cmd, stdout=stdout, stderr=stderr)
+
+- rpm_list = [
+- os.path.join(path, filename)
+- for filename in os.listdir(path)
+- if filename.endswith(".rpm")
+- ]
++ raise err
++
++ return stdout, stderr
++
++ @classmethod
++ def call_sign_bin(cls, cmd, log):
++ """
++ Call sign_cmd and return (rc, stdout, stderr). Re-try the call
++ automatically upon certain failures (if that makes sense).
++ """
++ cmd_pretty = ' '.join(cmd)
++ for attempt in [1, 2, 3]:
++ log.info("Calling '%s' (attempt #%s)", cmd_pretty, attempt)
++ try:
++ handle = Popen(cmd, stdout=PIPE, stderr=PIPE, encoding="utf-8")
++ stdout, stderr = handle.communicate()
++ except (SubprocessError, OSError) as err:
++ new_err = CoprSignError("Failed to invoke '{}'".format(cmd_pretty))
++ raise new_err from err
++
++ if handle.returncode != 0:
++ log.warning("Command '%s' failed with: %s",
++ cmd_pretty, stderr.rstrip())
++ sleeptime = 20
++ log.warning("Going to sleep %ss and re-try.", sleeptime)
++ time.sleep(sleeptime)
++ continue
++ break
++ return handle.returncode, stdout, stderr
++
++ @classmethod
++ def sign_rpms_in_dir(cls, 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.
++
++ :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
++
++ :type log: logging.Logger
++
++ :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")
++ ]
+
+- if not rpm_list:
+- return
++ if not rpm_list:
++ return
+
+- hashtype = gpg_hashtype_for_chroot(chroot, opts)
++ hashtype = gpg_hashtype_for_chroot(chroot, opts)
+
+- 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, try_indefinitely=True)
++
++ errors = [] # tuples (rpm_filepath, exception)
++ for rpm in rpm_list:
++ try:
++ cls._sign_one(rpm, create_gpg_email(username, projectname, opts.sign_domain),
++ hashtype, log)
++ log.info("signed rpm: %s", rpm)
++
++ except CoprSignError as e:
++ log.exception("failed to sign rpm: %s", rpm)
++ errors.append((rpm, e))
++
++ if errors:
++ raise CoprSignError("Rpm sign failed, affected rpms: {}"
++ .format([err[0] for err in errors]))
++
++ @classmethod
++ def unsign_rpms_in_dir(cls, path, opts, log):
++ """
++ :param path: directory with rpms to be signed
++ :param Munch opts: backend config
++ :type log: logging.Logger
++ :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")
++ ]
++
++ if not rpm_list:
++ return
++
++ errors = [] # tuples (rpm_filepath, exception)
++ for rpm in rpm_list:
++ try:
++ cls._unsign_one(rpm)
++ log.info("unsigned rpm: %s", rpm)
++
++ except CoprSignError as e:
++ log.exception("failed to unsign rpm: %s", rpm)
++ errors.append((rpm, e))
++
++ if errors:
++ raise CoprSignError("Rpm unsign failed, affected rpms: {}"
++ .format([err[0] for err in errors]))
++
++def new_signer(opts):
++ if opts.sign_backend == "signatrust":
++ Signatrust.signatrust_token = opts.signatrust_token
++ Signatrust.signatrust_host = opts.signatrust_host
++ return Signatrust
++ else: # keep obs-signd as default backend
++ return ObsSign
++
++class ObsSign(Signer):
++ sign_cmd = "/bin/sign"
++
++ @classmethod
++ def get_pubkey(cls, username, projectname, log, sign_domain, outfile=None):
++ """
++ Retrieves public key for user/project from signer host.
++
++ :param sign_domain: the domain name of the sign key
++ :param outfile: [optional] file to write obtained key
++ :return: public keys
++
++ :raises CoprSignError: failed to retrieve key, see error message
++ :raises CoprSignNoKeyError: if there are no such user in keyring
++ """
++ usermail = create_gpg_email(username, projectname, sign_domain)
++ cmd = [cls.sign_cmd, "-u", usermail, "-p"]
++
++ returncode, stdout, stderr = cls.call_sign_bin(cmd, log)
++ if returncode != 0:
++ if "unknown key:" in stderr:
++ raise CoprSignNoKeyError(
++ "There are no gpg keys for user {} in keyring".format(username),
++ return_code=returncode,
++ cmd=cmd, stdout=stdout, stderr=stderr)
++ raise CoprSignError(
++ msg="Failed to get user pubkey\n"
++ "sign stdout: {}\n sign stderr: {}\n".format(stdout, stderr),
++ return_code=returncode,
++ cmd=cmd, stdout=stdout, stderr=stderr)
+
+- errors = [] # tuples (rpm_filepath, exception)
+- for rpm in rpm_list:
++ if outfile:
++ with open(outfile, "w") as handle:
++ handle.write(stdout)
++
++ return stdout
++
++ @classmethod
++ def _sign_one(cls, path, email, hashtype, log):
++ cmd = [cls.sign_cmd, "-4", "-h", hashtype, "-u", email, "-r", path]
++ returncode, stdout, stderr = cls.call_sign_bin(cmd, log)
++ if returncode != 0:
++ raise CoprSignError(
++ msg="Failed to sign {} by user {}".format(path, email),
++ return_code=returncode,
++ cmd=cmd, stdout=stdout, stderr=stderr)
++ return stdout, stderr
++
++ @classmethod
++ def create_user_keys(cls, username, projectname, opts, try_indefinitely=False):
++ """
++ Generate a new key-pair at sign host
++
++ :param username:
++ :param projectname:
++ :param opts: backend config
++
++ :return: None
++ """
++ data = {
++ "name_real": "{}_{}".format(username, projectname),
++ "name_email": create_gpg_email(username, projectname, opts.sign_domain)
++ }
++
++ log = get_redis_logger(opts, "sign", "actions")
++ keygen_url = "http://{}/gen_key".format(opts.keygen_host)
++ query = dict(url=keygen_url, data=data, method="post")
++ try:
++ request = SafeRequest(log=log, try_indefinitely=try_indefinitely)
++ response = request.send(**query)
++ except Exception as e:
++ raise CoprKeygenRequestError(
++ msg="Failed to create key-pair for user: {},"
++ " project:{} with error: {}"
++ .format(username, projectname, e), request=query)
++
++ if response.status_code >= 400:
++ raise CoprKeygenRequestError(
++ msg="Failed to create key-pair for user: {}, project:{}, status_code: {}, response: {}"
++ .format(username, projectname, response.status_code, response.text),
++ request=query, response=response)
++
++class Signatrust(Signer):
++ sign_cmd = "/usr/local/bin/client"
++ prefix = ""
++ signatrust_host = ""
++ signatrust_token = ""
++
++ @classmethod
++ def get_prefix(cls):
++ """
++ Get prefix of the user
++
++ As in copr, we set key attr with visibility=private
++ These keys' name were prefixed by user's email like:
++ tommylikehu@gmail.com:mywaaagh_admin_test
++ """
++ headers = {
++ "accept": "application/json",
++ "Authorization": cls.signatrust_token
++ }
++ try:
++ r = requests.get("{}/api/v1/users/info".format(cls.signatrust_host), headers=headers).json()
++ cls.prefix = r.get("email")
++ except Exception as e:
++ raise CoprKeygenRequestError(
++ msg="Failed to get userinfo", request="/api/v1/users/info")
++
++ @classmethod
++ def get_key_name(cls, username, projectname, prefix=True):
++ """
++ copr key_name rule in signatrust:
++ <user prefix>:<user_name>_<project_name>
++ """
++ if not cls.prefix:
++ cls.get_prefix()
++ if prefix:
++ return "{}:{}_{}".format(cls.prefix, username, projectname)
++ return "{}_{}".format(username, projectname)
++
++ @classmethod
++ def get_pubkey(cls, username, projectname, log, sign_domain, outfile=None):
++ """
++ get public key
++
++ https://domain:port/api/v1/keys/<key_name>/public_key
++ """
++ if not cls.prefix:
++ cls.get_prefix()
++
++ headers = {
++ "accept": "application/json",
++ "Authorization": cls.signatrust_token
++ }
++
++ key_name = cls.get_key_name(username, projectname)
++ url = "{}/api/v1/keys/{}/public_key".format(cls.signatrust_host, key_name)
+ try:
+- _sign_one(rpm, create_gpg_email(username, projectname, opts.sign_domain),
+- hashtype, log)
+- log.info("signed rpm: %s", rpm)
++ r = requests.get(url, headers=headers)
++ except Exception as e:
++ raise CoprKeygenRequestError(
++ msg="Failed to get public_key", request="/api/v1/keys/{}/public_key".format(key_name), response=r)
+
+- except CoprSignError as e:
+- log.exception("failed to sign rpm: %s", rpm)
+- errors.append((rpm, e))
++ if r.status_code == 404:
++ raise CoprSignNoKeyError(
++ "There are no gpg keys for user {} in keyring".format(username),
++ return_code=r.status_code,
++ cmd="GET {}".format(url), stdout="", stderr="")
++ elif r.status_code >= 400:
++ raise CoprKeygenRequestError(
++ msg="Failed to get user pubkey\n",
++ request="/api/v1/keys/{}/public_key".format(key_name), response=r)
++
++ if outfile:
++ with open(outfile, "wb") as handle:
++ handle.write(r.content)
++
++ return r.content
++
++ @classmethod
++ def sign_rpms_in_dir(cls, username, projectname, path, chroot, opts, log):
++ """batch sign rpms"""
++ if not cls.prefix:
++ cls.get_prefix()
++
++ if not cls._key_existed(username, projectname, opts):
++ cls.create_user_keys(username, projectname, opts)
++
++ # when we migrate copr keys into signatrust
++ # we fellow the rules:
++ # key_name = <user_email>:<user_name>_<project_name>
++ cmd = [cls.sign_cmd, "-c", "/etc/signatrust.toml", "add", "--file-type", "rpm", "--key-type", "pgp", "--key-name", cls.get_key_name(username, projectname), path]
++
++ returncode, stdout, stderr = cls.call_sign_bin(cmd, log)
++ if returncode != 0:
++ raise CoprSignError(
++ msg="Failed to sign rpms\n"
++ "sign stdout: {}\n sign stderr: {}\n".format(stdout, stderr),
++ return_code=returncode,
++ cmd=cmd, stdout=stdout, stderr=stderr)
+
+- if errors:
+- raise CoprSignError("Rpm sign failed, affected rpms: {}"
+- .format([err[0] for err in errors]))
++ if stderr:
++ # signatrust client will print error message for one rpm per line
++ failed_list = re.findall(r"failed to sign file (.*.rpm) due to error", stderr)
++
++ if failed_list:
++ failed_rpms = " ".join(map(os.path.basename, failed_list))
++ log.exception("failed to sign rpm: %s".format(failed_rpms))
++ raise CoprSignError("Rpm sign failed, affected rpms: {}"
++ .format(failed_rpms))
++
++ @classmethod
++ def unsign_rpms_in_dir(cls, path, opts, log):
++ """
++ signatrust will replace the signature infomation defaultly,
++ so there is no need to unsign, just return
++ """
++ return
+
++ @classmethod
++ def _key_existed(cls, username, projectname, opts):
++ """
++ check keyname existence
+
+-def create_user_keys(username, projectname, opts, try_indefinitely=False):
+- """
+- Generate a new key-pair at sign host
++ HEAD /api/v1/keys/
++ """
++ if not cls.prefix:
++ cls.get_prefix()
+
+- :param username:
+- :param projectname:
+- :param opts: backend config
++ key_name = cls.get_key_name(username, projectname, prefix=False)
+
+- :return: None
+- """
+- data = {
+- "name_real": "{}_{}".format(username, projectname),
+- "name_email": create_gpg_email(username, projectname, opts.sign_domain)
+- }
+-
+- log = get_redis_logger(opts, "sign", "actions")
+- keygen_url = "http://{}/gen_key".format(opts.keygen_host)
+- query = dict(url=keygen_url, data=data, method="post")
+- try:
+- request = SafeRequest(log=log, try_indefinitely=try_indefinitely)
+- response = request.send(**query)
+- except Exception as e:
+- raise CoprKeygenRequestError(
+- msg="Failed to create key-pair for user: {},"
+- " project:{} with error: {}"
+- .format(username, projectname, e), request=query)
+-
+- if response.status_code >= 400:
+- raise CoprKeygenRequestError(
+- msg="Failed to create key-pair for user: {}, project:{}, status_code: {}, response: {}"
+- .format(username, projectname, response.status_code, response.text),
+- request=query, response=response)
+-
+-
+-def _unsign_one(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()
+-
+- if handle.returncode != 0:
+- err = CoprSignError(
+- msg="Failed to unsign {}".format(path),
+- return_code=handle.returncode,
+- cmd=cmd, stdout=stdout, stderr=stderr)
+-
+- raise err
+-
+- return stdout, stderr
+-
+-
+-def unsign_rpms_in_dir(path, opts, log):
+- """
+- :param path: directory with rpms to be signed
+- :param Munch opts: backend config
+- :type log: logging.Logger
+- :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")
+- ]
++ query = {
++ "name": key_name,
++ "visibility": "private"
++ }
+
+- if not rpm_list:
+- return
++ headers = {
++ "accept": "application/json",
++ "Authorization": cls.signatrust_token
++ }
+
+- errors = [] # tuples (rpm_filepath, exception)
+- for rpm in rpm_list:
+ try:
+- _unsign_one(rpm)
+- log.info("unsigned rpm: %s", rpm)
+-
+- except CoprSignError as e:
+- log.exception("failed to unsign rpm: %s", rpm)
+- errors.append((rpm, e))
++ res = requests.head("{}/api/v1/keys/name_identical".format(opts.signatrust_host), headers=headers, params=query)
++ except Exception as e:
++ raise CoprKeygenRequestError(
++ msg="Failed to check key existence", request="/api/v1/keys/name_identical", response=res)
++
++ # signatrust return 200 means key name available
++ if res.status_code == 200:
++ return False
++ # signatrust return 409 means key name redundant
++ elif res.status_code == 409:
++ return True
++ else:
++ raise CoprKeygenRequestError(
++ msg="Failed to check key existence", request="/api/v1/keys/name_identical", response=res)
++
++ @classmethod
++ def create_user_keys(cls, username, projectname, opts, try_indefinitely=False):
++ """
++ create user key pair
++
++ POST /api/v1/keys/
++ """
++ if not cls.prefix:
++ cls.get_prefix()
++
++ if cls._key_existed(username, projectname, opts):
++ return
++ time_format = "%Y-%m-%d %H:%M:%S%z"
++ expire = datetime.now(datetime.now(timezone.utc).astimezone().tzinfo) + timedelta(days=opts.signatrust_key_expire)
++ data = {
++ "name": cls.get_key_name(username, projectname, prefix=False),
++ "description": "gpg key to sign rpms in {}/{}".format(username, projectname),
++ "key_type": "pgp",
++ "visibility": "private", # we use private key type for those key will not be seen by other users
++ "attributes": {
++ "digest_algorithm": "sha2_256",
++ "key_type": "rsa",
++ "key_length": "2048",
++ "email": "{}".format(create_gpg_email(username, projectname, opts.sign_domain)),
++ },
++ "expire_at": datetime.strftime(expire, time_format)
++ }
++
++ headers = {
++ "content-type": "application/json",
++ "accept": "application/json",
++ "Authorization": opts.signatrust_token
++ }
+
+- if errors:
+- raise CoprSignError("Rpm unsign failed, affected rpms: {}"
+- .format([err[0] for err in errors]))
++ try:
++ res = requests.post("{}/api/v1/keys/".format(opts.signatrust_host), headers=headers, json=data)
++ except Exception as e:
++ raise CoprKeygenRequestError(
++ msg="Failed to get userinfo", request="/api/v1/keys/", response=res)
++
++ 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())
+