Merge "[ops-sunbeam] Allow post-init to throw status exceptions" into main

This commit is contained in:
Zuul
2025-02-25 00:50:04 +00:00
committed by Gerrit Code Review
2 changed files with 36 additions and 10 deletions

View File

@@ -25,6 +25,9 @@ from typing import (
) )
import ops_sunbeam.tracing as sunbeam_tracing import ops_sunbeam.tracing as sunbeam_tracing
from ops_sunbeam.guard import (
BaseStatusExceptionError,
)
if TYPE_CHECKING: if TYPE_CHECKING:
from ops_sunbeam.charm import ( from ops_sunbeam.charm import (
@@ -101,5 +104,20 @@ class PostInitMeta(type):
def __call__(cls, *args, **kw): def __call__(cls, *args, **kw):
"""Call __post_init__ after __init__.""" """Call __post_init__ after __init__."""
instance = super().__call__(*args, **kw) instance = super().__call__(*args, **kw)
instance.__post_init__() try:
instance.__post_init__()
except BaseStatusExceptionError as e:
# Allow post init to raise an ops_sunbeam status
# exception without causing the charm to error.
# This status will be collected and set on the
# unit.
# import here to avoid circular import
from ops_sunbeam.charm import (
OSBaseOperatorCharm,
)
if isinstance(instance, OSBaseOperatorCharm):
instance.status.set(e.to_status())
else:
raise e
return instance return instance

View File

@@ -20,9 +20,11 @@ from contextlib import (
contextmanager, contextmanager,
) )
from ops.model import ( from ops import (
ActiveStatus,
BlockedStatus, BlockedStatus,
MaintenanceStatus, MaintenanceStatus,
StatusBase,
WaitingStatus, WaitingStatus,
) )
@@ -43,27 +45,33 @@ class GuardExceptionError(Exception):
class BaseStatusExceptionError(Exception): class BaseStatusExceptionError(Exception):
"""Charm is blocked.""" """Charm is blocked."""
def __init__(self, msg): status_type: type[StatusBase] = ActiveStatus
def __init__(self, msg: str):
super().__init__(msg)
self.msg = msg self.msg = msg
super().__init__(self.msg)
def to_status(self):
"""Convert the exception to an ops status."""
return self.status_type(self.msg)
class BlockedExceptionError(BaseStatusExceptionError): class BlockedExceptionError(BaseStatusExceptionError):
"""Charm is blocked.""" """Charm is blocked."""
pass status_type = BlockedStatus
class MaintenanceExceptionError(BaseStatusExceptionError): class MaintenanceExceptionError(BaseStatusExceptionError):
"""Charm is performing maintenance.""" """Charm is performing maintenance."""
pass status_type = MaintenanceStatus
class WaitingExceptionError(BaseStatusExceptionError): class WaitingExceptionError(BaseStatusExceptionError):
"""Charm is waiting.""" """Charm is waiting."""
pass status_type = WaitingStatus
@contextmanager @contextmanager
@@ -103,19 +111,19 @@ def guard(
logger.warning( logger.warning(
"Charm is blocked in section '%s' due to '%s'", section, str(e) "Charm is blocked in section '%s' due to '%s'", section, str(e)
) )
charm.status.set(BlockedStatus(e.msg)) charm.status.set(e.to_status())
except WaitingExceptionError as e: except WaitingExceptionError as e:
logger.warning( logger.warning(
"Charm is waiting in section '%s' due to '%s'", section, str(e) "Charm is waiting in section '%s' due to '%s'", section, str(e)
) )
charm.status.set(WaitingStatus(e.msg)) charm.status.set(e.to_status())
except MaintenanceExceptionError as e: except MaintenanceExceptionError as e:
logger.warning( logger.warning(
"Charm performing maintenance in section '%s' due to '%s'", "Charm performing maintenance in section '%s' due to '%s'",
section, section,
str(e), str(e),
) )
charm.status.set(MaintenanceStatus(e.msg)) charm.status.set(e.to_status())
except Exception as e: except Exception as e:
# something else went wrong # something else went wrong
if handle_exception: if handle_exception: