commit cc4543be44255aacd26541ad7f358305e0b329a8
Author: vzhestkov <vzhestkov@suse.com>
Date:   Fri Jun 7 16:32:49 2024 +0200

    Implement timeouts on sync of deb repos

diff --git a/python/spacewalk/common/repo.py b/python/spacewalk/common/repo.py
index 418837f2622..bd173058921 100644
--- a/python/spacewalk/common/repo.py
+++ b/python/spacewalk/common/repo.py
@@ -76,7 +76,7 @@ class DpkgRepo:
                 key = "/".join(parse.urlparse(self.__repo._url).path.strip("/").split("/")[-2:] + [key])
             return self[key]
 
-    def __init__(self, url: str, proxies: dict = None, gpg_verify: bool = True):
+    def __init__(self, url: str, proxies: dict = None, gpg_verify: bool = True, timeout: int = None):
         self._url = url
         self._flat_checked: typing.Optional[int] = None
         self._flat: bool = False
@@ -84,6 +84,7 @@ class DpkgRepo:
         self._release = DpkgRepo.EntryDict(self)
         self.proxies = proxies
         self.gpg_verify = gpg_verify
+        self.timeout = timeout
 
     def append_index_file(self, index_file: str) -> str:
         """
@@ -123,7 +124,11 @@ class DpkgRepo:
                             "File not found: {}".format(
                                 packages_url.replace("file://", "")), exc_info=True)
                 else:
-                    resp = requests.get(packages_url, proxies=self.proxies)
+                    resp = requests.get(
+                        packages_url,
+                        proxies=self.proxies,
+                        timeout=self.timeout,
+                    )
                     if resp.status_code == http.HTTPStatus.OK:
                         self._pkg_index = cnt_fname, resp.content
                         break
@@ -249,7 +254,11 @@ class DpkgRepo:
                 )
                 out = process.communicate(response.content, timeout=90)
             else:
-                signature_response = requests.get(self._get_parent_url(response.url, 1, "Release.gpg"), proxies=self.proxies)
+                signature_response = requests.get(
+                    self._get_parent_url(response.url, 1, "Release.gpg"),
+                    proxies=self.proxies,
+                    timeout=self.timeout,
+                )
                 if signature_response.status_code != http.HTTPStatus.OK:
                     return False
                 else:
@@ -352,9 +361,17 @@ class DpkgRepo:
     def _get_release_index_from_http(self) -> typing.Dict[str, "DpkgRepo.ReleaseEntry"]:
         # InRelease files take precedence per uyuni-rfc 00057-deb-repo-sync-gpg-check
         logging.debug("Fetching release file from local http: {}".format(self._url))
-        resp = requests.get(self._get_parent_url(self._url, 2, "InRelease"), proxies=self.proxies)
+        resp = requests.get(
+            self._get_parent_url(self._url, 2, "InRelease"),
+            proxies=self.proxies,
+            timeout=self.timeout,
+        )
         if resp.status_code != http.HTTPStatus.OK:
-            resp = requests.get(self._get_parent_url(self._url, 2, "Release"), proxies=self.proxies)
+            resp = requests.get(
+                self._get_parent_url(self._url, 2, "Release"),
+                proxies=self.proxies,
+                timeout=self.timeout,
+            )
 
         try:
             if resp.status_code not in [
@@ -378,9 +395,17 @@ class DpkgRepo:
             self._release = self._parse_release_index(resp.content.decode("utf-8"))
 
             if not self._release and self.is_flat():
-                resp = requests.get(self._get_parent_url(self._url, 0, "InRelease"), proxies=self.proxies)
+                resp = requests.get(
+                    self._get_parent_url(self._url, 0, "InRelease"),
+                    proxies=self.proxies,
+                    timeout=self.timeout,
+                )
                 if resp.status_code != http.HTTPStatus.OK:
-                    resp = requests.get(self._get_parent_url(self._url, 0, "Release"), proxies=self.proxies)
+                    resp = requests.get(
+                        self._get_parent_url(self._url, 0, "Release"),
+                        proxies=self.proxies,
+                        timeout=self.timeout,
+                    )
 
                 if resp.status_code == http.HTTPStatus.OK:
                     if self.gpg_verify and not self._has_valid_gpg_signature(resp.url, resp):
diff --git a/python/spacewalk/satellite_tools/repo_plugins/deb_src.py b/python/spacewalk/satellite_tools/repo_plugins/deb_src.py
index 4a07f21d67e..131cf63396f 100644
--- a/python/spacewalk/satellite_tools/repo_plugins/deb_src.py
+++ b/python/spacewalk/satellite_tools/repo_plugins/deb_src.py
@@ -93,10 +93,12 @@ class DebRepo:
         proxy_pass="",
         gpg_verify=True,
         channel_label=None,
+        timeout=None,
     ):
         self.url = url
         parts = url.rsplit('/dists/', 1)
         self.base_url = [parts[0]]
+        self.timeout = timeout
 
         parsed_url = urlparse.urlparse(url)
         query = urlparse.parse_qsl(parsed_url.query)
@@ -181,7 +183,9 @@ class DebRepo:
 
         :return:
         """
-        if not repo.DpkgRepo(self.url, self._get_proxies(), self.gpg_verify).verify_packages_index():
+        if not repo.DpkgRepo(
+            self.url, self._get_proxies(), self.gpg_verify, self.timeout
+        ).verify_packages_index():
             raise repo.GeneralRepoException("Package index checksum failure")
 
     def _get_proxies(self):
@@ -214,8 +218,13 @@ class DebRepo:
             return filename
         for _ in range(0, RETRIES):
             try:
-                data = requests.get(url, proxies=self._get_proxies(), cert=(self.sslclientcert, self.sslclientkey),
-                                    verify=self.sslcacert)
+                data = requests.get(
+                    url,
+                    proxies=self._get_proxies(),
+                    cert=(self.sslclientcert, self.sslclientkey),
+                    verify=self.sslcacert,
+                    timeout=self.timeout,
+                )
                 if not data.ok:
                     return ''
                 filename = os.path.join(self.basecachedir, os.path.basename(urlparse.urlparse(url).path))
@@ -330,13 +339,32 @@ class ContentSource:
             self.reponame = "".join([x if x.isalnum() else "_" for x in self.name])
             self.channel_label = channel_label
 
+            # configure network connection
+            try:
+                # bytes per second
+                self.minrate = int(CFG.REPOSYNC_MINRATE)
+            except ValueError:
+                self.minrate = 1000
+            try:
+                # seconds
+                self.timeout = int(CFG.REPOSYNC_TIMEOUT)
+            except ValueError:
+                self.timeout = 300
+
             # SUSE vendor repositories belongs to org = NULL
             # The repository cache root will be "/var/cache/rhn/reposync/REPOSITORY_LABEL/"
             root = os.path.join(CACHE_DIR, str(org or "NULL"), self.reponame)
-            self.repo = DebRepo(url, root,
-                                os.path.join(CFG.MOUNT_POINT, CFG.PREPENDED_DIR, self.org, 'stage'),
-                                self.proxy_addr, self.proxy_user, self.proxy_pass, gpg_verify=not(insecure),
-                                channel_label=channel_label)
+            self.repo = DebRepo(
+                url,
+                root,
+                os.path.join(CFG.MOUNT_POINT, CFG.PREPENDED_DIR, self.org, "stage"),
+                self.proxy_addr,
+                self.proxy_user,
+                self.proxy_pass,
+                gpg_verify=not (insecure),
+                channel_label=channel_label,
+                timeout=self.timeout,
+            )
             self.repo.verify()
 
             self.num_packages = 0
@@ -347,18 +375,6 @@ class ContentSource:
             if query:
                 self.authtoken = query
 
-            # configure network connection
-            try:
-                # bytes per second
-                self.minrate = int(CFG.REPOSYNC_MINRATE)
-            except ValueError:
-                self.minrate = 1000
-            try:
-                # seconds
-                self.timeout = int(CFG.REPOSYNC_TIMEOUT)
-            except ValueError:
-                self.timeout = 300
-
     def get_md_checksum_type(self):
         pass
 
