Skip to content

Commit

Permalink
IngressPerAppRequirer: retry when IP is not available
Browse files Browse the repository at this point in the history
  • Loading branch information
weiiwang01 committed Nov 24, 2024
1 parent f8b46b2 commit 26e89bc
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 14 deletions.
22 changes: 18 additions & 4 deletions lib/charms/traefik_k8s/v2/ingress.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def _on_ingress_revoked(self, event: IngressPerAppRevokedEvent):

# Increment this PATCH version before using `charmcraft publish-lib` or reset
# to 0 if you are raising the major API version
LIBPATCH = 14
LIBPATCH = 15

PYDEPS = ["pydantic"]

Expand Down Expand Up @@ -338,6 +338,10 @@ class DataValidationError(IngressError):
"""Raised when data validation fails on IPU relation data."""


class IpNotAvailableError(IngressError):
"""Raised when retrieving ip information from juju failed."""


class _IngressPerAppBase(Object):
"""Base class for IngressPerUnit interface classes."""

Expand Down Expand Up @@ -710,7 +714,12 @@ def __init__(

def _handle_relation(self, event):
# created, joined or changed: if we have auto data: publish it
self._publish_auto_data()
try:
self._publish_auto_data()
except IpNotAvailableError as e:
log.error(e)
event.defer()
return
if self.is_ready():
# Avoid spurious events, emit only when there is a NEW URL available
new_url = (
Expand All @@ -728,7 +737,12 @@ def _handle_relation_broken(self, event):

def _handle_upgrade_or_leader(self, event):
"""On upgrade/leadership change: ensure we publish the data we have."""
self._publish_auto_data()
try:
self._publish_auto_data()
except IpNotAvailableError as e:
log.error(e)
event.defer()
return

def is_ready(self):
"""The Requirer is ready if the Provider has sent valid data."""
Expand Down Expand Up @@ -794,7 +808,7 @@ def _publish_unit_data(
):
ip = str(bind_address)
else:
log.error("failed to retrieve ip information from juju")
raise IpNotAvailableError("failed to retrieve ip information from juju")

unit_databag = relation.data[self.unit]
try:
Expand Down
19 changes: 18 additions & 1 deletion tests/scenario/test_ingress_per_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
IngressRequirerUnitData,
)
from ops import CharmBase, Framework
from scenario import Context, Model, Mount, Relation, State
from scenario import Context, Model, Mount, Network, Relation, State

from tests.scenario._utils import create_ingress_relation
from tests.scenario.conftest import MOCK_LB_ADDRESS
Expand Down Expand Up @@ -188,6 +188,23 @@ def test_ingress_per_app_requirer_with_auto_data(host, ip, port, model, evt_name
}


@pytest.mark.parametrize("evt_name", ("joined", "changed"))
@pytest.mark.parametrize("leader", (True, False))
def test_ingress_per_app_requirer_retry_without_ip(model, evt_name, leader):
ipa = Relation("ingress")
state = State(
model=model,
leader=leader,
relations=[ipa],
networks={"ingress": Network(bind_addresses=[], egress_subnets=[], ingress_addresses=[])},
)
requirer_ctx = get_requirer_ctx("10.0.0.1", None, 80)
state_out = requirer_ctx.run(getattr(ipa, evt_name + "_event"), state)

assert len(state_out.deferred) == 1
assert state_out.deferred[0].name == f"ingress_relation_{evt_name}"


def test_ingress_per_app_cleanup_on_remove(model, traefik_ctx, traefik_container):
"""Check that config file is removed when a relation is."""
ipa = create_ingress_relation()
Expand Down
12 changes: 3 additions & 9 deletions tests/unit/test_lib_per_app_requires.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
IngressPerAppReadyEvent,
IngressPerAppRequirer,
IngressPerAppRevokedEvent,
IpNotAvailableError,
)
from ops.charm import CharmBase
from ops.testing import Harness
Expand Down Expand Up @@ -157,12 +158,5 @@ def test_ipa_events_juju_binding_failure(self):
self.harness.begin_with_initial_hooks()
self.rel_id = self.harness.add_relation("ingress", "traefik-app")
self.harness.add_relation_unit(self.rel_id, "traefik-app/0")
self.harness.charm.ipa.provide_ingress_requirements(port=80)

self.assertEqual(
# None is default so it gets omitted
self.harness.get_relation_data(self.rel_id, "ipa-requirer/0").get(
"ip", "<OMITTED>"
),
"<OMITTED>",
)
with self.assertRaises(IpNotAvailableError):
self.harness.charm.ipa.provide_ingress_requirements(port=80)

0 comments on commit 26e89bc

Please sign in to comment.