diff --git a/charms/aodh-k8s/src/charm.py b/charms/aodh-k8s/src/charm.py index c49c1ec6..23143c9a 100755 --- a/charms/aodh-k8s/src/charm.py +++ b/charms/aodh-k8s/src/charm.py @@ -252,13 +252,6 @@ class AodhOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm): db_sync_cmds = [["aodh-dbsync"]] - mandatory_relations = { - "database", - "identity-service", - "ingress-internal", - "amqp", - } - @property def service_conf(self) -> str: """Service default configuration file.""" diff --git a/charms/barbican-k8s/charmcraft.yaml b/charms/barbican-k8s/charmcraft.yaml index 55f35141..1f05562c 100644 --- a/charms/barbican-k8s/charmcraft.yaml +++ b/charms/barbican-k8s/charmcraft.yaml @@ -66,6 +66,7 @@ requires: interface: rabbitmq vault-kv: interface: vault-kv + optional: true limit: 1 receive-ca-cert: interface: certificate_transfer diff --git a/charms/barbican-k8s/src/charm.py b/charms/barbican-k8s/src/charm.py index 4399115e..4add10fe 100755 --- a/charms/barbican-k8s/src/charm.py +++ b/charms/barbican-k8s/src/charm.py @@ -250,12 +250,6 @@ class BarbicanOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm): service_name = "barbican-api" wsgi_admin_script = "/usr/bin/barbican-wsgi-api" wsgi_public_script = "/usr/bin/barbican-wsgi-api" - mandatory_relations = { - "database", - "amqp", - "identity-service", - "ingress-internal", - } db_sync_cmds = [ ["sudo", "-u", "barbican", "barbican-manage", "db", "upgrade"] @@ -435,9 +429,7 @@ class BarbicanOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm): class BarbicanVaultOperatorCharm(BarbicanOperatorCharm): """Vault specialized Barbican Operator Charm.""" - mandatory_relations = BarbicanOperatorCharm.mandatory_relations.union( - {VAULT_KV_RELATION} - ) + mandatory_relations = {VAULT_KV_RELATION} def __init__(self, *args): super().__init__(*args) diff --git a/charms/ceilometer-k8s/src/charm.py b/charms/ceilometer-k8s/src/charm.py index 5d61edf6..6427f6ed 100755 --- a/charms/ceilometer-k8s/src/charm.py +++ b/charms/ceilometer-k8s/src/charm.py @@ -181,7 +181,6 @@ class CeilometerOperatorCharm(sunbeam_charm.OSBaseOperatorCharmK8S): shared_metering_secret_key = "shared-metering-secret" db_sync_cmds = [["ceilometer-upgrade"]] - mandatory_relations = {"amqp", "identity-credentials", "gnocchi-db"} def __init__(self, framework: ops.framework): super().__init__(framework) diff --git a/charms/cinder-ceph-k8s/src/charm.py b/charms/cinder-ceph-k8s/src/charm.py index 1bb904cc..29ab3877 100755 --- a/charms/cinder-ceph-k8s/src/charm.py +++ b/charms/cinder-ceph-k8s/src/charm.py @@ -224,13 +224,6 @@ class CinderCephOperatorCharm(charm.OSBaseOperatorCharmK8S): ceph_access_relation_name = "ceph-access" - mandatory_relations = { - "database", - "amqp", - "ceph", - "storage-backend", - } - def __init__(self, framework): super().__init__(framework) self._state.set_default(api_ready=False) diff --git a/charms/cinder-k8s/src/charm.py b/charms/cinder-k8s/src/charm.py index 520def28..a514c562 100755 --- a/charms/cinder-k8s/src/charm.py +++ b/charms/cinder-k8s/src/charm.py @@ -201,14 +201,6 @@ class CinderOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm): wsgi_admin_script = "/usr/bin/cinder-wsgi" wsgi_public_script = "/usr/bin/cinder-wsgi" - mandatory_relations = { - "database", - "amqp", - "storage-backend", - "identity-service", - "ingress-internal", - } - db_sync_cmds = [ [ "sudo", diff --git a/charms/designate-bind-k8s/src/charm.py b/charms/designate-bind-k8s/src/charm.py index de96c29f..84281fbd 100755 --- a/charms/designate-bind-k8s/src/charm.py +++ b/charms/designate-bind-k8s/src/charm.py @@ -198,8 +198,6 @@ class BindOperatorCharm(sunbeam_charm.OSBaseOperatorCharmK8S): _state = StoredState() service_name = "designate-bind" - # mandatory_relations = {} - def __init__(self, *args): super().__init__(*args) self.framework.observe(self.on.secret_rotate, self._on_secret_rotate) diff --git a/charms/designate-k8s/src/charm.py b/charms/designate-k8s/src/charm.py index 8ef68136..13dd6708 100755 --- a/charms/designate-k8s/src/charm.py +++ b/charms/designate-k8s/src/charm.py @@ -361,14 +361,6 @@ class DesignateOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm): ["sudo", "-u", "designate", "designate-manage", "pool", "update"], ] - mandatory_relations = { - "database", - "identity-service", - "ingress-internal", - "amqp", - BIND_RNDC_RELATION, - } - def __init__(self, *args): super().__init__(*args) self.framework.observe(self.on.install, self._on_install) diff --git a/charms/glance-k8s/charmcraft.yaml b/charms/glance-k8s/charmcraft.yaml index b572e23c..22e828ab 100644 --- a/charms/glance-k8s/charmcraft.yaml +++ b/charms/glance-k8s/charmcraft.yaml @@ -327,9 +327,10 @@ requires: amqp: interface: rabbitmq optional: true + # ceph is not marked as optional as the GlanceStorage relation handler + # falls back to juju storage if ceph relation is not connected. ceph: interface: ceph-client - optional: true receive-ca-cert: interface: certificate_transfer optional: true diff --git a/charms/glance-k8s/src/charm.py b/charms/glance-k8s/src/charm.py index cfca5d46..fc40daf6 100755 --- a/charms/glance-k8s/src/charm.py +++ b/charms/glance-k8s/src/charm.py @@ -290,16 +290,6 @@ class GlanceOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm): ] ] - # ceph is included in the mandatory list as the GlanceStorage - # relation handler falls back to juju storage if ceph relation - # is not connected. - mandatory_relations = { - "database", - "identity-service", - "ingress-internal", - "ceph", - } - def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) self.framework.observe( diff --git a/charms/gnocchi-k8s/src/charm.py b/charms/gnocchi-k8s/src/charm.py index d86733e5..ed666c97 100755 --- a/charms/gnocchi-k8s/src/charm.py +++ b/charms/gnocchi-k8s/src/charm.py @@ -184,13 +184,6 @@ class GnocchiOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm): db_sync_cmds = [["gnocchi-upgrade"]] - mandatory_relations = { - "database", - "identity-service", - "ingress-internal", - "ceph", - } - @property def service_conf(self) -> str: """Service default configuration file.""" diff --git a/charms/heat-k8s/src/charm.py b/charms/heat-k8s/src/charm.py index c5c6fc29..dfa49860 100755 --- a/charms/heat-k8s/src/charm.py +++ b/charms/heat-k8s/src/charm.py @@ -203,14 +203,6 @@ class HeatOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm): db_sync_cmds = [["heat-manage", "db_sync"]] - mandatory_relations = { - "database", - "amqp", - "identity-service", - "traefik-route-internal", - "identity-ops", - } - def __init__(self, framework): self.traefik_route_public = None self.traefik_route_internal = None diff --git a/charms/horizon-k8s/src/charm.py b/charms/horizon-k8s/src/charm.py index 1ca6cb8c..6eaa8985 100755 --- a/charms/horizon-k8s/src/charm.py +++ b/charms/horizon-k8s/src/charm.py @@ -139,12 +139,6 @@ class HorizonOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm): ] ] - mandatory_relations = { - "database", - "ingress-internal", - "identity-credentials", - } - def __init__(self, framework): super().__init__(framework) self._state.set_default(plugins=[]) diff --git a/charms/keystone-k8s/charmcraft.yaml b/charms/keystone-k8s/charmcraft.yaml index 012bf66b..2a517855 100644 --- a/charms/keystone-k8s/charmcraft.yaml +++ b/charms/keystone-k8s/charmcraft.yaml @@ -151,6 +151,7 @@ requires: optional: true domain-config: interface: keystone-domain-config + optional: true logging: interface: loki_push_api optional: true diff --git a/charms/keystone-k8s/src/charm.py b/charms/keystone-k8s/src/charm.py index ef0c589c..497beb4c 100755 --- a/charms/keystone-k8s/src/charm.py +++ b/charms/keystone-k8s/src/charm.py @@ -334,7 +334,6 @@ class KeystoneOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm): domain_config_dir = Path("/etc/keystone/domains") domain_ca_dir = Path("/usr/local/share/ca-certificates") service_port = 5000 - mandatory_relations = {"database", "ingress-internal"} db_sync_cmds = [ [ "sudo", diff --git a/charms/magnum-k8s/src/charm.py b/charms/magnum-k8s/src/charm.py index 5ce24dd1..be0b8459 100755 --- a/charms/magnum-k8s/src/charm.py +++ b/charms/magnum-k8s/src/charm.py @@ -142,13 +142,6 @@ class MagnumOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm): service_name = "magnum-api" wsgi_admin_script = "/usr/bin/magnum-api-wsgi" wsgi_public_script = "/usr/bin/magnum-api-wsgi" - mandatory_relations = { - "database", - "amqp", - "identity-service", - "ingress-internal", - "identity-ops", - } db_sync_cmds = [["sudo", "-u", "magnum", "magnum-db-manage", "upgrade"]] diff --git a/charms/masakari-k8s/src/charm.py b/charms/masakari-k8s/src/charm.py index 0eb55c2c..428f827e 100755 --- a/charms/masakari-k8s/src/charm.py +++ b/charms/masakari-k8s/src/charm.py @@ -290,13 +290,6 @@ class MasakariHostMonitorPebbleHandler(sunbeam_chandlers.ServicePebbleHandler): class MasakariOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm): """Charm the service.""" - mandatory_relations = { - "database", - "amqp", - "identity-service", - "ingress-internal", - } - wsgi_admin_script = "/usr/bin/masakari-wsgi" wsgi_public_script = "/usr/bin/masakari-wsgi" diff --git a/charms/neutron-k8s/src/charm.py b/charms/neutron-k8s/src/charm.py index ce3ff2a8..dcde66ed 100755 --- a/charms/neutron-k8s/src/charm.py +++ b/charms/neutron-k8s/src/charm.py @@ -427,14 +427,6 @@ class NeutronServerOVNPebbleHandler(NeutronServerPebbleHandler): class NeutronOVNOperatorCharm(NeutronOperatorCharm): """Neutron charm class for OVN.""" - mandatory_relations = { - "amqp", - "database", - "ovsdb-cms", - "identity-service", - "ingress-internal", - } - @property def config_contexts(self) -> list[sunbeam_ctxts.ConfigContext]: """Configuration contexts for the operator.""" diff --git a/charms/nova-k8s/charmcraft.yaml b/charms/nova-k8s/charmcraft.yaml index f8a147c9..d1a92d9f 100644 --- a/charms/nova-k8s/charmcraft.yaml +++ b/charms/nova-k8s/charmcraft.yaml @@ -91,20 +91,11 @@ requires: limit: 1 amqp: interface: rabbitmq - image-service: - interface: glance identity-service: interface: keystone cloud-compute: interface: nova-compute - cinder-volume-service: - interface: cinder - neutron-network-service: - interface: neutron - neutron-api: - interface: neutron-api - placement: - interface: placement + optional: true receive-ca-cert: interface: certificate_transfer optional: true diff --git a/charms/nova-k8s/src/charm.py b/charms/nova-k8s/src/charm.py index 39f90645..9b9997a7 100755 --- a/charms/nova-k8s/src/charm.py +++ b/charms/nova-k8s/src/charm.py @@ -343,15 +343,6 @@ class NovaOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm): wsgi_admin_script = "/usr/bin/nova-api-wsgi" wsgi_public_script = "/usr/bin/nova-api-wsgi" shared_metadata_secret_key = "shared-metadata-secret" - mandatory_relations = { - "database", - "api-database", - "cell-database", - "amqp", - "identity-service", - "ingress-internal", - "traefik-route-internal", - } def __init__(self, framework): self.traefik_route_public = None diff --git a/charms/octavia-k8s/src/charm.py b/charms/octavia-k8s/src/charm.py index f7689922..0d68c496 100755 --- a/charms/octavia-k8s/src/charm.py +++ b/charms/octavia-k8s/src/charm.py @@ -300,13 +300,6 @@ class OctaviaOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm): class OctaviaOVNOperatorCharm(OctaviaOperatorCharm): """Charm the Octavia service with OVN provider.""" - mandatory_relations = { - "database", - "ovsdb-cms", - "identity-service", - "ingress-internal", - } - @property def config_contexts(self) -> List[sunbeam_config_contexts.ConfigContext]: """Configuration contexts for the operator.""" diff --git a/charms/openstack-exporter-k8s/src/charm.py b/charms/openstack-exporter-k8s/src/charm.py index 439a76d3..9b5d1d02 100755 --- a/charms/openstack-exporter-k8s/src/charm.py +++ b/charms/openstack-exporter-k8s/src/charm.py @@ -158,9 +158,6 @@ class GrafanaDashboardsRelationHandler(sunbeam_rhandlers.RelationHandler): class OSExporterOperatorCharm(sunbeam_charm.OSBaseOperatorCharmK8S): """Charm the service.""" - mandatory_relations = { - "identity-ops", - } service_name = "openstack-exporter" @property diff --git a/charms/openstack-hypervisor/charmcraft.yaml b/charms/openstack-hypervisor/charmcraft.yaml index f71d2e22..3590ca04 100644 --- a/charms/openstack-hypervisor/charmcraft.yaml +++ b/charms/openstack-hypervisor/charmcraft.yaml @@ -105,6 +105,7 @@ requires: interface: nova masakari-service: interface: service-ready + optional: true tracing: interface: tracing optional: true diff --git a/charms/openstack-hypervisor/src/charm.py b/charms/openstack-hypervisor/src/charm.py index 3dde31b2..0f5f0158 100755 --- a/charms/openstack-hypervisor/src/charm.py +++ b/charms/openstack-hypervisor/src/charm.py @@ -162,13 +162,6 @@ class HypervisorOperatorCharm(sunbeam_charm.OSBaseOperatorCharm): METADATA_SECRET_KEY = "ovn-metadata-proxy-shared-secret" DEFAULT_SECRET_LENGTH = 32 - mandatory_relations = { - "amqp", - "identity-credentials", - "ovsdb-cms", - "nova-service", - } - def __init__(self, framework: ops.framework.Framework) -> None: """Run constructor.""" super().__init__(framework) diff --git a/charms/openstack-images-sync-k8s/charmcraft.yaml b/charms/openstack-images-sync-k8s/charmcraft.yaml index f21bbc2e..137fa071 100644 --- a/charms/openstack-images-sync-k8s/charmcraft.yaml +++ b/charms/openstack-images-sync-k8s/charmcraft.yaml @@ -59,7 +59,6 @@ requires: ingress-internal: interface: ingress limit: 1 - optional: true ingress-public: interface: ingress optional: true diff --git a/charms/openstack-images-sync-k8s/src/charm.py b/charms/openstack-images-sync-k8s/src/charm.py index bbf70f16..bb6589ed 100755 --- a/charms/openstack-images-sync-k8s/src/charm.py +++ b/charms/openstack-images-sync-k8s/src/charm.py @@ -155,10 +155,6 @@ class OpenstackImagesSyncK8SCharm(sunbeam_charm.OSBaseOperatorAPICharm): """Charm the application.""" service_name = "openstack-images-sync" - mandatory_relations = { - "identity-service", - "ingress-internal", - } @property def service_conf(self) -> str: diff --git a/charms/ovn-central-k8s/src/charm.py b/charms/ovn-central-k8s/src/charm.py index c2ccc27d..45e6cad8 100755 --- a/charms/ovn-central-k8s/src/charm.py +++ b/charms/ovn-central-k8s/src/charm.py @@ -183,7 +183,7 @@ class OVNCentralOperatorCharm(sunbeam_charm.OSBaseOperatorCharmK8S): """Charm the service.""" _state = StoredState() - mandatory_relations = {"certificates", "peers"} + mandatory_relations = {"peers"} def __init__(self, framework): """Setup OVN central charm class.""" diff --git a/charms/ovn-relay-k8s/src/charm.py b/charms/ovn-relay-k8s/src/charm.py index 49938b2f..b21689fb 100755 --- a/charms/ovn-relay-k8s/src/charm.py +++ b/charms/ovn-relay-k8s/src/charm.py @@ -93,11 +93,6 @@ class OVNRelayPebbleHandler(ovn_chandlers.OVNPebbleHandler): class OVNRelayOperatorCharm(ovn_charm.OSBaseOVNOperatorCharm): """Charm the service.""" - mandatory_relations = { - "ovsdb-cms", - "certificates", - } - def __init__(self, framework): super().__init__(framework) self.service_patcher = KubernetesServicePatch( diff --git a/charms/tempest-k8s/charmcraft.yaml b/charms/tempest-k8s/charmcraft.yaml index 5afc6766..e6ed4434 100644 --- a/charms/tempest-k8s/charmcraft.yaml +++ b/charms/tempest-k8s/charmcraft.yaml @@ -117,6 +117,7 @@ requires: interface: keystone-resources logging: interface: loki_push_api + optional: true receive-ca-cert: interface: certificate_transfer optional: true diff --git a/charms/tempest-k8s/src/charm.py b/charms/tempest-k8s/src/charm.py index 63ee7949..2de9976b 100755 --- a/charms/tempest-k8s/src/charm.py +++ b/charms/tempest-k8s/src/charm.py @@ -107,8 +107,6 @@ class TempestOperatorCharm(sunbeam_charm.OSBaseOperatorCharmK8S): _state = ops.framework.StoredState() service_name = "tempest" - mandatory_relations = {"identity-ops"} - def __init__(self, framework: ops.framework.Framework) -> None: """Run the constructor.""" # config for openstack, used by tempest diff --git a/charms/watcher-k8s/src/charm.py b/charms/watcher-k8s/src/charm.py index ded7a596..889da001 100755 --- a/charms/watcher-k8s/src/charm.py +++ b/charms/watcher-k8s/src/charm.py @@ -118,12 +118,6 @@ class WatcherOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm): service_name = "watcher-api" wsgi_admin_script = "/usr/bin/watcher-api-wsgi" wsgi_public_script = "/usr/bin/watcher-api-wsgi" - mandatory_relations = { - "database", - "amqp", - "identity-service", - "ingress-internal", - } db_sync_cmds = [ [ diff --git a/ops-sunbeam/ops_sunbeam/charm.py b/ops-sunbeam/ops_sunbeam/charm.py index 8f09c116..a2752641 100644 --- a/ops-sunbeam/ops_sunbeam/charm.py +++ b/ops-sunbeam/ops_sunbeam/charm.py @@ -79,6 +79,7 @@ class OSBaseOperatorCharm(ops.charm.CharmBase): _state = ops.framework.StoredState() # Holds set of mandatory relations + # Auto-updates the mandatory requires relations from charmcraft.yaml mandatory_relations: set[str] = set() service_name: str @@ -92,6 +93,17 @@ class OSBaseOperatorCharm(ops.charm.CharmBase): "by ops_sunbeam" ) ) + + # Update mandatory relations from charmcraft.yaml definitions + requires_relations: set[str] = { + name + for name, metadata in self.meta.requires.items() + if metadata.optional is False + } + self.mandatory_relations = requires_relations.union( + self.mandatory_relations + ) + # unit_bootstrapped is stored in the local unit storage which is lost # when the pod is replaced, so this will revert to False on charm # upgrade or upgrade of the payload container. @@ -789,7 +801,6 @@ class OSBaseOperatorCharmK8S(OSBaseOperatorCharm): class OSBaseOperatorAPICharm(OSBaseOperatorCharmK8S): """Base class for OpenStack API operators.""" - mandatory_relations = {"database", "identity-service", "ingress-internal"} wsgi_admin_script: str wsgi_public_script: str diff --git a/ops-sunbeam/tests/unit_tests/test_charms.py b/ops-sunbeam/tests/unit_tests/test_charms.py index fa15455b..bae71217 100644 --- a/ops-sunbeam/tests/unit_tests/test_charms.py +++ b/ops-sunbeam/tests/unit_tests/test_charms.py @@ -108,6 +108,7 @@ requires: limit: 1 ingress-public: interface: ingress + optional: true limit: 1 amqp: interface: rabbitmq @@ -115,9 +116,11 @@ requires: interface: keystone identity-credentials: interface: keystone-credentials + optional: true limit: 1 ceph-access: interface: cinder-ceph-key + optional: true peers: peers: @@ -253,12 +256,6 @@ class MyAPICharm(sunbeam_charm.OSBaseOperatorAPICharm): service_name = "my-service" wsgi_admin_script = "/bin/wsgi_admin" wsgi_public_script = "/bin/wsgi_public" - mandatory_relations = { - "database", - "amqp", - "identity-service", - "ingress-public", - } def __init__(self, framework: "ops.framework.Framework") -> None: """Run constructor.""" diff --git a/ops-sunbeam/tests/unit_tests/test_core.py b/ops-sunbeam/tests/unit_tests/test_core.py index 27b620e4..66bd0ab0 100644 --- a/ops-sunbeam/tests/unit_tests/test_core.py +++ b/ops-sunbeam/tests/unit_tests/test_core.py @@ -365,7 +365,7 @@ class TestOSBaseOperatorAPICharm(_TestOSBaseOperatorAPICharm): self.harness.charm.get_mandatory_relations_not_ready( self.mock_event ), - {"identity-service", "ingress-public", "amqp"}, + {"identity-service", "ingress-internal", "amqp"}, ) amqp_rel_id = test_utils.add_base_amqp_relation(self.harness) @@ -374,7 +374,7 @@ class TestOSBaseOperatorAPICharm(_TestOSBaseOperatorAPICharm): self.harness.charm.get_mandatory_relations_not_ready( self.mock_event ), - {"ingress-public", "identity-service"}, + {"ingress-internal", "identity-service"}, ) identity_rel_id = test_utils.add_base_identity_service_relation( @@ -387,14 +387,14 @@ class TestOSBaseOperatorAPICharm(_TestOSBaseOperatorAPICharm): self.harness.charm.get_mandatory_relations_not_ready( self.mock_event ), - {"ingress-public"}, + {"ingress-internal"}, ) ingress_rel_id = test_utils.add_ingress_relation( - self.harness, "public" + self.harness, "internal" ) test_utils.add_ingress_relation_data( - self.harness, ingress_rel_id, "public" + self.harness, ingress_rel_id, "internal" ) ceph_access_rel_id = test_utils.add_base_ceph_access_relation( @@ -413,10 +413,10 @@ class TestOSBaseOperatorAPICharm(_TestOSBaseOperatorAPICharm): # Add an optional relation and test if relation_handlers_ready # returns True optional_rel_id = test_utils.add_ingress_relation( - self.harness, "internal" + self.harness, "public" ) test_utils.add_ingress_relation_data( - self.harness, optional_rel_id, "internal" + self.harness, optional_rel_id, "public" ) self.assertSetEqual( self.harness.charm.get_mandatory_relations_not_ready( @@ -432,15 +432,15 @@ class TestOSBaseOperatorAPICharm(_TestOSBaseOperatorAPICharm): self.harness.charm.get_mandatory_relations_not_ready( self.mock_event ), - {"ingress-public"}, + {"ingress-internal"}, ) # Add the mandatory relation back and retest relation_handlers_ready ingress_rel_id = test_utils.add_ingress_relation( - self.harness, "public" + self.harness, "internal" ) test_utils.add_ingress_relation_data( - self.harness, ingress_rel_id, "public" + self.harness, ingress_rel_id, "internal" ) self.assertSetEqual( self.harness.charm.get_mandatory_relations_not_ready(