Skip to content

Commit 9322222

Browse files
If Koji session becomes invalid, open a new one and try again (packit#2478)
If Koji session becomes invalid, open a new one and try again Fixes packit/packit-service#2608. Reviewed-by: Maja Massarini Reviewed-by: Nikola Forró Reviewed-by: Laura Barcziová Reviewed-by: Matej Focko
2 parents f80fb13 + 3a500fb commit 9322222

File tree

3 files changed

+141
-78
lines changed

3 files changed

+141
-78
lines changed

packit/utils/koji_helper.py

Lines changed: 47 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import logging
55
from datetime import date, datetime
6-
from typing import Optional, Union
6+
from typing import Callable, Optional, Union
77

88
import koji
99
from specfile.changelog import ChangelogEntry
@@ -14,18 +14,54 @@
1414
logger = logging.getLogger(__name__)
1515

1616

17-
class KojiHelper:
18-
"""
19-
Class for querying Koji.
17+
class SessionWrapper:
18+
def __init__(self) -> None:
19+
self.session = self._open_session()
20+
21+
def __getattr__(self, name: str) -> Callable:
22+
if name in self.__dict__:
23+
return self.__dict__[name]
24+
return self._wrap(getattr(self.session, name))
25+
26+
def _open_session(self) -> koji.ClientSession:
27+
return koji.ClientSession(baseurl=KOJI_BASEURL)
28+
29+
def _wrap(self, call: Callable) -> Callable:
30+
call_name = f"{call._VirtualMethod__name}()" # type: ignore[attr-defined]
31+
32+
def wrapper(*args, **kwargs):
33+
exceptions = []
34+
while True:
35+
try:
36+
return call(*args, **kwargs)
37+
except koji.ActionNotAllowed as e: # noqa: PERF203
38+
if (type(e), e.faultCode, e.args) in exceptions:
39+
# break the loop if the same exception has already occurred
40+
raise
41+
exceptions.append((type(e), e.faultCode, e.args))
42+
logger.debug(
43+
f"{call_name} requires authenticated Koji session, logging in",
44+
)
45+
self.session.gssapi_login()
46+
continue
47+
except koji.AuthError as e:
48+
if (type(e), e.faultCode, e.args) in exceptions:
49+
# break the loop if the same exception has already occurred
50+
raise
51+
exceptions.append((type(e), e.faultCode, e.args))
52+
logger.debug(
53+
f"Koji session authentication error during {call_name}: {e};"
54+
"opening new session",
55+
)
56+
self.session = self._open_session()
57+
continue
58+
59+
return wrapper
2060

21-
Attributes:
22-
session: Koji client session.
23-
"""
2461

25-
def __init__(self, session: Optional[koji.ClientSession] = None) -> None:
26-
self.session = (
27-
session if session is not None else koji.ClientSession(baseurl=KOJI_BASEURL)
28-
)
62+
class KojiHelper:
63+
def __init__(self) -> None:
64+
self.session = SessionWrapper()
2965

3066
def get_builds(self, package: str, since: datetime) -> list[dict]:
3167
"""
@@ -306,12 +342,6 @@ def create_sidetag(self, dist_git_branch: str) -> Optional[dict]:
306342
if not (build_tag := target.get("build_tag_name")):
307343
logger.debug(f"Failed to get build tag for {dist_git_branch}")
308344
return None
309-
if not self.session.logged_in:
310-
try:
311-
self.session.gssapi_login()
312-
except Exception as e:
313-
logger.debug(f"Authentication failed: {e}")
314-
return None
315345
try:
316346
info = self.session.createSideTag(build_tag)
317347
except Exception as e:
@@ -328,12 +358,6 @@ def remove_sidetag(self, sidetag: str) -> None:
328358
Args:
329359
sidetag: Sidetag name.
330360
"""
331-
if not self.session.logged_in:
332-
try:
333-
self.session.gssapi_login()
334-
except Exception as e:
335-
logger.debug(f"Authentication failed: {e}")
336-
return
337361
try:
338362
self.session.removeSideTag(sidetag)
339363
except Exception as e:
@@ -352,12 +376,6 @@ def tag_build(self, nvr: str, tag: str) -> Optional[str]:
352376
Returns:
353377
Task ID if tagging was successfully requested else None.
354378
"""
355-
if not self.session.logged_in:
356-
try:
357-
self.session.gssapi_login()
358-
except Exception as e:
359-
logger.debug(f"Authentication failed: {e}")
360-
return None
361379
try:
362380
task_id = self.session.tagBuild(tag, nvr)
363381
except Exception as e:
@@ -375,12 +393,6 @@ def untag_build(self, nvr: str, tag: str) -> None:
375393
nvr: NVR of the build.
376394
tag: Tag name.
377395
"""
378-
if not self.session.logged_in:
379-
try:
380-
self.session.gssapi_login()
381-
except Exception as e:
382-
logger.debug(f"Authentication failed: {e}")
383-
return
384396
try:
385397
self.session.untagBuild(tag, nvr, strict=True)
386398
except Exception as e:

tests/integration/test_create_update.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
from packit import distgit
1010
from packit.exceptions import PackitBodhiException
11-
from packit.utils.koji_helper import KojiHelper
11+
from packit.utils.koji_helper import KojiHelper, SessionWrapper
1212

1313

1414
@pytest.fixture()
@@ -275,6 +275,8 @@ def test_basic_bodhi_update(
275275
u, d, api = api_instance
276276
flexmock(api).should_receive("init_kerberos_ticket").at_least().once()
277277

278+
flexmock(SessionWrapper).should_receive("_open_session").and_return()
279+
278280
flexmock(KojiHelper).should_receive("get_candidate_tag").and_return(
279281
"f30-updates-candidate",
280282
)
@@ -329,6 +331,8 @@ def validate_save(kwargs, expected_kwargs):
329331
api.config.fas_user = "packit"
330332
flexmock(api).should_receive("init_kerberos_ticket").at_least().once()
331333

334+
flexmock(SessionWrapper).should_receive("_open_session").and_return()
335+
332336
flexmock(KojiHelper).should_receive("get_candidate_tag").and_return(
333337
"f30-updates-candidate",
334338
)

0 commit comments

Comments
 (0)