Skip to content

Make otel dependencies optional#360

Merged
bloodearnest merged 12 commits intomainfrom
optional-otel
Sep 25, 2025
Merged

Make otel dependencies optional#360
bloodearnest merged 12 commits intomainfrom
optional-otel

Conversation

@bloodearnest
Copy link
Copy Markdown
Member

@bloodearnest bloodearnest commented Sep 23, 2025

Make most of the otel dependencies optional, except for opentelemetry-api, which is still vendored. This removes them as required dependencies, but preserves the ability to use otel with opensafely run if needed. See #361 for more info.

Significant wins:

  • 9 less vendored packages, more than 50k lines of cod
  • remove our pkg_resources hack
  • we can move back to upstream vendoring, and archive our fork
  • full integration tests that run a real job, with and without the optional dependencies

Fixes: #361

This commit breaks tests, but it bundles the large deletions into
a single commit. This makes it easier to understand the later commits
that made this change.
All except opentelemetry-api, which is still vendored.

The actual code has already been deleted - this commit has the changes
that effected that removal, for easier understanding.

The setuptools package cannot be vendored, which is why it is our only
formal dependency. We needed to tweak the new requirements-prod just
method to not try to add it, by passing `--no-allow-unsafe`. We cannot
get rid of a runtime setup tools dependency just yet, but we maybe can
in future by upgrading otel libraries.

This meant we can also remove the pkg_resources hack we needed for otel
deps to work when vendored.
This ensures we can have telemetry during our tests
This tries to import the optional dependencies, and ensures the code
handles when they are not available. It adds an explict
TRACING_AVAILABLE flag, and if it's False, does no-ops in our manual
tracing functions.

It turns out we cannot mix and match otel libraries imported from our
vendored lib and globally in pip. This is an unfortunante implementation
detail of the otel implementation, the module path affects some internal
identity checks on objects.

So, we have our tracing module provide an abstraction. It either imports
the whole otel stack from the global namespace, if found (like in dev
and when installed with opensafely[tracing], but falls back to importing
the core `trace` module from `opensafely._vendor.opentelemetry` if they
are not found.

The rest of our code imports the `trace` module from jobrunner.tracing,
so it is always consistent in which version it uses. This actually
resulted in a much clearer api usage.
This modifies the package metadata with an extra called "tracing", which
installs the right packages.

It adds a functional test that installs into a virtualenv using this
extra, and that if you run a job with tracing configured, tracing
does work.

Also, I expanded the existing installation test, that now effectively
installs without the dependencies, with a similar functional test to
ensure that jobs still run in a normal installation. This caught a bug,
which is fixed by adding another no-op when TRACING_AVAILABLE is False.
@bloodearnest bloodearnest marked this pull request as ready for review September 23, 2025 15:40
It seems docker is install in windows CI images, but it doesn't work.
Thanks, Github.
Previously, to support vendoring otel packages, we had to fork vendoring
and add support for including package metadata, so that the otel plugin
system would find the right packages.

With those dependencies being unvendored, we do not need that any more,
so can revert to upstream.

This removes all the package metadata files from our _vendor dir.
Copy link
Copy Markdown
Contributor

@evansd evansd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So nice seeing all this cruft disappearing. The new trace() abstraction looks like a nice simplification as well.

Copy link
Copy Markdown
Contributor

@StevenMaude StevenMaude left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a really good and well-explained simplification.

I didn't really know much about how OpenTelemetry is installed for Python. So it was useful to see the explanation around how the opentelemetry-api package can be still used without the other related opentelemetry dependencies installed. Leaving opentelemetry-api vendored then means we don't need to manage the availability of the other dependencies throughout our code, but only in a few places.

Related to that, a tiny nit in the PR message:

Make most of the otel dependencies optional, except for opensafely-api, which is still vendored.

I think this should be opentelemetry-api.

@bloodearnest
Copy link
Copy Markdown
Member Author

Related to that, a tiny nit in the PR message:

Make most of the otel dependencies optional, except for opensafely-api, which is still vendored.

I think this should be opentelemetry-api.

Hah, if I had a nickel for every time I have mistyped open{telemetry/safely} in this PR...

@bloodearnest bloodearnest merged commit 69ed3bd into main Sep 25, 2025
15 checks passed
@bloodearnest bloodearnest deleted the optional-otel branch September 25, 2025 12:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make opentelemetry dependecies optional.

3 participants