diff --git a/deploy/lineage-attach/Dockerfile.otel-shim b/deploy/lineage-attach/Dockerfile.otel-shim index d8a084097..4ff60b452 100644 --- a/deploy/lineage-attach/Dockerfile.otel-shim +++ b/deploy/lineage-attach/Dockerfile.otel-shim @@ -67,8 +67,10 @@ COPY --from=ghcr.io/astral-sh/uv:0.9.24@sha256:816fdce3387ed2142e37d2e56e1b1b97c # `--system --break-system-packages` lets the install land in a non-venv # python too; both flags are inert when --python is a venv. ARG OTEL_CONTRIB_VERSION=0.65b0 -RUN pin="$("${VENV_PYTHON}" -c 'from importlib.metadata import version; v = version("opentelemetry-api"); \ - assert tuple(map(int, v.split(".")[:2])) >= (1, 34); print("opentelemetry-api==" + v)' 2>/dev/null)" \ +# The floor is an expression, not an `assert`: RUN inherits the base image's +# ENV, and PYTHONOPTIMIZE strips asserts. +RUN pin="$("${VENV_PYTHON}" -c 'from importlib.metadata import version; import sys; v = version("opentelemetry-api"); \ + tuple(map(int, v.split(".")[:2])) >= (1, 34) or sys.exit(1); print("opentelemetry-api==" + v)' 2>/dev/null)" \ || pin="opentelemetry-distro==${OTEL_CONTRIB_VERSION}"; \ echo "the bake is pinned to ${pin}"; \ uv pip install --no-cache --python "${VENV_PYTHON}" --system --break-system-packages "${pin}" \ @@ -81,7 +83,7 @@ RUN pin="$("${VENV_PYTHON}" -c 'from importlib.metadata import version; v = vers opentelemetry-instrumentation-aiohttp-client \ opentelemetry-instrumentation-urllib3 \ opentelemetry-instrumentation-threading \ - || { echo "REFUSING: the shim could not be installed alongside ${pin} (uv's error is above; if it found no solution, no contrib release is paired with that version)" >&2; exit 1; } + || { echo "REFUSING: the shim could not be installed alongside ${pin} (uv's error is above; RECIPE step 2 reads it)" >&2; exit 1; } # The install resolves into the app's OWN environment, so it may move a package # an app dependency pins (wrapt, deprecated, typing-extensions are the usual # suspects) — and the attestations import only OpenTelemetry, never the app. diff --git a/deploy/lineage-attach/RECIPE.md b/deploy/lineage-attach/RECIPE.md index aba8b0dce..83273323f 100644 --- a/deploy/lineage-attach/RECIPE.md +++ b/deploy/lineage-attach/RECIPE.md @@ -55,8 +55,8 @@ attestation runs before the load and prints nothing when it passes). Fail `REFUSING to bake … already instruments …` (exit 3): the app instruments itself — go to step 3 **without** `APP_CONTAINER`/`APP_IMAGE` (capture only). Fail `REFUSING to bake … no runnable Python found` (exit 3): outside the shim's envelope (DESIGN "The envelope") — same, capture only; or pass the interpreter as arg 3 if you know it. Fail `REFUSING to bake … is not present locally` (exit 3): wrong `IMAGE` — see Inputs; nothing was built. -Fail `REFUSING: the shim could not be installed alongside …` (the build fails): no contrib release is paired with the app's `opentelemetry-api` (a core newer than its contrib release, or a package index that lags) — uv's own error is above the line. -Fail `ATTESTATION FAILED` (exit 4): the bake itself is broken (the image was not loaded) — read the assertion it prints; not an app property. +Fail `REFUSING: the shim could not be installed alongside …` (the build fails): uv's own error is above the line and names the blocker; the two usual cases follow. Alongside `opentelemetry-api==…`: no contrib release is paired with the app's core yet (a core newer than its contrib release, or a package index that lags) — go to step 3 **without** `APP_CONTAINER`/`APP_IMAGE` (capture only) until the paired release reaches the index, then re-bake. Alongside `opentelemetry-distro==…` (the default, when the app carries no `opentelemetry-api` or one older than 1.34.0): the app's environment cannot take that release at all (uv says why, e.g. `depends on Python>=3.10`) — outside the envelope, capture only. +Fail `ATTESTATION FAILED` (exit 4): the bake itself is broken (the image was not loaded) — read the line it prints; not an app property. ## 3. Attach (once per Deployment) diff --git a/deploy/lineage-attach/build-otel-shim.sh b/deploy/lineage-attach/build-otel-shim.sh index d5e0b46ad..e19b97e00 100755 --- a/deploy/lineage-attach/build-otel-shim.sh +++ b/deploy/lineage-attach/build-otel-shim.sh @@ -249,7 +249,9 @@ build_image() { # Attestation: the bake proves itself before anything is loaded. Gate off, not # one opentelemetry module may load; gate on, the hook ran, the exporter -# selection is explicit, and the propagator injects a traceparent. +# selection is explicit, and the propagator injects a traceparent. No `assert` +# here: the interpreter runs under the image's own ENV, and PYTHONOPTIMIZE +# strips asserts. verify_inert() { if ! "$CONTAINER_TOOL" run --rm --network=none --entrypoint "$VENV_PYTHON" "$WRAPPER_TAG" -c ' import sys @@ -263,14 +265,14 @@ raise SystemExit("gate off, yet otel loaded: %s" % loaded if loaded else 0)'; th verify_propagates() { if ! "$CONTAINER_TOOL" run --rm --network=none -e LINEAGE_PROPAGATE=1 --entrypoint "$VENV_PYTHON" "$WRAPPER_TAG" -c ' import os, sys -assert "opentelemetry.instrumentation.auto_instrumentation" in sys.modules, "hook did not run" -assert os.environ.get("OTEL_TRACES_EXPORTER") is not None, "exporter selection not pinned" +"opentelemetry.instrumentation.auto_instrumentation" in sys.modules or sys.exit("hook did not run") +os.environ.get("OTEL_TRACES_EXPORTER") is not None or sys.exit("exporter selection not pinned") from opentelemetry import trace from opentelemetry.propagate import inject with trace.get_tracer("attest").start_as_current_span("attest"): carrier = {} inject(carrier) -assert "traceparent" in carrier, "propagator injects nothing: %r" % carrier'; then +"traceparent" in carrier or sys.exit("propagator injects nothing: %r" % carrier)'; then echo "ATTESTATION FAILED for ${WRAPPER_TAG}: gate on, but the hook did not come up." >&2 exit 4 fi