Add support for masakarimonitors
* Add new interface service-ready to check for service readiness of remote application. * Create a placeholder charm sunbeam-libs to place all the common libraries. The charm and the libraries need not be published to charmhub since at this point of time they are used internally by sunbeam. * Add provider to service-ready in masakari-k8s * Add requirer to service-ready in openstack-hypervisor and enable/disable snap option masakari.enable based on service-ready relation. Change-Id: I99feccee2c871fc5a581fdea6f45a541efc2a968
This commit is contained in:
parent
bd137e4c39
commit
0bdc19c4ea
@ -9,6 +9,7 @@ external-libraries:
|
|||||||
- charms.tempo_k8s.v1.charm_tracing
|
- charms.tempo_k8s.v1.charm_tracing
|
||||||
internal-libraries:
|
internal-libraries:
|
||||||
- charms.keystone_k8s.v1.identity_service
|
- charms.keystone_k8s.v1.identity_service
|
||||||
|
- charms.sunbeam_libs.v0.service_readiness
|
||||||
templates:
|
templates:
|
||||||
- parts/database-connection
|
- parts/database-connection
|
||||||
- parts/database-connection-settings
|
- parts/database-connection-settings
|
||||||
|
@ -98,6 +98,10 @@ requires:
|
|||||||
limit: 1
|
limit: 1
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
provides:
|
||||||
|
masakari-service:
|
||||||
|
interface: service-ready
|
||||||
|
|
||||||
peers:
|
peers:
|
||||||
peers:
|
peers:
|
||||||
interface: masakari-peer
|
interface: masakari-peer
|
||||||
|
@ -41,6 +41,9 @@ from charms.consul_k8s.v0.consul_cluster import (
|
|||||||
from ops import (
|
from ops import (
|
||||||
main,
|
main,
|
||||||
)
|
)
|
||||||
|
from ops.charm import (
|
||||||
|
RelationEvent,
|
||||||
|
)
|
||||||
from ops.model import (
|
from ops.model import (
|
||||||
BlockedStatus,
|
BlockedStatus,
|
||||||
)
|
)
|
||||||
@ -362,6 +365,15 @@ class MasakariOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
|
|||||||
)
|
)
|
||||||
handlers.append(self.consul_storage)
|
handlers.append(self.consul_storage)
|
||||||
|
|
||||||
|
self.svc_ready_handler = (
|
||||||
|
sunbeam_rhandlers.ServiceReadinessProviderHandler(
|
||||||
|
self,
|
||||||
|
"masakari-service",
|
||||||
|
self.handle_readiness_request_from_event,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
handlers.append(self.svc_ready_handler)
|
||||||
|
|
||||||
handlers = super().get_relation_handlers(handlers)
|
handlers = super().get_relation_handlers(handlers)
|
||||||
return handlers
|
return handlers
|
||||||
|
|
||||||
@ -399,6 +411,27 @@ class MasakariOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
|
|||||||
)
|
)
|
||||||
return pebble_handlers
|
return pebble_handlers
|
||||||
|
|
||||||
|
def post_config_setup(self):
|
||||||
|
"""Configuration steps after services have been setup."""
|
||||||
|
super().post_config_setup()
|
||||||
|
self.set_readiness_on_related_units()
|
||||||
|
|
||||||
|
def handle_readiness_request_from_event(
|
||||||
|
self, event: RelationEvent
|
||||||
|
) -> None:
|
||||||
|
"""Set service readiness in relation data."""
|
||||||
|
self.svc_ready_handler.interface.set_service_status(
|
||||||
|
event.relation, self.bootstrapped()
|
||||||
|
)
|
||||||
|
|
||||||
|
def set_readiness_on_related_units(self) -> None:
|
||||||
|
"""Set service readiness on masakari-service related units."""
|
||||||
|
logger.debug(
|
||||||
|
"Set service readiness on all connected masakari-service relations"
|
||||||
|
)
|
||||||
|
for relation in self.framework.model.relations["masakari-service"]:
|
||||||
|
self.svc_ready_handler.interface.set_service_status(relation, True)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def service_name(self):
|
def service_name(self):
|
||||||
"""Service name."""
|
"""Service name."""
|
||||||
|
@ -15,3 +15,4 @@ internal-libraries:
|
|||||||
- charms.cinder_ceph_k8s.v0.ceph_access
|
- charms.cinder_ceph_k8s.v0.ceph_access
|
||||||
- charms.ceilometer_k8s.v0.ceilometer_service
|
- charms.ceilometer_k8s.v0.ceilometer_service
|
||||||
- charms.nova_k8s.v0.nova_service
|
- charms.nova_k8s.v0.nova_service
|
||||||
|
- charms.sunbeam_libs.v0.service_readiness
|
||||||
|
@ -80,6 +80,8 @@ requires:
|
|||||||
optional: true
|
optional: true
|
||||||
nova-service:
|
nova-service:
|
||||||
interface: nova
|
interface: nova
|
||||||
|
masakari-service:
|
||||||
|
interface: service-ready
|
||||||
tracing:
|
tracing:
|
||||||
interface: tracing
|
interface: tracing
|
||||||
optional: true
|
optional: true
|
||||||
|
@ -292,6 +292,16 @@ class HypervisorOperatorCharm(sunbeam_charm.OSBaseOperatorCharm):
|
|||||||
mandatory="certificates" in self.mandatory_relations,
|
mandatory="certificates" in self.mandatory_relations,
|
||||||
)
|
)
|
||||||
handlers.append(self.certs)
|
handlers.append(self.certs)
|
||||||
|
if self.can_add_handler("masakari-service", handlers):
|
||||||
|
self.masakari_svc = (
|
||||||
|
sunbeam_rhandlers.ServiceReadinessRequiresHandler(
|
||||||
|
self,
|
||||||
|
"masakari-service",
|
||||||
|
self.configure_charm,
|
||||||
|
"masakari-service" in self.mandatory_relations,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
handlers.append(self.masakari_svc)
|
||||||
handlers = super().get_relation_handlers(handlers)
|
handlers = super().get_relation_handlers(handlers)
|
||||||
return handlers
|
return handlers
|
||||||
|
|
||||||
@ -468,6 +478,7 @@ class HypervisorOperatorCharm(sunbeam_charm.OSBaseOperatorCharm):
|
|||||||
snap_data.update(self._handle_ceilometer_service(contexts))
|
snap_data.update(self._handle_ceilometer_service(contexts))
|
||||||
snap_data.update(self._handle_nova_service(contexts))
|
snap_data.update(self._handle_nova_service(contexts))
|
||||||
snap_data.update(self._handle_receive_ca_cert(contexts))
|
snap_data.update(self._handle_receive_ca_cert(contexts))
|
||||||
|
snap_data.update(self._handle_masakari_service(contexts))
|
||||||
|
|
||||||
self.set_snap_data(snap_data)
|
self.set_snap_data(snap_data)
|
||||||
self.ensure_services_running()
|
self.ensure_services_running()
|
||||||
@ -518,6 +529,15 @@ class HypervisorOperatorCharm(sunbeam_charm.OSBaseOperatorCharm):
|
|||||||
|
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
def _handle_masakari_service(
|
||||||
|
self, contexts: sunbeam_core.OPSCharmContexts
|
||||||
|
) -> dict:
|
||||||
|
try:
|
||||||
|
return {"masakari.enable": contexts.masakari_service.service_ready}
|
||||||
|
except AttributeError:
|
||||||
|
logger.info("masakari_service relation not integrated")
|
||||||
|
return {"masakari.enable": False}
|
||||||
|
|
||||||
def _handle_receive_ca_cert(
|
def _handle_receive_ca_cert(
|
||||||
self, context: sunbeam_core.OPSCharmContexts
|
self, context: sunbeam_core.OPSCharmContexts
|
||||||
) -> dict:
|
) -> dict:
|
||||||
|
@ -162,6 +162,7 @@ class TestCharm(test_utils.CharmTestCase):
|
|||||||
"rabbitmq.url": "rabbit://hypervisor:rabbit.pass@10.0.0.13:5672/openstack",
|
"rabbitmq.url": "rabbit://hypervisor:rabbit.pass@10.0.0.13:5672/openstack",
|
||||||
"telemetry.enable": False,
|
"telemetry.enable": False,
|
||||||
"ca.bundle": None,
|
"ca.bundle": None,
|
||||||
|
"masakari.enable": False,
|
||||||
}
|
}
|
||||||
hypervisor_snap_mock.set.assert_any_call(expect_settings, typed=True)
|
hypervisor_snap_mock.set.assert_any_call(expect_settings, typed=True)
|
||||||
|
|
||||||
@ -195,6 +196,13 @@ class TestCharm(test_utils.CharmTestCase):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Add masakari-service relation
|
||||||
|
self.harness.add_relation(
|
||||||
|
"masakari-service",
|
||||||
|
"masakari",
|
||||||
|
app_data={"ready": "true"},
|
||||||
|
)
|
||||||
|
|
||||||
self.get_local_ip_by_default_route.return_value = "10.0.0.10"
|
self.get_local_ip_by_default_route.return_value = "10.0.0.10"
|
||||||
hypervisor_snap_mock = MagicMock()
|
hypervisor_snap_mock = MagicMock()
|
||||||
hypervisor_snap_mock.present = False
|
hypervisor_snap_mock.present = False
|
||||||
@ -266,5 +274,6 @@ class TestCharm(test_utils.CharmTestCase):
|
|||||||
"telemetry.enable": True,
|
"telemetry.enable": True,
|
||||||
"telemetry.publisher-secret": "FAKE_SECRET",
|
"telemetry.publisher-secret": "FAKE_SECRET",
|
||||||
"ca.bundle": None,
|
"ca.bundle": None,
|
||||||
|
"masakari.enable": True,
|
||||||
}
|
}
|
||||||
hypervisor_snap_mock.set.assert_any_call(expect_settings, typed=True)
|
hypervisor_snap_mock.set.assert_any_call(expect_settings, typed=True)
|
||||||
|
3
charms/sunbeam-libs/.sunbeam-build.yaml
Normal file
3
charms/sunbeam-libs/.sunbeam-build.yaml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
external-libraries: []
|
||||||
|
internal-libraries: []
|
||||||
|
templates: []
|
25
charms/sunbeam-libs/charmcraft.yaml
Normal file
25
charms/sunbeam-libs/charmcraft.yaml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
type: charm
|
||||||
|
title: Sunbeam common libraries
|
||||||
|
name: sunbeam-libs
|
||||||
|
summary: Sunbeam common libraries
|
||||||
|
description: |
|
||||||
|
Placeholder for the common libraries used in Sunbeam.
|
||||||
|
assumes:
|
||||||
|
- k8s-api
|
||||||
|
links:
|
||||||
|
source: https://opendev.org/openstack/sunbeam-charms
|
||||||
|
issues: https://bugs.launchpad.net/sunbeam-charms
|
||||||
|
|
||||||
|
base: ubuntu@24.04
|
||||||
|
platforms:
|
||||||
|
amd64:
|
||||||
|
|
||||||
|
containers:
|
||||||
|
placeholder:
|
||||||
|
resource: placeholder-image
|
||||||
|
|
||||||
|
resources:
|
||||||
|
placeholder-image:
|
||||||
|
description: OCI image for placeholder
|
||||||
|
type: oci-image
|
||||||
|
upstream-source: busybox
|
@ -0,0 +1,203 @@
|
|||||||
|
"""Service Provides and Requires module.
|
||||||
|
|
||||||
|
The interface `service-ready` is to inform that remote service is ready.
|
||||||
|
This library contains the Requires and Provides classes for handling
|
||||||
|
the service-ready interface.
|
||||||
|
|
||||||
|
Import `ServiceReadinessRequirer` in your charm, with the charm object and the
|
||||||
|
relation name:
|
||||||
|
- self
|
||||||
|
- "service"
|
||||||
|
|
||||||
|
Two events are also available to respond to:
|
||||||
|
- readiness_changed
|
||||||
|
- goneaway
|
||||||
|
|
||||||
|
A basic example showing the usage of this relation follows:
|
||||||
|
|
||||||
|
```
|
||||||
|
from charms.masakari_k8s.v0.service_readiness import (
|
||||||
|
ServiceReadinessRequirer
|
||||||
|
)
|
||||||
|
|
||||||
|
class ServiceClientCharm(CharmBase):
|
||||||
|
def __init__(self, *args):
|
||||||
|
super().__init__(*args)
|
||||||
|
# Service Requires
|
||||||
|
self._svc = ServiceReadinessRequirer(
|
||||||
|
self, "service",
|
||||||
|
)
|
||||||
|
self.framework.observe(
|
||||||
|
self._svc.on.readiness_changed,
|
||||||
|
self._on_service_readiness_changed
|
||||||
|
)
|
||||||
|
self.framework.observe(
|
||||||
|
self._svc.on.goneaway,
|
||||||
|
self._on_service_goneaway
|
||||||
|
)
|
||||||
|
|
||||||
|
def _on_service_readiness_changed(self, event):
|
||||||
|
'''React to the service readiness changed event.
|
||||||
|
|
||||||
|
This event happens when service relation is added to the
|
||||||
|
model and relation data is changed.
|
||||||
|
'''
|
||||||
|
# Do something with the configuration provided by relation.
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _on_service_goneaway(self, event):
|
||||||
|
'''React to the Service goneaway event.
|
||||||
|
|
||||||
|
This event happens when service relation is removed.
|
||||||
|
'''
|
||||||
|
# Service Relation has goneaway.
|
||||||
|
pass
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from ops.charm import (
|
||||||
|
CharmBase,
|
||||||
|
RelationBrokenEvent,
|
||||||
|
RelationChangedEvent,
|
||||||
|
RelationEvent,
|
||||||
|
)
|
||||||
|
from ops.framework import (
|
||||||
|
EventSource,
|
||||||
|
Object,
|
||||||
|
ObjectEvents,
|
||||||
|
)
|
||||||
|
from ops.model import (
|
||||||
|
Relation,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# The unique Charmhub library identifier, never change it
|
||||||
|
LIBID = "706872aa869c11ef9444175192825660"
|
||||||
|
|
||||||
|
# Increment this major API version when introducing breaking changes
|
||||||
|
LIBAPI = 0
|
||||||
|
|
||||||
|
# Increment this PATCH version before using `charmcraft publish-lib` or reset
|
||||||
|
# to 0 if you are raising the major API version
|
||||||
|
LIBPATCH = 1
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceReadinessRequestEvent(RelationEvent):
|
||||||
|
"""ServiceReadinessRequest Event."""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceReadinessProviderEvents(ObjectEvents):
|
||||||
|
"""Events class for `on`."""
|
||||||
|
|
||||||
|
service_readiness = EventSource(ServiceReadinessRequestEvent)
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceReadinessProvider(Object):
|
||||||
|
"""ServiceReadinessProvider class."""
|
||||||
|
|
||||||
|
on = ServiceReadinessProviderEvents()
|
||||||
|
|
||||||
|
def __init__(self, charm: CharmBase, relation_name: str):
|
||||||
|
super().__init__(charm, relation_name)
|
||||||
|
self.charm = charm
|
||||||
|
self.relation_name = relation_name
|
||||||
|
self.framework.observe(
|
||||||
|
self.charm.on[relation_name].relation_changed,
|
||||||
|
self._on_relation_changed,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _on_relation_changed(self, event: RelationChangedEvent):
|
||||||
|
"""Handle service relation changed."""
|
||||||
|
logging.debug(f"Service relation changed for relation {self.relation_name}")
|
||||||
|
self.on.service_readiness.emit(event.relation)
|
||||||
|
|
||||||
|
def set_service_status(self, relation: Relation, is_ready: bool) -> None:
|
||||||
|
"""Set service readiness status on the relation."""
|
||||||
|
if not self.charm.unit.is_leader():
|
||||||
|
logging.debug("Not a leader unit, skipping setting ready status")
|
||||||
|
return
|
||||||
|
|
||||||
|
logging.debug(
|
||||||
|
f"Setting ready status on relation {relation.app.name} "
|
||||||
|
f"{relation.name}/{relation.id}"
|
||||||
|
)
|
||||||
|
relation.data[self.charm.app]["ready"] = json.dumps(is_ready)
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceReadinessChangedEvent(RelationEvent):
|
||||||
|
"""ServiceReadinessChanged Event."""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceGoneAwayEvent(RelationEvent):
|
||||||
|
"""ServiceGoneAway Event."""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceReadinessRequirerEvents(ObjectEvents):
|
||||||
|
"""Events class for `on`."""
|
||||||
|
|
||||||
|
readiness_changed = EventSource(ServiceReadinessChangedEvent)
|
||||||
|
goneaway = EventSource(ServiceGoneAwayEvent)
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceReadinessRequirer(Object):
|
||||||
|
"""ServiceReadinessRequirer class."""
|
||||||
|
|
||||||
|
on = ServiceReadinessRequirerEvents()
|
||||||
|
|
||||||
|
def __init__(self, charm: CharmBase, relation_name: str):
|
||||||
|
super().__init__(charm, relation_name)
|
||||||
|
self.charm = charm
|
||||||
|
self.relation_name = relation_name
|
||||||
|
self.framework.observe(
|
||||||
|
self.charm.on[relation_name].relation_changed,
|
||||||
|
self._on_relation_changed,
|
||||||
|
)
|
||||||
|
self.framework.observe(
|
||||||
|
self.charm.on[relation_name].relation_broken,
|
||||||
|
self._on_relation_broken,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _on_relation_changed(self, event: RelationChangedEvent):
|
||||||
|
"""Handle Service relation changed."""
|
||||||
|
logging.debug(f"service readiness data changed for relation {self.relation_name}")
|
||||||
|
self.on.readiness_changed.emit(event.relation)
|
||||||
|
|
||||||
|
def _on_relation_broken(self, event: RelationBrokenEvent):
|
||||||
|
"""Handle Service relation broken."""
|
||||||
|
logging.debug(f"service readiness relation broken for {self.relation_name}")
|
||||||
|
self.on.goneaway.emit(event.relation)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _service_rel(self) -> Relation | None:
|
||||||
|
"""The service relation."""
|
||||||
|
return self.framework.model.get_relation(self.relation_name)
|
||||||
|
|
||||||
|
def get_remote_app_data(self, key: str) -> str | None:
|
||||||
|
"""Return the value for the given key from remote app data."""
|
||||||
|
if self._service_rel:
|
||||||
|
data = self._service_rel.data[
|
||||||
|
self._service_rel.app
|
||||||
|
]
|
||||||
|
return data.get(key)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def service_ready(self) -> bool:
|
||||||
|
"""Return if service is ready or not."""
|
||||||
|
is_ready = self.get_remote_app_data("ready")
|
||||||
|
if is_ready:
|
||||||
|
return json.loads(is_ready)
|
||||||
|
|
||||||
|
return False
|
36
charms/sunbeam-libs/src/charm.py
Executable file
36
charms/sunbeam-libs/src/charm.py
Executable file
@ -0,0 +1,36 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# Copyright 2024 Canonical Ltd.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
"""sunbeam-libs Charm.
|
||||||
|
|
||||||
|
This charm is a placeholder for sunbeam common libraries.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import ops_sunbeam.charm as sunbeam_charm
|
||||||
|
from ops import (
|
||||||
|
main,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SunbeamLibsCharm(sunbeam_charm.OSBaseOperatorCharmK8S):
|
||||||
|
"""Placeholder charm for Sunbeam common libs."""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def service_name(self):
|
||||||
|
"""Service name."""
|
||||||
|
return "placeholder"
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main(SunbeamLibsCharm)
|
17
charms/sunbeam-libs/tests/unit/__init__.py
Normal file
17
charms/sunbeam-libs/tests/unit/__init__.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# Copyright 2024 Canonical Ltd.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
"""Unit tests for sunbeam-libs."""
|
59
charms/sunbeam-libs/tests/unit/test_charm.py
Normal file
59
charms/sunbeam-libs/tests/unit/test_charm.py
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# Copyright 2024 Canonical Ltd.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
"""Tests for sunbeam-libs charm."""
|
||||||
|
|
||||||
|
import charm
|
||||||
|
import ops_sunbeam.test_utils as test_utils
|
||||||
|
|
||||||
|
|
||||||
|
class _SunbeamLibsCharm(charm.SunbeamLibsCharm):
|
||||||
|
"""Dummy class to satisfy reading proper charmcraft file."""
|
||||||
|
|
||||||
|
def __init__(self, framework):
|
||||||
|
self.seen_events = []
|
||||||
|
super().__init__(framework)
|
||||||
|
|
||||||
|
def _log_event(self, event):
|
||||||
|
self.seen_events.append(type(event).__name__)
|
||||||
|
|
||||||
|
def configure_charm(self, event):
|
||||||
|
super().configure_charm(event)
|
||||||
|
self._log_event(event)
|
||||||
|
|
||||||
|
|
||||||
|
class TestSunbeamLibsCharm(test_utils.CharmTestCase):
|
||||||
|
"""Class for testing sunbeam-libs charm."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""Run setup for unit tests."""
|
||||||
|
super().setUp(charm, [])
|
||||||
|
self.harness = test_utils.get_harness(
|
||||||
|
_SunbeamLibsCharm,
|
||||||
|
container_calls=self.container_calls,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.addCleanup(self.harness.cleanup)
|
||||||
|
|
||||||
|
def test_pebble_ready_handler(self):
|
||||||
|
"""Test Pebble ready event is captured."""
|
||||||
|
self.harness.begin()
|
||||||
|
self.assertEqual(self.harness.charm.seen_events, [])
|
||||||
|
test_utils.set_all_pebbles_ready(self.harness)
|
||||||
|
self.assertEqual(
|
||||||
|
self.harness.charm.seen_events,
|
||||||
|
["PebbleReadyEvent"],
|
||||||
|
)
|
@ -57,6 +57,7 @@ if typing.TYPE_CHECKING:
|
|||||||
import charms.loki_k8s.v1.loki_push_api as loki_push_api
|
import charms.loki_k8s.v1.loki_push_api as loki_push_api
|
||||||
import charms.nova_k8s.v0.nova_service as nova_service
|
import charms.nova_k8s.v0.nova_service as nova_service
|
||||||
import charms.rabbitmq_k8s.v0.rabbitmq as rabbitmq
|
import charms.rabbitmq_k8s.v0.rabbitmq as rabbitmq
|
||||||
|
import charms.sunbeam_libs.v0.service_readiness as service_readiness
|
||||||
import charms.tempo_k8s.v2.tracing as tracing
|
import charms.tempo_k8s.v2.tracing as tracing
|
||||||
import charms.tls_certificates_interface.v3.tls_certificates as tls_certificates
|
import charms.tls_certificates_interface.v3.tls_certificates as tls_certificates
|
||||||
import charms.traefik_k8s.v2.ingress as ingress
|
import charms.traefik_k8s.v2.ingress as ingress
|
||||||
@ -2450,3 +2451,137 @@ class GnocchiServiceRequiresHandler(RelationHandler):
|
|||||||
def ready(self) -> bool:
|
def ready(self) -> bool:
|
||||||
"""Whether handler is ready for use."""
|
"""Whether handler is ready for use."""
|
||||||
return self.interface.service_ready
|
return self.interface.service_ready
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
|
class ServiceReadinessRequiresHandler(RelationHandler):
|
||||||
|
"""Handle service-ready relation on the requires side."""
|
||||||
|
|
||||||
|
interface: "service_readiness.ServiceReadinessRequirer"
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
charm: "OSBaseOperatorCharm",
|
||||||
|
relation_name: str,
|
||||||
|
callback_f: Callable,
|
||||||
|
mandatory: bool = False,
|
||||||
|
):
|
||||||
|
"""Create a new service-ready requirer handler.
|
||||||
|
|
||||||
|
Create a new ServiceReadinessRequiresHandler that handles initial
|
||||||
|
events from the relation and invokes the provided callbacks based on
|
||||||
|
the event raised.
|
||||||
|
|
||||||
|
:param charm: the Charm class the handler is for
|
||||||
|
:type charm: ops.charm.CharmBase
|
||||||
|
:param relation_name: the relation the handler is bound to
|
||||||
|
:type relation_name: str
|
||||||
|
:param callback_f: the function to call when the nodes are connected
|
||||||
|
:type callback_f: Callable
|
||||||
|
:param mandatory: If the relation is mandatory to proceed with
|
||||||
|
configuring charm
|
||||||
|
:type mandatory: bool
|
||||||
|
"""
|
||||||
|
super().__init__(charm, relation_name, callback_f, mandatory)
|
||||||
|
|
||||||
|
def setup_event_handler(self) -> ops.framework.Object:
|
||||||
|
"""Configure event handlers for service-ready relation."""
|
||||||
|
import charms.sunbeam_libs.v0.service_readiness as service_readiness
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
f"Setting up service-ready event handler for {self.relation_name}"
|
||||||
|
)
|
||||||
|
svc = sunbeam_tracing.trace_type(
|
||||||
|
service_readiness.ServiceReadinessRequirer
|
||||||
|
)(
|
||||||
|
self.charm,
|
||||||
|
self.relation_name,
|
||||||
|
)
|
||||||
|
self.framework.observe(
|
||||||
|
svc.on.readiness_changed,
|
||||||
|
self._on_remote_service_readiness_changed,
|
||||||
|
)
|
||||||
|
self.framework.observe(
|
||||||
|
svc.on.goneaway,
|
||||||
|
self._on_remote_service_goneaway,
|
||||||
|
)
|
||||||
|
return svc
|
||||||
|
|
||||||
|
def _on_remote_service_readiness_changed(
|
||||||
|
self, event: ops.framework.EventBase
|
||||||
|
) -> None:
|
||||||
|
"""Handle config_changed event."""
|
||||||
|
logger.debug(
|
||||||
|
f"Remote service readiness changed event received for relation {self.relation_name}"
|
||||||
|
)
|
||||||
|
self.callback_f(event)
|
||||||
|
|
||||||
|
def _on_remote_service_goneaway(
|
||||||
|
self, event: ops.framework.EventBase
|
||||||
|
) -> None:
|
||||||
|
"""Handle gone_away event."""
|
||||||
|
logger.debug(
|
||||||
|
"Remote service gone away event received for relation {self.relation_name}"
|
||||||
|
)
|
||||||
|
self.callback_f(event)
|
||||||
|
if self.mandatory:
|
||||||
|
self.status.set(BlockedStatus("integration missing"))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ready(self) -> bool:
|
||||||
|
"""Whether handler is ready for use."""
|
||||||
|
return self.interface.service_ready
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
|
class ServiceReadinessProviderHandler(RelationHandler):
|
||||||
|
"""Handler for service-readiness relation on provider side."""
|
||||||
|
|
||||||
|
interface: "service_readiness.ServiceReadinessProvider"
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
charm: "OSBaseOperatorCharm",
|
||||||
|
relation_name: str,
|
||||||
|
callback_f: Callable,
|
||||||
|
):
|
||||||
|
"""Create a new service-readiness provider handler.
|
||||||
|
|
||||||
|
Create a new ServiceReadinessProvidesHandler that updates service
|
||||||
|
readiness on the related units.
|
||||||
|
|
||||||
|
:param charm: the Charm class the handler is for
|
||||||
|
:type charm: ops.charm.CharmBase
|
||||||
|
:param relation_name: the relation the handler is bound to
|
||||||
|
:type relation_name: str
|
||||||
|
:param callback_f: the function to call when the nodes are connected
|
||||||
|
:type callback_f: Callable
|
||||||
|
"""
|
||||||
|
super().__init__(charm, relation_name, callback_f)
|
||||||
|
|
||||||
|
def setup_event_handler(self):
|
||||||
|
"""Configure event handlers for service-readiness relation."""
|
||||||
|
import charms.sunbeam_libs.v0.service_readiness as service_readiness
|
||||||
|
|
||||||
|
logger.debug(f"Setting up event handler for {self.relation_name}")
|
||||||
|
|
||||||
|
svc = sunbeam_tracing.trace_type(
|
||||||
|
service_readiness.ServiceReadinessProvider
|
||||||
|
)(
|
||||||
|
self.charm,
|
||||||
|
self.relation_name,
|
||||||
|
)
|
||||||
|
self.framework.observe(
|
||||||
|
svc.on.service_readiness,
|
||||||
|
self._on_service_readiness,
|
||||||
|
)
|
||||||
|
return svc
|
||||||
|
|
||||||
|
def _on_service_readiness(self, event: ops.framework.EventBase) -> None:
|
||||||
|
"""Handle service readiness request event."""
|
||||||
|
self.callback_f(event)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ready(self) -> bool:
|
||||||
|
"""Report if relation is ready."""
|
||||||
|
return True
|
||||||
|
Loading…
Reference in New Issue
Block a user