#1826 Add basic telemetry support
Merged a month ago by lsedlar. Opened a month ago by lsedlar.
lsedlar/pungi opentelemetry  into  master

file modified
+19 -13
@@ -50,6 +50,7 @@ 

      translate_path_raw,

  )

  from pungi.metadata import compose_to_composeinfo

+ from pungi.otel import tracing

  

  try:

      # This is available since productmd >= 1.18
@@ -130,15 +131,16 @@ 

              cts_oidc_client_id = os.environ.get(

                  "CTS_OIDC_CLIENT_ID", ""

              ) or pungi_conf.get("cts_oidc_client_id", "")

-             token = retry_request(

-                 "post",

-                 cts_oidc_token_url,

-                 data={

-                     "grant_type": "client_credentials",

-                     "client_id": cts_oidc_client_id,

-                     "client_secret": os.environ.get("CTS_OIDC_CLIENT_SECRET", ""),

-                 },

-             ).json()["access_token"]

+             with tracing.span("obtain-oidc-token"):

+                 token = retry_request(

+                     "post",

+                     cts_oidc_token_url,

+                     data={

+                         "grant_type": "client_credentials",

+                         "client_id": cts_oidc_client_id,

+                         "client_secret": os.environ.get("CTS_OIDC_CLIENT_SECRET", ""),

+                     },

+                 ).json()["access_token"]

              auth = BearerAuth(token)

              del token

  
@@ -194,8 +196,9 @@ 

              "parent_compose_ids": parent_compose_ids,

              "respin_of": respin_of,

          }

-         with cts_auth(conf) as authentication:

-             rv = retry_request("post", url, json_data=data, auth=authentication)

+         with tracing.span("create-compose-in-cts"):

+             with cts_auth(conf) as authentication:

+                 rv = retry_request("post", url, json_data=data, auth=authentication)

  

          # Update local ComposeInfo with received ComposeInfo.

          cts_ci = ComposeInfo()
@@ -231,8 +234,9 @@ 

              "action": "set_url",

              "compose_url": compose_url,

          }

-         with cts_auth(conf) as authentication:

-             return retry_request("patch", url, json_data=data, auth=authentication)

+         with tracing.span("update-compose-url"):

+             with cts_auth(conf) as authentication:

+                 return retry_request("patch", url, json_data=data, auth=authentication)

  

  

  def get_compose_dir(
@@ -373,6 +377,7 @@ 

          self.ci_base.load(

              os.path.join(self.paths.work.topdir(arch="global"), "composeinfo-base.json")

          )

+         tracing.set_attribute("compose_id", self.compose_id)

  

          self.supported = supported

          if (
@@ -557,6 +562,7 @@ 

          old_status = self.get_status()

          if stat_msg == old_status:

              return

+         tracing.set_attribute("compose_status", stat_msg)

          if old_status == "FINISHED":

              msg = "Could not modify a FINISHED compose: %s" % self.topdir

              self.log_error(msg)

file added
+199
@@ -0,0 +1,199 @@ 

+ import itertools

+ import os

+ from contextlib import contextmanager

+ 

+ """

+ This module contains two classes with the same interface. An instance of one of

+ them is available as `tracing`. Which class is instantiated is selected

+ depending on whether environment variables configuring OTel are configured.

+ """

+ 

+ 

+ class DummyTracing:

+     """A dummy tracing module that doesn't actually do anything."""

+ 

+     @contextmanager

+     def span(self, *args, **kwargs):

+         yield

+ 

+     def set_attribute(self, name, value):

+         pass

+ 

+     def force_flush(self):

+         pass

+ 

+     def instrument_xmlrpc_proxy(self, proxy):

+         return proxy

+ 

+     def get_traceparent(self):

+         return None

+ 

+     def set_context(self, traceparent):

+         pass

+ 

+ 

+ class OtelTracing:

+     """This class implements the actual integration with opentelemetry."""

+ 

+     def __init__(self):

+         from opentelemetry import trace

+         from opentelemetry.sdk.resources import Resource

+         from opentelemetry.sdk.trace import TracerProvider

+         from opentelemetry.sdk.trace.export import (

+             BatchSpanProcessor,

+             ConsoleSpanExporter,

+         )

+         from opentelemetry.exporter.otlp.proto.http.trace_exporter import (

+             OTLPSpanExporter,

+         )

+         from opentelemetry.instrumentation.requests import RequestsInstrumentor

+ 

+         otel_endpoint = os.environ["OTEL_EXPORTER_OTLP_ENDPOINT"]

+         provider = TracerProvider(

+             resource=Resource(attributes={"service.name": "pungi"})

+         )

+         if "console" == otel_endpoint:

+             # This is for debugging the tracing locally.

+             self.processor = BatchSpanProcessor(ConsoleSpanExporter())

+         else:

+             self.processor = BatchSpanProcessor(OTLPSpanExporter())

+         provider.add_span_processor(self.processor)

+         trace.set_tracer_provider(provider)

+         self.tracer = trace.get_tracer(__name__)

+ 

+         traceparent = os.environ.get("TRACEPARENT")

+         if traceparent:

+             self.set_context(traceparent)

+ 

+         RequestsInstrumentor().instrument()

+ 

+     @contextmanager

+     def span(self, name, **attributes):

+         """Create a new span as a child of the current one. Attributes can be

+         passed via kwargs."""

+         with self.tracer.start_as_current_span(name, attributes=attributes) as span:

+             yield span

+ 

+     def get_traceparent(self):

+         from opentelemetry.trace.propagation.tracecontext import (

+             TraceContextTextMapPropagator,

+         )

+ 

+         carrier = {}

+         TraceContextTextMapPropagator().inject(carrier)

+         return carrier["traceparent"]

+ 

+     def set_attribute(self, name, value):

+         """Set an attribute on the current span."""

+         from opentelemetry import trace

+ 

+         span = trace.get_current_span()

+         span.set_attribute(name, value)

+ 

+     def force_flush(self):

+         """Ensure all spans and traces are sent out. Call this before the

+         process exits."""

+         self.processor.force_flush()

+ 

+     def instrument_xmlrpc_proxy(self, proxy):

+         return InstrumentedClientSession(proxy)

+ 

+     def set_context(self, traceparent):

+         """Configure current context to match the given traceparent."""

+         from opentelemetry import context

+         from opentelemetry.trace.propagation.tracecontext import (

+             TraceContextTextMapPropagator,

+         )

+ 

+         ctx = TraceContextTextMapPropagator().extract(

+             carrier={"traceparent": traceparent}

+         )

+         context.attach(ctx)

+ 

+ 

+ class InstrumentedClientSession:

+     """Wrapper around koji.ClientSession that creates spans for each API call.

+     RequestsInstrumentor can create spans at the HTTP requests level, but since

+     those all go the same XML-RPC endpoint, they are not very informative.

+ 

+     Multicall is not handled very well here. The spans will only have a

+     `multicall` boolean attribute, but they don't carry any additional data

+     that could group them.

+ 

+     Koji ClientSession supports three ways of making multicalls, but Pungi only

+     uses one, and that one is supported here.

+ 

+     Supported:

+ 

+         c.multicall = True

+         c.getBuild(1)

+         c.getBuild(2)

+         results = c.multiCall()

+ 

+     Not supported:

+ 

+         with c.multicall() as m:

+             r1 = m.getBuild(1)

+             r2 = m.getBuild(2)

+ 

+     Also not supported:

+ 

+         m = c.multicall()

+         r1 = m.getBuild(1)

+         r2 = m.getBuild(2)

+         m.call_all()

+ 

+     """

+ 

+     def __init__(self, session):

+         self.session = session

+ 

+     def _name(self, name):

+         """Helper for generating span names."""

+         return "%s.%s" % (self.session.__class__.__name__, name)

+ 

+     @property

+     def system(self):

+         """This is only ever used to get list of available API calls. It is

+         rather awkward though. Ideally we wouldn't really trace this at all,

+         but there's the underlying POST request to the hub, which is quite

+         confusing in the trace if there is no additional context."""

+         return self.session.system

+ 

+     @property

+     def multicall(self):

+         return self.session.multicall

+ 

+     @multicall.setter

+     def multicall(self, value):

+         self.session.multicall = value

+ 

+     def __getattr__(self, name):

+         return self._instrument_method(name, getattr(self.session, name))

+ 

+     def _instrument_method(self, name, callable):

+         def wrapper(*args, **kwargs):

+             with tracing.span(self._name(name)) as span:

+                 span.set_attribute("arguments", _format_args(args, kwargs))

+                 if self.session.multicall:

+                     tracing.set_attribute("multicall", True)

+                 return callable(*args, **kwargs)

+ 

+         return wrapper

+ 

+ 

+ def _format_args(args, kwargs):

+     """Turn args+kwargs into a single string. OTel could choke on more

+     complicated data."""

+     return ", ".join(

+         itertools.chain(

+             (repr(arg) for arg in args),

+             (f"{key}={value!r}" for key, value in kwargs.items()),

+         )

+     )

+ 

+ 

+ if "OTEL_EXPORTER_OTLP_ENDPOINT" in os.environ:

+     tracing = OtelTracing()

+ else:

+     tracing = DummyTracing()

file modified
+2 -1
@@ -23,7 +23,7 @@ 

  import re

  from copy import copy

  

- from kobo.threads import ThreadPool, WorkerThread

+ from kobo.threads import ThreadPool

  from kobo.shortcuts import run, force_list

  import kobo.rpmlib

  from productmd.images import Image
@@ -39,6 +39,7 @@ 

  from pungi.wrappers import kojiwrapper

  from pungi.phases.base import PhaseBase

  from pungi.runroot import Runroot, download_and_extract_archive

+ from pungi.threading import TelemetryWorkerThread as WorkerThread

  

  

  class BuildinstallPhase(PhaseBase):

file modified
+2 -1
@@ -24,7 +24,7 @@ 

  

  import productmd.treeinfo

  from productmd.images import Image

- from kobo.threads import ThreadPool, WorkerThread

+ from kobo.threads import ThreadPool

  from kobo.shortcuts import run, relative_path, compute_file_checksums

  

  from pungi.wrappers import iso
@@ -43,6 +43,7 @@ 

  from pungi.media_split import MediaSplitter, convert_media_size

  from pungi.compose_metadata.discinfo import read_discinfo, write_discinfo

  from pungi.runroot import Runroot

+ from pungi.threading import TelemetryWorkerThread as WorkerThread

  

  from .. import createiso

  

file modified
+2 -1
@@ -27,7 +27,7 @@ 

  import productmd.modules

  import productmd.rpms

  from kobo.shortcuts import relative_path, run

- from kobo.threads import ThreadPool, WorkerThread

+ from kobo.threads import ThreadPool

  

  from ..module_util import Modulemd, collect_module_defaults, collect_module_obsoletes

  from ..util import (
@@ -38,6 +38,7 @@ 

  from ..wrappers.createrepo import CreaterepoWrapper

  from ..wrappers.scm import get_dir_from_scm

  from .base import PhaseBase

+ from ..threading import TelemetryWorkerThread as WorkerThread

  

  CACHE_TOPDIR = "/var/cache/pungi/createrepo_c/"

  createrepo_lock = threading.Lock()

file modified
+2 -1
@@ -18,7 +18,8 @@ 

  import json

  

  from kobo.shortcuts import force_list

- from kobo.threads import ThreadPool, WorkerThread

+ from kobo.threads import ThreadPool

+ from pungi.threading import TelemetryWorkerThread as WorkerThread

  import productmd.treeinfo

  from productmd.extra_files import ExtraFiles

  

file modified
+2 -1
@@ -13,7 +13,8 @@ 

  from pungi.phases import base

  from pungi.linker import Linker

  from pungi.wrappers.kojiwrapper import KojiWrapper

- from kobo.threads import ThreadPool, WorkerThread

+ from kobo.threads import ThreadPool

+ from pungi.threading import TelemetryWorkerThread as WorkerThread

  from kobo.shortcuts import force_list

  from productmd.images import Image

  from productmd.rpms import Rpms

@@ -2,12 +2,13 @@ 

  

  import os

  import re

- from kobo.threads import ThreadPool, WorkerThread

+ from kobo.threads import ThreadPool

  

  from .base import ConfigGuardedPhase, PhaseLoggerMixin

  from .. import util

  from ..wrappers import kojiwrapper

  from ..phases.osbs import add_metadata

+ from ..threading import TelemetryWorkerThread as WorkerThread

  

  

  class ImageContainerPhase(PhaseLoggerMixin, ConfigGuardedPhase):

file modified
+2 -1
@@ -1,7 +1,7 @@ 

  # -*- coding: utf-8 -*-

  

  import os

- from kobo.threads import ThreadPool, WorkerThread

+ from kobo.threads import ThreadPool

  from kobo import shortcuts

  from productmd.images import Image

  
@@ -10,6 +10,7 @@ 

  from ..linker import Linker

  from ..wrappers import kojiwrapper

  from .image_build import EXTENSIONS

+ from ..threading import TelemetryWorkerThread as WorkerThread

  

  KIWIEXTENSIONS = [

      ("vhd-compressed", ["vhdfixed.xz"], "vhd.xz"),

@@ -9,8 +9,9 @@ 

  from pungi.phases.base import ConfigGuardedPhase, ImageConfigMixin, PhaseLoggerMixin

  from pungi.linker import Linker

  from pungi.wrappers.kojiwrapper import KojiWrapper

- from kobo.threads import ThreadPool, WorkerThread

+ from kobo.threads import ThreadPool

  from productmd.images import Image

+ from pungi.threading import TelemetryWorkerThread as WorkerThread

  

  

  class LiveMediaPhase(PhaseLoggerMixin, ImageConfigMixin, ConfigGuardedPhase):

file modified
+2 -1
@@ -5,7 +5,7 @@ 

  import fnmatch

  import json

  import os

- from kobo.threads import ThreadPool, WorkerThread

+ from kobo.threads import ThreadPool

  from kobo import shortcuts

  from productmd.rpms import Rpms

  
@@ -13,6 +13,7 @@ 

  from .. import util

  from ..wrappers import kojiwrapper

  from ..wrappers.scm import get_file_from_scm

+ from ..threading import TelemetryWorkerThread as WorkerThread

  

  

  class OSBSPhase(PhaseLoggerMixin, ConfigGuardedPhase):

file modified
+2 -1
@@ -1,7 +1,7 @@ 

  # -*- coding: utf-8 -*-

  

  import os

- from kobo.threads import ThreadPool, WorkerThread

+ from kobo.threads import ThreadPool

  from kobo import shortcuts

  from productmd.images import Image

  
@@ -10,6 +10,7 @@ 

  from ..linker import Linker

  from ..wrappers import kojiwrapper

  from .image_build import EXTENSIONS

+ from ..threading import TelemetryWorkerThread as WorkerThread

  

  # copy and modify EXTENSIONS with some that osbuild produces but which

  # do not exist as `koji image-build` formats

file modified
+2 -1
@@ -4,7 +4,7 @@ 

  import json

  import os

  from kobo import shortcuts

- from kobo.threads import ThreadPool, WorkerThread

+ from kobo.threads import ThreadPool

  from collections import OrderedDict

  

  from pungi.arch_utils import getBaseArch
@@ -14,6 +14,7 @@ 

  from ..ostree.utils import get_ref_from_treefile, get_commitid_from_commitid_file

  from ..util import get_repo_dicts, translate_path

  from ..wrappers import scm

+ from ..threading import TelemetryWorkerThread as WorkerThread

  

  

  class OSTreePhase(ConfigGuardedPhase):

@@ -1,7 +1,7 @@ 

  # -*- coding: utf-8 -*-

  

  import os

- from kobo.threads import ThreadPool, WorkerThread

+ from kobo.threads import ThreadPool

  import shlex

  import shutil

  from productmd import images
@@ -20,6 +20,7 @@ 

  )

  from ..wrappers import iso, lorax, scm

  from ..runroot import Runroot

+ from ..threading import TelemetryWorkerThread as WorkerThread

  

  

  class OstreeInstallerPhase(PhaseLoggerMixin, ConfigGuardedPhase):

@@ -31,11 +31,12 @@ 

  import kobo.rpmlib

  from kobo.shortcuts import compute_file_checksums

  

- from kobo.threads import WorkerThread, ThreadPool

+ from kobo.threads import ThreadPool

  

  from pungi.util import pkg_is_srpm, copy_all

  from pungi.arch import get_valid_arches, is_excluded

  from pungi.errors import UnsignedPackagesError

+ from pungi.threading import TelemetryWorkerThread as WorkerThread

  

  

  class ExtendedRpmWrapper(kobo.pkgset.SimpleRpmWrapper):
@@ -536,7 +537,7 @@ 

          pathinfo = self.koji_wrapper.koji_module.pathinfo

          paths = []

  

-         if "getRPMChecksums" in self.koji_proxy.system.listMethods():

+         if "getRPMChecksums" in self.koji_wrapper.koji_methods:

  

              def checksum_validator(keyname, pkg_path):

                  checksums = self.koji_proxy.getRPMChecksums(

file modified
+3 -1
@@ -1,7 +1,9 @@ 

  # -*- coding: utf-8 -*-

  

  from kobo import shortcuts

- from kobo.threads import ThreadPool, WorkerThread

+ from kobo.threads import ThreadPool

+ 

+ from pungi.threading import TelemetryWorkerThread as WorkerThread

  

  

  class WeaverPhase(object):

file modified
+23 -19
@@ -23,6 +23,7 @@ 

  from pungi.errors import UnsignedPackagesError

  from pungi.wrappers import kojiwrapper

  from pungi.util import rmtree

+ from pungi.otel import tracing

  

  

  # force C locales
@@ -646,22 +647,25 @@ 

      signal.signal(signal.SIGINT, sigterm_handler)

      signal.signal(signal.SIGTERM, sigterm_handler)

  

-     try:

-         main()

-     except (Exception, KeyboardInterrupt) as ex:

-         if COMPOSE:

-             COMPOSE.log_error("Compose run failed: %s" % ex)

-             COMPOSE.traceback(show_locals=getattr(ex, "show_locals", True))

-             COMPOSE.log_critical("Compose failed: %s" % COMPOSE.topdir)

-             COMPOSE.write_status("DOOMED")

-         else:

-             print("Exception: %s" % ex)

-             raise

-         sys.stdout.flush()

-         sys.stderr.flush()

-         sys.exit(1)

-     finally:

-         # Remove repositories cloned during ExtraFiles phase

-         process_id = os.getpid()

-         directoy_to_remove = "/tmp/pungi-temp-git-repos-" + str(process_id) + "/"

-         rmtree(directoy_to_remove)

+     with tracing.span("run-compose"):

+         try:

+             main()

+         except (Exception, KeyboardInterrupt) as ex:

+             if COMPOSE:

+                 COMPOSE.log_error("Compose run failed: %s" % ex)

+                 COMPOSE.traceback(show_locals=getattr(ex, "show_locals", True))

+                 COMPOSE.log_critical("Compose failed: %s" % COMPOSE.topdir)

+                 COMPOSE.write_status("DOOMED")

+             else:

+                 print("Exception: %s" % ex)

+                 raise

+             sys.stdout.flush()

+             sys.stderr.flush()

+             sys.exit(1)

+         finally:

+             # Remove repositories cloned during ExtraFiles phase

+             process_id = os.getpid()

+             directoy_to_remove = "/tmp/pungi-temp-git-repos-" + str(process_id) + "/"

+             rmtree(directoy_to_remove)

+     # Wait for all traces to be sent...

+     tracing.force_flush()

file added
+21
@@ -0,0 +1,21 @@ 

+ from kobo.threads import WorkerThread

+ 

+ from .otel import tracing

+ 

+ 

+ class TelemetryWorkerThread(WorkerThread):

+     """

+     Subclass of WorkerThread that captures current context when the thread is

+     created, and restores the context in the new thread.

+ 

+     A regular WorkerThread would start from an empty context, leading to any

+     spans created in the thread disconnected from the overall trace.

+     """

+ 

+     def __init__(self, *args, **kwargs):

+         self.traceparent = tracing.get_traceparent()

+         super(TelemetryWorkerThread, self).__init__(*args, **kwargs)

+ 

+     def run(self, *args, **kwargs):

+         tracing.set_context(self.traceparent)

+         super(TelemetryWorkerThread, self).run(*args, **kwargs)

file modified
+9 -6
@@ -32,9 +32,11 @@ 

  

  import kobo.conf

  from kobo.shortcuts import run, force_list

- from kobo.threads import WorkerThread, ThreadPool

+ from kobo.threads import ThreadPool

  from productmd.common import get_major_version

  from pungi.module_util import Modulemd

+ from pungi.otel import tracing

+ from pungi.threading import TelemetryWorkerThread as WorkerThread

  

  # Patterns that match all names of debuginfo packages

  DEBUG_PATTERNS = ["*-debuginfo", "*-debuginfo-*", "*-debugsource"]
@@ -880,11 +882,12 @@ 

  

  @retry(wait_on=RuntimeError)

  def git_ls_remote(baseurl, ref, credential_helper=None):

-     cmd = ["git"]

-     if credential_helper:

-         cmd.extend(["-c", "credential.useHttpPath=true"])

-         cmd.extend(["-c", "credential.helper=%s" % credential_helper])

-     return run(cmd + ["ls-remote", baseurl, ref], text=True, errors="replace")

+     with tracing.span("git-ls-remote", baseurl=baseurl, ref=ref):

+         cmd = ["git"]

+         if credential_helper:

+             cmd.extend(["-c", "credential.useHttpPath=true"])

+             cmd.extend(["-c", "credential.helper=%s" % credential_helper])

+         return run(cmd + ["ls-remote", baseurl, ref], text=True, errors="replace")

  

  

  def get_tz_offset():

@@ -33,6 +33,7 @@ 

  from datetime import timedelta

  

  from .. import util

+ from ..otel import tracing

  from ..arch_utils import getBaseArch

  

  
@@ -67,9 +68,11 @@ 

                  value = getattr(self.koji_module.config, key, None)

                  if value is not None:

                      session_opts[key] = value

-             self.koji_proxy = koji.ClientSession(

-                 self.koji_module.config.server, session_opts

+             self.koji_proxy = tracing.instrument_xmlrpc_proxy(

+                 koji.ClientSession(self.koji_module.config.server, session_opts)

              )

+             with tracing.span("koji.system.listMethods"):

+                 self.koji_methods = self.koji_proxy.system.listMethods()

  

      # This retry should be removed once https://pagure.io/koji/issue/3170 is

      # fixed and released.
@@ -971,7 +974,8 @@ 

                  os.utime(destination_file)

                  return destination_file

  

-             return self._atomic_download(url, destination_file, validator)

+             with tracing.span("download-rpm", url=url):

+                 return self._atomic_download(url, destination_file, validator)

  

      def get_file(self, path, validator=None):

          """

file modified
+5 -2
@@ -28,6 +28,7 @@ 

  from kobo.shortcuts import run, force_list

  from pungi.util import explode_rpm_package, makedirs, copy_all, temp_dir, retry

  from .kojiwrapper import KojiWrapper

+ from ..otel import tracing

  

  lock = threading.Lock()

  
@@ -229,7 +230,8 @@ 

          tmp_dir = self.get_temp_repo_path(scm_root, scm_branch)

          if not os.path.isdir(tmp_dir):

              makedirs(tmp_dir)

-             self._clone(scm_root, scm_branch, tmp_dir)

+             with tracing.span("git-clone", repo=scm_root, ref=scm_branch):

+                 self._clone(scm_root, scm_branch, tmp_dir)

          self.run_process_command(tmp_dir)

          return tmp_dir

  
@@ -377,7 +379,8 @@ 

              self.log_debug(

                  "Exporting container %s to %s: %s", scm_root, target_dir, cmd

              )

-             self.retry_run(cmd, can_fail=False)

+             with tracing.span("skopeo-copy", arch=arch, image=scm_root):

+                 self.retry_run(cmd, can_fail=False)

          except RuntimeError as e:

              self.log_error(

                  "Failed to copy container image: %s %s", e, getattr(e, "output", "")

file modified
+1 -9
@@ -129,14 +129,6 @@ 

          self.assertEqual({}, actual)

  

  

- class DummySystem(object):

-     def __init__(self):

-         self.methods = ["_listapi", "Dummy", "getRPM", "getRPMChecksums"]

- 

-     def listMethods(self):

-         return self.methods

- 

- 

  @mock.patch("pungi.phases.pkgset.pkgsets.ReaderPool", new=FakePool)

  @mock.patch("kobo.pkgset.FileCache", new=MockFileCache)

  class TestKojiPkgset(PkgsetCompareMixin, helpers.PungiTestCase):
@@ -149,7 +141,7 @@ 

          self.koji_downloader = helpers.FSKojiDownloader()

          self.koji_wrapper = mock.Mock()

          self.koji_wrapper.koji_proxy.listTaggedRPMS.return_value = self.tagged_rpms

-         self.koji_wrapper.koji_proxy.system = DummySystem()

+         self.koji_wrapper.koji_methods = ["getRPM", "getRPMChecksums"]

          self.koji_wrapper.koji_module.pathinfo = self.path_info

  

      def _touch_files(self, filenames):

This patch adds support for Opentelemetry. If OTEL_EXPORTER_OTLP_ENDPOINT env variable is defined, it will send traces there. Otherwise there is no change.

The whole compose is wrapped in a single span. Nested under that are spans for operations that involve a remote server.

  • Talking to CTS
  • Sending API requests to Koji
  • Any git repo clone

Here's how I'm testing this. I run Jaeger locally with this docker-compose file:

services:
  jaeger:
    image: docker.io/jaegertracing/opentelemetry-all-in-one:latest
    ports:
      # Frontend
      - "16686:16686"
      # OTLP over gRPC
      - "4317:55680"
      # OTLP over HTTP
      - "4318:55681"
    environment:
      COLLECTOR_OTLP_ENABLED: "true"

Then run a compose with OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 env var set. The dummy compose would work, but doesn't trace many things. Adding some extra_files, or downloading packages from a Koji instance, or talking to CTS adds some additional spans.

It is also required to install following packages:

  • opentelemetry-api
  • opentelemetry-sdk
  • opentelemetry-instrumentation-requests
  • opentelemetry-exporter-otlp-proto-http

They are only needed when telemetry is configured, so I'd like to avoid a hard dependency.

rebased onto b3e0b6d

a month ago

rebased onto b3e0b6d

a month ago

rebased onto b3e0b6d

a month ago

Just tried with dummy compose. Looks good to me. :thumbsup:

rebased onto b3e0b6d

a month ago

Pull-Request has been merged by lsedlar

a month ago