Merge "stop services when mandatory integrations are removed" into main
This commit is contained in:
commit
6011477f08
@ -35,6 +35,8 @@ import urllib
|
|||||||
from typing import (
|
from typing import (
|
||||||
List,
|
List,
|
||||||
Mapping,
|
Mapping,
|
||||||
|
Optional,
|
||||||
|
Set,
|
||||||
)
|
)
|
||||||
|
|
||||||
import ops.charm
|
import ops.charm
|
||||||
@ -234,9 +236,12 @@ class OSBaseOperatorCharm(ops.charm.CharmBase):
|
|||||||
):
|
):
|
||||||
raise sunbeam_guard.WaitingExceptionError("Leader not ready")
|
raise sunbeam_guard.WaitingExceptionError("Leader not ready")
|
||||||
|
|
||||||
def check_relation_handlers_ready(self):
|
def check_relation_handlers_ready(self, event: ops.framework.EventBase):
|
||||||
"""Check all relation handlers are ready."""
|
"""Check all relation handlers are ready."""
|
||||||
if not self.relation_handlers_ready():
|
not_ready_relations = self.get_mandatory_relations_not_ready(event)
|
||||||
|
if not_ready_relations:
|
||||||
|
logger.info(f"Relations {not_ready_relations} incomplete")
|
||||||
|
self.stop_services(not_ready_relations)
|
||||||
raise sunbeam_guard.WaitingExceptionError(
|
raise sunbeam_guard.WaitingExceptionError(
|
||||||
"Not all relations are ready"
|
"Not all relations are ready"
|
||||||
)
|
)
|
||||||
@ -252,7 +257,7 @@ class OSBaseOperatorCharm(ops.charm.CharmBase):
|
|||||||
def configure_unit(self, event: ops.framework.EventBase) -> None:
|
def configure_unit(self, event: ops.framework.EventBase) -> None:
|
||||||
"""Run configuration on this unit."""
|
"""Run configuration on this unit."""
|
||||||
self.check_leader_ready()
|
self.check_leader_ready()
|
||||||
self.check_relation_handlers_ready()
|
self.check_relation_handlers_ready(event)
|
||||||
self._state.unit_bootstrapped = True
|
self._state.unit_bootstrapped = True
|
||||||
|
|
||||||
def configure_app_leader(self, event):
|
def configure_app_leader(self, event):
|
||||||
@ -293,6 +298,10 @@ class OSBaseOperatorCharm(ops.charm.CharmBase):
|
|||||||
self.bootstrap_status.set(ActiveStatus())
|
self.bootstrap_status.set(ActiveStatus())
|
||||||
self.post_config_setup()
|
self.post_config_setup()
|
||||||
|
|
||||||
|
def stop_services(self, relation: Optional[Set[str]] = None) -> None:
|
||||||
|
"""Stop all running services."""
|
||||||
|
# Machine charms should implement this function if required.
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def supports_peer_relation(self) -> bool:
|
def supports_peer_relation(self) -> bool:
|
||||||
"""Whether the charm support the peers relation."""
|
"""Whether the charm support the peers relation."""
|
||||||
@ -354,22 +363,125 @@ class OSBaseOperatorCharm(ops.charm.CharmBase):
|
|||||||
# charms should handle the event if required
|
# charms should handle the event if required
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def relation_handlers_ready(self) -> bool:
|
def check_broken_relations(
|
||||||
"""Determine whether all relations are ready for use."""
|
self, relations: set, event: ops.framework.EventBase
|
||||||
|
) -> set:
|
||||||
|
"""Return all broken relations on given set of relations."""
|
||||||
|
broken_relations = set()
|
||||||
|
|
||||||
|
# Check for each relation if the event is gone away event.
|
||||||
|
# lazy import the events
|
||||||
|
# Note: Ceph relation not handled as there is no gone away event.
|
||||||
|
for relation in relations:
|
||||||
|
_is_broken = False
|
||||||
|
match relation:
|
||||||
|
case "database" | "api-database" | "cell-database":
|
||||||
|
from ops.charm import (
|
||||||
|
RelationBrokenEvent,
|
||||||
|
)
|
||||||
|
|
||||||
|
if isinstance(event, RelationBrokenEvent):
|
||||||
|
_is_broken = True
|
||||||
|
case "ingress-public" | "ingress-internal":
|
||||||
|
from charms.traefik_k8s.v2.ingress import (
|
||||||
|
IngressPerAppRevokedEvent,
|
||||||
|
)
|
||||||
|
|
||||||
|
if isinstance(event, IngressPerAppRevokedEvent):
|
||||||
|
_is_broken = True
|
||||||
|
case "identity-service":
|
||||||
|
from charms.keystone_k8s.v1.identity_service import (
|
||||||
|
IdentityServiceGoneAwayEvent,
|
||||||
|
)
|
||||||
|
|
||||||
|
if isinstance(event, IdentityServiceGoneAwayEvent):
|
||||||
|
_is_broken = True
|
||||||
|
case "amqp":
|
||||||
|
from charms.rabbitmq_k8s.v0.rabbitmq import (
|
||||||
|
RabbitMQGoneAwayEvent,
|
||||||
|
)
|
||||||
|
|
||||||
|
if isinstance(event, RabbitMQGoneAwayEvent):
|
||||||
|
_is_broken = True
|
||||||
|
case "certificates":
|
||||||
|
from charms.tls_certificates_interface.v1.tls_certificates import (
|
||||||
|
CertificateExpiredEvent,
|
||||||
|
)
|
||||||
|
|
||||||
|
if isinstance(event, CertificateExpiredEvent):
|
||||||
|
_is_broken = True
|
||||||
|
case "ovsdb-cms":
|
||||||
|
from charms.ovn_central_k8s.v0.ovsdb import (
|
||||||
|
OVSDBCMSGoneAwayEvent,
|
||||||
|
)
|
||||||
|
|
||||||
|
if isinstance(event, OVSDBCMSGoneAwayEvent):
|
||||||
|
_is_broken = True
|
||||||
|
case "identity-credentials":
|
||||||
|
from charms.keystone_k8s.v0.identity_credentials import (
|
||||||
|
IdentityCredentialsGoneAwayEvent,
|
||||||
|
)
|
||||||
|
|
||||||
|
if isinstance(event, IdentityCredentialsGoneAwayEvent):
|
||||||
|
_is_broken = True
|
||||||
|
case "identity-ops":
|
||||||
|
from charms.keystone_k8s.v0.identity_resource import (
|
||||||
|
IdentityOpsProviderGoneAwayEvent,
|
||||||
|
)
|
||||||
|
|
||||||
|
if isinstance(event, IdentityOpsProviderGoneAwayEvent):
|
||||||
|
_is_broken = True
|
||||||
|
case "gnocchi-db":
|
||||||
|
from charms.gnocchi_k8s.v0.gnocchi_service import (
|
||||||
|
GnocchiServiceGoneAwayEvent,
|
||||||
|
)
|
||||||
|
|
||||||
|
if isinstance(event, GnocchiServiceGoneAwayEvent):
|
||||||
|
_is_broken = True
|
||||||
|
case "ceph-access":
|
||||||
|
from charms.cinder_ceph_k8s.v0.ceph_access import (
|
||||||
|
CephAccessGoneAwayEvent,
|
||||||
|
)
|
||||||
|
|
||||||
|
if isinstance(event, CephAccessGoneAwayEvent):
|
||||||
|
_is_broken = True
|
||||||
|
case "dns-backend":
|
||||||
|
from charms.designate_bind_k8s.v0.bind_rndc import (
|
||||||
|
BindRndcGoneAwayEvent,
|
||||||
|
)
|
||||||
|
|
||||||
|
if isinstance(event, BindRndcGoneAwayEvent):
|
||||||
|
_is_broken = True
|
||||||
|
|
||||||
|
if _is_broken:
|
||||||
|
broken_relations.add(relation)
|
||||||
|
|
||||||
|
return broken_relations
|
||||||
|
|
||||||
|
def get_mandatory_relations_not_ready(
|
||||||
|
self, event: ops.framework.EventBase
|
||||||
|
) -> Set[str]:
|
||||||
|
"""Get mandatory relations that are not ready for use."""
|
||||||
ready_relations = {
|
ready_relations = {
|
||||||
handler.relation_name
|
handler.relation_name
|
||||||
for handler in self.relation_handlers
|
for handler in self.relation_handlers
|
||||||
if handler.mandatory and handler.ready
|
if handler.mandatory and handler.ready
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# The relation data for broken relations are not cleared during
|
||||||
|
# processing of gone away event. This is a temporary workaround
|
||||||
|
# to mark broken relations as not ready.
|
||||||
|
# The workaround can be removed once the below bug is resolved
|
||||||
|
# https://bugs.launchpad.net/juju/+bug/2024583
|
||||||
|
# https://github.com/canonical/operator/issues/940
|
||||||
|
broken_relations = self.check_broken_relations(ready_relations, event)
|
||||||
|
ready_relations = ready_relations.difference(broken_relations)
|
||||||
|
|
||||||
not_ready_relations = self.mandatory_relations.difference(
|
not_ready_relations = self.mandatory_relations.difference(
|
||||||
ready_relations
|
ready_relations
|
||||||
)
|
)
|
||||||
|
|
||||||
if len(not_ready_relations) != 0:
|
return not_ready_relations
|
||||||
logger.info(f"Relations {not_ready_relations} incomplete")
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def contexts(self) -> sunbeam_core.OPSCharmContexts:
|
def contexts(self) -> sunbeam_core.OPSCharmContexts:
|
||||||
"""Construct context for rendering templates."""
|
"""Construct context for rendering templates."""
|
||||||
@ -495,10 +607,19 @@ class OSBaseOperatorCharmK8S(OSBaseOperatorCharm):
|
|||||||
"Container service not ready"
|
"Container service not ready"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def stop_services(self, relation: Optional[Set[str]] = None) -> None:
|
||||||
|
"""Stop all running services."""
|
||||||
|
for ph in self.pebble_handlers:
|
||||||
|
if ph.pebble_ready:
|
||||||
|
logging.debug(
|
||||||
|
f"Stopping all services in container {ph.container_name}"
|
||||||
|
)
|
||||||
|
ph.stop_all()
|
||||||
|
|
||||||
def configure_unit(self, event: ops.framework.EventBase) -> None:
|
def configure_unit(self, event: ops.framework.EventBase) -> None:
|
||||||
"""Run configuration on this unit."""
|
"""Run configuration on this unit."""
|
||||||
self.check_leader_ready()
|
self.check_leader_ready()
|
||||||
self.check_relation_handlers_ready()
|
self.check_relation_handlers_ready(event)
|
||||||
self.open_ports()
|
self.open_ports()
|
||||||
self.init_container_services()
|
self.init_container_services()
|
||||||
self.check_pebble_handlers_ready()
|
self.check_pebble_handlers_ready()
|
||||||
|
@ -302,6 +302,20 @@ class PebbleHandler(ops.charm.Object):
|
|||||||
)
|
)
|
||||||
container.restart(service_name)
|
container.restart(service_name)
|
||||||
|
|
||||||
|
def stop_all(self) -> None:
|
||||||
|
"""Stop services in container."""
|
||||||
|
container = self.charm.unit.get_container(self.container_name)
|
||||||
|
if not container.can_connect():
|
||||||
|
logger.debug(
|
||||||
|
f"Container {self.container_name} not ready, no need to stop"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
services = container.get_services()
|
||||||
|
if services:
|
||||||
|
logger.debug("Stopping all services")
|
||||||
|
container.stop(*services.keys())
|
||||||
|
|
||||||
|
|
||||||
class ServicePebbleHandler(PebbleHandler):
|
class ServicePebbleHandler(PebbleHandler):
|
||||||
"""Container handler for containers which manage a service."""
|
"""Container handler for containers which manage a service."""
|
||||||
|
@ -554,6 +554,8 @@ class OVSDBCMSRequiresHandler(
|
|||||||
"""Handle OVSDB CMS change events."""
|
"""Handle OVSDB CMS change events."""
|
||||||
self.callback_f(event)
|
self.callback_f(event)
|
||||||
if self.mandatory:
|
if self.mandatory:
|
||||||
|
logger.debug("ovsdb-cms integration removed, stop services")
|
||||||
|
self.charm.stop_services({self.relation_name})
|
||||||
self.status.set(BlockedStatus("integration missing"))
|
self.status.set(BlockedStatus("integration missing"))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -300,6 +300,14 @@ class DBHandler(RelationHandler):
|
|||||||
getattr(db.on, f"{alias}_endpoints_changed"),
|
getattr(db.on, f"{alias}_endpoints_changed"),
|
||||||
self._on_database_updated,
|
self._on_database_updated,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Gone away events are not handled by the database interface library.
|
||||||
|
# So handle them in this class
|
||||||
|
self.framework.observe(
|
||||||
|
self.charm.on[self.relation_name].relation_broken,
|
||||||
|
self._on_database_relation_broken,
|
||||||
|
)
|
||||||
|
|
||||||
# this will be set to self.interface in parent class
|
# this will be set to self.interface in parent class
|
||||||
return db
|
return db
|
||||||
|
|
||||||
@ -315,6 +323,13 @@ class DBHandler(RelationHandler):
|
|||||||
logger.info(f"Received data: {display_data}")
|
logger.info(f"Received data: {display_data}")
|
||||||
self.callback_f(event)
|
self.callback_f(event)
|
||||||
|
|
||||||
|
def _on_database_relation_broken(
|
||||||
|
self, event: ops.framework.EventBase
|
||||||
|
) -> None:
|
||||||
|
"""Handle database gone away event."""
|
||||||
|
if self.mandatory:
|
||||||
|
self.status.set(BlockedStatus("integration missing"))
|
||||||
|
|
||||||
def get_relation_data(self) -> dict:
|
def get_relation_data(self) -> dict:
|
||||||
"""Load the data from the relation for consumption in the handler."""
|
"""Load the data from the relation for consumption in the handler."""
|
||||||
if len(self.interface.relations) > 0:
|
if len(self.interface.relations) > 0:
|
||||||
@ -1182,6 +1197,8 @@ class IdentityResourceRequiresHandler(RelationHandler):
|
|||||||
"""Handles provider_goneaway event."""
|
"""Handles provider_goneaway event."""
|
||||||
logger.info("Keystone provider not available process any requests")
|
logger.info("Keystone provider not available process any requests")
|
||||||
self.callback_f(event)
|
self.callback_f(event)
|
||||||
|
if self.mandatory:
|
||||||
|
self.status.set(BlockedStatus("integration missing"))
|
||||||
|
|
||||||
def _on_response_available(self, event) -> None:
|
def _on_response_available(self, event) -> None:
|
||||||
"""Handles response available events."""
|
"""Handles response available events."""
|
||||||
@ -1252,6 +1269,8 @@ class CeilometerServiceRequiresHandler(RelationHandler):
|
|||||||
"""Handle gone_away event."""
|
"""Handle gone_away event."""
|
||||||
logger.debug("Ceilometer service relation is departed/broken")
|
logger.debug("Ceilometer service relation is departed/broken")
|
||||||
self.callback_f(event)
|
self.callback_f(event)
|
||||||
|
if self.mandatory:
|
||||||
|
self.status.set(BlockedStatus("integration missing"))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ready(self) -> bool:
|
def ready(self) -> bool:
|
||||||
|
@ -41,6 +41,7 @@ class TestOSBaseOperatorCharm(test_utils.CharmTestCase):
|
|||||||
"""Charm test class setup."""
|
"""Charm test class setup."""
|
||||||
self.container_calls = test_utils.ContainerCalls()
|
self.container_calls = test_utils.ContainerCalls()
|
||||||
super().setUp(sunbeam_charm, self.PATCHES)
|
super().setUp(sunbeam_charm, self.PATCHES)
|
||||||
|
self.mock_event = mock.MagicMock()
|
||||||
self.harness = test_utils.get_harness(
|
self.harness = test_utils.get_harness(
|
||||||
test_charms.MyCharm,
|
test_charms.MyCharm,
|
||||||
test_charms.CHARM_METADATA,
|
test_charms.CHARM_METADATA,
|
||||||
@ -57,7 +58,12 @@ class TestOSBaseOperatorCharm(test_utils.CharmTestCase):
|
|||||||
|
|
||||||
def test_relation_handlers_ready(self) -> None:
|
def test_relation_handlers_ready(self) -> None:
|
||||||
"""Test relation handlers are ready."""
|
"""Test relation handlers are ready."""
|
||||||
self.assertTrue(self.harness.charm.relation_handlers_ready())
|
self.assertSetEqual(
|
||||||
|
self.harness.charm.get_mandatory_relations_not_ready(
|
||||||
|
self.mock_event
|
||||||
|
),
|
||||||
|
set(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestOSBaseOperatorCharmK8S(test_utils.CharmTestCase):
|
class TestOSBaseOperatorCharmK8S(test_utils.CharmTestCase):
|
||||||
@ -76,6 +82,7 @@ class TestOSBaseOperatorCharmK8S(test_utils.CharmTestCase):
|
|||||||
charm_config=test_charms.CHARM_CONFIG,
|
charm_config=test_charms.CHARM_CONFIG,
|
||||||
initial_charm_config=test_charms.INITIAL_CHARM_CONFIG,
|
initial_charm_config=test_charms.INITIAL_CHARM_CONFIG,
|
||||||
)
|
)
|
||||||
|
self.mock_event = mock.MagicMock()
|
||||||
self.harness.begin()
|
self.harness.begin()
|
||||||
self.addCleanup(self.harness.cleanup)
|
self.addCleanup(self.harness.cleanup)
|
||||||
|
|
||||||
@ -100,7 +107,12 @@ class TestOSBaseOperatorCharmK8S(test_utils.CharmTestCase):
|
|||||||
|
|
||||||
def test_relation_handlers_ready(self) -> None:
|
def test_relation_handlers_ready(self) -> None:
|
||||||
"""Test relation handlers are ready."""
|
"""Test relation handlers are ready."""
|
||||||
self.assertTrue(self.harness.charm.relation_handlers_ready())
|
self.assertSetEqual(
|
||||||
|
self.harness.charm.get_mandatory_relations_not_ready(
|
||||||
|
self.mock_event
|
||||||
|
),
|
||||||
|
set(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class _TestOSBaseOperatorAPICharm(test_utils.CharmTestCase):
|
class _TestOSBaseOperatorAPICharm(test_utils.CharmTestCase):
|
||||||
@ -113,6 +125,7 @@ class _TestOSBaseOperatorAPICharm(test_utils.CharmTestCase):
|
|||||||
self.container_calls = test_utils.ContainerCalls()
|
self.container_calls = test_utils.ContainerCalls()
|
||||||
|
|
||||||
super().setUp(sunbeam_charm, self.PATCHES)
|
super().setUp(sunbeam_charm, self.PATCHES)
|
||||||
|
self.mock_event = mock.MagicMock()
|
||||||
self.harness = test_utils.get_harness(
|
self.harness = test_utils.get_harness(
|
||||||
charm_to_test,
|
charm_to_test,
|
||||||
test_charms.API_CHARM_METADATA,
|
test_charms.API_CHARM_METADATA,
|
||||||
@ -345,11 +358,21 @@ class TestOSBaseOperatorAPICharm(_TestOSBaseOperatorAPICharm):
|
|||||||
# Add all mandatory relations and test relation_handlers_ready
|
# Add all mandatory relations and test relation_handlers_ready
|
||||||
db_rel_id = test_utils.add_base_db_relation(self.harness)
|
db_rel_id = test_utils.add_base_db_relation(self.harness)
|
||||||
test_utils.add_db_relation_credentials(self.harness, db_rel_id)
|
test_utils.add_db_relation_credentials(self.harness, db_rel_id)
|
||||||
self.assertFalse(self.harness.charm.relation_handlers_ready())
|
self.assertSetEqual(
|
||||||
|
self.harness.charm.get_mandatory_relations_not_ready(
|
||||||
|
self.mock_event
|
||||||
|
),
|
||||||
|
{"identity-service", "ingress-public", "amqp"},
|
||||||
|
)
|
||||||
|
|
||||||
amqp_rel_id = test_utils.add_base_amqp_relation(self.harness)
|
amqp_rel_id = test_utils.add_base_amqp_relation(self.harness)
|
||||||
test_utils.add_amqp_relation_credentials(self.harness, amqp_rel_id)
|
test_utils.add_amqp_relation_credentials(self.harness, amqp_rel_id)
|
||||||
self.assertFalse(self.harness.charm.relation_handlers_ready())
|
self.assertSetEqual(
|
||||||
|
self.harness.charm.get_mandatory_relations_not_ready(
|
||||||
|
self.mock_event
|
||||||
|
),
|
||||||
|
{"ingress-public", "identity-service"},
|
||||||
|
)
|
||||||
|
|
||||||
identity_rel_id = test_utils.add_base_identity_service_relation(
|
identity_rel_id = test_utils.add_base_identity_service_relation(
|
||||||
self.harness
|
self.harness
|
||||||
@ -357,7 +380,12 @@ class TestOSBaseOperatorAPICharm(_TestOSBaseOperatorAPICharm):
|
|||||||
test_utils.add_identity_service_relation_response(
|
test_utils.add_identity_service_relation_response(
|
||||||
self.harness, identity_rel_id
|
self.harness, identity_rel_id
|
||||||
)
|
)
|
||||||
self.assertFalse(self.harness.charm.relation_handlers_ready())
|
self.assertSetEqual(
|
||||||
|
self.harness.charm.get_mandatory_relations_not_ready(
|
||||||
|
self.mock_event
|
||||||
|
),
|
||||||
|
{"ingress-public"},
|
||||||
|
)
|
||||||
|
|
||||||
ingress_rel_id = test_utils.add_ingress_relation(
|
ingress_rel_id = test_utils.add_ingress_relation(
|
||||||
self.harness, "public"
|
self.harness, "public"
|
||||||
@ -372,7 +400,12 @@ class TestOSBaseOperatorAPICharm(_TestOSBaseOperatorAPICharm):
|
|||||||
test_utils.add_ceph_access_relation_response(
|
test_utils.add_ceph_access_relation_response(
|
||||||
self.harness, ceph_access_rel_id
|
self.harness, ceph_access_rel_id
|
||||||
)
|
)
|
||||||
self.assertTrue(self.harness.charm.relation_handlers_ready())
|
self.assertSetEqual(
|
||||||
|
self.harness.charm.get_mandatory_relations_not_ready(
|
||||||
|
self.mock_event
|
||||||
|
),
|
||||||
|
set(),
|
||||||
|
)
|
||||||
|
|
||||||
# Add an optional relation and test if relation_handlers_ready
|
# Add an optional relation and test if relation_handlers_ready
|
||||||
# returns True
|
# returns True
|
||||||
@ -382,12 +415,22 @@ class TestOSBaseOperatorAPICharm(_TestOSBaseOperatorAPICharm):
|
|||||||
test_utils.add_ingress_relation_data(
|
test_utils.add_ingress_relation_data(
|
||||||
self.harness, optional_rel_id, "internal"
|
self.harness, optional_rel_id, "internal"
|
||||||
)
|
)
|
||||||
self.assertTrue(self.harness.charm.relation_handlers_ready())
|
self.assertSetEqual(
|
||||||
|
self.harness.charm.get_mandatory_relations_not_ready(
|
||||||
|
self.mock_event
|
||||||
|
),
|
||||||
|
set(),
|
||||||
|
)
|
||||||
|
|
||||||
# Remove a mandatory relation and test if relation_handlers_ready
|
# Remove a mandatory relation and test if relation_handlers_ready
|
||||||
# returns False
|
# returns False
|
||||||
self.harness.remove_relation(ingress_rel_id)
|
self.harness.remove_relation(ingress_rel_id)
|
||||||
self.assertFalse(self.harness.charm.relation_handlers_ready())
|
self.assertSetEqual(
|
||||||
|
self.harness.charm.get_mandatory_relations_not_ready(
|
||||||
|
self.mock_event
|
||||||
|
),
|
||||||
|
{"ingress-public"},
|
||||||
|
)
|
||||||
|
|
||||||
# Add the mandatory relation back and retest relation_handlers_ready
|
# Add the mandatory relation back and retest relation_handlers_ready
|
||||||
ingress_rel_id = test_utils.add_ingress_relation(
|
ingress_rel_id = test_utils.add_ingress_relation(
|
||||||
@ -396,7 +439,12 @@ class TestOSBaseOperatorAPICharm(_TestOSBaseOperatorAPICharm):
|
|||||||
test_utils.add_ingress_relation_data(
|
test_utils.add_ingress_relation_data(
|
||||||
self.harness, ingress_rel_id, "public"
|
self.harness, ingress_rel_id, "public"
|
||||||
)
|
)
|
||||||
self.assertTrue(self.harness.charm.relation_handlers_ready())
|
self.assertSetEqual(
|
||||||
|
self.harness.charm.get_mandatory_relations_not_ready(
|
||||||
|
self.mock_event
|
||||||
|
),
|
||||||
|
set(),
|
||||||
|
)
|
||||||
|
|
||||||
def test_add_explicit_port(self):
|
def test_add_explicit_port(self):
|
||||||
"""Test add_explicit_port method."""
|
"""Test add_explicit_port method."""
|
||||||
|
Loading…
Reference in New Issue
Block a user