[*-k8s] implement log forwarder

Implement LogForwardHandler for every k8s charm to forward service
stdout to logging provider.

Tempest-k8s is excluded  because it implements its own logging handler.

Change-Id: Iccc9f1f911acfaaecf733fe78cc4bc3191a231d5
Signed-off-by: Guillaume Boutry <guillaume.boutry@canonical.com>
This commit is contained in:
Guillaume Boutry 2024-07-07 14:18:31 +02:00
parent 366b83e3eb
commit 0214e8f173
No known key found for this signature in database
GPG Key ID: E95E3326872E55DE
31 changed files with 547 additions and 366 deletions

View File

@ -75,6 +75,9 @@ requires:
receive-ca-cert: receive-ca-cert:
interface: certificate_transfer interface: certificate_transfer
optional: true optional: true
logging:
interface: loki_push_api
optional: true
provides: provides:
aodh: aodh:

View File

@ -45,6 +45,9 @@ requires:
receive-ca-cert: receive-ca-cert:
interface: certificate_transfer interface: certificate_transfer
optional: true optional: true
logging:
interface: loki_push_api
optional: true
peers: peers:
peers: peers:

View File

@ -52,6 +52,9 @@ requires:
receive-ca-cert: receive-ca-cert:
interface: certificate_transfer interface: certificate_transfer
optional: true optional: true
logging:
interface: loki_push_api
optional: true
peers: peers:
peers: peers:

View File

@ -42,6 +42,9 @@ requires:
identity-credentials: identity-credentials:
interface: keystone-credentials interface: keystone-credentials
optional: true optional: true
logging:
interface: loki_push_api
optional: true
provides: provides:
ceph-access: ceph-access:

View File

@ -60,6 +60,9 @@ requires:
receive-ca-cert: receive-ca-cert:
interface: certificate_transfer interface: certificate_transfer
optional: true optional: true
logging:
interface: loki_push_api
optional: true
peers: peers:
peers: peers:

View File

@ -31,6 +31,11 @@ provides:
dns-backend: dns-backend:
interface: bind-rndc interface: bind-rndc
requires:
logging:
interface: loki_push_api
optional: true
peers: peers:
peers: peers:
interface: bind-peer interface: bind-peer

View File

@ -52,6 +52,9 @@ requires:
receive-ca-cert: receive-ca-cert:
interface: certificate_transfer interface: certificate_transfer
optional: true optional: true
logging:
interface: loki_push_api
optional: true
peers: peers:
peers: peers:

View File

@ -71,6 +71,9 @@ requires:
receive-ca-cert: receive-ca-cert:
interface: certificate_transfer interface: certificate_transfer
optional: true optional: true
logging:
interface: loki_push_api
optional: true
provides: provides:
image-service: image-service:

View File

@ -54,6 +54,9 @@ requires:
receive-ca-cert: receive-ca-cert:
interface: certificate_transfer interface: certificate_transfer
optional: true optional: true
logging:
interface: loki_push_api
optional: true
provides: provides:
gnocchi-service: gnocchi-service:

View File

@ -57,6 +57,9 @@ requires:
receive-ca-cert: receive-ca-cert:
interface: certificate_transfer interface: certificate_transfer
optional: true optional: true
logging:
interface: loki_push_api
optional: true
peers: peers:
peers: peers:

View File

@ -25,7 +25,7 @@ containers:
resources: resources:
horizon-image: horizon-image:
type: oci-image type: oci-image
description: OCI image for Horizon description: OCI image for Horizon
# ghcr.io/canonical/horizon:2024.1 # ghcr.io/canonical/horizon:2024.1
upstream-source: ghcr.io/canonical/horizon:2024.1 upstream-source: ghcr.io/canonical/horizon:2024.1
@ -46,6 +46,9 @@ requires:
receive-ca-cert: receive-ca-cert:
interface: certificate_transfer interface: certificate_transfer
optional: true optional: true
logging:
interface: loki_push_api
optional: true
provides: provides:
horizon: horizon:

View File

@ -48,6 +48,9 @@ requires:
optional: true optional: true
domain-config: domain-config:
interface: keystone-domain-config interface: keystone-domain-config
logging:
interface: loki_push_api
optional: true
peers: peers:
peers: peers:

View File

@ -254,6 +254,7 @@ class TestKeystoneOperatorCharm(test_utils.CharmTestCase):
test_utils.add_db_relation_credentials( test_utils.add_db_relation_credentials(
self.harness, test_utils.add_base_db_relation(self.harness) self.harness, test_utils.add_base_db_relation(self.harness)
) )
test_utils.add_complete_logging_relation(self.harness)
self.km_mock.setup_keystone.assert_called_once_with() self.km_mock.setup_keystone.assert_called_once_with()
self.km_mock.setup_initial_projects_and_users.assert_called_once_with() self.km_mock.setup_initial_projects_and_users.assert_called_once_with()
@ -270,6 +271,7 @@ class TestKeystoneOperatorCharm(test_utils.CharmTestCase):
"credential-keys-secret-id": credential_secret_id, "credential-keys-secret-id": credential_secret_id,
}, },
) )
assert self.harness.charm.logging.ready
def test_on_peer_data_changed_no_bootstrap(self): def test_on_peer_data_changed_no_bootstrap(self):
"""Test peer_relation_changed on no bootstrap.""" """Test peer_relation_changed on no bootstrap."""

View File

@ -12,3 +12,7 @@ peers:
provides: provides:
domain-config: domain-config:
interface: keystone-domain-config interface: keystone-domain-config
requires:
logging:
interface: loki_push_api
optional: true

View File

@ -54,6 +54,9 @@ requires:
receive-ca-cert: receive-ca-cert:
interface: certificate_transfer interface: certificate_transfer
optional: true optional: true
logging:
interface: loki_push_api
optional: true
peers: peers:
peers: peers:

View File

@ -63,6 +63,9 @@ requires:
external-dns: external-dns:
interface: designate interface: designate
optional: true optional: true
logging:
interface: loki_push_api
optional: true
peers: peers:
peers: peers:

View File

@ -96,6 +96,9 @@ requires:
receive-ca-cert: receive-ca-cert:
interface: certificate_transfer interface: certificate_transfer
optional: true optional: true
logging:
interface: loki_push_api
optional: true
provides: provides:
nova-service: nova-service:

View File

@ -79,6 +79,9 @@ requires:
receive-ca-cert: receive-ca-cert:
interface: certificate_transfer interface: certificate_transfer
optional: true optional: true
logging:
interface: loki_push_api
optional: true
peers: peers:
peers: peers:

View File

@ -31,6 +31,9 @@ requires:
receive-ca-cert: receive-ca-cert:
interface: certificate_transfer interface: certificate_transfer
optional: true optional: true
logging:
interface: loki_push_api
optional: true
provides: provides:
metrics-endpoint: metrics-endpoint:

View File

@ -73,3 +73,6 @@ requires:
limit: 1 limit: 1
identity-service: identity-service:
interface: keystone interface: keystone
logging:
interface: loki_push_api
optional: true

View File

@ -69,6 +69,9 @@ resources:
requires: requires:
certificates: certificates:
interface: tls-certificates interface: tls-certificates
logging:
interface: loki_push_api
optional: true
provides: provides:
ovsdb: ovsdb:

View File

@ -34,6 +34,9 @@ requires:
interface: ovsdb-cms interface: ovsdb-cms
certificates: certificates:
interface: tls-certificates interface: tls-certificates
logging:
interface: loki_push_api
optional: true
provides: provides:
ovsdb-cms-relay: ovsdb-cms-relay:

View File

@ -43,6 +43,9 @@ requires:
receive-ca-cert: receive-ca-cert:
interface: certificate_transfer interface: certificate_transfer
optional: true optional: true
logging:
interface: loki_push_api
optional: true
provides: provides:
placement: placement:

View File

@ -62,6 +62,7 @@ requires:
interface: keystone-resources interface: keystone-resources
logging: logging:
interface: loki_push_api interface: loki_push_api
optional: true
provides: provides:
grafana-dashboard: grafana-dashboard:

View File

@ -157,7 +157,7 @@ class TempestOperatorCharm(sunbeam_charm.OSBaseOperatorCharmK8S):
schedule.valid schedule.valid
and schedule.value and schedule.value
and self.is_tempest_ready() and self.is_tempest_ready()
and self.loki.ready and self.logging.ready
and self.user_id_ops.ready and self.user_id_ops.ready
) )
@ -179,9 +179,27 @@ class TempestOperatorCharm(sunbeam_charm.OSBaseOperatorCharmK8S):
) )
] ]
def get_relation_handlers(self) -> List[sunbeam_rhandlers.RelationHandler]: def get_relation_handlers(
self, handlers: list[sunbeam_rhandlers.RelationHandler] | None = None
) -> list[sunbeam_rhandlers.RelationHandler]:
"""Relation handlers for the service.""" """Relation handlers for the service."""
handlers = super().get_relation_handlers() handlers = handlers or []
# Ensure the logging relation is before the one setup by the base class
self.logging = LoggingRelationHandler(
self,
LOKI_RELATION_NAME,
self.configure_charm,
mandatory=LOKI_RELATION_NAME in self.mandatory_relations,
)
handlers.append(self.logging)
handlers = super().get_relation_handlers(handlers)
self.grafana = GrafanaDashboardRelationHandler(
self,
"grafana-dashboard",
self.configure_charm,
mandatory="grafana-dashboard" in self.mandatory_relations,
)
handlers.append(self.grafana)
self.user_id_ops = TempestUserIdentityRelationHandler( self.user_id_ops = TempestUserIdentityRelationHandler(
self, self,
"identity-ops", "identity-ops",
@ -190,20 +208,6 @@ class TempestOperatorCharm(sunbeam_charm.OSBaseOperatorCharmK8S):
region=self.config["region"], region=self.config["region"],
) )
handlers.append(self.user_id_ops) handlers.append(self.user_id_ops)
self.loki = LoggingRelationHandler(
self,
LOKI_RELATION_NAME,
self.configure_charm,
mandatory="logging" in self.mandatory_relations,
)
handlers.append(self.loki)
self.grafana = GrafanaDashboardRelationHandler(
self,
"grafana-dashboard",
self.configure_charm,
mandatory="grafana-dashboard" in self.mandatory_relations,
)
handlers.append(self.grafana)
return handlers return handlers
def _get_proxy_environment(self) -> Dict[str, str]: def _get_proxy_environment(self) -> Dict[str, str]:
@ -350,9 +354,9 @@ class TempestOperatorCharm(sunbeam_charm.OSBaseOperatorCharmK8S):
else: else:
ensure_alert_rules_disabled() ensure_alert_rules_disabled()
if self.loki.ready: if self.logging.ready:
for relation in self.model.relations[LOKI_RELATION_NAME]: for relation in self.model.relations[LOKI_RELATION_NAME]:
self.loki.interface._handle_alert_rules(relation) self.logging.interface._handle_alert_rules(relation)
self.status.set(ActiveStatus("")) self.status.set(ActiveStatus(""))
logger.info("Finished configuring the tempest environment") logger.info("Finished configuring the tempest environment")

View File

@ -167,10 +167,9 @@ class TestTempestOperatorCharm(test_utils.CharmTestCase):
def add_logging_relation(self, harness): def add_logging_relation(self, harness):
"""Add logging relation.""" """Add logging relation."""
rel_id = harness.add_relation("logging", "loki") rel_id = test_utils.add_complete_logging_relation(harness)
harness.add_relation_unit(rel_id, "loki/0") harness.charm.logging.interface = Mock()
harness.charm.loki.interface = Mock() harness.charm.logging.interface._promtail_config = Mock()
harness.charm.loki.interface._promtail_config = Mock()
return rel_id return rel_id
def add_grafana_dashboard_relation(self, harness): def add_grafana_dashboard_relation(self, harness):
@ -625,7 +624,7 @@ class TestTempestOperatorCharm(test_utils.CharmTestCase):
rel_id = self.add_logging_relation(self.harness) rel_id = self.add_logging_relation(self.harness)
# client endpoints found # client endpoints found
self.harness.charm.loki.interface._promtail_config.return_value = { self.harness.charm.logging.interface._promtail_config.return_value = {
"clients": [ "clients": [
{ {
"url": "http://grafana-agent-k8s-endpoints:3500/loki/api/v1/push" "url": "http://grafana-agent-k8s-endpoints:3500/loki/api/v1/push"
@ -633,16 +632,16 @@ class TestTempestOperatorCharm(test_utils.CharmTestCase):
], ],
"other_key": "other_values", "other_key": "other_values",
} }
self.assertEqual(self.harness.charm.loki.ready, True) self.assertEqual(self.harness.charm.logging.ready, True)
# empty client endpoints # empty client endpoints
self.harness.charm.loki.interface._promtail_config.return_value = { self.harness.charm.logging.interface._promtail_config.return_value = {
"clients": [], "clients": [],
"other_key": "other_values", "other_key": "other_values",
} }
self.assertEqual(self.harness.charm.loki.ready, False) self.assertEqual(self.harness.charm.logging.ready, False)
# empty promtail config # empty promtail config
self.harness.remove_relation(rel_id) self.harness.remove_relation(rel_id)
self.harness.charm.loki.interface._promtail_config.return_value = {} self.harness.charm.logging.interface._promtail_config.return_value = {}
self.assertEqual(self.harness.charm.loki.ready, False) self.assertEqual(self.harness.charm.logging.ready, False)

653
common.sh
View File

@ -17,520 +17,533 @@ NULL_ARRAY=()
# Internal libs for component. If libs are repeated, reuse the existing component # Internal libs for component. If libs are repeated, reuse the existing component
INTERNAL_CEILOMETER_LIBS=( INTERNAL_CEILOMETER_LIBS=(
"keystone_k8s" "keystone_k8s"
"ceilometer_k8s" "ceilometer_k8s"
"gnocchi_k8s" "gnocchi_k8s"
) )
INTERNAL_CINDER_LIBS=( INTERNAL_CINDER_LIBS=(
"keystone_k8s" "keystone_k8s"
"cinder_k8s" "cinder_k8s"
) )
INTERNAL_CINDER_CEPH_LIBS=( INTERNAL_CINDER_CEPH_LIBS=(
"keystone_k8s" "keystone_k8s"
"cinder_k8s" "cinder_k8s"
"cinder_ceph_k8s" "cinder_ceph_k8s"
) )
INTERNAL_DESIGNATE_LIBS=( INTERNAL_DESIGNATE_LIBS=(
"keystone_k8s" "keystone_k8s"
"designate_bind_k8s" "designate_bind_k8s"
"designate_k8s" "designate_k8s"
) )
INTERNAL_DESIGNATE_BIND_LIBS=( INTERNAL_DESIGNATE_BIND_LIBS=(
"designate_bind_k8s" "designate_bind_k8s"
) )
INTERNAL_GNOCCHI_LIBS=( INTERNAL_GNOCCHI_LIBS=(
"keystone_k8s" "keystone_k8s"
"gnocchi_k8s" "gnocchi_k8s"
) )
INTERNAL_KEYSTONE_LIBS=( INTERNAL_KEYSTONE_LIBS=(
"keystone_k8s" "keystone_k8s"
) )
INTERNAL_NEUTRON_LIBS=( INTERNAL_NEUTRON_LIBS=(
"keystone_k8s" "keystone_k8s"
"ovn_central_k8s" "ovn_central_k8s"
"designate_k8s" "designate_k8s"
) )
INTERNAL_NOVA_LIBS=( INTERNAL_NOVA_LIBS=(
"keystone_k8s" "keystone_k8s"
"nova_k8s" "nova_k8s"
"sunbeam_nova_compute_operator" "sunbeam_nova_compute_operator"
) )
INTERNAL_OPENSTACK_HYPERVISOR_LIBS=( INTERNAL_OPENSTACK_HYPERVISOR_LIBS=(
"keystone_k8s" "keystone_k8s"
"ovn_central_k8s" "ovn_central_k8s"
"cinder_ceph_k8s" "cinder_ceph_k8s"
"ceilometer_k8s" "ceilometer_k8s"
"nova_k8s" "nova_k8s"
) )
INTERNAL_OPENSTACK_IMAGES_SYNC_LIBS=( INTERNAL_OPENSTACK_IMAGES_SYNC_LIBS=(
"keystone_k8s" "keystone_k8s"
) )
INTERNAL_OVN_CENTRAL_LIBS=( INTERNAL_OVN_CENTRAL_LIBS=(
"ovn_central_k8s" "ovn_central_k8s"
) )
# External libs for component. If libs are repeated, reuse the existing component # External libs for component. If libs are repeated, reuse the existing component
EXTERNAL_AODH_LIBS=( EXTERNAL_AODH_LIBS=(
"data_platform_libs" "data_platform_libs"
"rabbitmq_k8s" "rabbitmq_k8s"
"traefik_k8s" "traefik_k8s"
"certificate_transfer_interface" "certificate_transfer_interface"
"loki_k8s"
) )
EXTERNAL_BARBICAN_LIBS=( EXTERNAL_BARBICAN_LIBS=(
"data_platform_libs" "data_platform_libs"
"rabbitmq_k8s" "rabbitmq_k8s"
"traefik_k8s" "traefik_k8s"
"vault_k8s" "vault_k8s"
"certificate_transfer_interface" "certificate_transfer_interface"
"loki_k8s"
) )
EXTERNAL_CEILOMETER_LIBS=( EXTERNAL_CEILOMETER_LIBS=(
"rabbitmq_k8s" "rabbitmq_k8s"
"certificate_transfer_interface" "certificate_transfer_interface"
"loki_k8s"
) )
EXTERNAL_CINDER_CEPH_LIBS=( EXTERNAL_CINDER_CEPH_LIBS=(
"data_platform_libs" "data_platform_libs"
"rabbitmq_k8s" "rabbitmq_k8s"
"traefik_k8s" "traefik_k8s"
"loki_k8s"
) )
EXTERNAL_DESIGNATE_BIND_LIBS=( EXTERNAL_DESIGNATE_BIND_LIBS=(
"observability_libs" "observability_libs"
"loki_k8s"
) )
EXTERNAL_HEAT_LIBS=( EXTERNAL_HEAT_LIBS=(
"data_platform_libs" "data_platform_libs"
"rabbitmq_k8s" "rabbitmq_k8s"
"traefik_route_k8s" "traefik_route_k8s"
"certificate_transfer_interface" "certificate_transfer_interface"
"loki_k8s"
) )
EXTERNAL_NEUTRON_LIBS=( EXTERNAL_NEUTRON_LIBS=(
"data_platform_libs" "data_platform_libs"
"rabbitmq_k8s" "rabbitmq_k8s"
"traefik_k8s" "traefik_k8s"
"tls_certificates_interface" "tls_certificates_interface"
"certificate_transfer_interface" "certificate_transfer_interface"
"loki_k8s"
) )
EXTERNAL_NOVA_LIBS=( EXTERNAL_NOVA_LIBS=(
"data_platform_libs" "data_platform_libs"
"rabbitmq_k8s" "rabbitmq_k8s"
"traefik_k8s" "traefik_k8s"
"traefik_route_k8s" "traefik_route_k8s"
"certificate_transfer_interface" "certificate_transfer_interface"
"loki_k8s"
) )
EXTERNAL_OCTAVIA_LIBS=( EXTERNAL_OCTAVIA_LIBS=(
"data_platform_libs" "data_platform_libs"
"traefik_k8s" "traefik_k8s"
"tls_certificates_interface" "tls_certificates_interface"
"certificate_transfer_interface" "certificate_transfer_interface"
"loki_k8s"
) )
EXTERNAL_OPENSTACK_EXPORTER_LIBS=( EXTERNAL_OPENSTACK_EXPORTER_LIBS=(
"grafana_k8s" "grafana_k8s"
"prometheus_k8s" "prometheus_k8s"
"tls_certificates_interface" "tls_certificates_interface"
"certificate_transfer_interface" "certificate_transfer_interface"
"loki_k8s"
) )
EXTERNAL_OPENSTACK_HYPERVISOR_LIBS=( EXTERNAL_OPENSTACK_HYPERVISOR_LIBS=(
"data_platform_libs" "data_platform_libs"
"grafana_agent" "grafana_agent"
"observability_libs" "observability_libs"
"operator_libs_linux" "operator_libs_linux"
"rabbitmq_k8s" "rabbitmq_k8s"
"traefik_k8s" "traefik_k8s"
"tls_certificates_interface" "tls_certificates_interface"
"certificate_transfer_interface" "certificate_transfer_interface"
) )
EXTERNAL_OPENSTACK_IMAGES_SYNC_LIBS=( EXTERNAL_OPENSTACK_IMAGES_SYNC_LIBS=(
"traefik_k8s" "traefik_k8s"
"loki_k8s"
) )
EXTERNAL_SUNBEAM_CLUSTERD_LIBS=( EXTERNAL_SUNBEAM_CLUSTERD_LIBS=(
"operator_libs_linux" "operator_libs_linux"
"tls_certificates_interface" "tls_certificates_interface"
) )
EXTERNAL_SUNBEAM_MACHINE_LIBS=( EXTERNAL_SUNBEAM_MACHINE_LIBS=(
"operator_libs_linux" "operator_libs_linux"
) )
EXTERNAL_OVN_CENTRAL_LIBS=( EXTERNAL_OVN_CENTRAL_LIBS=(
"tls_certificates_interface" "tls_certificates_interface"
"loki_k8s"
) )
EXTERNAL_OVN_RELAY_LIBS=( EXTERNAL_OVN_RELAY_LIBS=(
"tls_certificates_interface" "tls_certificates_interface"
"observability_libs" "observability_libs"
"loki_k8s"
) )
EXTERNAL_TEMPEST_LIBS=( EXTERNAL_TEMPEST_LIBS=(
"observability_libs" "observability_libs"
"grafana_k8s" "grafana_k8s"
"loki_k8s" "loki_k8s"
) )
# Config template parts for each component. # Config template parts for each component.
CONFIG_TEMPLATES_AODH=( CONFIG_TEMPLATES_AODH=(
"parts/section-database" "parts/section-database"
"parts/database-connection" "parts/database-connection"
"parts/database-connection-settings" "parts/database-connection-settings"
"parts/section-identity" "parts/section-identity"
"parts/identity-data" "parts/identity-data"
"parts/section-oslo-messaging-rabbit" "parts/section-oslo-messaging-rabbit"
"parts/section-service-credentials" "parts/section-service-credentials"
"ca-bundle.pem.j2" "ca-bundle.pem.j2"
) )
CONFIG_TEMPLATES_BARBICAN=( CONFIG_TEMPLATES_BARBICAN=(
"parts/section-database" "parts/section-database"
"parts/database-connection" "parts/database-connection"
"parts/database-connection-settings" "parts/database-connection-settings"
"parts/section-identity" "parts/section-identity"
"parts/identity-data" "parts/identity-data"
"parts/section-oslo-messaging-rabbit" "parts/section-oslo-messaging-rabbit"
"parts/section-service-user" "parts/section-service-user"
"ca-bundle.pem.j2" "ca-bundle.pem.j2"
) )
CONFIG_TEMPLATES_CEILOMETER=( CONFIG_TEMPLATES_CEILOMETER=(
"parts/identity-data-id-creds" "parts/identity-data-id-creds"
"parts/section-oslo-messaging-rabbit" "parts/section-oslo-messaging-rabbit"
"parts/section-service-credentials-from-identity-service" "parts/section-service-credentials-from-identity-service"
"ca-bundle.pem.j2" "ca-bundle.pem.j2"
) )
CONFIG_TEMPLATES_CINDER=( CONFIG_TEMPLATES_CINDER=(
"parts/section-database" "parts/section-database"
"parts/database-connection" "parts/database-connection"
"parts/database-connection-settings" "parts/database-connection-settings"
"parts/section-identity" "parts/section-identity"
"parts/identity-data" "parts/identity-data"
"parts/section-oslo-messaging-rabbit" "parts/section-oslo-messaging-rabbit"
"parts/section-service-user" "parts/section-service-user"
"ca-bundle.pem.j2" "ca-bundle.pem.j2"
) )
CONFIG_TEMPLATES_CINDER_CEPH=( CONFIG_TEMPLATES_CINDER_CEPH=(
"parts/section-database" "parts/section-database"
"parts/database-connection" "parts/database-connection"
"parts/database-connection-settings" "parts/database-connection-settings"
"parts/section-oslo-messaging-rabbit" "parts/section-oslo-messaging-rabbit"
"parts/section-oslo-notifications" "parts/section-oslo-notifications"
) )
CONFIG_TEMPLATES_DESIGNATE=( CONFIG_TEMPLATES_DESIGNATE=(
"parts/database-connection" "parts/database-connection"
"parts/database-connection-settings" "parts/database-connection-settings"
"parts/section-identity" "parts/section-identity"
"parts/identity-data" "parts/identity-data"
"parts/section-oslo-messaging-rabbit" "parts/section-oslo-messaging-rabbit"
"parts/section-service-user" "parts/section-service-user"
"ca-bundle.pem.j2" "ca-bundle.pem.j2"
) )
CONFIG_TEMPLATES_GLANCE=( CONFIG_TEMPLATES_GLANCE=(
"parts/section-database" "parts/section-database"
"parts/database-connection" "parts/database-connection"
"parts/database-connection-settings" "parts/database-connection-settings"
"parts/section-identity" "parts/section-identity"
"parts/identity-data" "parts/identity-data"
"parts/section-oslo-messaging-rabbit" "parts/section-oslo-messaging-rabbit"
"parts/section-oslo-notifications" "parts/section-oslo-notifications"
"parts/section-service-user" "parts/section-service-user"
"ca-bundle.pem.j2" "ca-bundle.pem.j2"
) )
CONFIG_TEMPLATES_GNOCCHI=( CONFIG_TEMPLATES_GNOCCHI=(
"parts/database-connection" "parts/database-connection"
"parts/section-identity" "parts/section-identity"
"parts/identity-data" "parts/identity-data"
"ca-bundle.pem.j2" "ca-bundle.pem.j2"
) )
CONFIG_TEMPLATES_HEAT=( CONFIG_TEMPLATES_HEAT=(
"parts/section-database" "parts/section-database"
"parts/database-connection" "parts/database-connection"
"parts/database-connection-settings" "parts/database-connection-settings"
"parts/section-identity" "parts/section-identity"
"parts/identity-data" "parts/identity-data"
"parts/section-trustee" "parts/section-trustee"
"parts/section-oslo-messaging-rabbit" "parts/section-oslo-messaging-rabbit"
"ca-bundle.pem.j2" "ca-bundle.pem.j2"
) )
CONFIG_TEMPLATES_HORIZON=( CONFIG_TEMPLATES_HORIZON=(
"ca-bundle.pem.j2" "ca-bundle.pem.j2"
) )
CONFIG_TEMPLATES_KEYSTONE=( CONFIG_TEMPLATES_KEYSTONE=(
"parts/section-database" "parts/section-database"
"parts/database-connection" "parts/database-connection"
"parts/database-connection-settings" "parts/database-connection-settings"
"parts/section-federation" "parts/section-federation"
"parts/section-middleware" "parts/section-middleware"
"parts/section-oslo-cache" "parts/section-oslo-cache"
"parts/section-oslo-messaging-rabbit" "parts/section-oslo-messaging-rabbit"
"parts/section-oslo-middleware" "parts/section-oslo-middleware"
"parts/section-oslo-notifications" "parts/section-oslo-notifications"
"parts/section-signing" "parts/section-signing"
) )
CONFIG_TEMPLATES_MAGNUM=( CONFIG_TEMPLATES_MAGNUM=(
"parts/section-database" "parts/section-database"
"parts/database-connection" "parts/database-connection"
"parts/database-connection-settings" "parts/database-connection-settings"
"parts/section-identity" "parts/section-identity"
"parts/identity-data" "parts/identity-data"
"parts/section-oslo-messaging-rabbit" "parts/section-oslo-messaging-rabbit"
"parts/section-service-user" "parts/section-service-user"
"parts/section-trust" "parts/section-trust"
"ca-bundle.pem.j2" "ca-bundle.pem.j2"
) )
CONFIG_TEMPLATES_NEUTRON=( CONFIG_TEMPLATES_NEUTRON=(
"parts/section-database" "parts/section-database"
"parts/database-connection" "parts/database-connection"
"parts/database-connection-settings" "parts/database-connection-settings"
"parts/section-identity" "parts/section-identity"
"parts/identity-data" "parts/identity-data"
"parts/section-oslo-messaging-rabbit" "parts/section-oslo-messaging-rabbit"
"parts/section-service-user" "parts/section-service-user"
"ca-bundle.pem.j2" "ca-bundle.pem.j2"
) )
CONFIG_TEMPLATES_NOVA=${CONFIG_TEMPLATES_NEUTRON[@]} CONFIG_TEMPLATES_NOVA=${CONFIG_TEMPLATES_NEUTRON[@]}
CONFIG_TEMPLATES_OCTAVIA=( CONFIG_TEMPLATES_OCTAVIA=(
"parts/section-database" "parts/section-database"
"parts/database-connection" "parts/database-connection"
"parts/database-connection-settings" "parts/database-connection-settings"
"parts/section-identity" "parts/section-identity"
"parts/identity-data" "parts/identity-data"
"ca-bundle.pem.j2" "ca-bundle.pem.j2"
) )
CONFIG_TEMPLATES_PLACEMENT=( CONFIG_TEMPLATES_PLACEMENT=(
"parts/database-connection" "parts/database-connection"
"parts/database-connection-settings" "parts/database-connection-settings"
"parts/section-identity" "parts/section-identity"
"parts/identity-data" "parts/identity-data"
"parts/section-service-user" "parts/section-service-user"
"ca-bundle.pem.j2" "ca-bundle.pem.j2"
) )
declare -A INTERNAL_LIBS=( declare -A INTERNAL_LIBS=(
[aodh-k8s]=${INTERNAL_KEYSTONE_LIBS[@]} [aodh-k8s]=${INTERNAL_KEYSTONE_LIBS[@]}
[barbican-k8s]=${INTERNAL_KEYSTONE_LIBS[@]} [barbican-k8s]=${INTERNAL_KEYSTONE_LIBS[@]}
[ceilometer-k8s]=${INTERNAL_CEILOMETER_LIBS[@]} [ceilometer-k8s]=${INTERNAL_CEILOMETER_LIBS[@]}
[cinder-k8s]=${INTERNAL_CINDER_LIBS[@]} [cinder-k8s]=${INTERNAL_CINDER_LIBS[@]}
[cinder-ceph-k8s]=${INTERNAL_CINDER_CEPH_LIBS[@]} [cinder-ceph-k8s]=${INTERNAL_CINDER_CEPH_LIBS[@]}
[designate-k8s]=${INTERNAL_DESIGNATE_LIBS[@]} [designate-k8s]=${INTERNAL_DESIGNATE_LIBS[@]}
[designate-bind-k8s]=${INTERNAL_DESIGNATE_BIND_LIBS[@]} [designate-bind-k8s]=${INTERNAL_DESIGNATE_BIND_LIBS[@]}
[glance-k8s]=${INTERNAL_KEYSTONE_LIBS[@]} [glance-k8s]=${INTERNAL_KEYSTONE_LIBS[@]}
[gnocchi-k8s]=${INTERNAL_GNOCCHI_LIBS[@]} [gnocchi-k8s]=${INTERNAL_GNOCCHI_LIBS[@]}
[heat-k8s]=${INTERNAL_KEYSTONE_LIBS[@]} [heat-k8s]=${INTERNAL_KEYSTONE_LIBS[@]}
[horizon-k8s]=${INTERNAL_KEYSTONE_LIBS[@]} [horizon-k8s]=${INTERNAL_KEYSTONE_LIBS[@]}
[keystone-k8s]=${INTERNAL_KEYSTONE_LIBS[@]} [keystone-k8s]=${INTERNAL_KEYSTONE_LIBS[@]}
[keystone-ldap-k8s]=${INTERNAL_KEYSTONE_LIBS[@]} [keystone-ldap-k8s]=${INTERNAL_KEYSTONE_LIBS[@]}
[magnum-k8s]=${INTERNAL_KEYSTONE_LIBS[@]} [magnum-k8s]=${INTERNAL_KEYSTONE_LIBS[@]}
[neutron-k8s]=${INTERNAL_NEUTRON_LIBS[@]} [neutron-k8s]=${INTERNAL_NEUTRON_LIBS[@]}
[nova-k8s]=${INTERNAL_NOVA_LIBS[@]} [nova-k8s]=${INTERNAL_NOVA_LIBS[@]}
[octavia-k8s]=${INTERNAL_NEUTRON_LIBS[@]} [octavia-k8s]=${INTERNAL_NEUTRON_LIBS[@]}
[openstack-exporter-k8s]=${INTERNAL_KEYSTONE_LIBS[@]} [openstack-exporter-k8s]=${INTERNAL_KEYSTONE_LIBS[@]}
[openstack-hypervisor]=${INTERNAL_OPENSTACK_HYPERVISOR_LIBS[@]} [openstack-hypervisor]=${INTERNAL_OPENSTACK_HYPERVISOR_LIBS[@]}
[openstack-images-sync-k8s]=${INTERNAL_OPENSTACK_IMAGES_SYNC_LIBS[@]} [openstack-images-sync-k8s]=${INTERNAL_OPENSTACK_IMAGES_SYNC_LIBS[@]}
[sunbeam-clusterd]=${NULL_ARRAY[@]} [sunbeam-clusterd]=${NULL_ARRAY[@]}
[sunbeam-machine]=${NULL_ARRAY[@]} [sunbeam-machine]=${NULL_ARRAY[@]}
[ovn-central-k8s]=${INTERNAL_OVN_CENTRAL_LIBS[@]} [ovn-central-k8s]=${INTERNAL_OVN_CENTRAL_LIBS[@]}
[ovn-relay-k8s]=${INTERNAL_OVN_CENTRAL_LIBS[@]} [ovn-relay-k8s]=${INTERNAL_OVN_CENTRAL_LIBS[@]}
[placement-k8s]=${INTERNAL_KEYSTONE_LIBS[@]} [placement-k8s]=${INTERNAL_KEYSTONE_LIBS[@]}
[tempest-k8s]=${INTERNAL_KEYSTONE_LIBS[@]} [tempest-k8s]=${INTERNAL_KEYSTONE_LIBS[@]}
) )
declare -A EXTERNAL_LIBS=( declare -A EXTERNAL_LIBS=(
[aodh-k8s]=${EXTERNAL_AODH_LIBS[@]} [aodh-k8s]=${EXTERNAL_AODH_LIBS[@]}
[barbican-k8s]=${EXTERNAL_BARBICAN_LIBS[@]} [barbican-k8s]=${EXTERNAL_BARBICAN_LIBS[@]}
[ceilometer-k8s]=${EXTERNAL_CEILOMETER_LIBS[@]} [ceilometer-k8s]=${EXTERNAL_CEILOMETER_LIBS[@]}
[cinder-k8s]=${EXTERNAL_AODH_LIBS[@]} [cinder-k8s]=${EXTERNAL_AODH_LIBS[@]}
[cinder-ceph-k8s]=${EXTERNAL_CINDER_CEPH_LIBS[@]} [cinder-ceph-k8s]=${EXTERNAL_CINDER_CEPH_LIBS[@]}
[designate-k8s]=${EXTERNAL_AODH_LIBS[@]} [designate-k8s]=${EXTERNAL_AODH_LIBS[@]}
[designate-bind-k8s]=${EXTERNAL_DESIGNATE_BIND_LIBS[@]} [designate-bind-k8s]=${EXTERNAL_DESIGNATE_BIND_LIBS[@]}
[glance-k8s]=${EXTERNAL_AODH_LIBS[@]} [glance-k8s]=${EXTERNAL_AODH_LIBS[@]}
[gnocchi-k8s]=${EXTERNAL_AODH_LIBS[@]} [gnocchi-k8s]=${EXTERNAL_AODH_LIBS[@]}
[heat-k8s]=${EXTERNAL_HEAT_LIBS[@]} [heat-k8s]=${EXTERNAL_HEAT_LIBS[@]}
[horizon-k8s]=${EXTERNAL_AODH_LIBS[@]} [horizon-k8s]=${EXTERNAL_AODH_LIBS[@]}
[keystone-k8s]=${EXTERNAL_AODH_LIBS[@]} [keystone-k8s]=${EXTERNAL_AODH_LIBS[@]}
[keystone-ldap-k8s]=${NULL_ARRAY[@]} [keystone-ldap-k8s]=${NULL_ARRAY[@]}
[magnum-k8s]=${EXTERNAL_AODH_LIBS[@]} [magnum-k8s]=${EXTERNAL_AODH_LIBS[@]}
[neutron-k8s]=${EXTERNAL_NEUTRON_LIBS[@]} [neutron-k8s]=${EXTERNAL_NEUTRON_LIBS[@]}
[nova-k8s]=${EXTERNAL_NOVA_LIBS[@]} [nova-k8s]=${EXTERNAL_NOVA_LIBS[@]}
[octavia-k8s]=${EXTERNAL_OCTAVIA_LIBS[@]} [octavia-k8s]=${EXTERNAL_OCTAVIA_LIBS[@]}
[openstack-exporter-k8s]=${EXTERNAL_OPENSTACK_EXPORTER_LIBS[@]} [openstack-exporter-k8s]=${EXTERNAL_OPENSTACK_EXPORTER_LIBS[@]}
[openstack-hypervisor]=${EXTERNAL_OPENSTACK_HYPERVISOR_LIBS[@]} [openstack-hypervisor]=${EXTERNAL_OPENSTACK_HYPERVISOR_LIBS[@]}
[openstack-images-sync-k8s]=${EXTERNAL_OPENSTACK_IMAGES_SYNC_LIBS[@]} [openstack-images-sync-k8s]=${EXTERNAL_OPENSTACK_IMAGES_SYNC_LIBS[@]}
[sunbeam-clusterd]=${EXTERNAL_SUNBEAM_CLUSTERD_LIBS[@]} [sunbeam-clusterd]=${EXTERNAL_SUNBEAM_CLUSTERD_LIBS[@]}
[sunbeam-machine]=${EXTERNAL_SUNBEAM_MACHINE_LIBS[@]} [sunbeam-machine]=${EXTERNAL_SUNBEAM_MACHINE_LIBS[@]}
[ovn-central-k8s]=${EXTERNAL_OVN_CENTRAL_LIBS[@]} [ovn-central-k8s]=${EXTERNAL_OVN_CENTRAL_LIBS[@]}
[ovn-relay-k8s]=${EXTERNAL_OVN_RELAY_LIBS[@]} [ovn-relay-k8s]=${EXTERNAL_OVN_RELAY_LIBS[@]}
[placement-k8s]=${EXTERNAL_AODH_LIBS[@]} [placement-k8s]=${EXTERNAL_AODH_LIBS[@]}
[tempest-k8s]=${EXTERNAL_TEMPEST_LIBS[@]} [tempest-k8s]=${EXTERNAL_TEMPEST_LIBS[@]}
) )
declare -A CONFIG_TEMPLATES=( declare -A CONFIG_TEMPLATES=(
[aodh-k8s]=${CONFIG_TEMPLATES_AODH[@]} [aodh-k8s]=${CONFIG_TEMPLATES_AODH[@]}
[barbican-k8s]=${CONFIG_TEMPLATES_BARBICAN[@]} [barbican-k8s]=${CONFIG_TEMPLATES_BARBICAN[@]}
[ceilometer-k8s]=${CONFIG_TEMPLATES_CEILOMETER[@]} [ceilometer-k8s]=${CONFIG_TEMPLATES_CEILOMETER[@]}
[cinder-k8s]=${CONFIG_TEMPLATES_CINDER[@]} [cinder-k8s]=${CONFIG_TEMPLATES_CINDER[@]}
[cinder-ceph-k8s]=${CONFIG_TEMPLATES_CINDER_CEPH[@]} [cinder-ceph-k8s]=${CONFIG_TEMPLATES_CINDER_CEPH[@]}
[designate-k8s]=${CONFIG_TEMPLATES_DESIGNATE[@]} [designate-k8s]=${CONFIG_TEMPLATES_DESIGNATE[@]}
[designate-bind-k8s]=${NULL_ARRAY[@]} [designate-bind-k8s]=${NULL_ARRAY[@]}
[glance-k8s]=${CONFIG_TEMPLATES_GLANCE[@]} [glance-k8s]=${CONFIG_TEMPLATES_GLANCE[@]}
[gnocchi-k8s]=${CONFIG_TEMPLATES_GNOCCHI[@]} [gnocchi-k8s]=${CONFIG_TEMPLATES_GNOCCHI[@]}
[heat-k8s]=${CONFIG_TEMPLATES_HEAT[@]} [heat-k8s]=${CONFIG_TEMPLATES_HEAT[@]}
[horizon-k8s]=${CONFIG_TEMPLATES_HORIZON[@]} [horizon-k8s]=${CONFIG_TEMPLATES_HORIZON[@]}
[keystone-k8s]=${CONFIG_TEMPLATES_KEYSTONE[@]} [keystone-k8s]=${CONFIG_TEMPLATES_KEYSTONE[@]}
[keystone-ldap-k8s]=${NULL_ARRAY[@]} [keystone-ldap-k8s]=${NULL_ARRAY[@]}
[magnum-k8s]=${CONFIG_TEMPLATES_MAGNUM[@]} [magnum-k8s]=${CONFIG_TEMPLATES_MAGNUM[@]}
[neutron-k8s]=${CONFIG_TEMPLATES_NEUTRON[@]} [neutron-k8s]=${CONFIG_TEMPLATES_NEUTRON[@]}
[nova-k8s]=${CONFIG_TEMPLATES_NOVA[@]} [nova-k8s]=${CONFIG_TEMPLATES_NOVA[@]}
[octavia-k8s]=${CONFIG_TEMPLATES_OCTAVIA[@]} [octavia-k8s]=${CONFIG_TEMPLATES_OCTAVIA[@]}
[openstack-exporter-k8s]=${CONFIG_TEMPLATES_HORIZON[@]} [openstack-exporter-k8s]=${CONFIG_TEMPLATES_HORIZON[@]}
[openstack-hypervisor]=${NULL_ARRAY[@]} [openstack-hypervisor]=${NULL_ARRAY[@]}
[openstack-images-sync-k8s]=${NULL_ARRAY[@]} [openstack-images-sync-k8s]=${NULL_ARRAY[@]}
[sunbeam-clusterd]=${NULL_ARRAY[@]} [sunbeam-clusterd]=${NULL_ARRAY[@]}
[sunbeam-machine]=${NULL_ARRAY[@]} [sunbeam-machine]=${NULL_ARRAY[@]}
[ovn-central-k8s]=${NULL_ARRAY[@]} [ovn-central-k8s]=${NULL_ARRAY[@]}
[ovn-relay-k8s]=${NULL_ARRAY[@]} [ovn-relay-k8s]=${NULL_ARRAY[@]}
[placement-k8s]=${CONFIG_TEMPLATES_PLACEMENT[@]} [placement-k8s]=${CONFIG_TEMPLATES_PLACEMENT[@]}
[tempest-k8s]=${NULL_ARRAY[@]} [tempest-k8s]=${NULL_ARRAY[@]}
) )
function copy_ops_sunbeam { function copy_ops_sunbeam {
cp -rf ../../ops-sunbeam/ops_sunbeam lib/ cp -rf ../../ops-sunbeam/ops_sunbeam lib/
} }
function copy_internal_libs { function copy_internal_libs {
internal_libs_=${INTERNAL_LIBS[$1]} internal_libs_=${INTERNAL_LIBS[$1]}
echo "copy_internal_libs for $1:" echo "copy_internal_libs for $1:"
for lib in ${internal_libs_[@]}; do for lib in ${internal_libs_[@]}; do
echo "Copying $lib" echo "Copying $lib"
cp -rf ../../libs/internal/lib/charms/$lib lib/charms/ cp -rf ../../libs/internal/lib/charms/$lib lib/charms/
done done
} }
function copy_external_libs { function copy_external_libs {
echo "copy_external_libs for $1:" echo "copy_external_libs for $1:"
external_libs_=${EXTERNAL_LIBS[$1]} external_libs_=${EXTERNAL_LIBS[$1]}
for lib in ${external_libs_[@]}; do for lib in ${external_libs_[@]}; do
echo "Copying $lib" echo "Copying $lib"
cp -rf ../../libs/external/lib/charms/$lib lib/charms/ cp -rf ../../libs/external/lib/charms/$lib lib/charms/
done done
} }
function copy_config_templates { function copy_config_templates {
echo "copy_config_templates for $1:" echo "copy_config_templates for $1:"
config_templates_=${CONFIG_TEMPLATES[$1]} config_templates_=${CONFIG_TEMPLATES[$1]}
for part in ${config_templates_[@]}; do for part in ${config_templates_[@]}; do
echo "Copying $part" echo "Copying $part"
cp -rf ../../templates/$part src/templates/$part cp -rf ../../templates/$part src/templates/$part
done done
} }
function copy_juju_ignore { function copy_juju_ignore {
cp ../../.jujuignore . cp ../../.jujuignore .
} }
function copy_stestr_conf { function copy_stestr_conf {
cp ../../.stestr.conf . cp ../../.stestr.conf .
} }
function remove_libs { function remove_libs {
rm -rf lib rm -rf lib
} }
function remove_config_templates { function remove_config_templates {
echo "remove_config_templates for $1:" echo "remove_config_templates for $1:"
config_templates_=${CONFIG_TEMPLATES[$1]} config_templates_=${CONFIG_TEMPLATES[$1]}
for part in ${config_templates_[@]}; do for part in ${config_templates_[@]}; do
echo "Removing $part" echo "Removing $part"
rm src/templates/$part rm src/templates/$part
done done
if (test -d src/templates/parts) && (test -n "$(find src/templates/parts -maxdepth 0 -empty)") if (test -d src/templates/parts) && (test -n "$(find src/templates/parts -maxdepth 0 -empty)")
then then
remove_templates_parts_dir remove_templates_parts_dir
fi fi
} }
function remove_templates_parts_dir { function remove_templates_parts_dir {
rm -rf src/templates/parts rm -rf src/templates/parts
} }
function remove_juju_ignore { function remove_juju_ignore {
rm .jujuignore rm .jujuignore
} }
function remove_stestr_conf { function remove_stestr_conf {
rm .stestr.conf rm .stestr.conf
} }
function push_common_files { function push_common_files {
if [[ $# != 1 ]]; if [[ $# != 1 ]];
then then
echo "push_common_files: Expected one argument" echo "push_common_files: Expected one argument"
exit 1 exit 1
fi fi
pushd charms/$1 pushd charms/$1
mkdir -p lib/charms mkdir -p lib/charms
mkdir -p src/templates/parts mkdir -p src/templates/parts
copy_ops_sunbeam copy_ops_sunbeam
copy_internal_libs $1 copy_internal_libs $1
copy_external_libs $1 copy_external_libs $1
copy_config_templates $1 copy_config_templates $1
copy_stestr_conf copy_stestr_conf
copy_juju_ignore copy_juju_ignore
popd popd
} }
function pop_common_files { function pop_common_files {
pushd charms/$1 pushd charms/$1
remove_libs remove_libs
remove_config_templates $1 remove_config_templates $1
remove_stestr_conf remove_stestr_conf
remove_juju_ignore remove_juju_ignore
popd popd
} }
function copy_libs_for_ops_sunbeam { function copy_libs_for_ops_sunbeam {
mkdir -p tests/lib mkdir -p tests/lib
cp -rf ../libs/external/lib ../libs/internal/lib tests/ cp -rf ../libs/external/lib ../libs/internal/lib tests/
} }
function remove_libs_for_ops_sunbeam { function remove_libs_for_ops_sunbeam {
rm -rf tests/lib rm -rf tests/lib
} }

View File

@ -16,20 +16,24 @@ The provider side of the relation represents the server side, to which logs are
send log to Loki by implementing the consumer side of the `loki_push_api` relation interface. send log to Loki by implementing the consumer side of the `loki_push_api` relation interface.
For instance, a Promtail or Grafana agent charm which needs to send logs to Loki. For instance, a Promtail or Grafana agent charm which needs to send logs to Loki.
- `LogProxyConsumer`: This object can be used by any Charmed Operator which needs to - `LogProxyConsumer`: DEPRECATED.
send telemetry, such as logs, to Loki through a Log Proxy by implementing the consumer side of the This object can be used by any Charmed Operator which needs to send telemetry, such as logs, to
`loki_push_api` relation interface. Loki through a Log Proxy by implementing the consumer side of the `loki_push_api` relation
interface.
In order to be able to control the labels on the logs pushed this object adds a Pebble layer
that runs Promtail in the workload container, injecting Juju topology labels into the
logs on the fly.
This object is deprecated. Consider migrating to LogForwarder with the release of Juju 3.6 LTS.
- `LogForwarder`: This object can be used by any Charmed Operator which needs to send the workload - `LogForwarder`: This object can be used by any Charmed Operator which needs to send the workload
standard output (stdout) through Pebble's log forwarding mechanism, to Loki endpoints through the standard output (stdout) through Pebble's log forwarding mechanism, to Loki endpoints through the
`loki_push_api` relation interface. `loki_push_api` relation interface.
In order to be able to control the labels on the logs pushed this object updates the pebble layer's
"log-targets" section with Juju topology.
Filtering logs in Loki is largely performed on the basis of labels. In the Juju ecosystem, Juju Filtering logs in Loki is largely performed on the basis of labels. In the Juju ecosystem, Juju
topology labels are used to uniquely identify the workload which generates telemetry like logs. topology labels are used to uniquely identify the workload which generates telemetry like logs.
In order to be able to control the labels on the logs pushed this object adds a Pebble layer
that runs Promtail in the workload container, injecting Juju topology labels into the
logs on the fly.
## LokiPushApiProvider Library Usage ## LokiPushApiProvider Library Usage
@ -42,13 +46,14 @@ and three optional arguments.
- `charm`: A reference to the parent (Loki) charm. - `charm`: A reference to the parent (Loki) charm.
- `relation_name`: The name of the relation that the charm uses to interact - `relation_name`: The name of the relation that the charm uses to interact
with its clients, which implement `LokiPushApiConsumer` or `LogProxyConsumer`. with its clients, which implement `LokiPushApiConsumer` `LogForwarder`, or `LogProxyConsumer`
(note that LogProxyConsumer is deprecated).
If provided, this relation name must match a provided relation in metadata.yaml with the If provided, this relation name must match a provided relation in metadata.yaml with the
`loki_push_api` interface. `loki_push_api` interface.
The default relation name is "logging" for `LokiPushApiConsumer` and "log-proxy" for The default relation name is "logging" for `LokiPushApiConsumer` and `LogForwarder`, and
`LogProxyConsumer`. "log-proxy" for `LogProxyConsumer` (note that LogProxyConsumer is deprecated).
For example, a provider's `metadata.yaml` file may look as follows: For example, a provider's `metadata.yaml` file may look as follows:
@ -223,6 +228,9 @@ to do this with promtail.
## LogProxyConsumer Library Usage ## LogProxyConsumer Library Usage
> Note: This object is deprecated. Consider migrating to LogForwarder with the release of Juju 3.6
> LTS.
Let's say that we have a workload charm that produces logs, and we need to send those logs to a Let's say that we have a workload charm that produces logs, and we need to send those logs to a
workload implementing the `loki_push_api` interface, such as `Loki` or `Grafana Agent`. workload implementing the `loki_push_api` interface, such as `Loki` or `Grafana Agent`.
@ -519,7 +527,7 @@ LIBAPI = 1
# Increment this PATCH version before using `charmcraft publish-lib` or reset # Increment this PATCH version before using `charmcraft publish-lib` or reset
# to 0 if you are raising the major API version # to 0 if you are raising the major API version
LIBPATCH = 9 LIBPATCH = 11
PYDEPS = ["cosl"] PYDEPS = ["cosl"]
@ -535,17 +543,20 @@ PROMTAIL_BASE_URL = "https://github.com/canonical/loki-k8s-operator/releases/dow
# update all sha256 sums in PROMTAIL_BINARIES. To support a new architecture # update all sha256 sums in PROMTAIL_BINARIES. To support a new architecture
# you only need to add a new key value pair for the architecture in PROMTAIL_BINARIES. # you only need to add a new key value pair for the architecture in PROMTAIL_BINARIES.
PROMTAIL_VERSION = "v2.9.7" PROMTAIL_VERSION = "v2.9.7"
PROMTAIL_ARM_BINARY = {
"filename": "promtail-static-arm64",
"zipsha": "c083fdb45e5c794103f974eeb426489b4142438d9e10d0ae272b2aff886e249b",
"binsha": "4cd055c477a301c0bdfdbcea514e6e93f6df5d57425ce10ffc77f3e16fec1ddf",
}
PROMTAIL_BINARIES = { PROMTAIL_BINARIES = {
"amd64": { "amd64": {
"filename": "promtail-static-amd64", "filename": "promtail-static-amd64",
"zipsha": "6873cbdabf23062aeefed6de5f00ff382710332af3ab90a48c253ea17e08f465", "zipsha": "6873cbdabf23062aeefed6de5f00ff382710332af3ab90a48c253ea17e08f465",
"binsha": "28da9b99f81296fe297831f3bc9d92aea43b4a92826b8ff04ba433b8cb92fb50", "binsha": "28da9b99f81296fe297831f3bc9d92aea43b4a92826b8ff04ba433b8cb92fb50",
}, },
"arm64": { "arm64": PROMTAIL_ARM_BINARY,
"filename": "promtail-static-arm64", "aarch64": PROMTAIL_ARM_BINARY,
"zipsha": "c083fdb45e5c794103f974eeb426489b4142438d9e10d0ae272b2aff886e249b",
"binsha": "4cd055c477a301c0bdfdbcea514e6e93f6df5d57425ce10ffc77f3e16fec1ddf",
},
} }
# Paths in `charm` container # Paths in `charm` container
@ -1590,7 +1601,8 @@ class LokiPushApiConsumer(ConsumerBase):
the Loki API endpoint to push logs. It is intended for workloads that can speak the Loki API endpoint to push logs. It is intended for workloads that can speak
loki_push_api (https://grafana.com/docs/loki/latest/api/#push-log-entries-to-loki), such loki_push_api (https://grafana.com/docs/loki/latest/api/#push-log-entries-to-loki), such
as grafana-agent. as grafana-agent.
(If you only need to forward a few workload log files, then use LogProxyConsumer.) (If you need to forward workload stdout logs, then use LogForwarder; if you need to forward
log files, then use LogProxyConsumer.)
`LokiPushApiConsumer` can be instantiated as follows: `LokiPushApiConsumer` can be instantiated as follows:
@ -1765,6 +1777,9 @@ class LogProxyEvents(ObjectEvents):
class LogProxyConsumer(ConsumerBase): class LogProxyConsumer(ConsumerBase):
"""LogProxyConsumer class. """LogProxyConsumer class.
> Note: This object is deprecated. Consider migrating to LogForwarder with the release of Juju
> 3.6 LTS.
The `LogProxyConsumer` object provides a method for attaching `promtail` to The `LogProxyConsumer` object provides a method for attaching `promtail` to
a workload in order to generate structured logging data from applications a workload in order to generate structured logging data from applications
which traditionally log to syslog or do not have native Loki integration. which traditionally log to syslog or do not have native Loki integration.

View File

@ -653,6 +653,20 @@ class OSBaseOperatorCharmK8S(OSBaseOperatorCharm):
self.run_db_sync() self.run_db_sync()
self._state.unit_bootstrapped = True self._state.unit_bootstrapped = True
def get_relation_handlers(
self, handlers: list[sunbeam_rhandlers.RelationHandler] | None = None
) -> list[sunbeam_rhandlers.RelationHandler]:
"""Relation handlers for the service."""
handlers = handlers or []
if self.can_add_handler("logging", handlers):
self.logging = sunbeam_rhandlers.LogForwardHandler(
self,
"logging",
"logging" in self.mandatory_relations,
)
handlers.append(self.logging)
return super().get_relation_handlers(handlers)
def add_pebble_health_checks(self): def add_pebble_health_checks(self):
"""Add health checks for services in payload containers.""" """Add health checks for services in payload containers."""
for ph in self.pebble_handlers: for ph in self.pebble_handlers:

View File

@ -2178,3 +2178,45 @@ class NovaServiceRequiresHandler(RelationHandler):
return bool(self.interface.nova_spiceproxy_url) return bool(self.interface.nova_spiceproxy_url)
except (AttributeError, KeyError): except (AttributeError, KeyError):
return False return False
class LogForwardHandler(RelationHandler):
"""Handle log forward relation on the requires side."""
def __init__(
self,
charm: ops.charm.CharmBase,
relation_name: str,
mandatory: bool = False,
):
"""Create a new log-forward handler.
Create a new LogForwardHandler that handles initial
events from the relation and invokes the provided callbacks based on
the event raised.
:param charm: the Charm class the handler is for
:type charm: ops.charm.CharmBase
:param relation_name: the relation the handler is bound to
:type relation_name: str
:param mandatory: If the relation is mandatory to proceed with
configuring charm
:type mandatory: bool
"""
super().__init__(charm, relation_name, lambda *args: None, mandatory)
def setup_event_handler(self) -> ops.Object:
"""Configure event handlers for log forward relation."""
import charms.loki_k8s.v1.loki_push_api as loki_push_api
logger.debug("Setting up log forward event handler")
log_forwarder = loki_push_api.LogForwarder(
self.charm,
relation_name=self.relation_name,
)
return log_forwarder
@property
def ready(self) -> bool:
"""Whether handler is ready for use."""
return self.interface.is_ready()

View File

@ -611,6 +611,26 @@ def add_complete_peer_relation(harness: Harness) -> None:
return rel_id return rel_id
def add_base_logging_relation(harness: Harness) -> int:
"""Add logging relation."""
rel_id = harness.add_relation("logging", "loki")
harness.add_relation_unit(rel_id, "loki/0")
harness.update_relation_data(
rel_id,
"loki/0",
{
"endpoint": '{"url": "http://10.20.23.1/cos-loki-0/loki/api/v1/push"}'
},
)
return rel_id
def add_complete_logging_relation(harness: Harness) -> int:
"""Add complete ceph relation."""
rel_id = add_base_logging_relation(harness)
return rel_id
test_relations = { test_relations = {
"database": add_complete_db_relation, "database": add_complete_db_relation,
"amqp": add_complete_amqp_relation, "amqp": add_complete_amqp_relation,
@ -619,6 +639,7 @@ test_relations = {
"peers": add_complete_peer_relation, "peers": add_complete_peer_relation,
"certificates": add_complete_certificates_relation, "certificates": add_complete_certificates_relation,
"ceph": add_complete_ceph_relation, "ceph": add_complete_ceph_relation,
"logging": add_complete_logging_relation,
} }
@ -761,6 +782,7 @@ def get_harness(
with open(metadata_file) as f: with open(metadata_file) as f:
charm_metadata = f.read() charm_metadata = f.read()
os.environ["JUJU_VERSION"] = "3.4.4"
harness = Harness( harness = Harness(
charm_class, charm_class,
meta=charm_metadata, meta=charm_metadata,