Skip to content
This repository has been archived by the owner on Oct 8, 2024. It is now read-only.

Commit

Permalink
fix: remove stale opentelemetry_sdk packages at lib module import
Browse files Browse the repository at this point in the history
fixes #157 by moving the stale package culling of charm_tracing.py ahead of all package imports and widening its scope to any opentelemetry package directories.
  • Loading branch information
ca-scribner committed Aug 6, 2024
1 parent 0776c98 commit b7b25d2
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 28 deletions.
82 changes: 54 additions & 28 deletions lib/charms/tempo_k8s/v1/charm_tracing.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,14 +172,65 @@ def my_tracing_endpoint(self) -> Optional[str]:
provide an *absolute* path to the certificate file instead.
"""


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. When this patch is removed, un-ignore rule E402 for this file in the pyproject.toml (see setting
[tool.ruff.lint.per-file-ignores] in pyproject.toml).
This only has an effect if executed on an upgrade-charm event.
"""
# all imports are local to keep this function standalone, side-effect-free, and easy to revert later
import os

if os.getenv("JUJU_DISPATCH_PATH") != "hooks/upgrade-charm":
return

import logging
import shutil
from collections import defaultdict

from importlib_metadata import distributions

otel_logger = logging.getLogger("charm_tracing_otel_patcher")
otel_logger.debug("Applying _remove_stale_otel_sdk_packages patch on charm upgrade")
# group by name all distributions starting with "opentelemetry_"
otel_distributions = defaultdict(list)
for distribution in distributions():
name = distribution._normalized_name # type: ignore
if name.startswith("opentelemetry_"):
otel_distributions[name].append(distribution)

otel_logger.debug(f"Found {len(otel_distributions)} opentelemetry distributions")

# If we have multiple distributions with the same name, remove any that have 0 associated files
for name, distributions_ in otel_distributions.items():
if len(distributions_) <= 1:
continue

otel_logger.debug(f"Package {name} has multiple ({len(distributions_)}) distributions.")
for distribution in distributions_:
if not distribution.files: # Not None or empty list
path = distribution._path # type: ignore
otel_logger.info(f"Removing empty distribution of {name} at {path}.")
shutil.rmtree(path)

otel_logger.debug("Successfully applied _remove_stale_otel_sdk_packages patch. ")


_remove_stale_otel_sdk_packages()


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,
Expand Down Expand Up @@ -220,7 +271,7 @@ def my_tracing_endpoint(self) -> Optional[str]:
# Increment this PATCH version before using `charmcraft publish-lib` or reset
# to 0 if you are raising the major API version

LIBPATCH = 13
LIBPATCH = 14

PYDEPS = ["opentelemetry-exporter-otlp-proto-http==1.21.0"]

Expand Down Expand Up @@ -362,30 +413,6 @@ def _get_server_cert(
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,
Expand Down Expand Up @@ -421,7 +448,6 @@ def wrap_init(self: CharmBase, framework: Framework, *args, **kwargs):
# 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,
Expand Down
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ ignore = ["E501", "D107"]

[tool.ruff.lint.per-file-ignores]
"tests/*" = ["D100","D101","D102","D103","D104"]
# Remove charm_tracing.py E402 when _remove_stale_otel_sdk_packages() is removed
# from the library
"lib/charms/tempo_k8s/v1/charm_tracing.py" = ["E402"]

[lint.mccabe]
Expand Down

0 comments on commit b7b25d2

Please sign in to comment.