Skip to content

Commit

Permalink
rpmbuild, frontend: activate Red Hat subscription on demand
Browse files Browse the repository at this point in the history
  • Loading branch information
FrostyX committed Oct 4, 2024
1 parent 35a23e6 commit bc68241
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 10 deletions.
30 changes: 21 additions & 9 deletions backend/copr_backend/background_worker_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import json
import shlex

from subprocess import PIPE
from tempfile import NamedTemporaryFile
from datetime import datetime
from packaging import version
from cachetools.func import ttl_cache
Expand Down Expand Up @@ -242,21 +244,31 @@ def _check_copr_builder(self):
raise BuildRetry("Minimum version for builder is {}"
.format(MIN_BUILDER_VERSION))

def _check_mock_config(self):
config = "/etc/mock/{}.cfg".format(self.job.chroot)
command = "/usr/bin/test -f " + config
if self.job.chroot == "srpm-builds":
return
if self.ssh.run(command):
raise BuildRetry("Chroot config {} not found".format(config))

def _check_vm(self):
"""
Check that the VM is OK to start the build
"""
self.log.info("Checking that builder machine is OK")
self._check_copr_builder()
self._check_mock_config()

# We could open `self.job.backend_log` for appending and use it for
# stdout but I don't want to open the file for so long. This command
# can take 10 minutes to finish so I am afraid of data loss in case
# someone writes to the log in the meantime.
#
# Either way, the output won't be live and will appear only after this
# command finishes. Making it live is nontrivial but we have a good
# code for doing so in `resallocserver.manager.run_command`. Praiskup
# plans to generalize it into a separate package that we could
# eventually use here.
with NamedTemporaryFile(prefix="copr-builder-ready-") as tmp:
cmd = "copr-builder-ready " + self.job.chroot
rc = self.ssh.run(cmd, stdout=tmp, stderr=PIPE)
tmp.seek(0)
out = tmp.read().decode("utf-8")
self.log.info(out)
if rc:
raise BuildRetry("Builder wasn't ready, trying a new one")

def _fill_build_info_file(self):
"""
Expand Down
100 changes: 100 additions & 0 deletions rpmbuild/bin/copr-builder-ready
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#! /usr/bin/python3

"""
Final checks that the builder machine is ready to be used
Everything printed to STDOUT will be redirected to the copr-backend logs,
STDERR will be ignored.
"""

import os
import sys
import time
from fnmatch import fnmatch
from subprocess import run, PIPE
from copr_rpmbuild.config import Config


def check_mock_config(chroot):
"""
Does the mock config for this chroot exist?
"""
if chroot == "srpm-builds":
return

config = "/etc/mock/{}.cfg".format(chroot)
if os.path.isfile(config):
return

print("Chroot config {} not found".format(config))
sys.exit(1)


def subscription_required(chroot):
"""
Is subscription required for this task?
"""
config = Config()
config.load_config()

for pattern in config.rhsm:
if fnmatch(chroot, pattern):
return True
return False


def active_subscription():
"""
Is subscription active on this system?
"""
# This implementation requires root privileges which we fortunately have
# when calling this script. In case this function needs to be re-written to
# be used under a normal user, it could be done by checking the existence of
# the `/etc/pki/consumer/cert.pem` file. However, it will break in the
# following corner cases:
# - The system is registered and then halted for a long time and then
# booted again after the entitlement is no longer valid
# - The system is unregistered from the server (but not from the client
# itself), making all the client files stale
cmd = ["subscription-manager", "status"]
return run(cmd, stdout=PIPE, stderr=PIPE, check=False) == 0


def wait_for_subscription(timeout=600):
"""
Wait until this system has an active subscription
Activating Red Hat subscription may take a lot of time and historically, the
subscription service used to be unreliable, so we should wait for the
subscription only when necessary.
"""
start = time.time()
attempt = 1
while True:
print("Checking Red Hat subscription (attempt #{0})".format(attempt))
if active_subscription():
print("Red Hat subscription active")
return
if time.time() > start + timeout:
print("Waiting for Red Hat subscription timeouted!")
sys.exit(1)
time.sleep(30)
attempt += 1


def main():
"""
The entrypoint for this script
"""
try:
chroot = sys.argv[1]
check_mock_config(chroot)
if subscription_required(chroot):
wait_for_subscription()
except RuntimeError as ex:
print(ex)
sys.exit(1)


if __name__ == "__main__":
main()
5 changes: 5 additions & 0 deletions rpmbuild/copr-rpmbuild.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,8 @@
# cute
# multiline
# snippet
#
# Chroots that require active Red Hat subscription
# rhsm:
# - rhel-*
# - epel-*
3 changes: 3 additions & 0 deletions rpmbuild/copr_rpmbuild/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ class Config:
"""
Configuration class for copr-rpmbuild
"""

def __init__(self):
self.tags_to_mock_snippet = []
self.rhsm = []

def load_config(self):
"""
Expand All @@ -27,3 +29,4 @@ def load_config(self):
pass

self.tags_to_mock_snippet = config_data.get("tags_to_mock_snippet", [])
self.rhsm = config_data.get("rhsm", [])
9 changes: 8 additions & 1 deletion rpmbuild/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import shutil
import pprint
import shlex
from six.moves.urllib.parse import urlparse, urljoin, urlencode

from copr_common.request import SafeRequest, RequestError
from copr_rpmbuild import providers
Expand All @@ -25,7 +26,8 @@
locate_srpm,
package_version,
)
from six.moves.urllib.parse import urlparse, urljoin, urlencode
from copr_rpmbuild.config import Config
from copr_rpmbuild.rhsm import subscription_required, wait_for_subscription

log = logging.getLogger(__name__)
log.setLevel(logging.INFO)
Expand Down Expand Up @@ -256,6 +258,11 @@ def build_rpm(args, config):
task = get_task(args, config, build_config_url_path, task_id)
log_task(task)

copr_rpmbuild_config = Config()
copr_rpmbuild_config.load_config()
if subscription_required(task, copr_rpmbuild_config):
wait_for_subscription()

try:
source_json = {
"clone_url": task["git_repo"],
Expand Down

0 comments on commit bc68241

Please sign in to comment.