Implement tracing
Implement tracing relation for all the charms. Instrument most of ops.Object objects, including relation handlers, pebble handlers, and relation objects. Change-Id: I967ff858a63aa7d30094cf5a46491fce11195060 Signed-off-by: Guillaume Boutry <guillaume.boutry@canonical.com>
This commit is contained in:
parent
bd0990f2ad
commit
e5fb16d6b9
@ -4,6 +4,8 @@ external-libraries:
|
|||||||
- charms.traefik_k8s.v2.ingress
|
- charms.traefik_k8s.v2.ingress
|
||||||
- charms.certificate_transfer_interface.v0.certificate_transfer
|
- charms.certificate_transfer_interface.v0.certificate_transfer
|
||||||
- charms.loki_k8s.v1.loki_push_api
|
- charms.loki_k8s.v1.loki_push_api
|
||||||
|
- charms.tempo_k8s.v2.tracing
|
||||||
|
- charms.tempo_k8s.v1.charm_tracing
|
||||||
internal-libraries:
|
internal-libraries:
|
||||||
- charms.keystone_k8s.v1.identity_service
|
- charms.keystone_k8s.v1.identity_service
|
||||||
templates:
|
templates:
|
||||||
|
@ -78,6 +78,10 @@ requires:
|
|||||||
logging:
|
logging:
|
||||||
interface: loki_push_api
|
interface: loki_push_api
|
||||||
optional: true
|
optional: true
|
||||||
|
tracing:
|
||||||
|
interface: tracing
|
||||||
|
optional: true
|
||||||
|
limit: 1
|
||||||
|
|
||||||
provides:
|
provides:
|
||||||
aodh:
|
aodh:
|
||||||
|
@ -27,6 +27,7 @@ import ops.pebble
|
|||||||
import ops_sunbeam.charm as sunbeam_charm
|
import ops_sunbeam.charm as sunbeam_charm
|
||||||
import ops_sunbeam.container_handlers as sunbeam_chandlers
|
import ops_sunbeam.container_handlers as sunbeam_chandlers
|
||||||
import ops_sunbeam.core as sunbeam_core
|
import ops_sunbeam.core as sunbeam_core
|
||||||
|
import ops_sunbeam.tracing as sunbeam_tracing
|
||||||
from ops.framework import (
|
from ops.framework import (
|
||||||
StoredState,
|
StoredState,
|
||||||
)
|
)
|
||||||
@ -43,6 +44,7 @@ AODH_LISTENER_CONTAINER = "aodh-listener"
|
|||||||
AODH_EXPIRER_CONTAINER = "aodh-expirer"
|
AODH_EXPIRER_CONTAINER = "aodh-expirer"
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class AODHWSGIPebbleHandler(sunbeam_chandlers.WSGIPebbleHandler):
|
class AODHWSGIPebbleHandler(sunbeam_chandlers.WSGIPebbleHandler):
|
||||||
"""Pebble handler for AODH api service."""
|
"""Pebble handler for AODH api service."""
|
||||||
|
|
||||||
@ -56,6 +58,7 @@ class AODHWSGIPebbleHandler(sunbeam_chandlers.WSGIPebbleHandler):
|
|||||||
super().init_service(context)
|
super().init_service(context)
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class AODHEvaluatorPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
class AODHEvaluatorPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
||||||
"""Pebble handler for AODH Evaluator."""
|
"""Pebble handler for AODH Evaluator."""
|
||||||
|
|
||||||
@ -103,6 +106,7 @@ class AODHEvaluatorPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class AODHNotifierPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
class AODHNotifierPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
||||||
"""Pebble handler for AODH Notifier container."""
|
"""Pebble handler for AODH Notifier container."""
|
||||||
|
|
||||||
@ -147,6 +151,7 @@ class AODHNotifierPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class AODHListenerPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
class AODHListenerPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
||||||
"""Pebble handler for AODH Listener container."""
|
"""Pebble handler for AODH Listener container."""
|
||||||
|
|
||||||
@ -191,6 +196,7 @@ class AODHListenerPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class AODHExpirerPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
class AODHExpirerPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
||||||
"""Pebble handler for AODH Expirer container."""
|
"""Pebble handler for AODH Expirer container."""
|
||||||
|
|
||||||
@ -237,6 +243,7 @@ class AODHExpirerPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_sunbeam_charm
|
||||||
class AodhOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
|
class AodhOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
|
||||||
"""Charm the service."""
|
"""Charm the service."""
|
||||||
|
|
||||||
|
@ -5,6 +5,8 @@ external-libraries:
|
|||||||
- charms.vault_k8s.v0.vault_kv
|
- charms.vault_k8s.v0.vault_kv
|
||||||
- charms.certificate_transfer_interface.v0.certificate_transfer
|
- charms.certificate_transfer_interface.v0.certificate_transfer
|
||||||
- charms.loki_k8s.v1.loki_push_api
|
- charms.loki_k8s.v1.loki_push_api
|
||||||
|
- charms.tempo_k8s.v2.tracing
|
||||||
|
- charms.tempo_k8s.v1.charm_tracing
|
||||||
internal-libraries:
|
internal-libraries:
|
||||||
- charms.keystone_k8s.v1.identity_service
|
- charms.keystone_k8s.v1.identity_service
|
||||||
- charms.keystone_k8s.v0.identity_resource
|
- charms.keystone_k8s.v0.identity_resource
|
||||||
|
@ -48,6 +48,10 @@ requires:
|
|||||||
logging:
|
logging:
|
||||||
interface: loki_push_api
|
interface: loki_push_api
|
||||||
optional: true
|
optional: true
|
||||||
|
tracing:
|
||||||
|
interface: tracing
|
||||||
|
optional: true
|
||||||
|
limit: 1
|
||||||
|
|
||||||
peers:
|
peers:
|
||||||
peers:
|
peers:
|
||||||
|
@ -34,6 +34,7 @@ import ops_sunbeam.config_contexts as sunbeam_ctxts
|
|||||||
import ops_sunbeam.container_handlers as sunbeam_chandlers
|
import ops_sunbeam.container_handlers as sunbeam_chandlers
|
||||||
import ops_sunbeam.core as sunbeam_core
|
import ops_sunbeam.core as sunbeam_core
|
||||||
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
||||||
|
import ops_sunbeam.tracing as sunbeam_tracing
|
||||||
from charms.vault_k8s.v0 import (
|
from charms.vault_k8s.v0 import (
|
||||||
vault_kv,
|
vault_kv,
|
||||||
)
|
)
|
||||||
@ -60,6 +61,7 @@ class NoRelationError(Exception):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class WSGIBarbicanAdminConfigContext(sunbeam_ctxts.ConfigContext):
|
class WSGIBarbicanAdminConfigContext(sunbeam_ctxts.ConfigContext):
|
||||||
"""Configuration context for WSGI configuration."""
|
"""Configuration context for WSGI configuration."""
|
||||||
|
|
||||||
@ -77,6 +79,7 @@ class WSGIBarbicanAdminConfigContext(sunbeam_ctxts.ConfigContext):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class VaultKvRequiresHandler(sunbeam_rhandlers.RelationHandler):
|
class VaultKvRequiresHandler(sunbeam_rhandlers.RelationHandler):
|
||||||
"""Handler for vault-kv relation."""
|
"""Handler for vault-kv relation."""
|
||||||
|
|
||||||
@ -97,7 +100,7 @@ class VaultKvRequiresHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
def setup_event_handler(self) -> ops.Object:
|
def setup_event_handler(self) -> ops.Object:
|
||||||
"""Configure event handlers for a vault-kv relation."""
|
"""Configure event handlers for a vault-kv relation."""
|
||||||
logger.debug("Setting up vault-kv event handler")
|
logger.debug("Setting up vault-kv event handler")
|
||||||
interface = vault_kv.VaultKvRequires(
|
interface = sunbeam_tracing.trace_type(vault_kv.VaultKvRequires)(
|
||||||
self.charm,
|
self.charm,
|
||||||
self.relation_name,
|
self.relation_name,
|
||||||
self.mount_suffix,
|
self.mount_suffix,
|
||||||
@ -188,6 +191,7 @@ class VaultKvRequiresHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class BarbicanWorkerPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
class BarbicanWorkerPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
||||||
"""Pebble handler for Barbican worker."""
|
"""Pebble handler for Barbican worker."""
|
||||||
|
|
||||||
@ -430,6 +434,7 @@ class BarbicanOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
|
|||||||
return super().healthcheck_http_url + "?build"
|
return super().healthcheck_http_url + "?build"
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_sunbeam_charm
|
||||||
class BarbicanVaultOperatorCharm(BarbicanOperatorCharm):
|
class BarbicanVaultOperatorCharm(BarbicanOperatorCharm):
|
||||||
"""Vault specialized Barbican Operator Charm."""
|
"""Vault specialized Barbican Operator Charm."""
|
||||||
|
|
||||||
|
@ -2,6 +2,8 @@ external-libraries:
|
|||||||
- charms.rabbitmq_k8s.v0.rabbitmq
|
- charms.rabbitmq_k8s.v0.rabbitmq
|
||||||
- charms.certificate_transfer_interface.v0.certificate_transfer
|
- charms.certificate_transfer_interface.v0.certificate_transfer
|
||||||
- charms.loki_k8s.v1.loki_push_api
|
- charms.loki_k8s.v1.loki_push_api
|
||||||
|
- charms.tempo_k8s.v2.tracing
|
||||||
|
- charms.tempo_k8s.v1.charm_tracing
|
||||||
internal-libraries:
|
internal-libraries:
|
||||||
- charms.keystone_k8s.v0.identity_credentials
|
- charms.keystone_k8s.v0.identity_credentials
|
||||||
- charms.gnocchi_k8s.v0.gnocchi_service
|
- charms.gnocchi_k8s.v0.gnocchi_service
|
||||||
|
@ -55,6 +55,10 @@ requires:
|
|||||||
logging:
|
logging:
|
||||||
interface: loki_push_api
|
interface: loki_push_api
|
||||||
optional: true
|
optional: true
|
||||||
|
tracing:
|
||||||
|
interface: tracing
|
||||||
|
optional: true
|
||||||
|
limit: 1
|
||||||
|
|
||||||
peers:
|
peers:
|
||||||
peers:
|
peers:
|
||||||
|
@ -31,6 +31,7 @@ import ops_sunbeam.charm as sunbeam_charm
|
|||||||
import ops_sunbeam.container_handlers as sunbeam_chandlers
|
import ops_sunbeam.container_handlers as sunbeam_chandlers
|
||||||
import ops_sunbeam.core as sunbeam_core
|
import ops_sunbeam.core as sunbeam_core
|
||||||
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
||||||
|
import ops_sunbeam.tracing as sunbeam_tracing
|
||||||
from charms.ceilometer_k8s.v0.ceilometer_service import (
|
from charms.ceilometer_k8s.v0.ceilometer_service import (
|
||||||
CeilometerConfigRequestEvent,
|
CeilometerConfigRequestEvent,
|
||||||
CeilometerServiceProvides,
|
CeilometerServiceProvides,
|
||||||
@ -55,6 +56,7 @@ CEILOMETER_CENTRAL_CONTAINER = "ceilometer-central"
|
|||||||
CEILOMETER_NOTIFICATION_CONTAINER = "ceilometer-notification"
|
CEILOMETER_NOTIFICATION_CONTAINER = "ceilometer-notification"
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class GnocchiServiceRequiresHandler(sunbeam_rhandlers.RelationHandler):
|
class GnocchiServiceRequiresHandler(sunbeam_rhandlers.RelationHandler):
|
||||||
"""Handle gnocchi service relation on the requires side."""
|
"""Handle gnocchi service relation on the requires side."""
|
||||||
|
|
||||||
@ -120,6 +122,7 @@ class GnocchiServiceRequiresHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
return self.interface.service_ready
|
return self.interface.service_ready
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class CeilometerServiceProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
class CeilometerServiceProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
||||||
"""Handler for ceilometer service relation."""
|
"""Handler for ceilometer service relation."""
|
||||||
|
|
||||||
@ -154,6 +157,7 @@ class CeilometerServiceProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class CeilometerCentralPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
class CeilometerCentralPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
||||||
"""Pebble handler for ceilometer-central service."""
|
"""Pebble handler for ceilometer-central service."""
|
||||||
|
|
||||||
@ -189,6 +193,7 @@ class CeilometerCentralPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
|||||||
return self.charm.container_configs
|
return self.charm.container_configs
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class CeilometerNotificationPebbleHandler(
|
class CeilometerNotificationPebbleHandler(
|
||||||
sunbeam_chandlers.ServicePebbleHandler
|
sunbeam_chandlers.ServicePebbleHandler
|
||||||
):
|
):
|
||||||
@ -243,6 +248,7 @@ class CeilometerNotificationPebbleHandler(
|
|||||||
return _cconfigs
|
return _cconfigs
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_sunbeam_charm
|
||||||
class CeilometerOperatorCharm(sunbeam_charm.OSBaseOperatorCharmK8S):
|
class CeilometerOperatorCharm(sunbeam_charm.OSBaseOperatorCharmK8S):
|
||||||
"""Charm the service."""
|
"""Charm the service."""
|
||||||
|
|
||||||
|
@ -3,6 +3,8 @@ external-libraries:
|
|||||||
- charms.rabbitmq_k8s.v0.rabbitmq
|
- charms.rabbitmq_k8s.v0.rabbitmq
|
||||||
- charms.traefik_k8s.v2.ingress
|
- charms.traefik_k8s.v2.ingress
|
||||||
- charms.loki_k8s.v1.loki_push_api
|
- charms.loki_k8s.v1.loki_push_api
|
||||||
|
- charms.tempo_k8s.v2.tracing
|
||||||
|
- charms.tempo_k8s.v1.charm_tracing
|
||||||
internal-libraries:
|
internal-libraries:
|
||||||
- charms.keystone_k8s.v0.identity_credentials
|
- charms.keystone_k8s.v0.identity_credentials
|
||||||
- charms.cinder_k8s.v0.storage_backend
|
- charms.cinder_k8s.v0.storage_backend
|
||||||
|
@ -45,6 +45,10 @@ requires:
|
|||||||
logging:
|
logging:
|
||||||
interface: loki_push_api
|
interface: loki_push_api
|
||||||
optional: true
|
optional: true
|
||||||
|
tracing:
|
||||||
|
interface: tracing
|
||||||
|
optional: true
|
||||||
|
limit: 1
|
||||||
|
|
||||||
provides:
|
provides:
|
||||||
ceph-access:
|
ceph-access:
|
||||||
|
@ -39,6 +39,7 @@ import ops_sunbeam.core as core
|
|||||||
import ops_sunbeam.guard as sunbeam_guard
|
import ops_sunbeam.guard as sunbeam_guard
|
||||||
import ops_sunbeam.relation_handlers as relation_handlers
|
import ops_sunbeam.relation_handlers as relation_handlers
|
||||||
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
||||||
|
import ops_sunbeam.tracing as sunbeam_tracing
|
||||||
from ops.main import (
|
from ops.main import (
|
||||||
main,
|
main,
|
||||||
)
|
)
|
||||||
@ -50,6 +51,7 @@ from ops.model import (
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class CephConfigurationContext(config_contexts.ConfigContext):
|
class CephConfigurationContext(config_contexts.ConfigContext):
|
||||||
"""Configuration context to parse ceph parameters."""
|
"""Configuration context to parse ceph parameters."""
|
||||||
|
|
||||||
@ -68,6 +70,7 @@ class CephConfigurationContext(config_contexts.ConfigContext):
|
|||||||
return ctxt
|
return ctxt
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class CinderCephConfigurationContext(config_contexts.ConfigContext):
|
class CinderCephConfigurationContext(config_contexts.ConfigContext):
|
||||||
"""Configuration context for cinder parameters."""
|
"""Configuration context for cinder parameters."""
|
||||||
|
|
||||||
@ -94,13 +97,16 @@ class CinderCephConfigurationContext(config_contexts.ConfigContext):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class StorageBackendProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
class StorageBackendProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
||||||
"""Relation handler for storage-backend interface type."""
|
"""Relation handler for storage-backend interface type."""
|
||||||
|
|
||||||
def setup_event_handler(self):
|
def setup_event_handler(self):
|
||||||
"""Configure event handlers for an storage-backend relation."""
|
"""Configure event handlers for an storage-backend relation."""
|
||||||
logger.debug("Setting up Identity Service event handler")
|
logger.debug("Setting up Identity Service event handler")
|
||||||
sb_svc = sunbeam_storage_backend.StorageBackendProvides(
|
sb_svc = sunbeam_tracing.trace_type(
|
||||||
|
sunbeam_storage_backend.StorageBackendProvides
|
||||||
|
)(
|
||||||
self.charm,
|
self.charm,
|
||||||
self.relation_name,
|
self.relation_name,
|
||||||
)
|
)
|
||||||
@ -119,6 +125,7 @@ class StorageBackendProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
return self.interface.remote_ready()
|
return self.interface.remote_ready()
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class CephAccessProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
class CephAccessProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
||||||
"""Handler for identity service relation."""
|
"""Handler for identity service relation."""
|
||||||
|
|
||||||
@ -133,7 +140,9 @@ class CephAccessProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
def setup_event_handler(self):
|
def setup_event_handler(self):
|
||||||
"""Configure event handlers for an Identity service relation."""
|
"""Configure event handlers for an Identity service relation."""
|
||||||
logger.debug("Setting up Ceph Access event handler")
|
logger.debug("Setting up Ceph Access event handler")
|
||||||
ceph_access_svc = sunbeam_ceph_access.CephAccessProvides(
|
ceph_access_svc = sunbeam_tracing.trace_type(
|
||||||
|
sunbeam_ceph_access.CephAccessProvides
|
||||||
|
)(
|
||||||
self.charm,
|
self.charm,
|
||||||
self.relation_name,
|
self.relation_name,
|
||||||
)
|
)
|
||||||
@ -155,6 +164,7 @@ class CephAccessProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class CinderVolumePebbleHandler(container_handlers.PebbleHandler):
|
class CinderVolumePebbleHandler(container_handlers.PebbleHandler):
|
||||||
"""Pebble handler for cinder-volume service."""
|
"""Pebble handler for cinder-volume service."""
|
||||||
|
|
||||||
@ -199,6 +209,7 @@ class CinderVolumePebbleHandler(container_handlers.PebbleHandler):
|
|||||||
self.start_service()
|
self.start_service()
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_sunbeam_charm
|
||||||
class CinderCephOperatorCharm(charm.OSBaseOperatorCharmK8S):
|
class CinderCephOperatorCharm(charm.OSBaseOperatorCharmK8S):
|
||||||
"""Cinder/Ceph Operator charm."""
|
"""Cinder/Ceph Operator charm."""
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@ external-libraries:
|
|||||||
- charms.traefik_k8s.v2.ingress
|
- charms.traefik_k8s.v2.ingress
|
||||||
- charms.certificate_transfer_interface.v0.certificate_transfer
|
- charms.certificate_transfer_interface.v0.certificate_transfer
|
||||||
- charms.loki_k8s.v1.loki_push_api
|
- charms.loki_k8s.v1.loki_push_api
|
||||||
|
- charms.tempo_k8s.v2.tracing
|
||||||
|
- charms.tempo_k8s.v1.charm_tracing
|
||||||
internal-libraries:
|
internal-libraries:
|
||||||
- charms.keystone_k8s.v1.identity_service
|
- charms.keystone_k8s.v1.identity_service
|
||||||
templates:
|
templates:
|
||||||
|
@ -63,6 +63,10 @@ requires:
|
|||||||
logging:
|
logging:
|
||||||
interface: loki_push_api
|
interface: loki_push_api
|
||||||
optional: true
|
optional: true
|
||||||
|
tracing:
|
||||||
|
interface: tracing
|
||||||
|
optional: true
|
||||||
|
limit: 1
|
||||||
|
|
||||||
peers:
|
peers:
|
||||||
peers:
|
peers:
|
||||||
|
@ -34,6 +34,7 @@ import ops_sunbeam.charm as sunbeam_charm
|
|||||||
import ops_sunbeam.container_handlers as sunbeam_chandlers
|
import ops_sunbeam.container_handlers as sunbeam_chandlers
|
||||||
import ops_sunbeam.core as sunbeam_core
|
import ops_sunbeam.core as sunbeam_core
|
||||||
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
||||||
|
import ops_sunbeam.tracing as sunbeam_tracing
|
||||||
from ops.main import (
|
from ops.main import (
|
||||||
main,
|
main,
|
||||||
)
|
)
|
||||||
@ -45,6 +46,7 @@ CINDER_API_CONTAINER = "cinder-api"
|
|||||||
CINDER_SCHEDULER_CONTAINER = "cinder-scheduler"
|
CINDER_SCHEDULER_CONTAINER = "cinder-scheduler"
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class CinderWSGIPebbleHandler(sunbeam_chandlers.WSGIPebbleHandler):
|
class CinderWSGIPebbleHandler(sunbeam_chandlers.WSGIPebbleHandler):
|
||||||
"""Pebble handler for Cinder WSGI services."""
|
"""Pebble handler for Cinder WSGI services."""
|
||||||
|
|
||||||
@ -101,6 +103,7 @@ class CinderWSGIPebbleHandler(sunbeam_chandlers.WSGIPebbleHandler):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class CinderSchedulerPebbleHandler(sunbeam_chandlers.PebbleHandler):
|
class CinderSchedulerPebbleHandler(sunbeam_chandlers.PebbleHandler):
|
||||||
"""Pebble handler for Cinder Scheduler services."""
|
"""Pebble handler for Cinder Scheduler services."""
|
||||||
|
|
||||||
@ -159,13 +162,16 @@ class CinderSchedulerPebbleHandler(sunbeam_chandlers.PebbleHandler):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class StorageBackendRequiresHandler(sunbeam_rhandlers.RelationHandler):
|
class StorageBackendRequiresHandler(sunbeam_rhandlers.RelationHandler):
|
||||||
"""Relation handler for cinder storage backends."""
|
"""Relation handler for cinder storage backends."""
|
||||||
|
|
||||||
def setup_event_handler(self):
|
def setup_event_handler(self):
|
||||||
"""Configure event handlers for an Identity service relation."""
|
"""Configure event handlers for an Identity service relation."""
|
||||||
logger.debug("Setting up Identity Service event handler")
|
logger.debug("Setting up Identity Service event handler")
|
||||||
sb_svc = sunbeam_storage_backend.StorageBackendRequires(
|
sb_svc = sunbeam_tracing.trace_type(
|
||||||
|
sunbeam_storage_backend.StorageBackendRequires
|
||||||
|
)(
|
||||||
self.charm,
|
self.charm,
|
||||||
self.relation_name,
|
self.relation_name,
|
||||||
)
|
)
|
||||||
@ -188,6 +194,7 @@ class StorageBackendRequiresHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_sunbeam_charm
|
||||||
class CinderOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
|
class CinderOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
|
||||||
"""Charm the service."""
|
"""Charm the service."""
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
external-libraries:
|
external-libraries:
|
||||||
- charms.observability_libs.v1.kubernetes_service_patch
|
- charms.observability_libs.v1.kubernetes_service_patch
|
||||||
- charms.loki_k8s.v1.loki_push_api
|
- charms.loki_k8s.v1.loki_push_api
|
||||||
|
- charms.tempo_k8s.v2.tracing
|
||||||
|
- charms.tempo_k8s.v1.charm_tracing
|
||||||
|
@ -35,6 +35,10 @@ requires:
|
|||||||
logging:
|
logging:
|
||||||
interface: loki_push_api
|
interface: loki_push_api
|
||||||
optional: true
|
optional: true
|
||||||
|
tracing:
|
||||||
|
interface: tracing
|
||||||
|
optional: true
|
||||||
|
limit: 1
|
||||||
|
|
||||||
peers:
|
peers:
|
||||||
peers:
|
peers:
|
||||||
|
@ -40,6 +40,7 @@ import ops_sunbeam.charm as sunbeam_charm
|
|||||||
import ops_sunbeam.container_handlers as sunbeam_chandlers
|
import ops_sunbeam.container_handlers as sunbeam_chandlers
|
||||||
import ops_sunbeam.core as sunbeam_core
|
import ops_sunbeam.core as sunbeam_core
|
||||||
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
||||||
|
import ops_sunbeam.tracing as sunbeam_tracing
|
||||||
from ops.framework import (
|
from ops.framework import (
|
||||||
StoredState,
|
StoredState,
|
||||||
)
|
)
|
||||||
@ -55,6 +56,7 @@ RNDC_REVISION_KEY = "rndc_revision"
|
|||||||
RNDC_STORE_KEY = "rndc-store"
|
RNDC_STORE_KEY = "rndc-store"
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class BindPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
class BindPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
||||||
"""Pebble handler for designate-bind service."""
|
"""Pebble handler for designate-bind service."""
|
||||||
|
|
||||||
@ -74,6 +76,7 @@ class BindPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class BindRndcProvidesRelationHandler(sunbeam_rhandlers.RelationHandler):
|
class BindRndcProvidesRelationHandler(sunbeam_rhandlers.RelationHandler):
|
||||||
"""Handler for managing rndc clients."""
|
"""Handler for managing rndc clients."""
|
||||||
|
|
||||||
@ -91,7 +94,9 @@ class BindRndcProvidesRelationHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
|
|
||||||
def setup_event_handler(self) -> ops.Object:
|
def setup_event_handler(self) -> ops.Object:
|
||||||
"""Setup event handler for the relation."""
|
"""Setup event handler for the relation."""
|
||||||
interface = bind_rndc.BindRndcProvides(self.charm, BIND_RNDC_RELATION)
|
interface = sunbeam_tracing.trace_type(bind_rndc.BindRndcProvides)(
|
||||||
|
self.charm, BIND_RNDC_RELATION
|
||||||
|
)
|
||||||
self.framework.observe(
|
self.framework.observe(
|
||||||
interface.on.new_bind_client_attached,
|
interface.on.new_bind_client_attached,
|
||||||
self._on_bind_client_attached,
|
self._on_bind_client_attached,
|
||||||
@ -188,6 +193,7 @@ class BindRndcProvidesRelationHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_sunbeam_charm
|
||||||
class BindOperatorCharm(sunbeam_charm.OSBaseOperatorCharmK8S):
|
class BindOperatorCharm(sunbeam_charm.OSBaseOperatorCharmK8S):
|
||||||
"""Charm the service."""
|
"""Charm the service."""
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@ external-libraries:
|
|||||||
- charms.traefik_k8s.v2.ingress
|
- charms.traefik_k8s.v2.ingress
|
||||||
- charms.certificate_transfer_interface.v0.certificate_transfer
|
- charms.certificate_transfer_interface.v0.certificate_transfer
|
||||||
- charms.loki_k8s.v1.loki_push_api
|
- charms.loki_k8s.v1.loki_push_api
|
||||||
|
- charms.tempo_k8s.v2.tracing
|
||||||
|
- charms.tempo_k8s.v1.charm_tracing
|
||||||
internal-libraries:
|
internal-libraries:
|
||||||
- charms.keystone_k8s.v1.identity_service
|
- charms.keystone_k8s.v1.identity_service
|
||||||
- charms.designate_bind_k8s.v0.bind_rndc
|
- charms.designate_bind_k8s.v0.bind_rndc
|
||||||
|
@ -55,6 +55,10 @@ requires:
|
|||||||
logging:
|
logging:
|
||||||
interface: loki_push_api
|
interface: loki_push_api
|
||||||
optional: true
|
optional: true
|
||||||
|
tracing:
|
||||||
|
interface: tracing
|
||||||
|
optional: true
|
||||||
|
limit: 1
|
||||||
|
|
||||||
peers:
|
peers:
|
||||||
peers:
|
peers:
|
||||||
|
@ -39,6 +39,7 @@ import ops_sunbeam.container_handlers as sunbeam_chandlers
|
|||||||
import ops_sunbeam.core as sunbeam_core
|
import ops_sunbeam.core as sunbeam_core
|
||||||
import ops_sunbeam.guard as sunbeam_guard
|
import ops_sunbeam.guard as sunbeam_guard
|
||||||
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
||||||
|
import ops_sunbeam.tracing as sunbeam_tracing
|
||||||
import tenacity
|
import tenacity
|
||||||
from charms.designate_k8s.v0.designate_service import (
|
from charms.designate_k8s.v0.designate_service import (
|
||||||
DesignateEndpointRequestEvent,
|
DesignateEndpointRequestEvent,
|
||||||
@ -62,6 +63,7 @@ class NoRelationError(Exception):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class DesignatePebbleHandler(sunbeam_chandlers.WSGIPebbleHandler):
|
class DesignatePebbleHandler(sunbeam_chandlers.WSGIPebbleHandler):
|
||||||
"""Pebble handler for designate services."""
|
"""Pebble handler for designate services."""
|
||||||
|
|
||||||
@ -175,6 +177,7 @@ class DesignatePebbleHandler(sunbeam_chandlers.WSGIPebbleHandler):
|
|||||||
super().init_service(context)
|
super().init_service(context)
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class DesignateServiceProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
class DesignateServiceProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
||||||
"""Handler for designate service relation."""
|
"""Handler for designate service relation."""
|
||||||
|
|
||||||
@ -189,7 +192,7 @@ class DesignateServiceProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
def setup_event_handler(self):
|
def setup_event_handler(self):
|
||||||
"""Configure event handlers for an Ceilometer service relation."""
|
"""Configure event handlers for an Ceilometer service relation."""
|
||||||
logger.debug("Setting up Ceilometer service event handler")
|
logger.debug("Setting up Ceilometer service event handler")
|
||||||
svc = DesignateServiceProvides(
|
svc = sunbeam_tracing.trace_type(DesignateServiceProvides)(
|
||||||
self.charm,
|
self.charm,
|
||||||
self.relation_name,
|
self.relation_name,
|
||||||
)
|
)
|
||||||
@ -211,6 +214,7 @@ class DesignateServiceProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class BindRndcRequiresRelationHandler(sunbeam_rhandlers.RelationHandler):
|
class BindRndcRequiresRelationHandler(sunbeam_rhandlers.RelationHandler):
|
||||||
"""Relation handler class."""
|
"""Relation handler class."""
|
||||||
|
|
||||||
@ -228,7 +232,9 @@ class BindRndcRequiresRelationHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
|
|
||||||
def setup_event_handler(self) -> ops.Object:
|
def setup_event_handler(self) -> ops.Object:
|
||||||
"""Setup event handler for the relation."""
|
"""Setup event handler for the relation."""
|
||||||
interface = bind_rndc.BindRndcRequires(self.charm, BIND_RNDC_RELATION)
|
interface = sunbeam_tracing.trace_type(bind_rndc.BindRndcRequires)(
|
||||||
|
self.charm, BIND_RNDC_RELATION
|
||||||
|
)
|
||||||
self.framework.observe(
|
self.framework.observe(
|
||||||
interface.on.connected,
|
interface.on.connected,
|
||||||
self._on_bind_rndc_connected,
|
self._on_bind_rndc_connected,
|
||||||
@ -342,6 +348,7 @@ class BindRndcRequiresRelationHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_sunbeam_charm
|
||||||
class DesignateOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
|
class DesignateOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
|
||||||
"""Charm the service."""
|
"""Charm the service."""
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@ external-libraries:
|
|||||||
- charms.traefik_k8s.v2.ingress
|
- charms.traefik_k8s.v2.ingress
|
||||||
- charms.certificate_transfer_interface.v0.certificate_transfer
|
- charms.certificate_transfer_interface.v0.certificate_transfer
|
||||||
- charms.loki_k8s.v1.loki_push_api
|
- charms.loki_k8s.v1.loki_push_api
|
||||||
|
- charms.tempo_k8s.v2.tracing
|
||||||
|
- charms.tempo_k8s.v1.charm_tracing
|
||||||
internal-libraries:
|
internal-libraries:
|
||||||
- charms.keystone_k8s.v1.identity_service
|
- charms.keystone_k8s.v1.identity_service
|
||||||
templates:
|
templates:
|
||||||
|
@ -74,6 +74,10 @@ requires:
|
|||||||
logging:
|
logging:
|
||||||
interface: loki_push_api
|
interface: loki_push_api
|
||||||
optional: true
|
optional: true
|
||||||
|
tracing:
|
||||||
|
interface: tracing
|
||||||
|
optional: true
|
||||||
|
limit: 1
|
||||||
|
|
||||||
provides:
|
provides:
|
||||||
image-service:
|
image-service:
|
||||||
|
@ -35,6 +35,7 @@ import ops_sunbeam.container_handlers as sunbeam_chandlers
|
|||||||
import ops_sunbeam.core as sunbeam_core
|
import ops_sunbeam.core as sunbeam_core
|
||||||
import ops_sunbeam.guard as sunbeam_guard
|
import ops_sunbeam.guard as sunbeam_guard
|
||||||
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
||||||
|
import ops_sunbeam.tracing as sunbeam_tracing
|
||||||
from lightkube.core.client import (
|
from lightkube.core.client import (
|
||||||
Client,
|
Client,
|
||||||
)
|
)
|
||||||
@ -70,6 +71,7 @@ STORAGE_NAME = "local-repository"
|
|||||||
# and glance always interprets the mode-name as a requested version number.
|
# and glance always interprets the mode-name as a requested version number.
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class GlanceAPIPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
class GlanceAPIPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
||||||
"""Handler for glance api container."""
|
"""Handler for glance api container."""
|
||||||
|
|
||||||
@ -110,6 +112,7 @@ class GlanceAPIPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
|||||||
return super().init_service(context)
|
return super().init_service(context)
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class GlanceStorageRelationHandler(sunbeam_rhandlers.CephClientHandler):
|
class GlanceStorageRelationHandler(sunbeam_rhandlers.CephClientHandler):
|
||||||
"""A relation handler for optional glance storage relations.
|
"""A relation handler for optional glance storage relations.
|
||||||
|
|
||||||
@ -199,6 +202,7 @@ class GlanceStorageRelationHandler(sunbeam_rhandlers.CephClientHandler):
|
|||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class GlanceConfigContext(sunbeam_ctxts.ConfigContext):
|
class GlanceConfigContext(sunbeam_ctxts.ConfigContext):
|
||||||
"""Glance configuration context."""
|
"""Glance configuration context."""
|
||||||
|
|
||||||
@ -251,6 +255,7 @@ def bytes_from_string(value: str) -> int:
|
|||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_sunbeam_charm
|
||||||
class GlanceOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
|
class GlanceOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
|
||||||
"""Charm the service."""
|
"""Charm the service."""
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@ external-libraries:
|
|||||||
- charms.traefik_k8s.v2.ingress
|
- charms.traefik_k8s.v2.ingress
|
||||||
- charms.certificate_transfer_interface.v0.certificate_transfer
|
- charms.certificate_transfer_interface.v0.certificate_transfer
|
||||||
- charms.loki_k8s.v1.loki_push_api
|
- charms.loki_k8s.v1.loki_push_api
|
||||||
|
- charms.tempo_k8s.v2.tracing
|
||||||
|
- charms.tempo_k8s.v1.charm_tracing
|
||||||
internal-libraries:
|
internal-libraries:
|
||||||
- charms.keystone_k8s.v1.identity_service
|
- charms.keystone_k8s.v1.identity_service
|
||||||
templates:
|
templates:
|
||||||
|
@ -57,6 +57,10 @@ requires:
|
|||||||
logging:
|
logging:
|
||||||
interface: loki_push_api
|
interface: loki_push_api
|
||||||
optional: true
|
optional: true
|
||||||
|
tracing:
|
||||||
|
interface: tracing
|
||||||
|
optional: true
|
||||||
|
limit: 1
|
||||||
|
|
||||||
provides:
|
provides:
|
||||||
gnocchi-service:
|
gnocchi-service:
|
||||||
|
@ -31,6 +31,7 @@ import ops_sunbeam.container_handlers as sunbeam_chandlers
|
|||||||
import ops_sunbeam.core as sunbeam_core
|
import ops_sunbeam.core as sunbeam_core
|
||||||
import ops_sunbeam.guard as sunbeam_guard
|
import ops_sunbeam.guard as sunbeam_guard
|
||||||
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
||||||
|
import ops_sunbeam.tracing as sunbeam_tracing
|
||||||
from charms.gnocchi_k8s.v0.gnocchi_service import (
|
from charms.gnocchi_k8s.v0.gnocchi_service import (
|
||||||
GnocchiServiceProvides,
|
GnocchiServiceProvides,
|
||||||
GnocchiServiceReadinessRequestEvent,
|
GnocchiServiceReadinessRequestEvent,
|
||||||
@ -53,6 +54,7 @@ GNOCHHI_WSGI_CONTAINER = "gnocchi-api"
|
|||||||
GNOCCHI_METRICD_CONTAINER = "gnocchi-metricd"
|
GNOCCHI_METRICD_CONTAINER = "gnocchi-metricd"
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class GnocchiServiceProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
class GnocchiServiceProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
||||||
"""Handler for Gnocchi service relation on provider side."""
|
"""Handler for Gnocchi service relation on provider side."""
|
||||||
|
|
||||||
@ -79,7 +81,7 @@ class GnocchiServiceProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
def setup_event_handler(self):
|
def setup_event_handler(self):
|
||||||
"""Configure event handlers for Gnocchi service relation."""
|
"""Configure event handlers for Gnocchi service relation."""
|
||||||
logger.debug("Setting up Gnocchi service event handler")
|
logger.debug("Setting up Gnocchi service event handler")
|
||||||
svc = GnocchiServiceProvides(
|
svc = sunbeam_tracing.trace_type(GnocchiServiceProvides)(
|
||||||
self.charm,
|
self.charm,
|
||||||
self.relation_name,
|
self.relation_name,
|
||||||
)
|
)
|
||||||
@ -101,6 +103,7 @@ class GnocchiServiceProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class GnocchiWSGIPebbleHandler(sunbeam_chandlers.WSGIPebbleHandler):
|
class GnocchiWSGIPebbleHandler(sunbeam_chandlers.WSGIPebbleHandler):
|
||||||
"""Pebble handler for Gnocchi WSGI services."""
|
"""Pebble handler for Gnocchi WSGI services."""
|
||||||
|
|
||||||
@ -139,6 +142,7 @@ class GnocchiWSGIPebbleHandler(sunbeam_chandlers.WSGIPebbleHandler):
|
|||||||
return _cconfigs
|
return _cconfigs
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class GnocchiMetricdPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
class GnocchiMetricdPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
||||||
"""Pebble handler for Gnocchi metricd container."""
|
"""Pebble handler for Gnocchi metricd container."""
|
||||||
|
|
||||||
@ -314,6 +318,7 @@ class GnocchiOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
|
|||||||
self.svc_ready_handler.interface.set_service_status(relation, True)
|
self.svc_ready_handler.interface.set_service_status(relation, True)
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_sunbeam_charm
|
||||||
class GnocchiCephOperatorCharm(GnocchiOperatorCharm):
|
class GnocchiCephOperatorCharm(GnocchiOperatorCharm):
|
||||||
"""Charm the Gnocchi service with Ceph backend."""
|
"""Charm the Gnocchi service with Ceph backend."""
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@ external-libraries:
|
|||||||
- charms.traefik_route_k8s.v0.traefik_route
|
- charms.traefik_route_k8s.v0.traefik_route
|
||||||
- charms.certificate_transfer_interface.v0.certificate_transfer
|
- charms.certificate_transfer_interface.v0.certificate_transfer
|
||||||
- charms.loki_k8s.v1.loki_push_api
|
- charms.loki_k8s.v1.loki_push_api
|
||||||
|
- charms.tempo_k8s.v2.tracing
|
||||||
|
- charms.tempo_k8s.v1.charm_tracing
|
||||||
internal-libraries:
|
internal-libraries:
|
||||||
- charms.keystone_k8s.v1.identity_service
|
- charms.keystone_k8s.v1.identity_service
|
||||||
- charms.keystone_k8s.v0.identity_resource
|
- charms.keystone_k8s.v0.identity_resource
|
||||||
|
@ -60,6 +60,10 @@ requires:
|
|||||||
logging:
|
logging:
|
||||||
interface: loki_push_api
|
interface: loki_push_api
|
||||||
optional: true
|
optional: true
|
||||||
|
tracing:
|
||||||
|
interface: tracing
|
||||||
|
optional: true
|
||||||
|
limit: 1
|
||||||
|
|
||||||
peers:
|
peers:
|
||||||
peers:
|
peers:
|
||||||
|
@ -35,6 +35,7 @@ import ops_sunbeam.config_contexts as sunbeam_config_contexts
|
|||||||
import ops_sunbeam.container_handlers as sunbeam_chandlers
|
import ops_sunbeam.container_handlers as sunbeam_chandlers
|
||||||
import ops_sunbeam.core as sunbeam_core
|
import ops_sunbeam.core as sunbeam_core
|
||||||
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
||||||
|
import ops_sunbeam.tracing as sunbeam_tracing
|
||||||
from ops.framework import (
|
from ops.framework import (
|
||||||
StoredState,
|
StoredState,
|
||||||
)
|
)
|
||||||
@ -56,6 +57,7 @@ HEAT_API_PORT = 8004
|
|||||||
HEAT_API_CFN_PORT = 8000
|
HEAT_API_CFN_PORT = 8000
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class HeatAPIPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
class HeatAPIPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
||||||
"""Pebble handler for Heat API container."""
|
"""Pebble handler for Heat API container."""
|
||||||
|
|
||||||
@ -97,6 +99,7 @@ class HeatAPIPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class HeatCfnAPIPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
class HeatCfnAPIPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
||||||
"""Pebble handler for Heat CFN API container."""
|
"""Pebble handler for Heat CFN API container."""
|
||||||
|
|
||||||
@ -138,6 +141,7 @@ class HeatCfnAPIPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class HeatEnginePebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
class HeatEnginePebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
||||||
"""Pebble handler for Heat engine container."""
|
"""Pebble handler for Heat engine container."""
|
||||||
|
|
||||||
@ -162,6 +166,7 @@ class HeatEnginePebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class HeatConfigurationContext(sunbeam_config_contexts.ConfigContext):
|
class HeatConfigurationContext(sunbeam_config_contexts.ConfigContext):
|
||||||
"""Heat configuration context."""
|
"""Heat configuration context."""
|
||||||
|
|
||||||
@ -190,6 +195,7 @@ class HeatConfigurationContext(sunbeam_config_contexts.ConfigContext):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_sunbeam_charm
|
||||||
class HeatOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
|
class HeatOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
|
||||||
"""Charm the service."""
|
"""Charm the service."""
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@ external-libraries:
|
|||||||
- charms.traefik_k8s.v2.ingress
|
- charms.traefik_k8s.v2.ingress
|
||||||
- charms.certificate_transfer_interface.v0.certificate_transfer
|
- charms.certificate_transfer_interface.v0.certificate_transfer
|
||||||
- charms.loki_k8s.v1.loki_push_api
|
- charms.loki_k8s.v1.loki_push_api
|
||||||
|
- charms.tempo_k8s.v2.tracing
|
||||||
|
- charms.tempo_k8s.v1.charm_tracing
|
||||||
internal-libraries:
|
internal-libraries:
|
||||||
- charms.keystone_k8s.v0.identity_credentials
|
- charms.keystone_k8s.v0.identity_credentials
|
||||||
templates:
|
templates:
|
||||||
|
@ -49,6 +49,10 @@ requires:
|
|||||||
logging:
|
logging:
|
||||||
interface: loki_push_api
|
interface: loki_push_api
|
||||||
optional: true
|
optional: true
|
||||||
|
tracing:
|
||||||
|
interface: tracing
|
||||||
|
optional: true
|
||||||
|
limit: 1
|
||||||
|
|
||||||
provides:
|
provides:
|
||||||
horizon:
|
horizon:
|
||||||
|
@ -32,6 +32,7 @@ import ops_sunbeam.charm as sunbeam_charm
|
|||||||
import ops_sunbeam.container_handlers as sunbeam_chandlers
|
import ops_sunbeam.container_handlers as sunbeam_chandlers
|
||||||
import ops_sunbeam.core as sunbeam_core
|
import ops_sunbeam.core as sunbeam_core
|
||||||
import ops_sunbeam.guard as sunbeam_guard
|
import ops_sunbeam.guard as sunbeam_guard
|
||||||
|
import ops_sunbeam.tracing as sunbeam_tracing
|
||||||
from ops.main import (
|
from ops.main import (
|
||||||
main,
|
main,
|
||||||
)
|
)
|
||||||
@ -84,6 +85,7 @@ def manage_plugins(
|
|||||||
return tag in out
|
return tag in out
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class WSGIHorizonPebbleHandler(sunbeam_chandlers.WSGIPebbleHandler):
|
class WSGIHorizonPebbleHandler(sunbeam_chandlers.WSGIPebbleHandler):
|
||||||
"""Horizon Pebble Handler."""
|
"""Horizon Pebble Handler."""
|
||||||
|
|
||||||
@ -117,6 +119,7 @@ class WSGIHorizonPebbleHandler(sunbeam_chandlers.WSGIPebbleHandler):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_sunbeam_charm
|
||||||
class HorizonOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
|
class HorizonOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
|
||||||
"""Charm the service."""
|
"""Charm the service."""
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@ external-libraries:
|
|||||||
- charms.traefik_k8s.v2.ingress
|
- charms.traefik_k8s.v2.ingress
|
||||||
- charms.certificate_transfer_interface.v0.certificate_transfer
|
- charms.certificate_transfer_interface.v0.certificate_transfer
|
||||||
- charms.loki_k8s.v1.loki_push_api
|
- charms.loki_k8s.v1.loki_push_api
|
||||||
|
- charms.tempo_k8s.v2.tracing
|
||||||
|
- charms.tempo_k8s.v1.charm_tracing
|
||||||
templates:
|
templates:
|
||||||
- parts/section-database
|
- parts/section-database
|
||||||
- parts/database-connection
|
- parts/database-connection
|
||||||
|
@ -51,6 +51,10 @@ requires:
|
|||||||
logging:
|
logging:
|
||||||
interface: loki_push_api
|
interface: loki_push_api
|
||||||
optional: true
|
optional: true
|
||||||
|
tracing:
|
||||||
|
interface: tracing
|
||||||
|
optional: true
|
||||||
|
limit: 1
|
||||||
|
|
||||||
peers:
|
peers:
|
||||||
peers:
|
peers:
|
||||||
|
@ -59,6 +59,7 @@ import ops_sunbeam.core as sunbeam_core
|
|||||||
import ops_sunbeam.guard as sunbeam_guard
|
import ops_sunbeam.guard as sunbeam_guard
|
||||||
import ops_sunbeam.job_ctrl as sunbeam_job_ctrl
|
import ops_sunbeam.job_ctrl as sunbeam_job_ctrl
|
||||||
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
||||||
|
import ops_sunbeam.tracing as sunbeam_tracing
|
||||||
import pwgen
|
import pwgen
|
||||||
from charms.certificate_transfer_interface.v0.certificate_transfer import (
|
from charms.certificate_transfer_interface.v0.certificate_transfer import (
|
||||||
CertificateTransferProvides,
|
CertificateTransferProvides,
|
||||||
@ -96,6 +97,7 @@ KEYSTONE_CONF = "/etc/keystone/keystone.conf"
|
|||||||
LOGGING_CONF = "/etc/keystone/logging.conf"
|
LOGGING_CONF = "/etc/keystone/logging.conf"
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class KeystoneLoggingAdapter(sunbeam_contexts.ConfigContext):
|
class KeystoneLoggingAdapter(sunbeam_contexts.ConfigContext):
|
||||||
"""Config adapter to collect logging config."""
|
"""Config adapter to collect logging config."""
|
||||||
|
|
||||||
@ -117,6 +119,7 @@ class KeystoneLoggingAdapter(sunbeam_contexts.ConfigContext):
|
|||||||
return ctxt
|
return ctxt
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class KeystoneConfigAdapter(sunbeam_contexts.ConfigContext):
|
class KeystoneConfigAdapter(sunbeam_contexts.ConfigContext):
|
||||||
"""Config adapter to collect keystone config."""
|
"""Config adapter to collect keystone config."""
|
||||||
|
|
||||||
@ -150,6 +153,7 @@ class KeystoneConfigAdapter(sunbeam_contexts.ConfigContext):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class IdentityServiceProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
class IdentityServiceProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
||||||
"""Handler for identity service relation."""
|
"""Handler for identity service relation."""
|
||||||
|
|
||||||
@ -186,6 +190,7 @@ class IdentityServiceProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class DomainConfigHandler(sunbeam_rhandlers.RelationHandler):
|
class DomainConfigHandler(sunbeam_rhandlers.RelationHandler):
|
||||||
"""Handler for domain config relation."""
|
"""Handler for domain config relation."""
|
||||||
|
|
||||||
@ -228,6 +233,7 @@ class DomainConfigHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
return bool(self.get_domain_configs())
|
return bool(self.get_domain_configs())
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class IdentityCredentialsProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
class IdentityCredentialsProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
||||||
"""Handler for identity credentials relation."""
|
"""Handler for identity credentials relation."""
|
||||||
|
|
||||||
@ -264,6 +270,7 @@ class IdentityCredentialsProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class IdentityResourceProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
class IdentityResourceProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
||||||
"""Handler for identity resource relation."""
|
"""Handler for identity resource relation."""
|
||||||
|
|
||||||
@ -298,6 +305,7 @@ class IdentityResourceProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class WSGIKeystonePebbleHandler(sunbeam_chandlers.WSGIPebbleHandler):
|
class WSGIKeystonePebbleHandler(sunbeam_chandlers.WSGIPebbleHandler):
|
||||||
"""Keystone Pebble Handler."""
|
"""Keystone Pebble Handler."""
|
||||||
|
|
||||||
@ -316,6 +324,7 @@ class WSGIKeystonePebbleHandler(sunbeam_chandlers.WSGIPebbleHandler):
|
|||||||
super().init_service(context)
|
super().init_service(context)
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_sunbeam_charm(extra_types=(manager.KeystoneManager,))
|
||||||
class KeystoneOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
|
class KeystoneOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
|
||||||
"""Charm the service."""
|
"""Charm the service."""
|
||||||
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
external-libraries:
|
external-libraries:
|
||||||
- charms.loki_k8s.v1.loki_push_api
|
- charms.loki_k8s.v1.loki_push_api
|
||||||
|
- charms.tempo_k8s.v2.tracing
|
||||||
|
- charms.tempo_k8s.v1.charm_tracing
|
||||||
internal-libraries:
|
internal-libraries:
|
||||||
- charms.keystone_k8s.v0.domain_config
|
- charms.keystone_k8s.v0.domain_config
|
||||||
|
@ -16,3 +16,7 @@ requires:
|
|||||||
logging:
|
logging:
|
||||||
interface: loki_push_api
|
interface: loki_push_api
|
||||||
optional: true
|
optional: true
|
||||||
|
tracing:
|
||||||
|
interface: tracing
|
||||||
|
optional: true
|
||||||
|
limit: 1
|
||||||
|
@ -35,6 +35,7 @@ import ops.charm
|
|||||||
import ops_sunbeam.charm as sunbeam_charm
|
import ops_sunbeam.charm as sunbeam_charm
|
||||||
import ops_sunbeam.config_contexts as config_contexts
|
import ops_sunbeam.config_contexts as config_contexts
|
||||||
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
||||||
|
import ops_sunbeam.tracing as sunbeam_tracing
|
||||||
from ops.main import (
|
from ops.main import (
|
||||||
main,
|
main,
|
||||||
)
|
)
|
||||||
@ -43,6 +44,7 @@ from ops.main import (
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class LDAPConfigContext(config_contexts.ConfigContext):
|
class LDAPConfigContext(config_contexts.ConfigContext):
|
||||||
"""Configuration context for cinder parameters."""
|
"""Configuration context for cinder parameters."""
|
||||||
|
|
||||||
@ -58,6 +60,7 @@ class LDAPConfigContext(config_contexts.ConfigContext):
|
|||||||
return {"config": config}
|
return {"config": config}
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class DomainConfigProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
class DomainConfigProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
||||||
"""Handler for identity credentials relation."""
|
"""Handler for identity credentials relation."""
|
||||||
|
|
||||||
@ -72,7 +75,9 @@ class DomainConfigProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
def setup_event_handler(self):
|
def setup_event_handler(self):
|
||||||
"""Configure event handlers for a domain config relation."""
|
"""Configure event handlers for a domain config relation."""
|
||||||
logger.debug("Setting up domain config event handler")
|
logger.debug("Setting up domain config event handler")
|
||||||
self.domain_config = sunbeam_dc_svc.DomainConfigProvides(
|
self.domain_config = sunbeam_tracing.trace_type(
|
||||||
|
sunbeam_dc_svc.DomainConfigProvides
|
||||||
|
)(
|
||||||
self.charm,
|
self.charm,
|
||||||
self.relation_name,
|
self.relation_name,
|
||||||
)
|
)
|
||||||
@ -92,6 +97,7 @@ class DomainConfigProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_sunbeam_charm
|
||||||
class KeystoneLDAPK8SCharm(sunbeam_charm.OSBaseOperatorCharm):
|
class KeystoneLDAPK8SCharm(sunbeam_charm.OSBaseOperatorCharm):
|
||||||
"""Charm the service."""
|
"""Charm the service."""
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@ external-libraries:
|
|||||||
- charms.traefik_k8s.v2.ingress
|
- charms.traefik_k8s.v2.ingress
|
||||||
- charms.certificate_transfer_interface.v0.certificate_transfer
|
- charms.certificate_transfer_interface.v0.certificate_transfer
|
||||||
- charms.loki_k8s.v1.loki_push_api
|
- charms.loki_k8s.v1.loki_push_api
|
||||||
|
- charms.tempo_k8s.v2.tracing
|
||||||
|
- charms.tempo_k8s.v1.charm_tracing
|
||||||
internal-libraries:
|
internal-libraries:
|
||||||
- charms.keystone_k8s.v1.identity_service
|
- charms.keystone_k8s.v1.identity_service
|
||||||
- charms.keystone_k8s.v0.identity_resource
|
- charms.keystone_k8s.v0.identity_resource
|
||||||
|
@ -57,6 +57,10 @@ requires:
|
|||||||
logging:
|
logging:
|
||||||
interface: loki_push_api
|
interface: loki_push_api
|
||||||
optional: true
|
optional: true
|
||||||
|
tracing:
|
||||||
|
interface: tracing
|
||||||
|
optional: true
|
||||||
|
limit: 1
|
||||||
|
|
||||||
peers:
|
peers:
|
||||||
peers:
|
peers:
|
||||||
|
@ -29,6 +29,7 @@ import ops_sunbeam.config_contexts as sunbeam_config_contexts
|
|||||||
import ops_sunbeam.container_handlers as sunbeam_chandlers
|
import ops_sunbeam.container_handlers as sunbeam_chandlers
|
||||||
import ops_sunbeam.core as sunbeam_core
|
import ops_sunbeam.core as sunbeam_core
|
||||||
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
||||||
|
import ops_sunbeam.tracing as sunbeam_tracing
|
||||||
from ops.framework import (
|
from ops.framework import (
|
||||||
StoredState,
|
StoredState,
|
||||||
)
|
)
|
||||||
@ -43,6 +44,7 @@ MAGNUM_API_CONTAINER = "magnum-api"
|
|||||||
MAGNUM_CONDUCTOR_CONTAINER = "magnum-conductor"
|
MAGNUM_CONDUCTOR_CONTAINER = "magnum-conductor"
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class MagnumConfigurationContext(sunbeam_config_contexts.ConfigContext):
|
class MagnumConfigurationContext(sunbeam_config_contexts.ConfigContext):
|
||||||
"""Magnum configuration context."""
|
"""Magnum configuration context."""
|
||||||
|
|
||||||
@ -67,6 +69,7 @@ class MagnumConfigurationContext(sunbeam_config_contexts.ConfigContext):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class MagnumConductorPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
class MagnumConductorPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
||||||
"""Pebble handler for magnum worker."""
|
"""Pebble handler for magnum worker."""
|
||||||
|
|
||||||
@ -134,6 +137,7 @@ class MagnumConductorPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
|||||||
return self.pebble_ready
|
return self.pebble_ready
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_sunbeam_charm
|
||||||
class MagnumOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
|
class MagnumOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
|
||||||
"""Charm the service."""
|
"""Charm the service."""
|
||||||
|
|
||||||
|
@ -5,6 +5,8 @@ external-libraries:
|
|||||||
- charms.tls_certificates_interface.v3.tls_certificates
|
- charms.tls_certificates_interface.v3.tls_certificates
|
||||||
- charms.certificate_transfer_interface.v0.certificate_transfer
|
- charms.certificate_transfer_interface.v0.certificate_transfer
|
||||||
- charms.loki_k8s.v1.loki_push_api
|
- charms.loki_k8s.v1.loki_push_api
|
||||||
|
- charms.tempo_k8s.v2.tracing
|
||||||
|
- charms.tempo_k8s.v1.charm_tracing
|
||||||
internal-libraries:
|
internal-libraries:
|
||||||
- charms.keystone_k8s.v1.identity_service
|
- charms.keystone_k8s.v1.identity_service
|
||||||
- charms.ovn_central_k8s.v0.ovsdb
|
- charms.ovn_central_k8s.v0.ovsdb
|
||||||
|
@ -66,6 +66,10 @@ requires:
|
|||||||
logging:
|
logging:
|
||||||
interface: loki_push_api
|
interface: loki_push_api
|
||||||
optional: true
|
optional: true
|
||||||
|
tracing:
|
||||||
|
interface: tracing
|
||||||
|
optional: true
|
||||||
|
limit: 1
|
||||||
|
|
||||||
peers:
|
peers:
|
||||||
peers:
|
peers:
|
||||||
|
@ -36,6 +36,7 @@ import ops_sunbeam.guard as sunbeam_guard
|
|||||||
import ops_sunbeam.job_ctrl as sunbeam_job_ctrl
|
import ops_sunbeam.job_ctrl as sunbeam_job_ctrl
|
||||||
import ops_sunbeam.ovn.relation_handlers as ovn_rhandlers
|
import ops_sunbeam.ovn.relation_handlers as ovn_rhandlers
|
||||||
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
||||||
|
import ops_sunbeam.tracing as sunbeam_tracing
|
||||||
from ops.framework import (
|
from ops.framework import (
|
||||||
StoredState,
|
StoredState,
|
||||||
)
|
)
|
||||||
@ -49,6 +50,7 @@ from ops.model import (
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class DesignateServiceRequiresHandler(sunbeam_rhandlers.RelationHandler):
|
class DesignateServiceRequiresHandler(sunbeam_rhandlers.RelationHandler):
|
||||||
"""Handle external-dns relation on the requires side."""
|
"""Handle external-dns relation on the requires side."""
|
||||||
|
|
||||||
@ -80,7 +82,9 @@ class DesignateServiceRequiresHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
def setup_event_handler(self) -> None:
|
def setup_event_handler(self) -> None:
|
||||||
"""Configure event handlers for external-dns service relation."""
|
"""Configure event handlers for external-dns service relation."""
|
||||||
logger.debug("Setting up Designate service event handler")
|
logger.debug("Setting up Designate service event handler")
|
||||||
svc = designate_svc.DesignateServiceRequires(
|
svc = sunbeam_tracing.trace_type(
|
||||||
|
designate_svc.DesignateServiceRequires
|
||||||
|
)(
|
||||||
self.charm,
|
self.charm,
|
||||||
self.relation_name,
|
self.relation_name,
|
||||||
)
|
)
|
||||||
@ -117,6 +121,7 @@ class DesignateServiceRequiresHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class NeutronServerPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
class NeutronServerPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
||||||
"""Handler for interacting with pebble data."""
|
"""Handler for interacting with pebble data."""
|
||||||
|
|
||||||
@ -345,6 +350,7 @@ class NeutronOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
|
|||||||
# Neutron OVN Specific Code
|
# Neutron OVN Specific Code
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class OVNContext(sunbeam_ctxts.ConfigContext):
|
class OVNContext(sunbeam_ctxts.ConfigContext):
|
||||||
"""OVN configuration."""
|
"""OVN configuration."""
|
||||||
|
|
||||||
@ -380,6 +386,7 @@ class OVNContext(sunbeam_ctxts.ConfigContext):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class NeutronServerOVNPebbleHandler(NeutronServerPebbleHandler):
|
class NeutronServerOVNPebbleHandler(NeutronServerPebbleHandler):
|
||||||
"""Handler for interacting with neutron container."""
|
"""Handler for interacting with neutron container."""
|
||||||
|
|
||||||
@ -419,6 +426,7 @@ class NeutronServerOVNPebbleHandler(NeutronServerPebbleHandler):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_sunbeam_charm
|
||||||
class NeutronOVNOperatorCharm(NeutronOperatorCharm):
|
class NeutronOVNOperatorCharm(NeutronOperatorCharm):
|
||||||
"""Neutron charm class for OVN."""
|
"""Neutron charm class for OVN."""
|
||||||
|
|
||||||
|
@ -6,6 +6,8 @@ external-libraries:
|
|||||||
- charms.certificate_transfer_interface.v0.certificate_transfer
|
- charms.certificate_transfer_interface.v0.certificate_transfer
|
||||||
- charms.loki_k8s.v1.loki_push_api
|
- charms.loki_k8s.v1.loki_push_api
|
||||||
- charms.sunbeam_nova_compute_operator.v0.cloud_compute
|
- charms.sunbeam_nova_compute_operator.v0.cloud_compute
|
||||||
|
- charms.tempo_k8s.v2.tracing
|
||||||
|
- charms.tempo_k8s.v1.charm_tracing
|
||||||
internal-libraries:
|
internal-libraries:
|
||||||
- charms.keystone_k8s.v1.identity_service
|
- charms.keystone_k8s.v1.identity_service
|
||||||
templates:
|
templates:
|
||||||
|
@ -99,6 +99,10 @@ requires:
|
|||||||
logging:
|
logging:
|
||||||
interface: loki_push_api
|
interface: loki_push_api
|
||||||
optional: true
|
optional: true
|
||||||
|
tracing:
|
||||||
|
interface: tracing
|
||||||
|
optional: true
|
||||||
|
limit: 1
|
||||||
|
|
||||||
provides:
|
provides:
|
||||||
nova-service:
|
nova-service:
|
||||||
|
@ -34,6 +34,7 @@ import ops_sunbeam.config_contexts as sunbeam_ctxts
|
|||||||
import ops_sunbeam.container_handlers as sunbeam_chandlers
|
import ops_sunbeam.container_handlers as sunbeam_chandlers
|
||||||
import ops_sunbeam.core as sunbeam_core
|
import ops_sunbeam.core as sunbeam_core
|
||||||
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
||||||
|
import ops_sunbeam.tracing as sunbeam_tracing
|
||||||
from charms.nova_k8s.v0.nova_service import (
|
from charms.nova_k8s.v0.nova_service import (
|
||||||
NovaConfigRequestEvent,
|
NovaConfigRequestEvent,
|
||||||
NovaServiceProvides,
|
NovaServiceProvides,
|
||||||
@ -59,6 +60,7 @@ NOVA_SPICEPROXY_INGRESS_NAME = "nova-spiceproxy"
|
|||||||
NOVA_SPICEPROXY_INGRESS_PORT = 6082
|
NOVA_SPICEPROXY_INGRESS_PORT = 6082
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class WSGINovaMetadataConfigContext(sunbeam_ctxts.ConfigContext):
|
class WSGINovaMetadataConfigContext(sunbeam_ctxts.ConfigContext):
|
||||||
"""Configuration context for WSGI configuration."""
|
"""Configuration context for WSGI configuration."""
|
||||||
|
|
||||||
@ -76,6 +78,7 @@ class WSGINovaMetadataConfigContext(sunbeam_ctxts.ConfigContext):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class NovaSchedulerPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
class NovaSchedulerPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
||||||
"""Pebble handler for Nova scheduler."""
|
"""Pebble handler for Nova scheduler."""
|
||||||
|
|
||||||
@ -133,6 +136,7 @@ class NovaSchedulerPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
|||||||
return self.pebble_ready
|
return self.pebble_ready
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class NovaConductorPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
class NovaConductorPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
||||||
"""Pebble handler for Nova Conductor container."""
|
"""Pebble handler for Nova Conductor container."""
|
||||||
|
|
||||||
@ -176,6 +180,7 @@ class NovaConductorPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class NovaSpiceProxyPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
class NovaSpiceProxyPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
||||||
"""Pebble handler for Nova spice proxy."""
|
"""Pebble handler for Nova spice proxy."""
|
||||||
|
|
||||||
@ -233,6 +238,7 @@ class NovaSpiceProxyPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
|||||||
return self.pebble_ready
|
return self.pebble_ready
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class CloudComputeRequiresHandler(sunbeam_rhandlers.RelationHandler):
|
class CloudComputeRequiresHandler(sunbeam_rhandlers.RelationHandler):
|
||||||
"""Handles the cloud-compute relation on the requires side."""
|
"""Handles the cloud-compute relation on the requires side."""
|
||||||
|
|
||||||
@ -267,7 +273,9 @@ class CloudComputeRequiresHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
def setup_event_handler(self):
|
def setup_event_handler(self):
|
||||||
"""Configure event handlers for the cloud-compute service relation."""
|
"""Configure event handlers for the cloud-compute service relation."""
|
||||||
logger.debug("Setting up cloud-compute event handler")
|
logger.debug("Setting up cloud-compute event handler")
|
||||||
compute_service = cloud_compute.CloudComputeRequires(
|
compute_service = sunbeam_tracing.trace_type(
|
||||||
|
cloud_compute.CloudComputeRequires
|
||||||
|
)(
|
||||||
self.charm,
|
self.charm,
|
||||||
self.relation_name,
|
self.relation_name,
|
||||||
)
|
)
|
||||||
@ -293,6 +301,7 @@ class CloudComputeRequiresHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class NovaServiceProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
class NovaServiceProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
||||||
"""Handler for nova service relation."""
|
"""Handler for nova service relation."""
|
||||||
|
|
||||||
@ -307,7 +316,7 @@ class NovaServiceProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
def setup_event_handler(self):
|
def setup_event_handler(self):
|
||||||
"""Configure event handlers for nova service relation."""
|
"""Configure event handlers for nova service relation."""
|
||||||
logger.debug("Setting up Nova service event handler")
|
logger.debug("Setting up Nova service event handler")
|
||||||
svc = NovaServiceProvides(
|
svc = sunbeam_tracing.trace_type(NovaServiceProvides)(
|
||||||
self.charm,
|
self.charm,
|
||||||
self.relation_name,
|
self.relation_name,
|
||||||
)
|
)
|
||||||
@ -327,6 +336,7 @@ class NovaServiceProvidesHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_sunbeam_charm
|
||||||
class NovaOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
|
class NovaOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
|
||||||
"""Charm the service."""
|
"""Charm the service."""
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@ external-libraries:
|
|||||||
- charms.tls_certificates_interface.v3.tls_certificates
|
- charms.tls_certificates_interface.v3.tls_certificates
|
||||||
- charms.certificate_transfer_interface.v0.certificate_transfer
|
- charms.certificate_transfer_interface.v0.certificate_transfer
|
||||||
- charms.loki_k8s.v1.loki_push_api
|
- charms.loki_k8s.v1.loki_push_api
|
||||||
|
- charms.tempo_k8s.v2.tracing
|
||||||
|
- charms.tempo_k8s.v1.charm_tracing
|
||||||
internal-libraries:
|
internal-libraries:
|
||||||
- charms.keystone_k8s.v1.identity_service
|
- charms.keystone_k8s.v1.identity_service
|
||||||
- charms.keystone_k8s.v0.identity_resource
|
- charms.keystone_k8s.v0.identity_resource
|
||||||
|
@ -82,6 +82,10 @@ requires:
|
|||||||
logging:
|
logging:
|
||||||
interface: loki_push_api
|
interface: loki_push_api
|
||||||
optional: true
|
optional: true
|
||||||
|
tracing:
|
||||||
|
interface: tracing
|
||||||
|
optional: true
|
||||||
|
limit: 1
|
||||||
|
|
||||||
peers:
|
peers:
|
||||||
peers:
|
peers:
|
||||||
|
@ -33,6 +33,7 @@ import ops_sunbeam.container_handlers as sunbeam_chandlers
|
|||||||
import ops_sunbeam.core as sunbeam_core
|
import ops_sunbeam.core as sunbeam_core
|
||||||
import ops_sunbeam.ovn.relation_handlers as ovn_rhandlers
|
import ops_sunbeam.ovn.relation_handlers as ovn_rhandlers
|
||||||
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
||||||
|
import ops_sunbeam.tracing as sunbeam_tracing
|
||||||
from ops.framework import (
|
from ops.framework import (
|
||||||
StoredState,
|
StoredState,
|
||||||
)
|
)
|
||||||
@ -47,6 +48,7 @@ OCTAVIA_HOUSEKEEPING_CONTAINER = "octavia-housekeeping"
|
|||||||
OCTAVIA_AGENT_SOCKET_DIR = "/var/run/octavia"
|
OCTAVIA_AGENT_SOCKET_DIR = "/var/run/octavia"
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class OctaviaDriverAgentPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
class OctaviaDriverAgentPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
||||||
"""Pebble handler for Octavia Driver Agent."""
|
"""Pebble handler for Octavia Driver Agent."""
|
||||||
|
|
||||||
@ -72,6 +74,7 @@ class OctaviaDriverAgentPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class OctaviaHousekeepingPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
class OctaviaHousekeepingPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
||||||
"""Pebble handler for Octavia Housekeeping."""
|
"""Pebble handler for Octavia Housekeeping."""
|
||||||
|
|
||||||
@ -97,6 +100,7 @@ class OctaviaHousekeepingPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class OVNContext(sunbeam_config_contexts.ConfigContext):
|
class OVNContext(sunbeam_config_contexts.ConfigContext):
|
||||||
"""OVN configuration."""
|
"""OVN configuration."""
|
||||||
|
|
||||||
@ -295,6 +299,7 @@ class OctaviaOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
|
|||||||
return ops
|
return ops
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_sunbeam_charm
|
||||||
class OctaviaOVNOperatorCharm(OctaviaOperatorCharm):
|
class OctaviaOVNOperatorCharm(OctaviaOperatorCharm):
|
||||||
"""Charm the Octavia service with OVN provider."""
|
"""Charm the Octavia service with OVN provider."""
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@ external-libraries:
|
|||||||
- charms.tls_certificates_interface.v3.tls_certificates
|
- charms.tls_certificates_interface.v3.tls_certificates
|
||||||
- charms.certificate_transfer_interface.v0.certificate_transfer
|
- charms.certificate_transfer_interface.v0.certificate_transfer
|
||||||
- charms.loki_k8s.v1.loki_push_api
|
- charms.loki_k8s.v1.loki_push_api
|
||||||
|
- charms.tempo_k8s.v2.tracing
|
||||||
|
- charms.tempo_k8s.v1.charm_tracing
|
||||||
internal-libraries:
|
internal-libraries:
|
||||||
- charms.keystone_k8s.v0.identity_resource
|
- charms.keystone_k8s.v0.identity_resource
|
||||||
templates:
|
templates:
|
||||||
|
@ -34,6 +34,10 @@ requires:
|
|||||||
logging:
|
logging:
|
||||||
interface: loki_push_api
|
interface: loki_push_api
|
||||||
optional: true
|
optional: true
|
||||||
|
tracing:
|
||||||
|
interface: tracing
|
||||||
|
optional: true
|
||||||
|
limit: 1
|
||||||
|
|
||||||
provides:
|
provides:
|
||||||
metrics-endpoint:
|
metrics-endpoint:
|
||||||
|
@ -33,6 +33,7 @@ import ops_sunbeam.config_contexts as sunbeam_config_contexts
|
|||||||
import ops_sunbeam.container_handlers as sunbeam_chandlers
|
import ops_sunbeam.container_handlers as sunbeam_chandlers
|
||||||
import ops_sunbeam.core as sunbeam_core
|
import ops_sunbeam.core as sunbeam_core
|
||||||
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
||||||
|
import ops_sunbeam.tracing as sunbeam_tracing
|
||||||
from ops.main import (
|
from ops.main import (
|
||||||
main,
|
main,
|
||||||
)
|
)
|
||||||
@ -43,6 +44,7 @@ CONFIGURE_SECRET_PREFIX = "configure-"
|
|||||||
CONTAINER = "openstack-exporter"
|
CONTAINER = "openstack-exporter"
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class OSExporterConfigurationContext(sunbeam_config_contexts.ConfigContext):
|
class OSExporterConfigurationContext(sunbeam_config_contexts.ConfigContext):
|
||||||
"""OSExporter configuration context."""
|
"""OSExporter configuration context."""
|
||||||
|
|
||||||
@ -75,6 +77,7 @@ class OSExporterConfigurationContext(sunbeam_config_contexts.ConfigContext):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class OSExporterPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
class OSExporterPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
||||||
"""Pebble handler for the container."""
|
"""Pebble handler for the container."""
|
||||||
|
|
||||||
@ -107,6 +110,7 @@ class OSExporterPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class MetricsEndpointRelationHandler(sunbeam_rhandlers.RelationHandler):
|
class MetricsEndpointRelationHandler(sunbeam_rhandlers.RelationHandler):
|
||||||
"""Relation handler for Metrics Endpoint relation."""
|
"""Relation handler for Metrics Endpoint relation."""
|
||||||
|
|
||||||
@ -117,9 +121,9 @@ class MetricsEndpointRelationHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
def setup_event_handler(self) -> ops.Object:
|
def setup_event_handler(self) -> ops.Object:
|
||||||
"""Configure event handlers for the relation."""
|
"""Configure event handlers for the relation."""
|
||||||
logger.debug("Setting up Metrics Endpoint event handler")
|
logger.debug("Setting up Metrics Endpoint event handler")
|
||||||
interface = prometheus_scrape.MetricsEndpointProvider(
|
interface = sunbeam_tracing.trace_type(
|
||||||
self.charm, jobs=self.charm._scrape_jobs
|
prometheus_scrape.MetricsEndpointProvider
|
||||||
)
|
)(self.charm, jobs=self.charm._scrape_jobs)
|
||||||
|
|
||||||
return interface
|
return interface
|
||||||
|
|
||||||
@ -129,6 +133,7 @@ class MetricsEndpointRelationHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class GrafanaDashboardsRelationHandler(sunbeam_rhandlers.RelationHandler):
|
class GrafanaDashboardsRelationHandler(sunbeam_rhandlers.RelationHandler):
|
||||||
"""Relation handler for Grafana Dashboards relation."""
|
"""Relation handler for Grafana Dashboards relation."""
|
||||||
|
|
||||||
@ -137,7 +142,9 @@ class GrafanaDashboardsRelationHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
def setup_event_handler(self) -> ops.Object:
|
def setup_event_handler(self) -> ops.Object:
|
||||||
"""Configure event handlers for the relation."""
|
"""Configure event handlers for the relation."""
|
||||||
logger.debug("Setting up Grafana Dashboard event handler")
|
logger.debug("Setting up Grafana Dashboard event handler")
|
||||||
interface = grafana_dashboard.GrafanaDashboardProvider(
|
interface = sunbeam_tracing.trace_type(
|
||||||
|
grafana_dashboard.GrafanaDashboardProvider
|
||||||
|
)(
|
||||||
self.charm,
|
self.charm,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -149,6 +156,7 @@ class GrafanaDashboardsRelationHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_sunbeam_charm
|
||||||
class OSExporterOperatorCharm(sunbeam_charm.OSBaseOperatorCharmK8S):
|
class OSExporterOperatorCharm(sunbeam_charm.OSBaseOperatorCharmK8S):
|
||||||
"""Charm the service."""
|
"""Charm the service."""
|
||||||
|
|
||||||
|
@ -7,6 +7,8 @@ external-libraries:
|
|||||||
- charms.traefik_k8s.v2.ingress
|
- charms.traefik_k8s.v2.ingress
|
||||||
- charms.tls_certificates_interface.v3.tls_certificates
|
- charms.tls_certificates_interface.v3.tls_certificates
|
||||||
- charms.certificate_transfer_interface.v0.certificate_transfer
|
- charms.certificate_transfer_interface.v0.certificate_transfer
|
||||||
|
- charms.tempo_k8s.v2.tracing
|
||||||
|
- charms.tempo_k8s.v1.charm_tracing
|
||||||
internal-libraries:
|
internal-libraries:
|
||||||
- charms.keystone_k8s.v0.identity_credentials
|
- charms.keystone_k8s.v0.identity_credentials
|
||||||
- charms.ovn_central_k8s.v0.ovsdb
|
- charms.ovn_central_k8s.v0.ovsdb
|
||||||
|
@ -28,6 +28,10 @@ requires:
|
|||||||
optional: true
|
optional: true
|
||||||
nova-service:
|
nova-service:
|
||||||
interface: nova
|
interface: nova
|
||||||
|
tracing:
|
||||||
|
interface: tracing
|
||||||
|
optional: true
|
||||||
|
limit: 1
|
||||||
|
|
||||||
provides:
|
provides:
|
||||||
cos-agent:
|
cos-agent:
|
||||||
|
@ -40,6 +40,7 @@ import ops_sunbeam.core as sunbeam_core
|
|||||||
import ops_sunbeam.guard as sunbeam_guard
|
import ops_sunbeam.guard as sunbeam_guard
|
||||||
import ops_sunbeam.ovn.relation_handlers as ovn_relation_handlers
|
import ops_sunbeam.ovn.relation_handlers as ovn_relation_handlers
|
||||||
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
||||||
|
import ops_sunbeam.tracing as sunbeam_tracing
|
||||||
from charms.ceilometer_k8s.v0.ceilometer_service import (
|
from charms.ceilometer_k8s.v0.ceilometer_service import (
|
||||||
CeilometerConfigChangedEvent,
|
CeilometerConfigChangedEvent,
|
||||||
CeilometerServiceGoneAwayEvent,
|
CeilometerServiceGoneAwayEvent,
|
||||||
@ -70,6 +71,7 @@ MIGRATION_BINDING = "migration"
|
|||||||
MTLS_USAGES = {x509.OID_SERVER_AUTH, x509.OID_CLIENT_AUTH}
|
MTLS_USAGES = {x509.OID_SERVER_AUTH, x509.OID_CLIENT_AUTH}
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class MTlsCertificatesHandler(sunbeam_rhandlers.TlsCertificatesHandler):
|
class MTlsCertificatesHandler(sunbeam_rhandlers.TlsCertificatesHandler):
|
||||||
"""Handler for certificates interface."""
|
"""Handler for certificates interface."""
|
||||||
|
|
||||||
@ -143,6 +145,7 @@ class MTlsCertificatesHandler(sunbeam_rhandlers.TlsCertificatesHandler):
|
|||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_sunbeam_charm(extra_types=(snap.SnapCache, snap.Snap))
|
||||||
class HypervisorOperatorCharm(sunbeam_charm.OSBaseOperatorCharm):
|
class HypervisorOperatorCharm(sunbeam_charm.OSBaseOperatorCharm):
|
||||||
"""Charm the service."""
|
"""Charm the service."""
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
external-libraries:
|
external-libraries:
|
||||||
- charms.traefik_k8s.v2.ingress
|
- charms.traefik_k8s.v2.ingress
|
||||||
- charms.loki_k8s.v1.loki_push_api
|
- charms.loki_k8s.v1.loki_push_api
|
||||||
|
- charms.tempo_k8s.v2.tracing
|
||||||
|
- charms.tempo_k8s.v1.charm_tracing
|
||||||
internal-libraries:
|
internal-libraries:
|
||||||
- charms.keystone_k8s.v1.identity_service
|
- charms.keystone_k8s.v1.identity_service
|
||||||
|
@ -17,6 +17,20 @@ bases:
|
|||||||
- name: ubuntu
|
- name: ubuntu
|
||||||
channel: "22.04"
|
channel: "22.04"
|
||||||
|
|
||||||
|
parts:
|
||||||
|
charm:
|
||||||
|
build-packages:
|
||||||
|
- git
|
||||||
|
- libffi-dev
|
||||||
|
- libssl-dev
|
||||||
|
- pkg-config
|
||||||
|
- rustc
|
||||||
|
- cargo
|
||||||
|
charm-binary-python-packages:
|
||||||
|
- cryptography
|
||||||
|
- jsonschema
|
||||||
|
- jinja2
|
||||||
|
|
||||||
config:
|
config:
|
||||||
options:
|
options:
|
||||||
debug:
|
debug:
|
||||||
@ -76,3 +90,7 @@ requires:
|
|||||||
logging:
|
logging:
|
||||||
interface: loki_push_api
|
interface: loki_push_api
|
||||||
optional: true
|
optional: true
|
||||||
|
tracing:
|
||||||
|
interface: tracing
|
||||||
|
optional: true
|
||||||
|
limit: 1
|
||||||
|
@ -31,6 +31,7 @@ import ops_sunbeam.charm as sunbeam_charm
|
|||||||
import ops_sunbeam.config_contexts as sunbeam_config_contexts
|
import ops_sunbeam.config_contexts as sunbeam_config_contexts
|
||||||
import ops_sunbeam.container_handlers as sunbeam_chandlers
|
import ops_sunbeam.container_handlers as sunbeam_chandlers
|
||||||
import ops_sunbeam.core as sunbeam_core
|
import ops_sunbeam.core as sunbeam_core
|
||||||
|
import ops_sunbeam.tracing as sunbeam_tracing
|
||||||
from charms.keystone_k8s.v1.identity_service import (
|
from charms.keystone_k8s.v1.identity_service import (
|
||||||
IdentityServiceRequires,
|
IdentityServiceRequires,
|
||||||
)
|
)
|
||||||
@ -56,6 +57,7 @@ def _frequency_to_seconds(frequency: str) -> int:
|
|||||||
raise ValueError(f"Unknown frequency {frequency!r}")
|
raise ValueError(f"Unknown frequency {frequency!r}")
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class SyncCharmConfigContext(sunbeam_config_contexts.CharmConfigContext):
|
class SyncCharmConfigContext(sunbeam_config_contexts.CharmConfigContext):
|
||||||
"""Configure context for templates."""
|
"""Configure context for templates."""
|
||||||
|
|
||||||
@ -72,6 +74,7 @@ class SyncCharmConfigContext(sunbeam_config_contexts.CharmConfigContext):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class HttpSyncConfigContext(sunbeam_config_contexts.ConfigContext):
|
class HttpSyncConfigContext(sunbeam_config_contexts.ConfigContext):
|
||||||
"""Configuration context for the http sync service."""
|
"""Configuration context for the http sync service."""
|
||||||
|
|
||||||
@ -88,6 +91,7 @@ class HttpSyncConfigContext(sunbeam_config_contexts.ConfigContext):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class OpenstackImagesSyncPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
class OpenstackImagesSyncPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
||||||
"""Handler for openstack images sync container."""
|
"""Handler for openstack images sync container."""
|
||||||
|
|
||||||
@ -146,6 +150,7 @@ class OpenstackImagesSyncPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
|||||||
return super().init_service(context)
|
return super().init_service(context)
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_sunbeam_charm
|
||||||
class OpenstackImagesSyncK8SCharm(sunbeam_charm.OSBaseOperatorAPICharm):
|
class OpenstackImagesSyncK8SCharm(sunbeam_charm.OSBaseOperatorAPICharm):
|
||||||
"""Charm the application."""
|
"""Charm the application."""
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
external-libraries:
|
external-libraries:
|
||||||
- charms.tls_certificates_interface.v3.tls_certificates
|
- charms.tls_certificates_interface.v3.tls_certificates
|
||||||
- charms.loki_k8s.v1.loki_push_api
|
- charms.loki_k8s.v1.loki_push_api
|
||||||
|
- charms.tempo_k8s.v2.tracing
|
||||||
|
- charms.tempo_k8s.v1.charm_tracing
|
||||||
|
@ -72,6 +72,10 @@ requires:
|
|||||||
logging:
|
logging:
|
||||||
interface: loki_push_api
|
interface: loki_push_api
|
||||||
optional: true
|
optional: true
|
||||||
|
tracing:
|
||||||
|
interface: tracing
|
||||||
|
optional: true
|
||||||
|
limit: 1
|
||||||
|
|
||||||
provides:
|
provides:
|
||||||
ovsdb:
|
ovsdb:
|
||||||
|
@ -35,6 +35,7 @@ import ops_sunbeam.ovn.config_contexts as ovn_ctxts
|
|||||||
import ops_sunbeam.ovn.container_handlers as ovn_chandlers
|
import ops_sunbeam.ovn.container_handlers as ovn_chandlers
|
||||||
import ops_sunbeam.ovn.relation_handlers as ovn_rhandlers
|
import ops_sunbeam.ovn.relation_handlers as ovn_rhandlers
|
||||||
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
||||||
|
import ops_sunbeam.tracing as sunbeam_tracing
|
||||||
import ovn
|
import ovn
|
||||||
import ovsdb as ch_ovsdb
|
import ovsdb as ch_ovsdb
|
||||||
import tenacity
|
import tenacity
|
||||||
@ -53,6 +54,7 @@ OVN_NORTHD_CONTAINER = "ovn-northd"
|
|||||||
OVN_DB_CONTAINERS = [OVN_SB_DB_CONTAINER, OVN_NB_DB_CONTAINER]
|
OVN_DB_CONTAINERS = [OVN_SB_DB_CONTAINER, OVN_NB_DB_CONTAINER]
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class OVNNorthBPebbleHandler(ovn_chandlers.OVNPebbleHandler):
|
class OVNNorthBPebbleHandler(ovn_chandlers.OVNPebbleHandler):
|
||||||
"""Handler for North OVN DB."""
|
"""Handler for North OVN DB."""
|
||||||
|
|
||||||
@ -82,6 +84,7 @@ class OVNNorthBPebbleHandler(ovn_chandlers.OVNPebbleHandler):
|
|||||||
return _cc
|
return _cc
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class OVNNorthBDBPebbleHandler(ovn_chandlers.OVNPebbleHandler):
|
class OVNNorthBDBPebbleHandler(ovn_chandlers.OVNPebbleHandler):
|
||||||
"""Handler for North-bound OVN DB."""
|
"""Handler for North-bound OVN DB."""
|
||||||
|
|
||||||
@ -129,6 +132,7 @@ class OVNNorthBDBPebbleHandler(ovn_chandlers.OVNPebbleHandler):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class OVNSouthBDBPebbleHandler(ovn_chandlers.OVNPebbleHandler):
|
class OVNSouthBDBPebbleHandler(ovn_chandlers.OVNPebbleHandler):
|
||||||
"""Handler for South-bound OVN DB."""
|
"""Handler for South-bound OVN DB."""
|
||||||
|
|
||||||
@ -176,6 +180,7 @@ class OVNSouthBDBPebbleHandler(ovn_chandlers.OVNPebbleHandler):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_sunbeam_charm
|
||||||
class OVNCentralOperatorCharm(sunbeam_charm.OSBaseOperatorCharmK8S):
|
class OVNCentralOperatorCharm(sunbeam_charm.OSBaseOperatorCharmK8S):
|
||||||
"""Charm the service."""
|
"""Charm the service."""
|
||||||
|
|
||||||
|
@ -2,5 +2,7 @@ external-libraries:
|
|||||||
- charms.tls_certificates_interface.v3.tls_certificates
|
- charms.tls_certificates_interface.v3.tls_certificates
|
||||||
- charms.observability_libs.v1.kubernetes_service_patch
|
- charms.observability_libs.v1.kubernetes_service_patch
|
||||||
- charms.loki_k8s.v1.loki_push_api
|
- charms.loki_k8s.v1.loki_push_api
|
||||||
|
- charms.tempo_k8s.v2.tracing
|
||||||
|
- charms.tempo_k8s.v1.charm_tracing
|
||||||
internal-libraries:
|
internal-libraries:
|
||||||
- charms.ovn_central_k8s.v0.ovsdb
|
- charms.ovn_central_k8s.v0.ovsdb
|
||||||
|
@ -37,6 +37,10 @@ requires:
|
|||||||
logging:
|
logging:
|
||||||
interface: loki_push_api
|
interface: loki_push_api
|
||||||
optional: true
|
optional: true
|
||||||
|
tracing:
|
||||||
|
interface: tracing
|
||||||
|
optional: true
|
||||||
|
limit: 1
|
||||||
|
|
||||||
provides:
|
provides:
|
||||||
ovsdb-cms-relay:
|
ovsdb-cms-relay:
|
||||||
|
@ -42,6 +42,7 @@ import ops_sunbeam.ovn.config_contexts as ovn_ctxts
|
|||||||
import ops_sunbeam.ovn.container_handlers as ovn_chandlers
|
import ops_sunbeam.ovn.container_handlers as ovn_chandlers
|
||||||
import ops_sunbeam.ovn.relation_handlers as ovn_relation_handlers
|
import ops_sunbeam.ovn.relation_handlers as ovn_relation_handlers
|
||||||
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
||||||
|
import ops_sunbeam.tracing as sunbeam_tracing
|
||||||
from charms.observability_libs.v1.kubernetes_service_patch import (
|
from charms.observability_libs.v1.kubernetes_service_patch import (
|
||||||
KubernetesServicePatch,
|
KubernetesServicePatch,
|
||||||
)
|
)
|
||||||
@ -57,6 +58,7 @@ logger = logging.getLogger(__name__)
|
|||||||
OVSDB_SERVER = "ovsdb-server"
|
OVSDB_SERVER = "ovsdb-server"
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class OVNRelayPebbleHandler(ovn_chandlers.OVNPebbleHandler):
|
class OVNRelayPebbleHandler(ovn_chandlers.OVNPebbleHandler):
|
||||||
"""Handler for OVN Relay container."""
|
"""Handler for OVN Relay container."""
|
||||||
|
|
||||||
@ -89,6 +91,7 @@ class OVNRelayPebbleHandler(ovn_chandlers.OVNPebbleHandler):
|
|||||||
self.start_service()
|
self.start_service()
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_sunbeam_charm
|
||||||
class OVNRelayOperatorCharm(ovn_charm.OSBaseOVNOperatorCharm):
|
class OVNRelayOperatorCharm(ovn_charm.OSBaseOVNOperatorCharm):
|
||||||
"""Charm the service."""
|
"""Charm the service."""
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@ external-libraries:
|
|||||||
- charms.traefik_k8s.v2.ingress
|
- charms.traefik_k8s.v2.ingress
|
||||||
- charms.certificate_transfer_interface.v0.certificate_transfer
|
- charms.certificate_transfer_interface.v0.certificate_transfer
|
||||||
- charms.loki_k8s.v1.loki_push_api
|
- charms.loki_k8s.v1.loki_push_api
|
||||||
|
- charms.tempo_k8s.v2.tracing
|
||||||
|
- charms.tempo_k8s.v1.charm_tracing
|
||||||
internal-libraries:
|
internal-libraries:
|
||||||
- charms.keystone_k8s.v1.identity_service
|
- charms.keystone_k8s.v1.identity_service
|
||||||
templates:
|
templates:
|
||||||
|
@ -46,6 +46,10 @@ requires:
|
|||||||
logging:
|
logging:
|
||||||
interface: loki_push_api
|
interface: loki_push_api
|
||||||
optional: true
|
optional: true
|
||||||
|
tracing:
|
||||||
|
interface: tracing
|
||||||
|
optional: true
|
||||||
|
limit: 1
|
||||||
|
|
||||||
provides:
|
provides:
|
||||||
placement:
|
placement:
|
||||||
|
@ -29,6 +29,7 @@ import ops.pebble
|
|||||||
import ops_sunbeam.charm as sunbeam_charm
|
import ops_sunbeam.charm as sunbeam_charm
|
||||||
import ops_sunbeam.container_handlers as sunbeam_chandlers
|
import ops_sunbeam.container_handlers as sunbeam_chandlers
|
||||||
import ops_sunbeam.core as sunbeam_core
|
import ops_sunbeam.core as sunbeam_core
|
||||||
|
import ops_sunbeam.tracing as sunbeam_tracing
|
||||||
from ops.framework import (
|
from ops.framework import (
|
||||||
StoredState,
|
StoredState,
|
||||||
)
|
)
|
||||||
@ -39,6 +40,7 @@ from ops.main import (
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class WSGIPlacementPebbleHandler(sunbeam_chandlers.WSGIPebbleHandler):
|
class WSGIPlacementPebbleHandler(sunbeam_chandlers.WSGIPebbleHandler):
|
||||||
"""Placement Pebble Handler."""
|
"""Placement Pebble Handler."""
|
||||||
|
|
||||||
@ -59,6 +61,7 @@ class WSGIPlacementPebbleHandler(sunbeam_chandlers.WSGIPebbleHandler):
|
|||||||
super().init_service(context)
|
super().init_service(context)
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_sunbeam_charm
|
||||||
class PlacementOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
|
class PlacementOperatorCharm(sunbeam_charm.OSBaseOperatorAPICharm):
|
||||||
"""Charm the service."""
|
"""Charm the service."""
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
external-libraries:
|
external-libraries:
|
||||||
- charms.operator_libs_linux.v2.snap
|
- charms.operator_libs_linux.v2.snap
|
||||||
- charms.tls_certificates_interface.v3.tls_certificates
|
- charms.tls_certificates_interface.v3.tls_certificates
|
||||||
|
- charms.tempo_k8s.v2.tracing
|
||||||
|
- charms.tempo_k8s.v1.charm_tracing
|
||||||
|
@ -57,3 +57,7 @@ requires:
|
|||||||
certificates:
|
certificates:
|
||||||
interface: tls-certificates
|
interface: tls-certificates
|
||||||
optional: True
|
optional: True
|
||||||
|
tracing:
|
||||||
|
interface: tracing
|
||||||
|
optional: true
|
||||||
|
limit: 1
|
||||||
|
@ -33,6 +33,7 @@ import ops.framework
|
|||||||
import ops_sunbeam.charm as sunbeam_charm
|
import ops_sunbeam.charm as sunbeam_charm
|
||||||
import ops_sunbeam.guard as sunbeam_guard
|
import ops_sunbeam.guard as sunbeam_guard
|
||||||
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
||||||
|
import ops_sunbeam.tracing as sunbeam_tracing
|
||||||
import requests
|
import requests
|
||||||
import tenacity
|
import tenacity
|
||||||
from charms.operator_libs_linux.v2 import (
|
from charms.operator_libs_linux.v2 import (
|
||||||
@ -64,6 +65,7 @@ def _identity(x: bool) -> bool:
|
|||||||
return x
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class ClusterCertificatesHandler(sunbeam_rhandlers.TlsCertificatesHandler):
|
class ClusterCertificatesHandler(sunbeam_rhandlers.TlsCertificatesHandler):
|
||||||
"""Handler for certificates interface."""
|
"""Handler for certificates interface."""
|
||||||
|
|
||||||
@ -145,6 +147,7 @@ class ClusterCertificatesHandler(sunbeam_rhandlers.TlsCertificatesHandler):
|
|||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_sunbeam_charm
|
||||||
class SunbeamClusterdCharm(sunbeam_charm.OSBaseOperatorCharm):
|
class SunbeamClusterdCharm(sunbeam_charm.OSBaseOperatorCharm):
|
||||||
"""Charm the service."""
|
"""Charm the service."""
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ import ops
|
|||||||
import ops_sunbeam.charm as sunbeam_charm
|
import ops_sunbeam.charm as sunbeam_charm
|
||||||
import ops_sunbeam.interfaces as sunbeam_interfaces
|
import ops_sunbeam.interfaces as sunbeam_interfaces
|
||||||
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
||||||
|
import ops_sunbeam.tracing as sunbeam_tracing
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -133,6 +134,7 @@ class ClusterdPeers(sunbeam_interfaces.OperatorPeers):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class ClusterdPeerHandler(sunbeam_rhandlers.BasePeerHandler):
|
class ClusterdPeerHandler(sunbeam_rhandlers.BasePeerHandler):
|
||||||
"""Base handler for managing a peers relation."""
|
"""Base handler for managing a peers relation."""
|
||||||
|
|
||||||
@ -151,7 +153,7 @@ class ClusterdPeerHandler(sunbeam_rhandlers.BasePeerHandler):
|
|||||||
def setup_event_handler(self) -> ops.Object:
|
def setup_event_handler(self) -> ops.Object:
|
||||||
"""Configure event handlers for peer relation."""
|
"""Configure event handlers for peer relation."""
|
||||||
logger.debug("Setting up peer event handler")
|
logger.debug("Setting up peer event handler")
|
||||||
peer_int = ClusterdPeers(self.charm, self.relation_name) # type: ignore
|
peer_int = sunbeam_tracing.trace_type(ClusterdPeers(self.charm, self.relation_name)) # type: ignore
|
||||||
|
|
||||||
self.framework.observe(peer_int.on.add_node, self._on_add_node)
|
self.framework.observe(peer_int.on.add_node, self._on_add_node)
|
||||||
self.framework.observe(peer_int.on.node_added, self._on_node_added)
|
self.framework.observe(peer_int.on.node_added, self._on_node_added)
|
||||||
|
@ -1,2 +1,4 @@
|
|||||||
external-libraries:
|
external-libraries:
|
||||||
- charms.operator_libs_linux.v0.sysctl
|
- charms.operator_libs_linux.v0.sysctl
|
||||||
|
- charms.tempo_k8s.v2.tracing
|
||||||
|
- charms.tempo_k8s.v1.charm_tracing
|
||||||
|
@ -9,3 +9,9 @@ description: |
|
|||||||
|
|
||||||
# This charm has no peer relation by design. This charm needs to scale to
|
# This charm has no peer relation by design. This charm needs to scale to
|
||||||
# hundreds of units and this is limited by the peer relation.
|
# hundreds of units and this is limited by the peer relation.
|
||||||
|
|
||||||
|
requires:
|
||||||
|
tracing:
|
||||||
|
interface: tracing
|
||||||
|
optional: true
|
||||||
|
limit: 1
|
||||||
|
@ -26,6 +26,7 @@ import logging
|
|||||||
import ops.framework
|
import ops.framework
|
||||||
import ops_sunbeam.charm as sunbeam_charm
|
import ops_sunbeam.charm as sunbeam_charm
|
||||||
import ops_sunbeam.guard as sunbeam_guard
|
import ops_sunbeam.guard as sunbeam_guard
|
||||||
|
import ops_sunbeam.tracing as sunbeam_tracing
|
||||||
from charms.operator_libs_linux.v0 import (
|
from charms.operator_libs_linux.v0 import (
|
||||||
sysctl,
|
sysctl,
|
||||||
)
|
)
|
||||||
@ -37,6 +38,7 @@ ETC_ENVIRONMENT = "/etc/environment"
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_sunbeam_charm
|
||||||
class SunbeamMachineCharm(sunbeam_charm.OSBaseOperatorCharm):
|
class SunbeamMachineCharm(sunbeam_charm.OSBaseOperatorCharm):
|
||||||
"""Charm the service."""
|
"""Charm the service."""
|
||||||
|
|
||||||
|
@ -3,6 +3,8 @@ external-libraries:
|
|||||||
- charms.grafana_k8s.v0.grafana_dashboard
|
- charms.grafana_k8s.v0.grafana_dashboard
|
||||||
- charms.loki_k8s.v1.loki_push_api
|
- charms.loki_k8s.v1.loki_push_api
|
||||||
- charms.certificate_transfer_interface.v0.certificate_transfer
|
- charms.certificate_transfer_interface.v0.certificate_transfer
|
||||||
|
- charms.tempo_k8s.v2.tracing
|
||||||
|
- charms.tempo_k8s.v1.charm_tracing
|
||||||
internal-libraries:
|
internal-libraries:
|
||||||
- charms.keystone_k8s.v0.identity_resource
|
- charms.keystone_k8s.v0.identity_resource
|
||||||
templates:
|
templates:
|
||||||
|
@ -65,6 +65,10 @@ requires:
|
|||||||
receive-ca-cert:
|
receive-ca-cert:
|
||||||
interface: certificate_transfer
|
interface: certificate_transfer
|
||||||
optional: true
|
optional: true
|
||||||
|
tracing:
|
||||||
|
interface: tracing
|
||||||
|
optional: true
|
||||||
|
limit: 1
|
||||||
|
|
||||||
provides:
|
provides:
|
||||||
grafana-dashboard:
|
grafana-dashboard:
|
||||||
|
@ -35,6 +35,7 @@ import ops_sunbeam.container_handlers as sunbeam_chandlers
|
|||||||
import ops_sunbeam.core as sunbeam_core
|
import ops_sunbeam.core as sunbeam_core
|
||||||
import ops_sunbeam.guard as sunbeam_guard
|
import ops_sunbeam.guard as sunbeam_guard
|
||||||
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
||||||
|
import ops_sunbeam.tracing as sunbeam_tracing
|
||||||
from handlers import (
|
from handlers import (
|
||||||
GrafanaDashboardRelationHandler,
|
GrafanaDashboardRelationHandler,
|
||||||
LoggingRelationHandler,
|
LoggingRelationHandler,
|
||||||
@ -84,6 +85,7 @@ LOKI_RELATION_NAME = "logging"
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class TempestConfigurationContext(ConfigContext):
|
class TempestConfigurationContext(ConfigContext):
|
||||||
"""Configuration context for tempest."""
|
"""Configuration context for tempest."""
|
||||||
|
|
||||||
@ -98,6 +100,7 @@ class TempestConfigurationContext(ConfigContext):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_sunbeam_charm
|
||||||
class TempestOperatorCharm(sunbeam_charm.OSBaseOperatorCharmK8S):
|
class TempestOperatorCharm(sunbeam_charm.OSBaseOperatorCharmK8S):
|
||||||
"""Charm the service."""
|
"""Charm the service."""
|
||||||
|
|
||||||
|
@ -37,6 +37,7 @@ import ops.model
|
|||||||
import ops.pebble
|
import ops.pebble
|
||||||
import ops_sunbeam.container_handlers as sunbeam_chandlers
|
import ops_sunbeam.container_handlers as sunbeam_chandlers
|
||||||
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
import ops_sunbeam.relation_handlers as sunbeam_rhandlers
|
||||||
|
import ops_sunbeam.tracing as sunbeam_tracing
|
||||||
from utils.alert_rules import (
|
from utils.alert_rules import (
|
||||||
ALERT_RULES_PATH,
|
ALERT_RULES_PATH,
|
||||||
)
|
)
|
||||||
@ -69,6 +70,7 @@ def assert_ready(f):
|
|||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class TempestPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
class TempestPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
||||||
"""Pebble handler for the container."""
|
"""Pebble handler for the container."""
|
||||||
|
|
||||||
@ -301,6 +303,7 @@ class TempestPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
|||||||
logger.warning("Clean-up failed")
|
logger.warning("Clean-up failed")
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class TempestUserIdentityRelationHandler(sunbeam_rhandlers.RelationHandler):
|
class TempestUserIdentityRelationHandler(sunbeam_rhandlers.RelationHandler):
|
||||||
"""Relation handler for identity ops."""
|
"""Relation handler for identity ops."""
|
||||||
|
|
||||||
@ -385,7 +388,7 @@ class TempestUserIdentityRelationHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
import charms.keystone_k8s.v0.identity_resource as id_ops
|
import charms.keystone_k8s.v0.identity_resource as id_ops
|
||||||
|
|
||||||
logger.debug("Setting up Identity Resource event handler")
|
logger.debug("Setting up Identity Resource event handler")
|
||||||
ops_svc = id_ops.IdentityResourceRequires(
|
ops_svc = sunbeam_tracing.trace_type(id_ops.IdentityResourceRequires)(
|
||||||
self.charm,
|
self.charm,
|
||||||
self.relation_name,
|
self.relation_name,
|
||||||
)
|
)
|
||||||
@ -649,13 +652,16 @@ class TempestUserIdentityRelationHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
self.callback_f(event)
|
self.callback_f(event)
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class GrafanaDashboardRelationHandler(sunbeam_rhandlers.RelationHandler):
|
class GrafanaDashboardRelationHandler(sunbeam_rhandlers.RelationHandler):
|
||||||
"""Relation handler for grafana-dashboard relation."""
|
"""Relation handler for grafana-dashboard relation."""
|
||||||
|
|
||||||
def setup_event_handler(self) -> ops.framework.Object:
|
def setup_event_handler(self) -> ops.framework.Object:
|
||||||
"""Configure event handlers for the relation."""
|
"""Configure event handlers for the relation."""
|
||||||
logger.debug("Setting up Grafana Dashboards Provider event handler")
|
logger.debug("Setting up Grafana Dashboards Provider event handler")
|
||||||
interface = grafana_dashboard.GrafanaDashboardProvider(
|
interface = sunbeam_tracing.trace_type(
|
||||||
|
grafana_dashboard.GrafanaDashboardProvider
|
||||||
|
)(
|
||||||
self.charm,
|
self.charm,
|
||||||
relation_name=self.relation_name,
|
relation_name=self.relation_name,
|
||||||
dashboards_path="src/grafana_dashboards",
|
dashboards_path="src/grafana_dashboards",
|
||||||
@ -668,13 +674,14 @@ class GrafanaDashboardRelationHandler(sunbeam_rhandlers.RelationHandler):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class LoggingRelationHandler(sunbeam_rhandlers.RelationHandler):
|
class LoggingRelationHandler(sunbeam_rhandlers.RelationHandler):
|
||||||
"""Relation handler for logging relation."""
|
"""Relation handler for logging relation."""
|
||||||
|
|
||||||
def setup_event_handler(self) -> ops.framework.Object:
|
def setup_event_handler(self) -> ops.framework.Object:
|
||||||
"""Configure event handlers for the relation."""
|
"""Configure event handlers for the relation."""
|
||||||
logger.debug("Setting up Logging Provider event handler")
|
logger.debug("Setting up Logging Provider event handler")
|
||||||
interface = loki_push_api.LogProxyConsumer(
|
interface = sunbeam_tracing.trace_type(loki_push_api.LogProxyConsumer)(
|
||||||
self.charm,
|
self.charm,
|
||||||
recursive=True,
|
recursive=True,
|
||||||
relation_name=self.relation_name,
|
relation_name=self.relation_name,
|
||||||
|
702
libs/external/lib/charms/tempo_k8s/v1/charm_tracing.py
vendored
Normal file
702
libs/external/lib/charms/tempo_k8s/v1/charm_tracing.py
vendored
Normal file
@ -0,0 +1,702 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# Copyright 2022 Canonical Ltd.
|
||||||
|
# See LICENSE file for licensing details.
|
||||||
|
|
||||||
|
"""This charm library contains utilities to instrument your Charm with opentelemetry tracing data collection.
|
||||||
|
|
||||||
|
(yes! charm code, not workload code!)
|
||||||
|
|
||||||
|
This means that, if your charm is related to, for example, COS' Tempo charm, you will be able to inspect
|
||||||
|
in real time from the Grafana dashboard the execution flow of your charm.
|
||||||
|
|
||||||
|
# Quickstart
|
||||||
|
Fetch the following charm libs (and ensure the minimum version/revision numbers are satisfied):
|
||||||
|
|
||||||
|
charmcraft fetch-lib charms.tempo_k8s.v2.tracing # >= 1.10
|
||||||
|
charmcraft fetch-lib charms.tempo_k8s.v1.charm_tracing # >= 2.7
|
||||||
|
|
||||||
|
Then edit your charm code to include:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# import the necessary charm libs
|
||||||
|
from charms.tempo_k8s.v2.tracing import TracingEndpointRequirer, charm_tracing_config
|
||||||
|
from charms.tempo_k8s.v1.charm_tracing import charm_tracing
|
||||||
|
|
||||||
|
# decorate your charm class with charm_tracing:
|
||||||
|
@charm_tracing(
|
||||||
|
# forward-declare the instance attributes that the instrumentor will look up to obtain the
|
||||||
|
# tempo endpoint and server certificate
|
||||||
|
tracing_endpoint="tracing_endpoint",
|
||||||
|
server_cert="server_cert"
|
||||||
|
)
|
||||||
|
class MyCharm(CharmBase):
|
||||||
|
_path_to_cert = "/path/to/cert.crt"
|
||||||
|
# path to cert file **in the charm container**. Its presence will be used to determine whether
|
||||||
|
# the charm is ready to use tls for encrypting charm traces. If your charm does not support tls,
|
||||||
|
# you can ignore this and pass None to charm_tracing_config.
|
||||||
|
# If you do support TLS, you'll need to make sure that the server cert is copied to this location
|
||||||
|
# and kept up to date so the instrumentor can use it.
|
||||||
|
|
||||||
|
def __init__(self, ...):
|
||||||
|
...
|
||||||
|
self.tracing = TracingEndpointRequirer(self, ...)
|
||||||
|
self.tracing_endpoint, self.server_cert = charm_tracing_config(self.tracing, self._path_to_cert)
|
||||||
|
```
|
||||||
|
|
||||||
|
# Detailed usage
|
||||||
|
To use this library, you need to do two things:
|
||||||
|
1) decorate your charm class with
|
||||||
|
|
||||||
|
`@trace_charm(tracing_endpoint="my_tracing_endpoint")`
|
||||||
|
|
||||||
|
2) add to your charm a "my_tracing_endpoint" (you can name this attribute whatever you like)
|
||||||
|
**property**, **method** or **instance attribute** that returns an otlp http/https endpoint url.
|
||||||
|
If you are using the ``charms.tempo_k8s.v2.tracing.TracingEndpointRequirer`` as
|
||||||
|
``self.tracing = TracingEndpointRequirer(self)``, the implementation could be:
|
||||||
|
|
||||||
|
```
|
||||||
|
@property
|
||||||
|
def my_tracing_endpoint(self) -> Optional[str]:
|
||||||
|
'''Tempo endpoint for charm tracing'''
|
||||||
|
if self.tracing.is_ready():
|
||||||
|
return self.tracing.get_endpoint("otlp_http")
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
```
|
||||||
|
|
||||||
|
At this point your charm will be automatically instrumented so that:
|
||||||
|
- charm execution starts a trace, containing
|
||||||
|
- every event as a span (including custom events)
|
||||||
|
- every charm method call (except dunders) as a span
|
||||||
|
|
||||||
|
|
||||||
|
## TLS support
|
||||||
|
If your charm integrates with a TLS provider which is also trusted by the tracing provider (the Tempo charm),
|
||||||
|
you can configure ``charm_tracing`` to use TLS by passing a ``server_cert`` parameter to the decorator.
|
||||||
|
|
||||||
|
If your charm is not trusting the same CA as the Tempo endpoint it is sending traces to,
|
||||||
|
you'll need to implement a cert-transfer relation to obtain the CA certificate from the same
|
||||||
|
CA that Tempo is using.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
```
|
||||||
|
from charms.tempo_k8s.v1.charm_tracing import trace_charm
|
||||||
|
@trace_charm(
|
||||||
|
tracing_endpoint="my_tracing_endpoint",
|
||||||
|
server_cert="_server_cert"
|
||||||
|
)
|
||||||
|
class MyCharm(CharmBase):
|
||||||
|
self._server_cert = "/path/to/server.crt"
|
||||||
|
...
|
||||||
|
|
||||||
|
def on_tls_changed(self, e) -> Optional[str]:
|
||||||
|
# update the server cert on the charm container for charm tracing
|
||||||
|
Path(self._server_cert).write_text(self.get_server_cert())
|
||||||
|
|
||||||
|
def on_tls_broken(self, e) -> Optional[str]:
|
||||||
|
# remove the server cert so charm_tracing won't try to use tls anymore
|
||||||
|
Path(self._server_cert).unlink()
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## More fine-grained manual instrumentation
|
||||||
|
if you wish to add more spans to the trace, you can do so by getting a hold of the tracer like so:
|
||||||
|
```
|
||||||
|
import opentelemetry
|
||||||
|
...
|
||||||
|
def get_tracer(self) -> opentelemetry.trace.Tracer:
|
||||||
|
return opentelemetry.trace.get_tracer(type(self).__name__)
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, the tracer is named after the charm type. If you wish to override that, you can pass
|
||||||
|
a different ``service_name`` argument to ``trace_charm``.
|
||||||
|
|
||||||
|
See the official opentelemetry Python SDK documentation for usage:
|
||||||
|
https://opentelemetry-python.readthedocs.io/en/latest/
|
||||||
|
|
||||||
|
## Upgrading from `v0`
|
||||||
|
|
||||||
|
If you are upgrading from `charm_tracing` v0, you need to take the following steps (assuming you already
|
||||||
|
have the newest version of the library in your charm):
|
||||||
|
1) If you need the dependency for your tests, add the following dependency to your charm project
|
||||||
|
(or, if your project had a dependency on `opentelemetry-exporter-otlp-proto-grpc` only because
|
||||||
|
of `charm_tracing` v0, you can replace it with):
|
||||||
|
|
||||||
|
`opentelemetry-exporter-otlp-proto-http>=1.21.0`.
|
||||||
|
|
||||||
|
2) Update the charm method referenced to from ``@trace`` and ``@trace_charm``,
|
||||||
|
to return from ``TracingEndpointRequirer.get_endpoint("otlp_http")`` instead of ``grpc_http``.
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```
|
||||||
|
from charms.tempo_k8s.v0.charm_tracing import trace_charm
|
||||||
|
|
||||||
|
@trace_charm(
|
||||||
|
tracing_endpoint="my_tracing_endpoint",
|
||||||
|
)
|
||||||
|
class MyCharm(CharmBase):
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
@property
|
||||||
|
def my_tracing_endpoint(self) -> Optional[str]:
|
||||||
|
'''Tempo endpoint for charm tracing'''
|
||||||
|
if self.tracing.is_ready():
|
||||||
|
return self.tracing.otlp_grpc_endpoint() # OLD API, DEPRECATED.
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
```
|
||||||
|
|
||||||
|
needs to be replaced with:
|
||||||
|
|
||||||
|
```
|
||||||
|
from charms.tempo_k8s.v1.charm_tracing import trace_charm
|
||||||
|
|
||||||
|
@trace_charm(
|
||||||
|
tracing_endpoint="my_tracing_endpoint",
|
||||||
|
)
|
||||||
|
class MyCharm(CharmBase):
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
@property
|
||||||
|
def my_tracing_endpoint(self) -> Optional[str]:
|
||||||
|
'''Tempo endpoint for charm tracing'''
|
||||||
|
if self.tracing.is_ready():
|
||||||
|
return self.tracing.get_endpoint("otlp_http") # NEW API, use this.
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
```
|
||||||
|
|
||||||
|
3) If you were passing a certificate (str) using `server_cert`, you need to change it to
|
||||||
|
provide an *absolute* path to the certificate file instead.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import functools
|
||||||
|
import inspect
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
from contextlib import contextmanager
|
||||||
|
from contextvars import Context, ContextVar, copy_context
|
||||||
|
from importlib.metadata import distributions
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import (
|
||||||
|
Any,
|
||||||
|
Callable,
|
||||||
|
Generator,
|
||||||
|
Optional,
|
||||||
|
Sequence,
|
||||||
|
Type,
|
||||||
|
TypeVar,
|
||||||
|
Union,
|
||||||
|
cast,
|
||||||
|
)
|
||||||
|
|
||||||
|
import opentelemetry
|
||||||
|
import ops
|
||||||
|
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
|
||||||
|
from opentelemetry.sdk.resources import Resource
|
||||||
|
from opentelemetry.sdk.trace import Span, TracerProvider
|
||||||
|
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
||||||
|
from opentelemetry.trace import INVALID_SPAN, Tracer
|
||||||
|
from opentelemetry.trace import get_current_span as otlp_get_current_span
|
||||||
|
from opentelemetry.trace import (
|
||||||
|
get_tracer,
|
||||||
|
get_tracer_provider,
|
||||||
|
set_span_in_context,
|
||||||
|
set_tracer_provider,
|
||||||
|
)
|
||||||
|
from ops.charm import CharmBase
|
||||||
|
from ops.framework import Framework
|
||||||
|
|
||||||
|
# The unique Charmhub library identifier, never change it
|
||||||
|
LIBID = "cb1705dcd1a14ca09b2e60187d1215c7"
|
||||||
|
|
||||||
|
# Increment this major API version when introducing breaking changes
|
||||||
|
LIBAPI = 1
|
||||||
|
|
||||||
|
# Increment this PATCH version before using `charmcraft publish-lib` or reset
|
||||||
|
# to 0 if you are raising the major API version
|
||||||
|
|
||||||
|
LIBPATCH = 12
|
||||||
|
|
||||||
|
PYDEPS = ["opentelemetry-exporter-otlp-proto-http==1.21.0"]
|
||||||
|
|
||||||
|
logger = logging.getLogger("tracing")
|
||||||
|
dev_logger = logging.getLogger("tracing-dev")
|
||||||
|
|
||||||
|
# set this to 0 if you are debugging/developing this library source
|
||||||
|
dev_logger.setLevel(logging.CRITICAL)
|
||||||
|
|
||||||
|
|
||||||
|
_CharmType = Type[CharmBase] # the type CharmBase and any subclass thereof
|
||||||
|
_C = TypeVar("_C", bound=_CharmType)
|
||||||
|
_T = TypeVar("_T", bound=type)
|
||||||
|
_F = TypeVar("_F", bound=Type[Callable])
|
||||||
|
tracer: ContextVar[Tracer] = ContextVar("tracer")
|
||||||
|
_GetterType = Union[Callable[[_CharmType], Optional[str]], property]
|
||||||
|
|
||||||
|
CHARM_TRACING_ENABLED = "CHARM_TRACING_ENABLED"
|
||||||
|
|
||||||
|
|
||||||
|
def is_enabled() -> bool:
|
||||||
|
"""Whether charm tracing is enabled."""
|
||||||
|
return os.getenv(CHARM_TRACING_ENABLED, "1") == "1"
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def charm_tracing_disabled():
|
||||||
|
"""Contextmanager to temporarily disable charm tracing.
|
||||||
|
|
||||||
|
For usage in tests.
|
||||||
|
"""
|
||||||
|
previous = os.getenv(CHARM_TRACING_ENABLED, "1")
|
||||||
|
os.environ[CHARM_TRACING_ENABLED] = "0"
|
||||||
|
yield
|
||||||
|
os.environ[CHARM_TRACING_ENABLED] = previous
|
||||||
|
|
||||||
|
|
||||||
|
def get_current_span() -> Union[Span, None]:
|
||||||
|
"""Return the currently active Span, if there is one, else None.
|
||||||
|
|
||||||
|
If you'd rather keep your logic unconditional, you can use opentelemetry.trace.get_current_span,
|
||||||
|
which will return an object that behaves like a span but records no data.
|
||||||
|
"""
|
||||||
|
span = otlp_get_current_span()
|
||||||
|
if span is INVALID_SPAN:
|
||||||
|
return None
|
||||||
|
return cast(Span, span)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_tracer_from_context(ctx: Context) -> Optional[ContextVar]:
|
||||||
|
tracers = [v for v in ctx if v is not None and v.name == "tracer"]
|
||||||
|
if tracers:
|
||||||
|
return tracers[0]
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _get_tracer() -> Optional[Tracer]:
|
||||||
|
"""Find tracer in context variable and as a fallback locate it in the full context."""
|
||||||
|
try:
|
||||||
|
return tracer.get()
|
||||||
|
except LookupError:
|
||||||
|
try:
|
||||||
|
ctx: Context = copy_context()
|
||||||
|
if context_tracer := _get_tracer_from_context(ctx):
|
||||||
|
return context_tracer.get()
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
except LookupError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def _span(name: str) -> Generator[Optional[Span], Any, Any]:
|
||||||
|
"""Context to create a span if there is a tracer, otherwise do nothing."""
|
||||||
|
if tracer := _get_tracer():
|
||||||
|
with tracer.start_as_current_span(name) as span:
|
||||||
|
yield cast(Span, span)
|
||||||
|
else:
|
||||||
|
yield None
|
||||||
|
|
||||||
|
|
||||||
|
class TracingError(RuntimeError):
|
||||||
|
"""Base class for errors raised by this module."""
|
||||||
|
|
||||||
|
|
||||||
|
class UntraceableObjectError(TracingError):
|
||||||
|
"""Raised when an object you're attempting to instrument cannot be autoinstrumented."""
|
||||||
|
|
||||||
|
|
||||||
|
class TLSError(TracingError):
|
||||||
|
"""Raised when the tracing endpoint is https but we don't have a cert yet."""
|
||||||
|
|
||||||
|
|
||||||
|
def _get_tracing_endpoint(
|
||||||
|
tracing_endpoint_attr: str,
|
||||||
|
charm_instance: object,
|
||||||
|
charm_type: type,
|
||||||
|
):
|
||||||
|
_tracing_endpoint = getattr(charm_instance, tracing_endpoint_attr)
|
||||||
|
if callable(_tracing_endpoint):
|
||||||
|
tracing_endpoint = _tracing_endpoint()
|
||||||
|
else:
|
||||||
|
tracing_endpoint = _tracing_endpoint
|
||||||
|
|
||||||
|
if tracing_endpoint is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
elif not isinstance(tracing_endpoint, str):
|
||||||
|
raise TypeError(
|
||||||
|
f"{charm_type.__name__}.{tracing_endpoint_attr} should resolve to a tempo endpoint (string); "
|
||||||
|
f"got {tracing_endpoint} instead."
|
||||||
|
)
|
||||||
|
|
||||||
|
dev_logger.debug(f"Setting up span exporter to endpoint: {tracing_endpoint}/v1/traces")
|
||||||
|
return f"{tracing_endpoint}/v1/traces"
|
||||||
|
|
||||||
|
|
||||||
|
def _get_server_cert(
|
||||||
|
server_cert_attr: str,
|
||||||
|
charm_instance: ops.CharmBase,
|
||||||
|
charm_type: Type[ops.CharmBase],
|
||||||
|
):
|
||||||
|
_server_cert = getattr(charm_instance, server_cert_attr)
|
||||||
|
if callable(_server_cert):
|
||||||
|
server_cert = _server_cert()
|
||||||
|
else:
|
||||||
|
server_cert = _server_cert
|
||||||
|
|
||||||
|
if server_cert is None:
|
||||||
|
logger.warning(
|
||||||
|
f"{charm_type}.{server_cert_attr} is None; sending traces over INSECURE connection."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
elif not Path(server_cert).is_absolute():
|
||||||
|
raise ValueError(
|
||||||
|
f"{charm_type}.{server_cert_attr} should resolve to a valid tls cert absolute path (string | Path)); "
|
||||||
|
f"got {server_cert} instead."
|
||||||
|
)
|
||||||
|
return server_cert
|
||||||
|
|
||||||
|
|
||||||
|
def _remove_stale_otel_sdk_packages():
|
||||||
|
"""Hack to remove stale opentelemetry sdk packages from the charm's python venv.
|
||||||
|
|
||||||
|
See https://github.com/canonical/grafana-agent-operator/issues/146 and
|
||||||
|
https://bugs.launchpad.net/juju/+bug/2058335 for more context. This patch can be removed after
|
||||||
|
this juju issue is resolved and sufficient time has passed to expect most users of this library
|
||||||
|
have migrated to the patched version of juju.
|
||||||
|
|
||||||
|
This only does something if executed on an upgrade-charm event.
|
||||||
|
"""
|
||||||
|
if os.getenv("JUJU_DISPATCH_PATH") == "hooks/upgrade-charm":
|
||||||
|
logger.debug("Executing _remove_stale_otel_sdk_packages patch on charm upgrade")
|
||||||
|
# Find any opentelemetry_sdk distributions
|
||||||
|
otel_sdk_distributions = list(distributions(name="opentelemetry_sdk"))
|
||||||
|
# If there is more than 1, inspect each and if it has 0 entrypoints, infer that it is stale
|
||||||
|
if len(otel_sdk_distributions) > 1:
|
||||||
|
for distribution in otel_sdk_distributions:
|
||||||
|
if len(distribution.entry_points) == 0:
|
||||||
|
# Distribution appears to be empty. Remove it
|
||||||
|
path = distribution._path # type: ignore
|
||||||
|
logger.debug(f"Removing empty opentelemetry_sdk distribution at: {path}")
|
||||||
|
shutil.rmtree(path)
|
||||||
|
|
||||||
|
|
||||||
|
def _setup_root_span_initializer(
|
||||||
|
charm_type: _CharmType,
|
||||||
|
tracing_endpoint_attr: str,
|
||||||
|
server_cert_attr: Optional[str],
|
||||||
|
service_name: Optional[str] = None,
|
||||||
|
):
|
||||||
|
"""Patch the charm's initializer."""
|
||||||
|
original_init = charm_type.__init__
|
||||||
|
|
||||||
|
@functools.wraps(original_init)
|
||||||
|
def wrap_init(self: CharmBase, framework: Framework, *args, **kwargs):
|
||||||
|
# we're using 'self' here because this is charm init code, makes sense to read what's below
|
||||||
|
# from the perspective of the charm. Self.unit.name...
|
||||||
|
|
||||||
|
original_init(self, framework, *args, **kwargs)
|
||||||
|
# we call this from inside the init context instead of, say, _autoinstrument, because we want it to
|
||||||
|
# be checked on a per-charm-instantiation basis, not on a per-type-declaration one.
|
||||||
|
if not is_enabled():
|
||||||
|
# this will only happen during unittesting, hopefully, so it's fine to log a
|
||||||
|
# bit more verbosely
|
||||||
|
logger.info("Tracing DISABLED: skipping root span initialization")
|
||||||
|
return
|
||||||
|
|
||||||
|
# already init some attrs that will be reinited later by calling original_init:
|
||||||
|
# self.framework = framework
|
||||||
|
# self.handle = Handle(None, self.handle_kind, None)
|
||||||
|
|
||||||
|
original_event_context = framework._event_context
|
||||||
|
# default service name isn't just app name because it could conflict with the workload service name
|
||||||
|
_service_name = service_name or f"{self.app.name}-charm"
|
||||||
|
|
||||||
|
unit_name = self.unit.name
|
||||||
|
# apply hacky patch to remove stale opentelemetry sdk packages on upgrade-charm.
|
||||||
|
# it could be trouble if someone ever decides to implement their own tracer parallel to
|
||||||
|
# ours and before the charm has inited. We assume they won't.
|
||||||
|
_remove_stale_otel_sdk_packages()
|
||||||
|
resource = Resource.create(
|
||||||
|
attributes={
|
||||||
|
"service.name": _service_name,
|
||||||
|
"compose_service": _service_name,
|
||||||
|
"charm_type": type(self).__name__,
|
||||||
|
# juju topology
|
||||||
|
"juju_unit": unit_name,
|
||||||
|
"juju_application": self.app.name,
|
||||||
|
"juju_model": self.model.name,
|
||||||
|
"juju_model_uuid": self.model.uuid,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
provider = TracerProvider(resource=resource)
|
||||||
|
|
||||||
|
# if anything goes wrong with retrieving the endpoint, we let the exception bubble up.
|
||||||
|
tracing_endpoint = _get_tracing_endpoint(tracing_endpoint_attr, self, charm_type)
|
||||||
|
|
||||||
|
if not tracing_endpoint:
|
||||||
|
# tracing is off if tracing_endpoint is None
|
||||||
|
return
|
||||||
|
|
||||||
|
server_cert: Optional[Union[str, Path]] = (
|
||||||
|
_get_server_cert(server_cert_attr, self, charm_type) if server_cert_attr else None
|
||||||
|
)
|
||||||
|
|
||||||
|
if tracing_endpoint.startswith("https://") and not server_cert:
|
||||||
|
raise TLSError(
|
||||||
|
"Tracing endpoint is https, but no server_cert has been passed."
|
||||||
|
"Please point @trace_charm to a `server_cert` attr."
|
||||||
|
)
|
||||||
|
|
||||||
|
exporter = OTLPSpanExporter(
|
||||||
|
endpoint=tracing_endpoint,
|
||||||
|
certificate_file=str(Path(server_cert).absolute()) if server_cert else None,
|
||||||
|
timeout=2,
|
||||||
|
)
|
||||||
|
|
||||||
|
processor = BatchSpanProcessor(exporter)
|
||||||
|
provider.add_span_processor(processor)
|
||||||
|
set_tracer_provider(provider)
|
||||||
|
_tracer = get_tracer(_service_name) # type: ignore
|
||||||
|
_tracer_token = tracer.set(_tracer)
|
||||||
|
|
||||||
|
dispatch_path = os.getenv("JUJU_DISPATCH_PATH", "") # something like hooks/install
|
||||||
|
event_name = dispatch_path.split("/")[1] if "/" in dispatch_path else dispatch_path
|
||||||
|
root_span_name = f"{unit_name}: {event_name} event"
|
||||||
|
span = _tracer.start_span(root_span_name, attributes={"juju.dispatch_path": dispatch_path})
|
||||||
|
|
||||||
|
# all these shenanigans are to work around the fact that the opentelemetry tracing API is built
|
||||||
|
# on the assumption that spans will be used as contextmanagers.
|
||||||
|
# Since we don't (as we need to close the span on framework.commit),
|
||||||
|
# we need to manually set the root span as current.
|
||||||
|
ctx = set_span_in_context(span)
|
||||||
|
|
||||||
|
# log a trace id, so we can pick it up from the logs (and jhack) to look it up in tempo.
|
||||||
|
root_trace_id = hex(span.get_span_context().trace_id)[2:] # strip 0x prefix
|
||||||
|
logger.debug(f"Starting root trace with id={root_trace_id!r}.")
|
||||||
|
|
||||||
|
span_token = opentelemetry.context.attach(ctx) # type: ignore
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def wrap_event_context(event_name: str):
|
||||||
|
dev_logger.info(f"entering event context: {event_name}")
|
||||||
|
# when the framework enters an event context, we create a span.
|
||||||
|
with _span("event: " + event_name) as event_context_span:
|
||||||
|
if event_context_span:
|
||||||
|
# todo: figure out how to inject event attrs in here
|
||||||
|
event_context_span.add_event(event_name)
|
||||||
|
yield original_event_context(event_name)
|
||||||
|
|
||||||
|
framework._event_context = wrap_event_context # type: ignore
|
||||||
|
|
||||||
|
original_close = framework.close
|
||||||
|
|
||||||
|
@functools.wraps(original_close)
|
||||||
|
def wrap_close():
|
||||||
|
dev_logger.info("tearing down tracer and flushing traces")
|
||||||
|
span.end()
|
||||||
|
opentelemetry.context.detach(span_token) # type: ignore
|
||||||
|
tracer.reset(_tracer_token)
|
||||||
|
tp = cast(TracerProvider, get_tracer_provider())
|
||||||
|
tp.force_flush(timeout_millis=1000) # don't block for too long
|
||||||
|
tp.shutdown()
|
||||||
|
original_close()
|
||||||
|
|
||||||
|
framework.close = wrap_close
|
||||||
|
return
|
||||||
|
|
||||||
|
charm_type.__init__ = wrap_init # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
def trace_charm(
|
||||||
|
tracing_endpoint: str,
|
||||||
|
server_cert: Optional[str] = None,
|
||||||
|
service_name: Optional[str] = None,
|
||||||
|
extra_types: Sequence[type] = (),
|
||||||
|
) -> Callable[[_T], _T]:
|
||||||
|
"""Autoinstrument the decorated charm with tracing telemetry.
|
||||||
|
|
||||||
|
Use this function to get out-of-the-box traces for all events emitted on this charm and all
|
||||||
|
method calls on instances of this class.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
>>> from charms.tempo_k8s.v1.charm_tracing import trace_charm
|
||||||
|
>>> from charms.tempo_k8s.v1.tracing import TracingEndpointRequirer
|
||||||
|
>>> from ops import CharmBase
|
||||||
|
>>>
|
||||||
|
>>> @trace_charm(
|
||||||
|
>>> tracing_endpoint="tempo_otlp_http_endpoint",
|
||||||
|
>>> )
|
||||||
|
>>> class MyCharm(CharmBase):
|
||||||
|
>>>
|
||||||
|
>>> def __init__(self, framework: Framework):
|
||||||
|
>>> ...
|
||||||
|
>>> self.tracing = TracingEndpointRequirer(self)
|
||||||
|
>>>
|
||||||
|
>>> @property
|
||||||
|
>>> def tempo_otlp_http_endpoint(self) -> Optional[str]:
|
||||||
|
>>> if self.tracing.is_ready():
|
||||||
|
>>> return self.tracing.otlp_http_endpoint()
|
||||||
|
>>> else:
|
||||||
|
>>> return None
|
||||||
|
>>>
|
||||||
|
|
||||||
|
:param tracing_endpoint: name of a method, property or attribute on the charm type that returns an
|
||||||
|
optional (fully resolvable) tempo url to which the charm traces will be pushed.
|
||||||
|
If None, tracing will be effectively disabled.
|
||||||
|
:param server_cert: name of a method, property or attribute on the charm type that returns an
|
||||||
|
optional absolute path to a CA certificate file to be used when sending traces to a remote server.
|
||||||
|
If it returns None, an _insecure_ connection will be used. To avoid errors in transient
|
||||||
|
situations where the endpoint is already https but there is no certificate on disk yet, it
|
||||||
|
is recommended to disable tracing (by returning None from the tracing_endpoint) altogether
|
||||||
|
until the cert has been written to disk.
|
||||||
|
:param service_name: service name tag to attach to all traces generated by this charm.
|
||||||
|
Defaults to the juju application name this charm is deployed under.
|
||||||
|
:param extra_types: pass any number of types that you also wish to autoinstrument.
|
||||||
|
For example, charm libs, relation endpoint wrappers, workload abstractions, ...
|
||||||
|
"""
|
||||||
|
|
||||||
|
def _decorator(charm_type: _T) -> _T:
|
||||||
|
"""Autoinstrument the wrapped charmbase type."""
|
||||||
|
_autoinstrument(
|
||||||
|
charm_type,
|
||||||
|
tracing_endpoint_attr=tracing_endpoint,
|
||||||
|
server_cert_attr=server_cert,
|
||||||
|
service_name=service_name,
|
||||||
|
extra_types=extra_types,
|
||||||
|
)
|
||||||
|
return charm_type
|
||||||
|
|
||||||
|
return _decorator
|
||||||
|
|
||||||
|
|
||||||
|
def _autoinstrument(
|
||||||
|
charm_type: _T,
|
||||||
|
tracing_endpoint_attr: str,
|
||||||
|
server_cert_attr: Optional[str] = None,
|
||||||
|
service_name: Optional[str] = None,
|
||||||
|
extra_types: Sequence[type] = (),
|
||||||
|
) -> _T:
|
||||||
|
"""Set up tracing on this charm class.
|
||||||
|
|
||||||
|
Use this function to get out-of-the-box traces for all events emitted on this charm and all
|
||||||
|
method calls on instances of this class.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
>>> from charms.tempo_k8s.v1.charm_tracing import _autoinstrument
|
||||||
|
>>> from ops.main import main
|
||||||
|
>>> _autoinstrument(
|
||||||
|
>>> MyCharm,
|
||||||
|
>>> tracing_endpoint_attr="tempo_otlp_http_endpoint",
|
||||||
|
>>> service_name="MyCharm",
|
||||||
|
>>> extra_types=(Foo, Bar)
|
||||||
|
>>> )
|
||||||
|
>>> main(MyCharm)
|
||||||
|
|
||||||
|
:param charm_type: the CharmBase subclass to autoinstrument.
|
||||||
|
:param tracing_endpoint_attr: name of a method, property or attribute on the charm type that returns an
|
||||||
|
optional (fully resolvable) tempo url to which the charm traces will be pushed.
|
||||||
|
If None, tracing will be effectively disabled.
|
||||||
|
:param server_cert_attr: name of a method, property or attribute on the charm type that returns an
|
||||||
|
optional absolute path to a CA certificate file to be used when sending traces to a remote server.
|
||||||
|
If it returns None, an _insecure_ connection will be used. To avoid errors in transient
|
||||||
|
situations where the endpoint is already https but there is no certificate on disk yet, it
|
||||||
|
is recommended to disable tracing (by returning None from the tracing_endpoint) altogether
|
||||||
|
until the cert has been written to disk.
|
||||||
|
:param service_name: service name tag to attach to all traces generated by this charm.
|
||||||
|
Defaults to the juju application name this charm is deployed under.
|
||||||
|
:param extra_types: pass any number of types that you also wish to autoinstrument.
|
||||||
|
For example, charm libs, relation endpoint wrappers, workload abstractions, ...
|
||||||
|
"""
|
||||||
|
dev_logger.info(f"instrumenting {charm_type}")
|
||||||
|
_setup_root_span_initializer(
|
||||||
|
charm_type,
|
||||||
|
tracing_endpoint_attr,
|
||||||
|
server_cert_attr=server_cert_attr,
|
||||||
|
service_name=service_name,
|
||||||
|
)
|
||||||
|
trace_type(charm_type)
|
||||||
|
for type_ in extra_types:
|
||||||
|
trace_type(type_)
|
||||||
|
|
||||||
|
return charm_type
|
||||||
|
|
||||||
|
|
||||||
|
def trace_type(cls: _T) -> _T:
|
||||||
|
"""Set up tracing on this class.
|
||||||
|
|
||||||
|
Use this decorator to get out-of-the-box traces for all method calls on instances of this class.
|
||||||
|
It assumes that this class is only instantiated after a charm type decorated with `@trace_charm`
|
||||||
|
has been instantiated.
|
||||||
|
"""
|
||||||
|
dev_logger.info(f"instrumenting {cls}")
|
||||||
|
for name, method in inspect.getmembers(cls, predicate=inspect.isfunction):
|
||||||
|
dev_logger.info(f"discovered {method}")
|
||||||
|
|
||||||
|
if method.__name__.startswith("__"):
|
||||||
|
dev_logger.info(f"skipping {method} (dunder)")
|
||||||
|
continue
|
||||||
|
|
||||||
|
new_method = trace_method(method)
|
||||||
|
if isinstance(inspect.getattr_static(cls, method.__name__), staticmethod):
|
||||||
|
new_method = staticmethod(new_method)
|
||||||
|
setattr(cls, name, new_method)
|
||||||
|
|
||||||
|
return cls
|
||||||
|
|
||||||
|
|
||||||
|
def trace_method(method: _F) -> _F:
|
||||||
|
"""Trace this method.
|
||||||
|
|
||||||
|
A span will be opened when this method is called and closed when it returns.
|
||||||
|
"""
|
||||||
|
return _trace_callable(method, "method")
|
||||||
|
|
||||||
|
|
||||||
|
def trace_function(function: _F) -> _F:
|
||||||
|
"""Trace this function.
|
||||||
|
|
||||||
|
A span will be opened when this function is called and closed when it returns.
|
||||||
|
"""
|
||||||
|
return _trace_callable(function, "function")
|
||||||
|
|
||||||
|
|
||||||
|
def _trace_callable(callable: _F, qualifier: str) -> _F:
|
||||||
|
dev_logger.info(f"instrumenting {callable}")
|
||||||
|
|
||||||
|
# sig = inspect.signature(callable)
|
||||||
|
@functools.wraps(callable)
|
||||||
|
def wrapped_function(*args, **kwargs): # type: ignore
|
||||||
|
name = getattr(callable, "__qualname__", getattr(callable, "__name__", str(callable)))
|
||||||
|
with _span(f"{qualifier} call: {name}"): # type: ignore
|
||||||
|
return callable(*args, **kwargs) # type: ignore
|
||||||
|
|
||||||
|
# wrapped_function.__signature__ = sig
|
||||||
|
return wrapped_function # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
def trace(obj: Union[Type, Callable]):
|
||||||
|
"""Trace this object and send the resulting spans to Tempo.
|
||||||
|
|
||||||
|
It will dispatch to ``trace_type`` if the decorated object is a class, otherwise
|
||||||
|
``trace_function``.
|
||||||
|
"""
|
||||||
|
if isinstance(obj, type):
|
||||||
|
if issubclass(obj, CharmBase):
|
||||||
|
raise ValueError(
|
||||||
|
"cannot use @trace on CharmBase subclasses: use @trace_charm instead "
|
||||||
|
"(we need some arguments!)"
|
||||||
|
)
|
||||||
|
return trace_type(obj)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
return trace_function(obj)
|
||||||
|
except Exception:
|
||||||
|
raise UntraceableObjectError(
|
||||||
|
f"cannot create span from {type(obj)}; instrument {obj} manually."
|
||||||
|
)
|
987
libs/external/lib/charms/tempo_k8s/v2/tracing.py
vendored
Normal file
987
libs/external/lib/charms/tempo_k8s/v2/tracing.py
vendored
Normal file
@ -0,0 +1,987 @@
|
|||||||
|
# Copyright 2024 Canonical Ltd.
|
||||||
|
# See LICENSE file for licensing details.
|
||||||
|
"""## Overview.
|
||||||
|
|
||||||
|
This document explains how to integrate with the Tempo charm for the purpose of pushing traces to a
|
||||||
|
tracing endpoint provided by Tempo. It also explains how alternative implementations of the Tempo charm
|
||||||
|
may maintain the same interface and be backward compatible with all currently integrated charms.
|
||||||
|
|
||||||
|
## Requirer Library Usage
|
||||||
|
|
||||||
|
Charms seeking to push traces to Tempo, must do so using the `TracingEndpointRequirer`
|
||||||
|
object from this charm library. For the simplest use cases, using the `TracingEndpointRequirer`
|
||||||
|
object only requires instantiating it, typically in the constructor of your charm. The
|
||||||
|
`TracingEndpointRequirer` constructor requires the name of the relation over which a tracing endpoint
|
||||||
|
is exposed by the Tempo charm, and a list of protocols it intends to send traces with.
|
||||||
|
This relation must use the `tracing` interface.
|
||||||
|
The `TracingEndpointRequirer` object may be instantiated as follows
|
||||||
|
|
||||||
|
from charms.tempo_k8s.v2.tracing import TracingEndpointRequirer
|
||||||
|
|
||||||
|
def __init__(self, *args):
|
||||||
|
super().__init__(*args)
|
||||||
|
# ...
|
||||||
|
self.tracing = TracingEndpointRequirer(self,
|
||||||
|
protocols=['otlp_grpc', 'otlp_http', 'jaeger_http_thrift']
|
||||||
|
)
|
||||||
|
# ...
|
||||||
|
|
||||||
|
Note that the first argument (`self`) to `TracingEndpointRequirer` is always a reference to the
|
||||||
|
parent charm.
|
||||||
|
|
||||||
|
Alternatively to providing the list of requested protocols at init time, the charm can do it at
|
||||||
|
any point in time by calling the
|
||||||
|
`TracingEndpointRequirer.request_protocols(*protocol:str, relation:Optional[Relation])` method.
|
||||||
|
Using this method also allows you to use per-relation protocols.
|
||||||
|
|
||||||
|
Units of provider charms obtain the tempo endpoint to which they will push their traces by calling
|
||||||
|
`TracingEndpointRequirer.get_endpoint(protocol: str)`, where `protocol` is, for example:
|
||||||
|
- `otlp_grpc`
|
||||||
|
- `otlp_http`
|
||||||
|
- `zipkin`
|
||||||
|
- `tempo`
|
||||||
|
|
||||||
|
If the `protocol` is not in the list of protocols that the charm requested at endpoint set-up time,
|
||||||
|
the library will raise an error.
|
||||||
|
|
||||||
|
## Requirer Library Usage
|
||||||
|
|
||||||
|
The `TracingEndpointProvider` object may be used by charms to manage relations with their
|
||||||
|
trace sources. For this purposes a Tempo-like charm needs to do two things
|
||||||
|
|
||||||
|
1. Instantiate the `TracingEndpointProvider` object by providing it a
|
||||||
|
reference to the parent (Tempo) charm and optionally the name of the relation that the Tempo charm
|
||||||
|
uses to interact with its trace sources. This relation must conform to the `tracing` interface
|
||||||
|
and it is strongly recommended that this relation be named `tracing` which is its
|
||||||
|
default value.
|
||||||
|
|
||||||
|
For example a Tempo charm may instantiate the `TracingEndpointProvider` in its constructor as
|
||||||
|
follows
|
||||||
|
|
||||||
|
from charms.tempo_k8s.v2.tracing import TracingEndpointProvider
|
||||||
|
|
||||||
|
def __init__(self, *args):
|
||||||
|
super().__init__(*args)
|
||||||
|
# ...
|
||||||
|
self.tracing = TracingEndpointProvider(self)
|
||||||
|
# ...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
""" # noqa: W505
|
||||||
|
import enum
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import (
|
||||||
|
TYPE_CHECKING,
|
||||||
|
Any,
|
||||||
|
Dict,
|
||||||
|
List,
|
||||||
|
Literal,
|
||||||
|
MutableMapping,
|
||||||
|
Optional,
|
||||||
|
Sequence,
|
||||||
|
Tuple,
|
||||||
|
Union,
|
||||||
|
cast,
|
||||||
|
)
|
||||||
|
|
||||||
|
import pydantic
|
||||||
|
from ops.charm import (
|
||||||
|
CharmBase,
|
||||||
|
CharmEvents,
|
||||||
|
RelationBrokenEvent,
|
||||||
|
RelationEvent,
|
||||||
|
RelationRole,
|
||||||
|
)
|
||||||
|
from ops.framework import EventSource, Object
|
||||||
|
from ops.model import ModelError, Relation
|
||||||
|
from pydantic import BaseModel, ConfigDict, Field
|
||||||
|
|
||||||
|
# The unique Charmhub library identifier, never change it
|
||||||
|
LIBID = "12977e9aa0b34367903d8afeb8c3d85d"
|
||||||
|
|
||||||
|
# Increment this major API version when introducing breaking changes
|
||||||
|
LIBAPI = 2
|
||||||
|
|
||||||
|
# Increment this PATCH version before using `charmcraft publish-lib` or reset
|
||||||
|
# to 0 if you are raising the major API version
|
||||||
|
LIBPATCH = 8
|
||||||
|
|
||||||
|
PYDEPS = ["pydantic"]
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
DEFAULT_RELATION_NAME = "tracing"
|
||||||
|
RELATION_INTERFACE_NAME = "tracing"
|
||||||
|
|
||||||
|
# Supported list rationale https://github.com/canonical/tempo-coordinator-k8s-operator/issues/8
|
||||||
|
ReceiverProtocol = Literal[
|
||||||
|
"zipkin",
|
||||||
|
"otlp_grpc",
|
||||||
|
"otlp_http",
|
||||||
|
"jaeger_grpc",
|
||||||
|
"jaeger_thrift_http",
|
||||||
|
]
|
||||||
|
|
||||||
|
RawReceiver = Tuple[ReceiverProtocol, str]
|
||||||
|
"""Helper type. A raw receiver is defined as a tuple consisting of the protocol name, and the (external, if available),
|
||||||
|
(secured, if available) resolvable server url.
|
||||||
|
"""
|
||||||
|
|
||||||
|
BUILTIN_JUJU_KEYS = {"ingress-address", "private-address", "egress-subnets"}
|
||||||
|
|
||||||
|
|
||||||
|
class TransportProtocolType(str, enum.Enum):
|
||||||
|
"""Receiver Type."""
|
||||||
|
|
||||||
|
http = "http"
|
||||||
|
grpc = "grpc"
|
||||||
|
|
||||||
|
|
||||||
|
receiver_protocol_to_transport_protocol: Dict[ReceiverProtocol, TransportProtocolType] = {
|
||||||
|
"zipkin": TransportProtocolType.http,
|
||||||
|
"otlp_grpc": TransportProtocolType.grpc,
|
||||||
|
"otlp_http": TransportProtocolType.http,
|
||||||
|
"jaeger_thrift_http": TransportProtocolType.http,
|
||||||
|
"jaeger_grpc": TransportProtocolType.grpc,
|
||||||
|
}
|
||||||
|
"""A mapping between telemetry protocols and their corresponding transport protocol.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class TracingError(Exception):
|
||||||
|
"""Base class for custom errors raised by this library."""
|
||||||
|
|
||||||
|
|
||||||
|
class NotReadyError(TracingError):
|
||||||
|
"""Raised by the provider wrapper if a requirer hasn't published the required data (yet)."""
|
||||||
|
|
||||||
|
|
||||||
|
class ProtocolNotRequestedError(TracingError):
|
||||||
|
"""Raised if the user attempts to obtain an endpoint for a protocol it did not request."""
|
||||||
|
|
||||||
|
|
||||||
|
class DataValidationError(TracingError):
|
||||||
|
"""Raised when data validation fails on IPU relation data."""
|
||||||
|
|
||||||
|
|
||||||
|
class AmbiguousRelationUsageError(TracingError):
|
||||||
|
"""Raised when one wrongly assumes that there can only be one relation on an endpoint."""
|
||||||
|
|
||||||
|
|
||||||
|
if int(pydantic.version.VERSION.split(".")[0]) < 2:
|
||||||
|
|
||||||
|
class DatabagModel(BaseModel): # type: ignore
|
||||||
|
"""Base databag model."""
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
"""Pydantic config."""
|
||||||
|
|
||||||
|
# ignore any extra fields in the databag
|
||||||
|
extra = "ignore"
|
||||||
|
"""Ignore any extra fields in the databag."""
|
||||||
|
allow_population_by_field_name = True
|
||||||
|
"""Allow instantiating this class by field name (instead of forcing alias)."""
|
||||||
|
|
||||||
|
_NEST_UNDER = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def load(cls, databag: MutableMapping):
|
||||||
|
"""Load this model from a Juju databag."""
|
||||||
|
if cls._NEST_UNDER:
|
||||||
|
return cls.parse_obj(json.loads(databag[cls._NEST_UNDER]))
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = {
|
||||||
|
k: json.loads(v)
|
||||||
|
for k, v in databag.items()
|
||||||
|
# Don't attempt to parse model-external values
|
||||||
|
if k in {f.alias for f in cls.__fields__.values()}
|
||||||
|
}
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
msg = f"invalid databag contents: expecting json. {databag}"
|
||||||
|
logger.error(msg)
|
||||||
|
raise DataValidationError(msg) from e
|
||||||
|
|
||||||
|
try:
|
||||||
|
return cls.parse_raw(json.dumps(data)) # type: ignore
|
||||||
|
except pydantic.ValidationError as e:
|
||||||
|
msg = f"failed to validate databag: {databag}"
|
||||||
|
logger.debug(msg, exc_info=True)
|
||||||
|
raise DataValidationError(msg) from e
|
||||||
|
|
||||||
|
def dump(self, databag: Optional[MutableMapping] = None, clear: bool = True):
|
||||||
|
"""Write the contents of this model to Juju databag.
|
||||||
|
|
||||||
|
:param databag: the databag to write the data to.
|
||||||
|
:param clear: ensure the databag is cleared before writing it.
|
||||||
|
"""
|
||||||
|
if clear and databag:
|
||||||
|
databag.clear()
|
||||||
|
|
||||||
|
if databag is None:
|
||||||
|
databag = {}
|
||||||
|
|
||||||
|
if self._NEST_UNDER:
|
||||||
|
databag[self._NEST_UNDER] = self.json(by_alias=True)
|
||||||
|
return databag
|
||||||
|
|
||||||
|
dct = self.dict()
|
||||||
|
for key, field in self.__fields__.items(): # type: ignore
|
||||||
|
value = dct[key]
|
||||||
|
databag[field.alias or key] = json.dumps(value)
|
||||||
|
|
||||||
|
return databag
|
||||||
|
|
||||||
|
else:
|
||||||
|
from pydantic import ConfigDict
|
||||||
|
|
||||||
|
class DatabagModel(BaseModel):
|
||||||
|
"""Base databag model."""
|
||||||
|
|
||||||
|
model_config = ConfigDict(
|
||||||
|
# ignore any extra fields in the databag
|
||||||
|
extra="ignore",
|
||||||
|
# Allow instantiating this class by field name (instead of forcing alias).
|
||||||
|
populate_by_name=True,
|
||||||
|
# Custom config key: whether to nest the whole datastructure (as json)
|
||||||
|
# under a field or spread it out at the toplevel.
|
||||||
|
_NEST_UNDER=None, # type: ignore
|
||||||
|
)
|
||||||
|
"""Pydantic config."""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def load(cls, databag: MutableMapping):
|
||||||
|
"""Load this model from a Juju databag."""
|
||||||
|
nest_under = cls.model_config.get("_NEST_UNDER") # type: ignore
|
||||||
|
if nest_under:
|
||||||
|
return cls.model_validate(json.loads(databag[nest_under])) # type: ignore
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = {
|
||||||
|
k: json.loads(v)
|
||||||
|
for k, v in databag.items()
|
||||||
|
# Don't attempt to parse model-external values
|
||||||
|
if k in {(f.alias or n) for n, f in cls.__fields__.items()}
|
||||||
|
}
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
msg = f"invalid databag contents: expecting json. {databag}"
|
||||||
|
logger.error(msg)
|
||||||
|
raise DataValidationError(msg) from e
|
||||||
|
|
||||||
|
try:
|
||||||
|
return cls.model_validate_json(json.dumps(data)) # type: ignore
|
||||||
|
except pydantic.ValidationError as e:
|
||||||
|
msg = f"failed to validate databag: {databag}"
|
||||||
|
logger.debug(msg, exc_info=True)
|
||||||
|
raise DataValidationError(msg) from e
|
||||||
|
|
||||||
|
def dump(self, databag: Optional[MutableMapping] = None, clear: bool = True):
|
||||||
|
"""Write the contents of this model to Juju databag.
|
||||||
|
|
||||||
|
:param databag: the databag to write the data to.
|
||||||
|
:param clear: ensure the databag is cleared before writing it.
|
||||||
|
"""
|
||||||
|
if clear and databag:
|
||||||
|
databag.clear()
|
||||||
|
|
||||||
|
if databag is None:
|
||||||
|
databag = {}
|
||||||
|
nest_under = self.model_config.get("_NEST_UNDER")
|
||||||
|
if nest_under:
|
||||||
|
databag[nest_under] = self.model_dump_json( # type: ignore
|
||||||
|
by_alias=True,
|
||||||
|
# skip keys whose values are default
|
||||||
|
exclude_defaults=True,
|
||||||
|
)
|
||||||
|
return databag
|
||||||
|
|
||||||
|
dct = self.model_dump() # type: ignore
|
||||||
|
for key, field in self.model_fields.items(): # type: ignore
|
||||||
|
value = dct[key]
|
||||||
|
if value == field.default:
|
||||||
|
continue
|
||||||
|
databag[field.alias or key] = json.dumps(value)
|
||||||
|
|
||||||
|
return databag
|
||||||
|
|
||||||
|
|
||||||
|
# todo use models from charm-relation-interfaces
|
||||||
|
if int(pydantic.version.VERSION.split(".")[0]) < 2:
|
||||||
|
|
||||||
|
class ProtocolType(BaseModel): # type: ignore
|
||||||
|
"""Protocol Type."""
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
"""Pydantic config."""
|
||||||
|
|
||||||
|
use_enum_values = True
|
||||||
|
"""Allow serializing enum values."""
|
||||||
|
|
||||||
|
name: str = Field(
|
||||||
|
...,
|
||||||
|
description="Receiver protocol name. What protocols are supported (and what they are called) "
|
||||||
|
"may differ per provider.",
|
||||||
|
examples=["otlp_grpc", "otlp_http", "tempo_http"],
|
||||||
|
)
|
||||||
|
|
||||||
|
type: TransportProtocolType = Field(
|
||||||
|
...,
|
||||||
|
description="The transport protocol used by this receiver.",
|
||||||
|
examples=["http", "grpc"],
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
class ProtocolType(BaseModel):
|
||||||
|
"""Protocol Type."""
|
||||||
|
|
||||||
|
model_config = ConfigDict(
|
||||||
|
# Allow serializing enum values.
|
||||||
|
use_enum_values=True
|
||||||
|
)
|
||||||
|
"""Pydantic config."""
|
||||||
|
|
||||||
|
name: str = Field(
|
||||||
|
...,
|
||||||
|
description="Receiver protocol name. What protocols are supported (and what they are called) "
|
||||||
|
"may differ per provider.",
|
||||||
|
examples=["otlp_grpc", "otlp_http", "tempo_http"],
|
||||||
|
)
|
||||||
|
|
||||||
|
type: TransportProtocolType = Field(
|
||||||
|
...,
|
||||||
|
description="The transport protocol used by this receiver.",
|
||||||
|
examples=["http", "grpc"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Receiver(BaseModel):
|
||||||
|
"""Specification of an active receiver."""
|
||||||
|
|
||||||
|
protocol: ProtocolType = Field(..., description="Receiver protocol name and type.")
|
||||||
|
url: str = Field(
|
||||||
|
...,
|
||||||
|
description="""URL at which the receiver is reachable. If there's an ingress, it would be the external URL.
|
||||||
|
Otherwise, it would be the service's fqdn or internal IP.
|
||||||
|
If the protocol type is grpc, the url will not contain a scheme.""",
|
||||||
|
examples=[
|
||||||
|
"http://traefik_address:2331",
|
||||||
|
"https://traefik_address:2331",
|
||||||
|
"http://tempo_public_ip:2331",
|
||||||
|
"https://tempo_public_ip:2331",
|
||||||
|
"tempo_public_ip:2331",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TracingProviderAppData(DatabagModel): # noqa: D101
|
||||||
|
"""Application databag model for the tracing provider."""
|
||||||
|
|
||||||
|
receivers: List[Receiver] = Field(
|
||||||
|
...,
|
||||||
|
description="List of all receivers enabled on the tracing provider.",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TracingRequirerAppData(DatabagModel): # noqa: D101
|
||||||
|
"""Application databag model for the tracing requirer."""
|
||||||
|
|
||||||
|
receivers: List[ReceiverProtocol]
|
||||||
|
"""Requested receivers."""
|
||||||
|
|
||||||
|
|
||||||
|
class _AutoSnapshotEvent(RelationEvent):
|
||||||
|
__args__: Tuple[str, ...] = ()
|
||||||
|
__optional_kwargs__: Dict[str, Any] = {}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def __attrs__(cls):
|
||||||
|
return cls.__args__ + tuple(cls.__optional_kwargs__.keys())
|
||||||
|
|
||||||
|
def __init__(self, handle, relation, *args, **kwargs):
|
||||||
|
super().__init__(handle, relation)
|
||||||
|
|
||||||
|
if not len(self.__args__) == len(args):
|
||||||
|
raise TypeError("expected {} args, got {}".format(len(self.__args__), len(args)))
|
||||||
|
|
||||||
|
for attr, obj in zip(self.__args__, args):
|
||||||
|
setattr(self, attr, obj)
|
||||||
|
for attr, default in self.__optional_kwargs__.items():
|
||||||
|
obj = kwargs.get(attr, default)
|
||||||
|
setattr(self, attr, obj)
|
||||||
|
|
||||||
|
def snapshot(self) -> dict:
|
||||||
|
dct = super().snapshot()
|
||||||
|
for attr in self.__attrs__():
|
||||||
|
obj = getattr(self, attr)
|
||||||
|
try:
|
||||||
|
dct[attr] = obj
|
||||||
|
except ValueError as e:
|
||||||
|
raise ValueError(
|
||||||
|
"cannot automagically serialize {}: "
|
||||||
|
"override this method and do it "
|
||||||
|
"manually.".format(obj)
|
||||||
|
) from e
|
||||||
|
|
||||||
|
return dct
|
||||||
|
|
||||||
|
def restore(self, snapshot: dict) -> None:
|
||||||
|
super().restore(snapshot)
|
||||||
|
for attr, obj in snapshot.items():
|
||||||
|
setattr(self, attr, obj)
|
||||||
|
|
||||||
|
|
||||||
|
class RelationNotFoundError(Exception):
|
||||||
|
"""Raised if no relation with the given name is found."""
|
||||||
|
|
||||||
|
def __init__(self, relation_name: str):
|
||||||
|
self.relation_name = relation_name
|
||||||
|
self.message = "No relation named '{}' found".format(relation_name)
|
||||||
|
super().__init__(self.message)
|
||||||
|
|
||||||
|
|
||||||
|
class RelationInterfaceMismatchError(Exception):
|
||||||
|
"""Raised if the relation with the given name has an unexpected interface."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
relation_name: str,
|
||||||
|
expected_relation_interface: str,
|
||||||
|
actual_relation_interface: str,
|
||||||
|
):
|
||||||
|
self.relation_name = relation_name
|
||||||
|
self.expected_relation_interface = expected_relation_interface
|
||||||
|
self.actual_relation_interface = actual_relation_interface
|
||||||
|
self.message = (
|
||||||
|
"The '{}' relation has '{}' as interface rather than the expected '{}'".format(
|
||||||
|
relation_name, actual_relation_interface, expected_relation_interface
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
super().__init__(self.message)
|
||||||
|
|
||||||
|
|
||||||
|
class RelationRoleMismatchError(Exception):
|
||||||
|
"""Raised if the relation with the given name has a different role than expected."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
relation_name: str,
|
||||||
|
expected_relation_role: RelationRole,
|
||||||
|
actual_relation_role: RelationRole,
|
||||||
|
):
|
||||||
|
self.relation_name = relation_name
|
||||||
|
self.expected_relation_interface = expected_relation_role
|
||||||
|
self.actual_relation_role = actual_relation_role
|
||||||
|
self.message = "The '{}' relation has role '{}' rather than the expected '{}'".format(
|
||||||
|
relation_name, repr(actual_relation_role), repr(expected_relation_role)
|
||||||
|
)
|
||||||
|
|
||||||
|
super().__init__(self.message)
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_relation_by_interface_and_direction(
|
||||||
|
charm: CharmBase,
|
||||||
|
relation_name: str,
|
||||||
|
expected_relation_interface: str,
|
||||||
|
expected_relation_role: RelationRole,
|
||||||
|
):
|
||||||
|
"""Validate a relation.
|
||||||
|
|
||||||
|
Verifies that the `relation_name` provided: (1) exists in metadata.yaml,
|
||||||
|
(2) declares as interface the interface name passed as `relation_interface`
|
||||||
|
and (3) has the right "direction", i.e., it is a relation that `charm`
|
||||||
|
provides or requires.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
charm: a `CharmBase` object to scan for the matching relation.
|
||||||
|
relation_name: the name of the relation to be verified.
|
||||||
|
expected_relation_interface: the interface name to be matched by the
|
||||||
|
relation named `relation_name`.
|
||||||
|
expected_relation_role: whether the `relation_name` must be either
|
||||||
|
provided or required by `charm`.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
RelationNotFoundError: If there is no relation in the charm's metadata.yaml
|
||||||
|
with the same name as provided via `relation_name` argument.
|
||||||
|
RelationInterfaceMismatchError: The relation with the same name as provided
|
||||||
|
via `relation_name` argument does not have the same relation interface
|
||||||
|
as specified via the `expected_relation_interface` argument.
|
||||||
|
RelationRoleMismatchError: If the relation with the same name as provided
|
||||||
|
via `relation_name` argument does not have the same role as specified
|
||||||
|
via the `expected_relation_role` argument.
|
||||||
|
"""
|
||||||
|
if relation_name not in charm.meta.relations:
|
||||||
|
raise RelationNotFoundError(relation_name)
|
||||||
|
|
||||||
|
relation = charm.meta.relations[relation_name]
|
||||||
|
|
||||||
|
# fixme: why do we need to cast here?
|
||||||
|
actual_relation_interface = cast(str, relation.interface_name)
|
||||||
|
|
||||||
|
if actual_relation_interface != expected_relation_interface:
|
||||||
|
raise RelationInterfaceMismatchError(
|
||||||
|
relation_name, expected_relation_interface, actual_relation_interface
|
||||||
|
)
|
||||||
|
|
||||||
|
if expected_relation_role is RelationRole.provides:
|
||||||
|
if relation_name not in charm.meta.provides:
|
||||||
|
raise RelationRoleMismatchError(
|
||||||
|
relation_name, RelationRole.provides, RelationRole.requires
|
||||||
|
)
|
||||||
|
elif expected_relation_role is RelationRole.requires:
|
||||||
|
if relation_name not in charm.meta.requires:
|
||||||
|
raise RelationRoleMismatchError(
|
||||||
|
relation_name, RelationRole.requires, RelationRole.provides
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise TypeError("Unexpected RelationDirection: {}".format(expected_relation_role))
|
||||||
|
|
||||||
|
|
||||||
|
class RequestEvent(RelationEvent):
|
||||||
|
"""Event emitted when a remote requests a tracing endpoint."""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def requested_receivers(self) -> List[ReceiverProtocol]:
|
||||||
|
"""List of receiver protocols that have been requested."""
|
||||||
|
relation = self.relation
|
||||||
|
app = relation.app
|
||||||
|
if not app:
|
||||||
|
raise NotReadyError("relation.app is None")
|
||||||
|
|
||||||
|
return TracingRequirerAppData.load(relation.data[app]).receivers
|
||||||
|
|
||||||
|
|
||||||
|
class BrokenEvent(RelationBrokenEvent):
|
||||||
|
"""Event emitted when a relation on tracing is broken."""
|
||||||
|
|
||||||
|
|
||||||
|
class TracingEndpointProviderEvents(CharmEvents):
|
||||||
|
"""TracingEndpointProvider events."""
|
||||||
|
|
||||||
|
request = EventSource(RequestEvent)
|
||||||
|
broken = EventSource(BrokenEvent)
|
||||||
|
|
||||||
|
|
||||||
|
class TracingEndpointProvider(Object):
|
||||||
|
"""Class representing a trace receiver service."""
|
||||||
|
|
||||||
|
on = TracingEndpointProviderEvents() # type: ignore
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
charm: CharmBase,
|
||||||
|
external_url: Optional[str] = None,
|
||||||
|
relation_name: str = DEFAULT_RELATION_NAME,
|
||||||
|
):
|
||||||
|
"""Initialize.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
charm: a `CharmBase` instance that manages this instance of the Tempo service.
|
||||||
|
external_url: external address of the node hosting the tempo server,
|
||||||
|
if an ingress is present.
|
||||||
|
relation_name: an optional string name of the relation between `charm`
|
||||||
|
and the Tempo charmed service. The default is "tracing".
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
RelationNotFoundError: If there is no relation in the charm's metadata.yaml
|
||||||
|
with the same name as provided via `relation_name` argument.
|
||||||
|
RelationInterfaceMismatchError: The relation with the same name as provided
|
||||||
|
via `relation_name` argument does not have the `tracing` relation
|
||||||
|
interface.
|
||||||
|
RelationRoleMismatchError: If the relation with the same name as provided
|
||||||
|
via `relation_name` argument does not have the `RelationRole.requires`
|
||||||
|
role.
|
||||||
|
"""
|
||||||
|
_validate_relation_by_interface_and_direction(
|
||||||
|
charm, relation_name, RELATION_INTERFACE_NAME, RelationRole.provides
|
||||||
|
)
|
||||||
|
|
||||||
|
super().__init__(charm, relation_name + "tracing-provider")
|
||||||
|
self._charm = charm
|
||||||
|
self._external_url = external_url
|
||||||
|
self._relation_name = relation_name
|
||||||
|
self.framework.observe(
|
||||||
|
self._charm.on[relation_name].relation_joined, self._on_relation_event
|
||||||
|
)
|
||||||
|
self.framework.observe(
|
||||||
|
self._charm.on[relation_name].relation_created, self._on_relation_event
|
||||||
|
)
|
||||||
|
self.framework.observe(
|
||||||
|
self._charm.on[relation_name].relation_changed, self._on_relation_event
|
||||||
|
)
|
||||||
|
self.framework.observe(
|
||||||
|
self._charm.on[relation_name].relation_broken, self._on_relation_broken_event
|
||||||
|
)
|
||||||
|
|
||||||
|
def _on_relation_broken_event(self, e: RelationBrokenEvent):
|
||||||
|
"""Handle relation broken events."""
|
||||||
|
self.on.broken.emit(e.relation)
|
||||||
|
|
||||||
|
def _on_relation_event(self, e: RelationEvent):
|
||||||
|
"""Handle relation created/joined/changed events."""
|
||||||
|
if self.is_requirer_ready(e.relation):
|
||||||
|
self.on.request.emit(e.relation)
|
||||||
|
|
||||||
|
def is_requirer_ready(self, relation: Relation):
|
||||||
|
"""Attempt to determine if requirer has already populated app data."""
|
||||||
|
try:
|
||||||
|
self._get_requested_protocols(relation)
|
||||||
|
except NotReadyError:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_requested_protocols(relation: Relation):
|
||||||
|
app = relation.app
|
||||||
|
if not app:
|
||||||
|
raise NotReadyError("relation.app is None")
|
||||||
|
|
||||||
|
try:
|
||||||
|
databag = TracingRequirerAppData.load(relation.data[app])
|
||||||
|
except (json.JSONDecodeError, pydantic.ValidationError, DataValidationError):
|
||||||
|
logger.info(f"relation {relation} is not ready to talk tracing")
|
||||||
|
raise NotReadyError()
|
||||||
|
return databag.receivers
|
||||||
|
|
||||||
|
def requested_protocols(self):
|
||||||
|
"""All receiver protocols that have been requested by our related apps."""
|
||||||
|
requested_protocols = set()
|
||||||
|
for relation in self.relations:
|
||||||
|
try:
|
||||||
|
protocols = self._get_requested_protocols(relation)
|
||||||
|
except NotReadyError:
|
||||||
|
continue
|
||||||
|
requested_protocols.update(protocols)
|
||||||
|
return requested_protocols
|
||||||
|
|
||||||
|
@property
|
||||||
|
def relations(self) -> List[Relation]:
|
||||||
|
"""All relations active on this endpoint."""
|
||||||
|
return self._charm.model.relations[self._relation_name]
|
||||||
|
|
||||||
|
def publish_receivers(self, receivers: Sequence[RawReceiver]):
|
||||||
|
"""Let all requirers know that these receivers are active and listening."""
|
||||||
|
if not self._charm.unit.is_leader():
|
||||||
|
raise RuntimeError("only leader can do this")
|
||||||
|
|
||||||
|
for relation in self.relations:
|
||||||
|
try:
|
||||||
|
TracingProviderAppData(
|
||||||
|
receivers=[
|
||||||
|
Receiver(
|
||||||
|
url=url,
|
||||||
|
protocol=ProtocolType(
|
||||||
|
name=protocol,
|
||||||
|
type=receiver_protocol_to_transport_protocol[protocol],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
for protocol, url in receivers
|
||||||
|
],
|
||||||
|
).dump(relation.data[self._charm.app])
|
||||||
|
|
||||||
|
except ModelError as e:
|
||||||
|
# args are bytes
|
||||||
|
msg = e.args[0]
|
||||||
|
if isinstance(msg, bytes):
|
||||||
|
if msg.startswith(
|
||||||
|
b"ERROR cannot read relation application settings: permission denied"
|
||||||
|
):
|
||||||
|
logger.error(
|
||||||
|
f"encountered error {e} while attempting to update_relation_data."
|
||||||
|
f"The relation must be gone."
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
class EndpointRemovedEvent(RelationBrokenEvent):
|
||||||
|
"""Event representing a change in one of the receiver endpoints."""
|
||||||
|
|
||||||
|
|
||||||
|
class EndpointChangedEvent(_AutoSnapshotEvent):
|
||||||
|
"""Event representing a change in one of the receiver endpoints."""
|
||||||
|
|
||||||
|
__args__ = ("_receivers",)
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
_receivers = [] # type: List[dict]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def receivers(self) -> List[Receiver]:
|
||||||
|
"""Cast receivers back from dict."""
|
||||||
|
return [Receiver(**i) for i in self._receivers]
|
||||||
|
|
||||||
|
|
||||||
|
class TracingEndpointRequirerEvents(CharmEvents):
|
||||||
|
"""TracingEndpointRequirer events."""
|
||||||
|
|
||||||
|
endpoint_changed = EventSource(EndpointChangedEvent)
|
||||||
|
endpoint_removed = EventSource(EndpointRemovedEvent)
|
||||||
|
|
||||||
|
|
||||||
|
class TracingEndpointRequirer(Object):
|
||||||
|
"""A tracing endpoint for Tempo."""
|
||||||
|
|
||||||
|
on = TracingEndpointRequirerEvents() # type: ignore
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
charm: CharmBase,
|
||||||
|
relation_name: str = DEFAULT_RELATION_NAME,
|
||||||
|
protocols: Optional[List[ReceiverProtocol]] = None,
|
||||||
|
):
|
||||||
|
"""Construct a tracing requirer for a Tempo charm.
|
||||||
|
|
||||||
|
If your application supports pushing traces to a distributed tracing backend, the
|
||||||
|
`TracingEndpointRequirer` object enables your charm to easily access endpoint information
|
||||||
|
exchanged over a `tracing` relation interface.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
charm: a `CharmBase` object that manages this
|
||||||
|
`TracingEndpointRequirer` object. Typically, this is `self` in the instantiating
|
||||||
|
class.
|
||||||
|
relation_name: an optional string name of the relation between `charm`
|
||||||
|
and the Tempo charmed service. The default is "tracing". It is strongly
|
||||||
|
advised not to change the default, so that people deploying your charm will have a
|
||||||
|
consistent experience with all other charms that provide tracing endpoints.
|
||||||
|
protocols: optional list of protocols that the charm intends to send traces with.
|
||||||
|
The provider will enable receivers for these and only these protocols,
|
||||||
|
so be sure to enable all protocols the charm or its workload are going to need.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
RelationNotFoundError: If there is no relation in the charm's metadata.yaml
|
||||||
|
with the same name as provided via `relation_name` argument.
|
||||||
|
RelationInterfaceMismatchError: The relation with the same name as provided
|
||||||
|
via `relation_name` argument does not have the `tracing` relation
|
||||||
|
interface.
|
||||||
|
RelationRoleMismatchError: If the relation with the same name as provided
|
||||||
|
via `relation_name` argument does not have the `RelationRole.provides`
|
||||||
|
role.
|
||||||
|
"""
|
||||||
|
_validate_relation_by_interface_and_direction(
|
||||||
|
charm, relation_name, RELATION_INTERFACE_NAME, RelationRole.requires
|
||||||
|
)
|
||||||
|
|
||||||
|
super().__init__(charm, relation_name)
|
||||||
|
|
||||||
|
self._is_single_endpoint = charm.meta.relations[relation_name].limit == 1
|
||||||
|
|
||||||
|
self._charm = charm
|
||||||
|
self._relation_name = relation_name
|
||||||
|
|
||||||
|
events = self._charm.on[self._relation_name]
|
||||||
|
self.framework.observe(events.relation_changed, self._on_tracing_relation_changed)
|
||||||
|
self.framework.observe(events.relation_broken, self._on_tracing_relation_broken)
|
||||||
|
|
||||||
|
if protocols:
|
||||||
|
self.request_protocols(protocols)
|
||||||
|
|
||||||
|
def request_protocols(
|
||||||
|
self, protocols: Sequence[ReceiverProtocol], relation: Optional[Relation] = None
|
||||||
|
):
|
||||||
|
"""Publish the list of protocols which the provider should activate."""
|
||||||
|
# todo: should we check if _is_single_endpoint and len(self.relations) > 1 and raise, here?
|
||||||
|
relations = [relation] if relation else self.relations
|
||||||
|
|
||||||
|
if not protocols:
|
||||||
|
# empty sequence
|
||||||
|
raise ValueError(
|
||||||
|
"You need to pass a nonempty sequence of protocols to `request_protocols`."
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
if self._charm.unit.is_leader():
|
||||||
|
for relation in relations:
|
||||||
|
TracingRequirerAppData(
|
||||||
|
receivers=list(protocols),
|
||||||
|
).dump(relation.data[self._charm.app])
|
||||||
|
|
||||||
|
except ModelError as e:
|
||||||
|
# args are bytes
|
||||||
|
msg = e.args[0]
|
||||||
|
if isinstance(msg, bytes):
|
||||||
|
if msg.startswith(
|
||||||
|
b"ERROR cannot read relation application settings: permission denied"
|
||||||
|
):
|
||||||
|
logger.error(
|
||||||
|
f"encountered error {e} while attempting to request_protocols."
|
||||||
|
f"The relation must be gone."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
raise
|
||||||
|
|
||||||
|
@property
|
||||||
|
def relations(self) -> List[Relation]:
|
||||||
|
"""The tracing relations associated with this endpoint."""
|
||||||
|
return self._charm.model.relations[self._relation_name]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _relation(self) -> Optional[Relation]:
|
||||||
|
"""If this wraps a single endpoint, the relation bound to it, if any."""
|
||||||
|
if not self._is_single_endpoint:
|
||||||
|
objname = type(self).__name__
|
||||||
|
raise AmbiguousRelationUsageError(
|
||||||
|
f"This {objname} wraps a {self._relation_name} endpoint that has "
|
||||||
|
"limit != 1. We can't determine what relation, of the possibly many, you are "
|
||||||
|
f"talking about. Please pass a relation instance while calling {objname}, "
|
||||||
|
"or set limit=1 in the charm metadata."
|
||||||
|
)
|
||||||
|
relations = self.relations
|
||||||
|
return relations[0] if relations else None
|
||||||
|
|
||||||
|
def is_ready(self, relation: Optional[Relation] = None):
|
||||||
|
"""Is this endpoint ready?"""
|
||||||
|
relation = relation or self._relation
|
||||||
|
if not relation:
|
||||||
|
logger.debug(f"no relation on {self._relation_name !r}: tracing not ready")
|
||||||
|
return False
|
||||||
|
if relation.data is None:
|
||||||
|
logger.error(f"relation data is None for {relation}")
|
||||||
|
return False
|
||||||
|
if not relation.app:
|
||||||
|
logger.error(f"{relation} event received but there is no relation.app")
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
databag = dict(relation.data[relation.app])
|
||||||
|
TracingProviderAppData.load(databag)
|
||||||
|
|
||||||
|
except (json.JSONDecodeError, pydantic.ValidationError, DataValidationError):
|
||||||
|
logger.info(f"failed validating relation data for {relation}")
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _on_tracing_relation_changed(self, event):
|
||||||
|
"""Notify the providers that there is new endpoint information available."""
|
||||||
|
relation = event.relation
|
||||||
|
if not self.is_ready(relation):
|
||||||
|
self.on.endpoint_removed.emit(relation) # type: ignore
|
||||||
|
return
|
||||||
|
|
||||||
|
data = TracingProviderAppData.load(relation.data[relation.app])
|
||||||
|
self.on.endpoint_changed.emit(relation, [i.dict() for i in data.receivers]) # type: ignore
|
||||||
|
|
||||||
|
def _on_tracing_relation_broken(self, event: RelationBrokenEvent):
|
||||||
|
"""Notify the providers that the endpoint is broken."""
|
||||||
|
relation = event.relation
|
||||||
|
self.on.endpoint_removed.emit(relation) # type: ignore
|
||||||
|
|
||||||
|
def get_all_endpoints(
|
||||||
|
self, relation: Optional[Relation] = None
|
||||||
|
) -> Optional[TracingProviderAppData]:
|
||||||
|
"""Unmarshalled relation data."""
|
||||||
|
relation = relation or self._relation
|
||||||
|
if not self.is_ready(relation):
|
||||||
|
return
|
||||||
|
return TracingProviderAppData.load(relation.data[relation.app]) # type: ignore
|
||||||
|
|
||||||
|
def _get_endpoint(
|
||||||
|
self, relation: Optional[Relation], protocol: ReceiverProtocol
|
||||||
|
) -> Optional[str]:
|
||||||
|
app_data = self.get_all_endpoints(relation)
|
||||||
|
if not app_data:
|
||||||
|
return None
|
||||||
|
receivers: List[Receiver] = list(
|
||||||
|
filter(lambda i: i.protocol.name == protocol, app_data.receivers)
|
||||||
|
)
|
||||||
|
if not receivers:
|
||||||
|
logger.error(f"no receiver found with protocol={protocol!r}")
|
||||||
|
return
|
||||||
|
if len(receivers) > 1:
|
||||||
|
logger.error(
|
||||||
|
f"too many receivers with protocol={protocol!r}; using first one. Found: {receivers}"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
receiver = receivers[0]
|
||||||
|
return receiver.url
|
||||||
|
|
||||||
|
def get_endpoint(
|
||||||
|
self, protocol: ReceiverProtocol, relation: Optional[Relation] = None
|
||||||
|
) -> Optional[str]:
|
||||||
|
"""Receiver endpoint for the given protocol."""
|
||||||
|
endpoint = self._get_endpoint(relation or self._relation, protocol=protocol)
|
||||||
|
if not endpoint:
|
||||||
|
requested_protocols = set()
|
||||||
|
relations = [relation] if relation else self.relations
|
||||||
|
for relation in relations:
|
||||||
|
try:
|
||||||
|
databag = TracingRequirerAppData.load(relation.data[self._charm.app])
|
||||||
|
except DataValidationError:
|
||||||
|
continue
|
||||||
|
|
||||||
|
requested_protocols.update(databag.receivers)
|
||||||
|
|
||||||
|
if protocol not in requested_protocols:
|
||||||
|
raise ProtocolNotRequestedError(protocol, relation)
|
||||||
|
|
||||||
|
return None
|
||||||
|
return endpoint
|
||||||
|
|
||||||
|
|
||||||
|
def charm_tracing_config(
|
||||||
|
endpoint_requirer: TracingEndpointRequirer, cert_path: Optional[Union[Path, str]]
|
||||||
|
) -> Tuple[Optional[str], Optional[str]]:
|
||||||
|
"""Utility function to determine the charm_tracing config you will likely want.
|
||||||
|
|
||||||
|
If no endpoint is provided:
|
||||||
|
disable charm tracing.
|
||||||
|
If https endpoint is provided but cert_path is not found on disk:
|
||||||
|
disable charm tracing.
|
||||||
|
If https endpoint is provided and cert_path is None:
|
||||||
|
ERROR
|
||||||
|
Else:
|
||||||
|
proceed with charm tracing (with or without tls, as appropriate)
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
If you are using charm_tracing >= v1.9:
|
||||||
|
>>> from lib.charms.tempo_k8s.v1.charm_tracing import trace_charm
|
||||||
|
>>> from lib.charms.tempo_k8s.v2.tracing import charm_tracing_config
|
||||||
|
>>> @trace_charm(tracing_endpoint="my_endpoint", cert_path="cert_path")
|
||||||
|
>>> class MyCharm(...):
|
||||||
|
>>> _cert_path = "/path/to/cert/on/charm/container.crt"
|
||||||
|
>>> def __init__(self, ...):
|
||||||
|
>>> self.tracing = TracingEndpointRequirer(...)
|
||||||
|
>>> self.my_endpoint, self.cert_path = charm_tracing_config(
|
||||||
|
... self.tracing, self._cert_path)
|
||||||
|
|
||||||
|
If you are using charm_tracing < v1.9:
|
||||||
|
>>> from lib.charms.tempo_k8s.v1.charm_tracing import trace_charm
|
||||||
|
>>> from lib.charms.tempo_k8s.v2.tracing import charm_tracing_config
|
||||||
|
>>> @trace_charm(tracing_endpoint="my_endpoint", cert_path="cert_path")
|
||||||
|
>>> class MyCharm(...):
|
||||||
|
>>> _cert_path = "/path/to/cert/on/charm/container.crt"
|
||||||
|
>>> def __init__(self, ...):
|
||||||
|
>>> self.tracing = TracingEndpointRequirer(...)
|
||||||
|
>>> self._my_endpoint, self._cert_path = charm_tracing_config(
|
||||||
|
... self.tracing, self._cert_path)
|
||||||
|
>>> @property
|
||||||
|
>>> def my_endpoint(self):
|
||||||
|
>>> return self._my_endpoint
|
||||||
|
>>> @property
|
||||||
|
>>> def cert_path(self):
|
||||||
|
>>> return self._cert_path
|
||||||
|
|
||||||
|
"""
|
||||||
|
if not endpoint_requirer.is_ready():
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
endpoint = endpoint_requirer.get_endpoint("otlp_http")
|
||||||
|
if not endpoint:
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
is_https = endpoint.startswith("https://")
|
||||||
|
|
||||||
|
if is_https:
|
||||||
|
if cert_path is None:
|
||||||
|
raise TracingError("Cannot send traces to an https endpoint without a certificate.")
|
||||||
|
elif not Path(cert_path).exists():
|
||||||
|
# if endpoint is https BUT we don't have a server_cert yet:
|
||||||
|
# disable charm tracing until we do to prevent tls errors
|
||||||
|
return None, None
|
||||||
|
return endpoint, str(cert_path)
|
||||||
|
else:
|
||||||
|
return endpoint, None
|
@ -138,6 +138,10 @@ class OSBaseOperatorCharm(ops.charm.CharmBase):
|
|||||||
) -> List[sunbeam_rhandlers.RelationHandler]:
|
) -> List[sunbeam_rhandlers.RelationHandler]:
|
||||||
"""Relation handlers for the service."""
|
"""Relation handlers for the service."""
|
||||||
handlers = handlers or []
|
handlers = handlers or []
|
||||||
|
if self.can_add_handler("tracing", handlers):
|
||||||
|
self.tracing = sunbeam_rhandlers.TracingRequireHandler(
|
||||||
|
self, "tracing", "tracing" in self.mandatory_relations
|
||||||
|
)
|
||||||
if self.can_add_handler("amqp", handlers):
|
if self.can_add_handler("amqp", handlers):
|
||||||
self.amqp = sunbeam_rhandlers.RabbitMQHandler(
|
self.amqp = sunbeam_rhandlers.RabbitMQHandler(
|
||||||
self,
|
self,
|
||||||
@ -201,6 +205,12 @@ class OSBaseOperatorCharm(ops.charm.CharmBase):
|
|||||||
|
|
||||||
return handlers
|
return handlers
|
||||||
|
|
||||||
|
def get_tracing_endpoint(self) -> str | None:
|
||||||
|
"""Get the tracing endpoint for the service."""
|
||||||
|
if hasattr(self, "tracing"):
|
||||||
|
return self.tracing.tracing_endpoint()
|
||||||
|
return None
|
||||||
|
|
||||||
def get_sans_ips(self) -> List[str]:
|
def get_sans_ips(self) -> List[str]:
|
||||||
"""Return Subject Alternate Names to use in cert for service."""
|
"""Return Subject Alternate Names to use in cert for service."""
|
||||||
str_ips_sans = [str(s) for s in self._ip_sans()]
|
str_ips_sans = [str(s) for s in self._ip_sans()]
|
||||||
|
@ -31,6 +31,7 @@ from typing import (
|
|||||||
Tuple,
|
Tuple,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
import ops_sunbeam.tracing as sunbeam_tracing
|
||||||
from ops.charm import (
|
from ops.charm import (
|
||||||
CharmBase,
|
CharmBase,
|
||||||
)
|
)
|
||||||
@ -123,6 +124,7 @@ class Status:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class StatusPool(Object):
|
class StatusPool(Object):
|
||||||
"""A pool of Status objects.
|
"""A pool of Status objects.
|
||||||
|
|
||||||
|
@ -28,6 +28,8 @@ from typing import (
|
|||||||
TYPE_CHECKING,
|
TYPE_CHECKING,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
import ops_sunbeam.tracing as sunbeam_tracing
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
import ops_sunbeam.charm
|
import ops_sunbeam.charm
|
||||||
|
|
||||||
@ -38,6 +40,7 @@ ERASURE_CODED = "erasure-coded"
|
|||||||
REPLICATED = "replicated"
|
REPLICATED = "replicated"
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class ConfigContext:
|
class ConfigContext:
|
||||||
"""Base class used for creating a config context."""
|
"""Base class used for creating a config context."""
|
||||||
|
|
||||||
@ -63,6 +66,7 @@ class ConfigContext:
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class CharmConfigContext(ConfigContext):
|
class CharmConfigContext(ConfigContext):
|
||||||
"""A context containing all of the charms config options."""
|
"""A context containing all of the charms config options."""
|
||||||
|
|
||||||
@ -71,6 +75,7 @@ class CharmConfigContext(ConfigContext):
|
|||||||
return self.charm.config
|
return self.charm.config
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class WSGIWorkerConfigContext(ConfigContext):
|
class WSGIWorkerConfigContext(ConfigContext):
|
||||||
"""Configuration context for WSGI configuration."""
|
"""Configuration context for WSGI configuration."""
|
||||||
|
|
||||||
@ -88,6 +93,7 @@ class WSGIWorkerConfigContext(ConfigContext):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class CephConfigurationContext(ConfigContext):
|
class CephConfigurationContext(ConfigContext):
|
||||||
"""Ceph configuration context."""
|
"""Ceph configuration context."""
|
||||||
|
|
||||||
@ -103,6 +109,7 @@ class CephConfigurationContext(ConfigContext):
|
|||||||
return ctxt
|
return ctxt
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class CinderCephConfigurationContext(ConfigContext):
|
class CinderCephConfigurationContext(ConfigContext):
|
||||||
"""Cinder Ceph configuration context."""
|
"""Cinder Ceph configuration context."""
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ import ops.pebble
|
|||||||
import ops_sunbeam.compound_status as compound_status
|
import ops_sunbeam.compound_status as compound_status
|
||||||
import ops_sunbeam.core as sunbeam_core
|
import ops_sunbeam.core as sunbeam_core
|
||||||
import ops_sunbeam.templating as sunbeam_templating
|
import ops_sunbeam.templating as sunbeam_templating
|
||||||
|
import ops_sunbeam.tracing as sunbeam_tracing
|
||||||
from ops.model import (
|
from ops.model import (
|
||||||
ActiveStatus,
|
ActiveStatus,
|
||||||
BlockedStatus,
|
BlockedStatus,
|
||||||
@ -47,6 +48,7 @@ ContainerDir = collections.namedtuple(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class PebbleHandler(ops.framework.Object):
|
class PebbleHandler(ops.framework.Object):
|
||||||
"""Base handler for Pebble based containers."""
|
"""Base handler for Pebble based containers."""
|
||||||
|
|
||||||
@ -319,6 +321,7 @@ class PebbleHandler(ops.framework.Object):
|
|||||||
"""Called when files have changed before restarting services."""
|
"""Called when files have changed before restarting services."""
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class ServicePebbleHandler(PebbleHandler):
|
class ServicePebbleHandler(PebbleHandler):
|
||||||
"""Container handler for containers which manage a service."""
|
"""Container handler for containers which manage a service."""
|
||||||
|
|
||||||
@ -356,6 +359,7 @@ class ServicePebbleHandler(PebbleHandler):
|
|||||||
self.start_all(restart=restart)
|
self.start_all(restart=restart)
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class WSGIPebbleHandler(PebbleHandler):
|
class WSGIPebbleHandler(PebbleHandler):
|
||||||
"""WSGI oriented handler for a Pebble managed container."""
|
"""WSGI oriented handler for a Pebble managed container."""
|
||||||
|
|
||||||
|
@ -23,6 +23,8 @@ from typing import (
|
|||||||
Union,
|
Union,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
import ops_sunbeam.tracing as sunbeam_tracing
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from ops_sunbeam.charm import (
|
from ops_sunbeam.charm import (
|
||||||
OSBaseOperatorCharm,
|
OSBaseOperatorCharm,
|
||||||
@ -41,6 +43,7 @@ ContainerConfigFile = collections.namedtuple(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class OPSCharmContexts:
|
class OPSCharmContexts:
|
||||||
"""Set of config contexts and contexts from relation handlers."""
|
"""Set of config contexts and contexts from relation handlers."""
|
||||||
|
|
||||||
|
@ -20,8 +20,10 @@ These are not specific to a relation.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from .. import config_contexts as sunbeam_ccontexts
|
from .. import config_contexts as sunbeam_ccontexts
|
||||||
|
from .. import tracing as sunbeam_tracing
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class OVNDBConfigContext(sunbeam_ccontexts.ConfigContext):
|
class OVNDBConfigContext(sunbeam_ccontexts.ConfigContext):
|
||||||
"""Context for OVN charms."""
|
"""Context for OVN charms."""
|
||||||
|
|
||||||
|
@ -24,8 +24,10 @@ from ops.model import (
|
|||||||
|
|
||||||
from .. import container_handlers as sunbeam_chandlers
|
from .. import container_handlers as sunbeam_chandlers
|
||||||
from .. import core as sunbeam_core
|
from .. import core as sunbeam_core
|
||||||
|
from .. import tracing as sunbeam_tracing
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class OVNPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
class OVNPebbleHandler(sunbeam_chandlers.ServicePebbleHandler):
|
||||||
"""Common class for OVN services."""
|
"""Common class for OVN services."""
|
||||||
|
|
||||||
|
@ -32,10 +32,12 @@ from ops.model import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from .. import relation_handlers as sunbeam_rhandlers
|
from .. import relation_handlers as sunbeam_rhandlers
|
||||||
|
from .. import tracing as sunbeam_tracing
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class OVNRelationUtils:
|
class OVNRelationUtils:
|
||||||
"""Common utilities for processing OVN relations."""
|
"""Common utilities for processing OVN relations."""
|
||||||
|
|
||||||
@ -306,6 +308,7 @@ class OVNRelationUtils:
|
|||||||
return list(set(addresses))
|
return list(set(addresses))
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class OVNDBClusterPeerHandler(
|
class OVNDBClusterPeerHandler(
|
||||||
sunbeam_rhandlers.BasePeerHandler, OVNRelationUtils
|
sunbeam_rhandlers.BasePeerHandler, OVNRelationUtils
|
||||||
):
|
):
|
||||||
@ -458,6 +461,7 @@ class OVNDBClusterPeerHandler(
|
|||||||
return ctxt
|
return ctxt
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class OVSDBCMSProvidesHandler(
|
class OVSDBCMSProvidesHandler(
|
||||||
sunbeam_rhandlers.RelationHandler, OVNRelationUtils
|
sunbeam_rhandlers.RelationHandler, OVNRelationUtils
|
||||||
):
|
):
|
||||||
@ -481,7 +485,7 @@ class OVSDBCMSProvidesHandler(
|
|||||||
logger.debug("Setting up ovs-cms provides event handler")
|
logger.debug("Setting up ovs-cms provides event handler")
|
||||||
import charms.ovn_central_k8s.v0.ovsdb as ovsdb
|
import charms.ovn_central_k8s.v0.ovsdb as ovsdb
|
||||||
|
|
||||||
ovsdb_svc = ovsdb.OVSDBCMSProvides(
|
ovsdb_svc = sunbeam_tracing.trace_type(ovsdb.OVSDBCMSProvides)(
|
||||||
self.charm,
|
self.charm,
|
||||||
self.relation_name,
|
self.relation_name,
|
||||||
)
|
)
|
||||||
@ -510,6 +514,7 @@ class OVSDBCMSProvidesHandler(
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class OVSDBCMSRequiresHandler(
|
class OVSDBCMSRequiresHandler(
|
||||||
sunbeam_rhandlers.RelationHandler, OVNRelationUtils
|
sunbeam_rhandlers.RelationHandler, OVNRelationUtils
|
||||||
):
|
):
|
||||||
@ -532,7 +537,7 @@ class OVSDBCMSRequiresHandler(
|
|||||||
logger.debug("Setting up ovs-cms requires event handler")
|
logger.debug("Setting up ovs-cms requires event handler")
|
||||||
import charms.ovn_central_k8s.v0.ovsdb as ovsdb
|
import charms.ovn_central_k8s.v0.ovsdb as ovsdb
|
||||||
|
|
||||||
ovsdb_svc = ovsdb.OVSDBCMSRequires(
|
ovsdb_svc = sunbeam_tracing.trace_type(ovsdb.OVSDBCMSRequires)(
|
||||||
self.charm,
|
self.charm,
|
||||||
self.relation_name,
|
self.relation_name,
|
||||||
)
|
)
|
||||||
|
@ -38,6 +38,7 @@ import ops.charm
|
|||||||
import ops.framework
|
import ops.framework
|
||||||
import ops_sunbeam.compound_status as compound_status
|
import ops_sunbeam.compound_status as compound_status
|
||||||
import ops_sunbeam.interfaces as sunbeam_interfaces
|
import ops_sunbeam.interfaces as sunbeam_interfaces
|
||||||
|
import ops_sunbeam.tracing as sunbeam_tracing
|
||||||
from ops.model import (
|
from ops.model import (
|
||||||
ActiveStatus,
|
ActiveStatus,
|
||||||
BlockedStatus,
|
BlockedStatus,
|
||||||
@ -53,6 +54,7 @@ ERASURE_CODED = "erasure-coded"
|
|||||||
REPLICATED = "replicated"
|
REPLICATED = "replicated"
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class RelationHandler(ops.framework.Object):
|
class RelationHandler(ops.framework.Object):
|
||||||
"""Base handler class for relations.
|
"""Base handler class for relations.
|
||||||
|
|
||||||
@ -151,6 +153,7 @@ class RelationHandler(ops.framework.Object):
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class IngressHandler(RelationHandler):
|
class IngressHandler(RelationHandler):
|
||||||
"""Base class to handle Ingress relations."""
|
"""Base class to handle Ingress relations."""
|
||||||
|
|
||||||
@ -175,7 +178,7 @@ class IngressHandler(RelationHandler):
|
|||||||
IngressPerAppRequirer,
|
IngressPerAppRequirer,
|
||||||
)
|
)
|
||||||
|
|
||||||
interface = IngressPerAppRequirer(
|
interface = sunbeam_tracing.trace_type(IngressPerAppRequirer)(
|
||||||
self.charm,
|
self.charm,
|
||||||
self.relation_name,
|
self.relation_name,
|
||||||
port=self.default_ingress_port,
|
port=self.default_ingress_port,
|
||||||
@ -247,14 +250,17 @@ class IngressHandler(RelationHandler):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class IngressInternalHandler(IngressHandler):
|
class IngressInternalHandler(IngressHandler):
|
||||||
"""Handler for Ingress relations on internal interface."""
|
"""Handler for Ingress relations on internal interface."""
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class IngressPublicHandler(IngressHandler):
|
class IngressPublicHandler(IngressHandler):
|
||||||
"""Handler for Ingress relations on public interface."""
|
"""Handler for Ingress relations on public interface."""
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class DBHandler(RelationHandler):
|
class DBHandler(RelationHandler):
|
||||||
"""Handler for DB relations."""
|
"""Handler for DB relations."""
|
||||||
|
|
||||||
@ -285,6 +291,10 @@ class DBHandler(RelationHandler):
|
|||||||
# from trigger handlers for other dbs.
|
# from trigger handlers for other dbs.
|
||||||
# It also must be a valid python identifier.
|
# It also must be a valid python identifier.
|
||||||
alias = self.relation_name.replace("-", "_")
|
alias = self.relation_name.replace("-", "_")
|
||||||
|
# tracing this library is currently failing
|
||||||
|
# implement when either one of these is fixed:
|
||||||
|
# https://github.com/canonical/tempo-k8s-operator/issues/155
|
||||||
|
# https://github.com/canonical/data-platform-libs/issues/186
|
||||||
db = DatabaseRequires(
|
db = DatabaseRequires(
|
||||||
self.charm,
|
self.charm,
|
||||||
self.relation_name,
|
self.relation_name,
|
||||||
@ -386,6 +396,7 @@ class DBHandler(RelationHandler):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class RabbitMQHandler(RelationHandler):
|
class RabbitMQHandler(RelationHandler):
|
||||||
"""Handler for managing a rabbitmq relation."""
|
"""Handler for managing a rabbitmq relation."""
|
||||||
|
|
||||||
@ -412,7 +423,7 @@ class RabbitMQHandler(RelationHandler):
|
|||||||
# has this relation.
|
# has this relation.
|
||||||
import charms.rabbitmq_k8s.v0.rabbitmq as sunbeam_rabbitmq
|
import charms.rabbitmq_k8s.v0.rabbitmq as sunbeam_rabbitmq
|
||||||
|
|
||||||
amqp = sunbeam_rabbitmq.RabbitMQRequires(
|
amqp = sunbeam_tracing.trace_type(sunbeam_rabbitmq.RabbitMQRequires)(
|
||||||
self.charm, self.relation_name, self.username, self.vhost
|
self.charm, self.relation_name, self.username, self.vhost
|
||||||
)
|
)
|
||||||
self.framework.observe(amqp.on.ready, self._on_amqp_ready)
|
self.framework.observe(amqp.on.ready, self._on_amqp_ready)
|
||||||
@ -473,12 +484,14 @@ class RabbitMQHandler(RelationHandler):
|
|||||||
return ctxt
|
return ctxt
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class AMQPHandler(RabbitMQHandler):
|
class AMQPHandler(RabbitMQHandler):
|
||||||
"""Backwards compatibility class for older library consumers."""
|
"""Backwards compatibility class for older library consumers."""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class IdentityServiceRequiresHandler(RelationHandler):
|
class IdentityServiceRequiresHandler(RelationHandler):
|
||||||
"""Handler for managing a identity-service relation."""
|
"""Handler for managing a identity-service relation."""
|
||||||
|
|
||||||
@ -501,7 +514,7 @@ class IdentityServiceRequiresHandler(RelationHandler):
|
|||||||
logger.debug("Setting up Identity Service event handler")
|
logger.debug("Setting up Identity Service event handler")
|
||||||
import charms.keystone_k8s.v1.identity_service as sun_id
|
import charms.keystone_k8s.v1.identity_service as sun_id
|
||||||
|
|
||||||
id_svc = sun_id.IdentityServiceRequires(
|
id_svc = sunbeam_tracing.trace_type(sun_id.IdentityServiceRequires)(
|
||||||
self.charm, self.relation_name, self.service_endpoints, self.region
|
self.charm, self.relation_name, self.service_endpoints, self.region
|
||||||
)
|
)
|
||||||
self.framework.observe(
|
self.framework.observe(
|
||||||
@ -544,6 +557,7 @@ class IdentityServiceRequiresHandler(RelationHandler):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class BasePeerHandler(RelationHandler):
|
class BasePeerHandler(RelationHandler):
|
||||||
"""Base handler for managing a peers relation."""
|
"""Base handler for managing a peers relation."""
|
||||||
|
|
||||||
@ -554,7 +568,9 @@ class BasePeerHandler(RelationHandler):
|
|||||||
logger.debug("Setting up peer event handler")
|
logger.debug("Setting up peer event handler")
|
||||||
# Lazy import to ensure this lib is only required if the charm
|
# Lazy import to ensure this lib is only required if the charm
|
||||||
# has this relation.
|
# has this relation.
|
||||||
peer_int = sunbeam_interfaces.OperatorPeers(
|
peer_int = sunbeam_tracing.trace_type(
|
||||||
|
sunbeam_interfaces.OperatorPeers
|
||||||
|
)(
|
||||||
self.charm,
|
self.charm,
|
||||||
self.relation_name,
|
self.relation_name,
|
||||||
)
|
)
|
||||||
@ -639,6 +655,7 @@ class BasePeerHandler(RelationHandler):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class CephClientHandler(RelationHandler):
|
class CephClientHandler(RelationHandler):
|
||||||
"""Handler for ceph-client interface."""
|
"""Handler for ceph-client interface."""
|
||||||
|
|
||||||
@ -663,7 +680,7 @@ class CephClientHandler(RelationHandler):
|
|||||||
# has this relation.
|
# has this relation.
|
||||||
import interface_ceph_client.ceph_client as ceph_client
|
import interface_ceph_client.ceph_client as ceph_client
|
||||||
|
|
||||||
ceph = ceph_client.CephClientRequires(
|
ceph = sunbeam_tracing.trace_type(ceph_client.CephClientRequires)(
|
||||||
self.charm,
|
self.charm,
|
||||||
self.relation_name,
|
self.relation_name,
|
||||||
)
|
)
|
||||||
@ -857,6 +874,7 @@ class _Store(abc.ABC):
|
|||||||
self.save_entry(name, entry)
|
self.save_entry(name, entry)
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class TlsCertificatesHandler(RelationHandler):
|
class TlsCertificatesHandler(RelationHandler):
|
||||||
"""Handler for certificates interface."""
|
"""Handler for certificates interface."""
|
||||||
|
|
||||||
@ -1008,9 +1026,9 @@ class TlsCertificatesHandler(RelationHandler):
|
|||||||
TLSCertificatesRequiresV3,
|
TLSCertificatesRequiresV3,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.certificates = TLSCertificatesRequiresV3(
|
self.certificates = sunbeam_tracing.trace_type(
|
||||||
self.charm, "certificates"
|
TLSCertificatesRequiresV3
|
||||||
)
|
)(self.charm, "certificates")
|
||||||
|
|
||||||
self.framework.observe(
|
self.framework.observe(
|
||||||
self.charm.on.certificates_relation_joined,
|
self.charm.on.certificates_relation_joined,
|
||||||
@ -1247,6 +1265,7 @@ class TlsCertificatesHandler(RelationHandler):
|
|||||||
return ctxt
|
return ctxt
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class IdentityCredentialsRequiresHandler(RelationHandler):
|
class IdentityCredentialsRequiresHandler(RelationHandler):
|
||||||
"""Handles the identity credentials relation on the requires side."""
|
"""Handles the identity credentials relation on the requires side."""
|
||||||
|
|
||||||
@ -1277,7 +1296,9 @@ class IdentityCredentialsRequiresHandler(RelationHandler):
|
|||||||
import charms.keystone_k8s.v0.identity_credentials as identity_credentials
|
import charms.keystone_k8s.v0.identity_credentials as identity_credentials
|
||||||
|
|
||||||
logger.debug("Setting up the identity-credentials event handler")
|
logger.debug("Setting up the identity-credentials event handler")
|
||||||
credentials_service = identity_credentials.IdentityCredentialsRequires(
|
credentials_service = sunbeam_tracing.trace_type(
|
||||||
|
identity_credentials.IdentityCredentialsRequires
|
||||||
|
)(
|
||||||
self.charm,
|
self.charm,
|
||||||
self.relation_name,
|
self.relation_name,
|
||||||
)
|
)
|
||||||
@ -1308,6 +1329,7 @@ class IdentityCredentialsRequiresHandler(RelationHandler):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class IdentityResourceRequiresHandler(RelationHandler):
|
class IdentityResourceRequiresHandler(RelationHandler):
|
||||||
"""Handles the identity resource relation on the requires side."""
|
"""Handles the identity resource relation on the requires side."""
|
||||||
|
|
||||||
@ -1341,7 +1363,7 @@ class IdentityResourceRequiresHandler(RelationHandler):
|
|||||||
import charms.keystone_k8s.v0.identity_resource as ops_svc
|
import charms.keystone_k8s.v0.identity_resource as ops_svc
|
||||||
|
|
||||||
logger.debug("Setting up Identity Resource event handler")
|
logger.debug("Setting up Identity Resource event handler")
|
||||||
ops_svc = ops_svc.IdentityResourceRequires(
|
ops_svc = sunbeam_tracing.trace_type(ops_svc.IdentityResourceRequires)(
|
||||||
self.charm,
|
self.charm,
|
||||||
self.relation_name,
|
self.relation_name,
|
||||||
)
|
)
|
||||||
@ -1384,6 +1406,7 @@ class IdentityResourceRequiresHandler(RelationHandler):
|
|||||||
return self.interface.ready()
|
return self.interface.ready()
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class CeilometerServiceRequiresHandler(RelationHandler):
|
class CeilometerServiceRequiresHandler(RelationHandler):
|
||||||
"""Handle ceilometer service relation on the requires side."""
|
"""Handle ceilometer service relation on the requires side."""
|
||||||
|
|
||||||
@ -1417,7 +1440,9 @@ class CeilometerServiceRequiresHandler(RelationHandler):
|
|||||||
import charms.ceilometer_k8s.v0.ceilometer_service as ceilometer_svc
|
import charms.ceilometer_k8s.v0.ceilometer_service as ceilometer_svc
|
||||||
|
|
||||||
logger.debug("Setting up Ceilometer service event handler")
|
logger.debug("Setting up Ceilometer service event handler")
|
||||||
svc = ceilometer_svc.CeilometerServiceRequires(
|
svc = sunbeam_tracing.trace_type(
|
||||||
|
ceilometer_svc.CeilometerServiceRequires
|
||||||
|
)(
|
||||||
self.charm,
|
self.charm,
|
||||||
self.relation_name,
|
self.relation_name,
|
||||||
)
|
)
|
||||||
@ -1454,6 +1479,7 @@ class CeilometerServiceRequiresHandler(RelationHandler):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class CephAccessRequiresHandler(RelationHandler):
|
class CephAccessRequiresHandler(RelationHandler):
|
||||||
"""Handles the ceph access relation on the requires side."""
|
"""Handles the ceph access relation on the requires side."""
|
||||||
|
|
||||||
@ -1484,7 +1510,9 @@ class CephAccessRequiresHandler(RelationHandler):
|
|||||||
import charms.cinder_ceph_k8s.v0.ceph_access as ceph_access
|
import charms.cinder_ceph_k8s.v0.ceph_access as ceph_access
|
||||||
|
|
||||||
logger.debug("Setting up the ceph-access event handler")
|
logger.debug("Setting up the ceph-access event handler")
|
||||||
ceph_access = ceph_access.CephAccessRequires(
|
ceph_access = sunbeam_tracing.trace_type(
|
||||||
|
ceph_access.CephAccessRequires
|
||||||
|
)(
|
||||||
self.charm,
|
self.charm,
|
||||||
self.relation_name,
|
self.relation_name,
|
||||||
)
|
)
|
||||||
@ -1524,6 +1552,7 @@ class CephAccessRequiresHandler(RelationHandler):
|
|||||||
ExtraOpsProcess = Callable[[ops.EventBase, dict], None]
|
ExtraOpsProcess = Callable[[ops.EventBase, dict], None]
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class UserIdentityResourceRequiresHandler(RelationHandler):
|
class UserIdentityResourceRequiresHandler(RelationHandler):
|
||||||
"""Handle user management on IdentityResource relation."""
|
"""Handle user management on IdentityResource relation."""
|
||||||
|
|
||||||
@ -1585,7 +1614,7 @@ class UserIdentityResourceRequiresHandler(RelationHandler):
|
|||||||
import charms.keystone_k8s.v0.identity_resource as id_ops
|
import charms.keystone_k8s.v0.identity_resource as id_ops
|
||||||
|
|
||||||
logger.debug("Setting up Identity Resource event handler")
|
logger.debug("Setting up Identity Resource event handler")
|
||||||
ops_svc = id_ops.IdentityResourceRequires(
|
ops_svc = sunbeam_tracing.trace_type(id_ops.IdentityResourceRequires)(
|
||||||
self.charm,
|
self.charm,
|
||||||
self.relation_name,
|
self.relation_name,
|
||||||
)
|
)
|
||||||
@ -1958,6 +1987,7 @@ class UserIdentityResourceRequiresHandler(RelationHandler):
|
|||||||
return self.get_config_credentials() is not None
|
return self.get_config_credentials() is not None
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class CertificateTransferRequiresHandler(RelationHandler):
|
class CertificateTransferRequiresHandler(RelationHandler):
|
||||||
"""Handle certificate transfer relation on the requires side."""
|
"""Handle certificate transfer relation on the requires side."""
|
||||||
|
|
||||||
@ -1994,7 +2024,7 @@ class CertificateTransferRequiresHandler(RelationHandler):
|
|||||||
CertificateTransferRequires,
|
CertificateTransferRequires,
|
||||||
)
|
)
|
||||||
|
|
||||||
recv_ca_cert = CertificateTransferRequires(
|
recv_ca_cert = sunbeam_tracing.trace_type(CertificateTransferRequires)(
|
||||||
self.charm, "receive-ca-cert"
|
self.charm, "receive-ca-cert"
|
||||||
)
|
)
|
||||||
self.framework.observe(
|
self.framework.observe(
|
||||||
@ -2039,6 +2069,7 @@ class CertificateTransferRequiresHandler(RelationHandler):
|
|||||||
return {"ca_bundle": "\n".join(ca_bundle)}
|
return {"ca_bundle": "\n".join(ca_bundle)}
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class TraefikRouteHandler(RelationHandler):
|
class TraefikRouteHandler(RelationHandler):
|
||||||
"""Base class to handle traefik route relations."""
|
"""Base class to handle traefik route relations."""
|
||||||
|
|
||||||
@ -2061,7 +2092,7 @@ class TraefikRouteHandler(RelationHandler):
|
|||||||
TraefikRouteRequirer,
|
TraefikRouteRequirer,
|
||||||
)
|
)
|
||||||
|
|
||||||
interface = TraefikRouteRequirer(
|
interface = sunbeam_tracing.trace_type(TraefikRouteRequirer)(
|
||||||
self.charm,
|
self.charm,
|
||||||
self.model.get_relation(self.relation_name),
|
self.model.get_relation(self.relation_name),
|
||||||
self.relation_name,
|
self.relation_name,
|
||||||
@ -2112,6 +2143,7 @@ class TraefikRouteHandler(RelationHandler):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class NovaServiceRequiresHandler(RelationHandler):
|
class NovaServiceRequiresHandler(RelationHandler):
|
||||||
"""Handle nova service relation on the requires side."""
|
"""Handle nova service relation on the requires side."""
|
||||||
|
|
||||||
@ -2145,7 +2177,7 @@ class NovaServiceRequiresHandler(RelationHandler):
|
|||||||
import charms.nova_k8s.v0.nova_service as nova_svc
|
import charms.nova_k8s.v0.nova_service as nova_svc
|
||||||
|
|
||||||
logger.debug("Setting up Nova service event handler")
|
logger.debug("Setting up Nova service event handler")
|
||||||
svc = nova_svc.NovaServiceRequires(
|
svc = sunbeam_tracing.trace_type(nova_svc.NovaServiceRequires)(
|
||||||
self.charm,
|
self.charm,
|
||||||
self.relation_name,
|
self.relation_name,
|
||||||
)
|
)
|
||||||
@ -2180,6 +2212,7 @@ class NovaServiceRequiresHandler(RelationHandler):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
class LogForwardHandler(RelationHandler):
|
class LogForwardHandler(RelationHandler):
|
||||||
"""Handle log forward relation on the requires side."""
|
"""Handle log forward relation on the requires side."""
|
||||||
|
|
||||||
@ -2210,7 +2243,7 @@ class LogForwardHandler(RelationHandler):
|
|||||||
import charms.loki_k8s.v1.loki_push_api as loki_push_api
|
import charms.loki_k8s.v1.loki_push_api as loki_push_api
|
||||||
|
|
||||||
logger.debug("Setting up log forward event handler")
|
logger.debug("Setting up log forward event handler")
|
||||||
log_forwarder = loki_push_api.LogForwarder(
|
log_forwarder = sunbeam_tracing.trace_type(loki_push_api.LogForwarder)(
|
||||||
self.charm,
|
self.charm,
|
||||||
relation_name=self.relation_name,
|
relation_name=self.relation_name,
|
||||||
)
|
)
|
||||||
@ -2220,3 +2253,54 @@ class LogForwardHandler(RelationHandler):
|
|||||||
def ready(self) -> bool:
|
def ready(self) -> bool:
|
||||||
"""Whether handler is ready for use."""
|
"""Whether handler is ready for use."""
|
||||||
return self.interface.is_ready()
|
return self.interface.is_ready()
|
||||||
|
|
||||||
|
|
||||||
|
@sunbeam_tracing.trace_type
|
||||||
|
class TracingRequireHandler(RelationHandler):
|
||||||
|
"""Handle tracing relation on the requires side."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
charm: ops.charm.CharmBase,
|
||||||
|
relation_name: str,
|
||||||
|
mandatory: bool = False,
|
||||||
|
protocols: list[str] | None = None,
|
||||||
|
) -> None:
|
||||||
|
"""Create a new tracing-relation handler.
|
||||||
|
|
||||||
|
:param charm: the Charm class the handler
|
||||||
|
:type charm: ops.charm.CharmBase
|
||||||
|
:param relation_name: the relation the handler is bound to
|
||||||
|
:type relation_name: str
|
||||||
|
:param mandatory: If the relation is mandatory to proceed with
|
||||||
|
configuring charm.
|
||||||
|
:type mandatory: bool
|
||||||
|
"""
|
||||||
|
if protocols is None:
|
||||||
|
protocols = ["otlp_http"]
|
||||||
|
self.protocols = protocols
|
||||||
|
super().__init__(charm, relation_name, lambda *args: None, mandatory)
|
||||||
|
|
||||||
|
def setup_event_handler(self) -> ops.Object:
|
||||||
|
"""Configure event handlers for tracing relation."""
|
||||||
|
import charms.tempo_k8s.v2.tracing as tracing
|
||||||
|
|
||||||
|
tracing_interface = sunbeam_tracing.trace_type(
|
||||||
|
tracing.TracingEndpointRequirer
|
||||||
|
)(
|
||||||
|
self.charm,
|
||||||
|
self.relation_name,
|
||||||
|
protocols=self.protocols, # type: ignore[arg-type]
|
||||||
|
)
|
||||||
|
|
||||||
|
return tracing_interface
|
||||||
|
|
||||||
|
def tracing_endpoint(self) -> str | None:
|
||||||
|
"""Otlp endpoint for charm tracing."""
|
||||||
|
if self.ready():
|
||||||
|
return self.interface.get_endpoint("otlp_http")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def ready(self) -> bool:
|
||||||
|
"""Whether handler is ready for use."""
|
||||||
|
return self.interface.is_ready()
|
||||||
|
130
ops-sunbeam/ops_sunbeam/tracing.py
Normal file
130
ops-sunbeam/ops_sunbeam/tracing.py
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
|
||||||
|
"""Utilities for tracing."""
|
||||||
|
|
||||||
|
from typing import (
|
||||||
|
Any,
|
||||||
|
Callable,
|
||||||
|
Optional,
|
||||||
|
Sequence,
|
||||||
|
TypeVar,
|
||||||
|
overload,
|
||||||
|
)
|
||||||
|
|
||||||
|
_T = TypeVar("_T")
|
||||||
|
|
||||||
|
try:
|
||||||
|
from charms.tempo_k8s.v1.charm_tracing import (
|
||||||
|
trace_type,
|
||||||
|
)
|
||||||
|
except ImportError:
|
||||||
|
|
||||||
|
def trace_type(cls: _T) -> _T:
|
||||||
|
"""No-op decorator for tracing."""
|
||||||
|
return cls
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
from charms.tempo_k8s.v1.charm_tracing import (
|
||||||
|
trace_charm,
|
||||||
|
)
|
||||||
|
except ImportError:
|
||||||
|
|
||||||
|
def trace_charm(
|
||||||
|
tracing_endpoint: str,
|
||||||
|
server_cert: Optional[str] = None,
|
||||||
|
service_name: Optional[str] = None,
|
||||||
|
extra_types: Sequence[type] = (),
|
||||||
|
) -> Callable[[_T], _T]:
|
||||||
|
"""No-op decorator for tracing."""
|
||||||
|
|
||||||
|
def _wrapper(charm_cls: _T) -> _T:
|
||||||
|
return charm_cls
|
||||||
|
|
||||||
|
return _wrapper
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def trace_sunbeam_charm(
|
||||||
|
*,
|
||||||
|
tracing_endpoint: str = "get_tracing_endpoint",
|
||||||
|
server_cert: Optional[str] = None,
|
||||||
|
service_name: Optional[str] = None,
|
||||||
|
extra_types: Sequence[type] = (),
|
||||||
|
) -> Callable[[_T], _T]:
|
||||||
|
... # fmt: skip
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def trace_sunbeam_charm(
|
||||||
|
charm_cls: _T,
|
||||||
|
/,
|
||||||
|
) -> _T:
|
||||||
|
... # fmt: skip
|
||||||
|
|
||||||
|
|
||||||
|
def trace_sunbeam_charm(*args, **kwargs) -> Any:
|
||||||
|
"""Decorator for tracing sunbeam charms.
|
||||||
|
|
||||||
|
This decorator allows either decorating a charm class directly or
|
||||||
|
passing parameters to the decorator.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
@trace_sunbeam_charm
|
||||||
|
class MyCharm(...):
|
||||||
|
...
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
@trace_sunbeam_charm(
|
||||||
|
tracing_endpoint="get_tracing_endpoint",
|
||||||
|
server_cert="path/to/server.crt",
|
||||||
|
service_name="my-service",
|
||||||
|
extra_types=(MyType,),
|
||||||
|
)
|
||||||
|
class MyCharm(...):
|
||||||
|
...
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
class MyCharm(...):
|
||||||
|
...
|
||||||
|
|
||||||
|
MyCharm = trace_sunbeam_charm(MyCharm)
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
MyCharm = trace_sunbeam_charm(
|
||||||
|
tracing_endpoint="get_tracing_endpoint",
|
||||||
|
server_cert="path/to/server.crt",
|
||||||
|
service_name="my-service",
|
||||||
|
extra_types=(MyType,),
|
||||||
|
)(MyCharm)
|
||||||
|
"""
|
||||||
|
if len(args) == 1 and not kwargs:
|
||||||
|
charm_cls = args[0]
|
||||||
|
return trace_charm(
|
||||||
|
tracing_endpoint="get_tracing_endpoint",
|
||||||
|
)(charm_cls)
|
||||||
|
|
||||||
|
return trace_charm(
|
||||||
|
tracing_endpoint=kwargs.get(
|
||||||
|
"tracing_endpoint", "get_tracing_endpoint"
|
||||||
|
),
|
||||||
|
server_cert=kwargs.get("server_cert"),
|
||||||
|
service_name=kwargs.get("service_name"),
|
||||||
|
extra_types=kwargs.get("extra_types", ()),
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user