Skip to content

Commit efec665

Browse files
fix(lib-injection): install from local wheels [backport #5576 to 1.12] (#5580)
Backports #5576 to 1.12. Change the installation mechanism to only download the wheels and install from the wheels on process start. Formerly, the wheels were merged together. This causes issues as ddtrace has versioned dependencies based on the Python version. eg. bytecode 0.13 is required for Python <3.8 and 0.14 is required for >3.8. These packages were erroneously merged together. The risk of this change is that the mechanism is back to `pip install`-ing at runtime which means: - Performance overhead for application startup. - Incorrect version of `pip` / `python` are used. The image size might also be bigger as the ddtrace files aren't deduped. Hopefully the compression used by the image registries is smart enough to figure this out. These tradeoffs are better than having a broken install however. The automated tests that we have already should cover this change. Fixes #5565, #5512 ## Checklist - [x] Change(s) are motivated and described in the PR description. - [x] Testing strategy is described if automated tests are not included in the PR. - [x] Risk is outlined (performance impact, potential for breakage, maintainability, etc). - [x] Change is maintainable (easy to change, telemetry, documentation). - [x] [Library release note guidelines](https://ddtrace.readthedocs.io/en/stable/contributing.html#Release-Note-Guidelines) are followed. - [x] Documentation is included (in-code, generated user docs, [public corp docs](https://github.com/DataDog/documentation/)). - [x] PR description includes explicit acknowledgement/acceptance of the performance implications of this PR as reported in the benchmarks PR comment. ## Reviewer Checklist - [x] Title is accurate. - [x] No unnecessary changes are introduced. - [x] Description motivates each change. - [x] Avoids breaking [API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces) changes unless absolutely necessary. - [x] Testing strategy adequately addresses listed risk(s). - [x] Change is maintainable (easy to change, telemetry, documentation). - [x] Release note makes sense to a user of the library. - [x] Reviewer has explicitly acknowledged and discussed the performance implications of this PR as reported in the benchmarks PR comment. Co-authored-by: Kyle Verhoog <[email protected]>
1 parent 28dd2b1 commit efec665

File tree

6 files changed

+61
-40
lines changed

6 files changed

+61
-40
lines changed

.github/workflows/lib-injection.yml

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ jobs:
6262
run: |
6363
docker run \
6464
-d \
65+
--name=testagent \
6566
--network=test-inject \
6667
-p 8126:8126 \
6768
ghcr.io/datadog/dd-apm-test-agent/ddapm-test-agent:v1.7.2
@@ -70,14 +71,13 @@ jobs:
7071
cd lib-injection
7172
mkdir -p lib-injection/ddtrace_pkgs
7273
cp sitecustomize.py lib-injection/
73-
./dl_megawheel.py \
74+
./dl_wheels.py \
7475
--ddtrace-version=v1.10 \
7576
--python-version=3.11 \
7677
--python-version=3.10 \
7778
--python-version=3.9 \
7879
--python-version=3.8 \
7980
--python-version=3.7 \
80-
--ddtrace-version=v1.10 \
8181
--arch x86_64 \
8282
--platform manylinux2014 \
8383
--output-dir ddtrace_pkgs \
@@ -94,15 +94,19 @@ jobs:
9494
--network test-inject \
9595
-p 18080:18080 \
9696
-e PYTHONPATH=/lib-injection \
97+
-e DD_TRACE_AGENT_URL=http://testagent:8126 \
9798
-v $PWD/lib-injection:/lib-injection \
9899
${{matrix.variant}}
99100
# Wait for the app to start
100-
sleep 5
101+
sleep 20
101102
docker logs ${{matrix.variant}}
102103
- name: Test the app
103104
run: |
104105
curl http://localhost:18080
105106
sleep 1 # wait for traces to be sent
106-
- name: Check test agent
107+
- name: Print traces
108+
run: curl http://localhost:8126/test/traces
109+
- name: Check test agent received a trace
107110
run: |
108-
curl http://localhost:8126/test/traces
111+
N=$(curl http://localhost:8126/test/traces | jq -r -e 'length')
112+
[[ $N == "1" ]]

lib-injection/Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ ARG DDTRACE_PYTHON_VERSION
66
RUN python3 -m pip install -U pip==23.0.1
77
RUN python3 -m pip install packaging==23.0
88
RUN mkdir -p pkgs
9-
ADD ./dl_megawheel.py .
9+
ADD ./dl_wheels.py .
1010
# Note that we only get Python >= 3.7. This is to keep the size of the image
1111
# as small as possible.
12-
RUN python3 dl_megawheel.py \
12+
RUN python3 dl_wheels.py \
1313
--python-version=3.11 \
1414
--python-version=3.10 \
1515
--python-version=3.9 \

lib-injection/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ It is responsible for providing the files necessary to run `ddtrace` in an
1212
arbitrary downstream application container. It also provides a script to copy
1313
the necessary files to a given directory.
1414

15-
The `dl_megawheel.py` script provides a portable `ddtrace` package. It is
16-
responsible for downloading and merging the published wheels of `ddtrace` and
17-
its dependencies.
15+
The `dl_wheels.py` script provides a portable set of `ddtrace` wheels. It is
16+
responsible for downloading the published wheels of `ddtrace` and its
17+
dependencies.
1818

1919
The Datadog Admission Controller injects the InitContainer with a new volume
2020
mount to the application deployment. The script to copy files out of the

lib-injection/dl_megawheel.py renamed to lib-injection/dl_wheels.py

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,14 @@
1313
20.0.2.
1414
1515
Usage:
16-
./dl_megawheel.py --help
16+
./dl_wheels.py --help
1717
1818
1919
The downloaded wheels can then be installed locally using:
2020
pip install --no-index --find-links <dir_of_downloaded_wheels> ddtrace
2121
"""
2222
import argparse
2323
import itertools
24-
import os
2524
import subprocess
2625
import sys
2726

@@ -105,12 +104,3 @@
105104

106105
if not args.dry_run:
107106
subprocess.run(cmd, capture_output=not args.verbose, check=True)
108-
109-
# Unzip all the wheels into the output directory
110-
wheel_files = [f for f in os.listdir(dl_dir) if f.endswith(".whl")]
111-
for whl in wheel_files:
112-
wheel_file = os.path.join(dl_dir, whl)
113-
# -q for quieter output, else we get all of the files being unzipped.
114-
subprocess.run(["unzip", "-q", "-o", wheel_file, "-d", dl_dir])
115-
# Remove the wheel as it has been unpacked
116-
os.remove(wheel_file)

lib-injection/sitecustomize.py

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,50 @@
11
"""
2-
When included on the PYTHONPATH this module will initialize the PYTHONPATH to
3-
include the directory containing the ddtrace package and its dependencies. It
4-
then imports the ddtrace.bootstrap.sitecustomize module to automatically
5-
instrument the application.
2+
This module when included on the PYTHONPATH will install the ddtrace package
3+
from the locally available wheels that are included in the image.
64
"""
75
import os
86
import sys
97

108

11-
def _add_to_pythonpath(path):
12-
# type: (str) -> None
13-
"""Adds a path to the start of PYTHONPATH."""
9+
def _configure_ddtrace():
10+
# This import has the same effect as ddtrace-run for the current process.
11+
import ddtrace.bootstrap.sitecustomize
12+
13+
bootstrap_dir = os.path.abspath(os.path.dirname(ddtrace.bootstrap.sitecustomize.__file__))
1414
prev_python_path = os.getenv("PYTHONPATH", "")
15-
os.environ["PYTHONPATH"] = "%s%s%s" % (path, os.pathsep, prev_python_path)
16-
sys.path.insert(0, path)
15+
os.environ["PYTHONPATH"] = "%s%s%s" % (bootstrap_dir, os.path.pathsep, prev_python_path)
16+
17+
# Also insert the bootstrap dir in the path of the current python process.
18+
sys.path.insert(0, bootstrap_dir)
19+
print("datadog autoinstrumentation: successfully configured python package")
20+
21+
22+
# Avoid infinite loop when attempting to install ddtrace. This flag is set when
23+
# the subprocess is launched to perform the installation.
24+
if "DDTRACE_PYTHON_INSTALL_IN_PROGRESS" not in os.environ:
25+
try:
26+
import ddtrace # noqa: F401
27+
28+
except ImportError:
29+
import subprocess
1730

31+
print("datadog autoinstrumentation: installing python package")
1832

19-
pkgs_path = os.path.join(os.path.dirname(__file__), "ddtrace_pkgs")
20-
bootstrap_dir = os.path.join(pkgs_path, "ddtrace", "bootstrap")
21-
_add_to_pythonpath(pkgs_path)
22-
_add_to_pythonpath(bootstrap_dir)
33+
# Set the flag to avoid an infinite loop.
34+
env = os.environ.copy()
35+
env["DDTRACE_PYTHON_INSTALL_IN_PROGRESS"] = "true"
2336

24-
try:
25-
import ddtrace.bootstrap.sitecustomize # noqa: F401
26-
except BaseException as e:
27-
print("datadog autoinstrumentation: ddtrace failed to install:\n %s" % str(e))
28-
else:
29-
print("datadog autoinstrumentation: ddtrace successfully installed")
37+
# Execute the installation with the current interpreter
38+
try:
39+
pkgs_path = os.path.join(os.path.dirname(__file__), "ddtrace_pkgs")
40+
subprocess.run(
41+
[sys.executable, "-m", "pip", "install", "--find-links", pkgs_path, "ddtrace"], env=env, check=True
42+
)
43+
except BaseException as e:
44+
print("datadog autoinstrumentation: failed to install python package %s" % str(e))
45+
else:
46+
print("datadog autoinstrumentation: successfully installed python package")
47+
_configure_ddtrace()
48+
else:
49+
print("datadog autoinstrumentation: ddtrace already installed, skipping install")
50+
_configure_ddtrace()
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
fixes:
3+
- |
4+
lib-injection: Switch installation to install from included wheels. Prior,
5+
the wheels were merged together which caused conflicts between versions
6+
of dependencies based on Python version.

0 commit comments

Comments
 (0)