Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,65 @@ jobs:
uses: pypa/gh-action-pypi-publish@897895f1e160c830e369f9779632ebc134688e1b
with:
repository-url: https://upload.pypi.org/legacy/

publish-docker:
needs:
- packages
runs-on: ubuntu-latest
permissions:
attestations: write
id-token: write
contents: write
env:
DOCKER_IMAGE_NAME: docker.elastic.co/observability/elastic-otel-python
steps:
- uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1

- name: Log in to the Elastic Container registry
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
with:
registry: ${{ secrets.ELASTIC_DOCKER_REGISTRY }}
username: ${{ secrets.ELASTIC_DOCKER_USERNAME }}
password: ${{ secrets.ELASTIC_DOCKER_PASSWORD }}

- uses: actions/download-artifact@v4
with:
name: packages
path: dist

- name: Extract metadata (tags, labels)
id: docker-meta
uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5.5.1
with:
images: ${{ env.DOCKER_IMAGE_NAME }}
tags: |
type=semver,pattern={{version}}
# "edge" Docker tag on git push to default branch
type=edge
labels: |
org.opencontainers.image.vendor=Elastic
org.opencontainers.image.title=elastic-otel-python
org.opencontainers.image.description=Elastic Distribution of OpenTelemetry Python

- name: Build and push image
id: docker-push
uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 # v6.7.0
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
file: operator/Dockerfile
tags: ${{ steps.docker-meta.outputs.tags }}
labels: ${{ steps.docker-meta.outputs.labels }}
build-args: |
DISTRO_DIR=./dist/

- name: generate build provenance (containers)
uses: actions/attest-build-provenance@1c608d11d69870c2092266b3f9a6f3abbf17002c # v1.4.3
with:
subject-name: "${{ env.DOCKER_IMAGE_NAME }}"
subject-digest: ${{ steps.docker-push.outputs.digest }}
push-to-registry: true
23 changes: 23 additions & 0 deletions operator/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
FROM docker.elastic.co/wolfi/python:3.12-dev@sha256:e90d34b9e1ecbf8b8092fe9037e86298dec69ec7e9f144a2575cd3db67cea2cb AS build

ENV LANG=C.UTF-8
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

ARG DISTRO_DIR

COPY ${DISTRO_DIR} /opt/distro

WORKDIR /operator-build

ADD operator/requirements.txt .

RUN mkdir workspace

RUN pip install --no-cache-dir --target workspace /opt/distro/*.whl -r requirements.txt

FROM docker.elastic.co/wolfi/chainguard-base:latest@sha256:6fbf07849a440c8dca9aa7e9cb56ed3ecaa9eb40f8a4f36b39393d7b32d78ecc

COPY --from=build /operator-build/workspace /autoinstrumentation

RUN chmod -R go+r /autoinstrumentation
22 changes: 22 additions & 0 deletions operator/Dockerfile.alpine
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# This is a dockerfile for local testing
FROM python:3.12-alpine AS build

ARG DISTRO_DIR

COPY ${DISTRO_DIR} /opt/distro

WORKDIR /operator-build

ADD operator/requirements.txt .

RUN mkdir workspace

RUN apk add gcc python3-dev musl-dev linux-headers

RUN pip install --target workspace /opt/distro/*.whl -r requirements.txt

FROM python:3.12-alpine

COPY --from=build /operator-build/workspace /autoinstrumentation

RUN chmod -R go+r /autoinstrumentation
13 changes: 13 additions & 0 deletions operator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Docker images for Kubernetes OpenTelemetry Operator

In this directory there are two *Dockerfile*s:
- `Dockerfile`, that build the published image, based on Wolfi a glibc based image
- `Dockerfile.alpine`, that can be used for building a testing musl based image

## Local build

From the root of this repository you can build and make available the image locally with:

```bash
docker buildx build -f operator/Dockerfile --build-arg DISTRO_DIR=./dist -t elastic-otel-python-operator:test-wolfi --load .
```
50 changes: 50 additions & 0 deletions operator/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
opentelemetry-propagator-aws-xray==1.0.2
opentelemetry-propagator-ot-trace==0.48b0

opentelemetry-instrumentation-aio-pika==0.48b0
opentelemetry-instrumentation-aiohttp-client==0.48b0
opentelemetry-instrumentation-aiohttp-server==0.48b0
opentelemetry-instrumentation-aiopg==0.48b0
opentelemetry-instrumentation-asgi==0.48b0
opentelemetry-instrumentation-asyncio==0.48b0
opentelemetry-instrumentation-asyncpg==0.48b0
opentelemetry-instrumentation-aws-lambda==0.48b0
opentelemetry-instrumentation-boto==0.48b0
opentelemetry-instrumentation-boto3sqs==0.48b0
opentelemetry-instrumentation-botocore==0.48b0
opentelemetry-instrumentation-cassandra==0.48b0
opentelemetry-instrumentation-celery==0.48b0
opentelemetry-instrumentation-confluent-kafka==0.48b0
opentelemetry-instrumentation-dbapi==0.48b0
opentelemetry-instrumentation-django==0.48b0
opentelemetry-instrumentation-elasticsearch==0.48b0
opentelemetry-instrumentation-falcon==0.48b0
opentelemetry-instrumentation-fastapi==0.48b0
opentelemetry-instrumentation-flask==0.48b0
opentelemetry-instrumentation-grpc==0.48b0
opentelemetry-instrumentation-httpx==0.48b0
opentelemetry-instrumentation-jinja2==0.48b0
opentelemetry-instrumentation-kafka-python==0.48b0
opentelemetry-instrumentation-logging==0.48b0
opentelemetry-instrumentation-mysql==0.48b0
opentelemetry-instrumentation-mysqlclient==0.48b0
opentelemetry-instrumentation-pika==0.48b0
opentelemetry-instrumentation-psycopg==0.48b0
opentelemetry-instrumentation-psycopg2==0.48b0
opentelemetry-instrumentation-pymemcache==0.48b0
opentelemetry-instrumentation-pymongo==0.48b0
opentelemetry-instrumentation-pymysql==0.48b0
opentelemetry-instrumentation-pyramid==0.48b0
opentelemetry-instrumentation-redis==0.48b0
opentelemetry-instrumentation-remoulade==0.48b0
opentelemetry-instrumentation-requests==0.48b0
opentelemetry-instrumentation-sqlalchemy==0.48b0
opentelemetry-instrumentation-sqlite3==0.48b0
opentelemetry-instrumentation-starlette==0.48b0
opentelemetry-instrumentation-system-metrics==0.48b0
opentelemetry-instrumentation-threading==0.48b0
opentelemetry-instrumentation-tornado==0.48b0
opentelemetry-instrumentation-tortoiseorm==0.48b0
opentelemetry-instrumentation-urllib==0.48b0
opentelemetry-instrumentation-urllib3==0.48b0
opentelemetry-instrumentation-wsgi==0.48b0
6 changes: 6 additions & 0 deletions renovate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"github>elastic/renovate-config:only-chainguard"
]
}
18 changes: 17 additions & 1 deletion src/elasticotel/distro/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
# limitations under the License.

import os
from logging import getLogger

from opentelemetry.environment_variables import (
OTEL_METRICS_EXPORTER,
Expand All @@ -36,13 +37,28 @@
from elasticotel.distro.environment_variables import ELASTIC_OTEL_SYSTEM_METRICS_ENABLED


logger = getLogger(__name__)


class ElasticOpenTelemetryConfigurator(_OTelSDKConfigurator):
pass


class ElasticOpenTelemetryDistro(BaseDistro):
def load_instrumentor(self, entry_point: EntryPoint, **kwargs):
instrumentor_class: BaseInstrumentor = entry_point.load()
# When running in the k8s operator loading of an instrumentor may fail because the environment
# in which python extensions are built does not match the one from the running container.
# There are at least two cases:
# - different python version
# - different kind of wheels, e.g. manylinux vs musllinux
# To avoid the distro loading to fail catch ImportError here, that is the kind of exception we see
# when loading shared objects or cython extensions fails.
try:
instrumentor_class: BaseInstrumentor = entry_point.load()
except ImportError:
logger.exception("Instrumenting of %s failed", entry_point.name)
return

instrumentor_kwargs = {}
if instrumentor_class == SystemMetricsInstrumentor:
system_metrics_configuration = os.environ.get(ELASTIC_OTEL_SYSTEM_METRICS_ENABLED, "false")
Expand Down
15 changes: 15 additions & 0 deletions tests/distro/test_distro.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,18 @@ def test_load_instrumentor_default_kwargs_for_instrumentors(self):
distro.load_instrumentor(entryPoint_mock)

instrumentor_mock.assert_called_once_with()

def test_load_instrumentor_handles_import_error_from_instrumentor_loading(self):
distro = ElasticOpenTelemetryDistro()
entryPoint_mock = mock.Mock()
entryPoint_mock.load.side_effect = ImportError

distro.load_instrumentor(entryPoint_mock)

def test_load_instrumentor_forwards_exceptions(self):
distro = ElasticOpenTelemetryDistro()
entryPoint_mock = mock.Mock()
entryPoint_mock.load.side_effect = ValueError

with self.assertRaises(ValueError):
distro.load_instrumentor(entryPoint_mock)