From c5bfb966c553c874a6e1041363564b3ce7bb83ce Mon Sep 17 00:00:00 2001 From: Anuraag Agrawal Date: Thu, 11 Nov 2021 15:31:35 +0900 Subject: [PATCH 1/3] Add autoinstrumentation for Python --- README.md | 9 +- .../v1alpha1/instrumentation_types.go | 13 + .../v1alpha1/zz_generated.deepcopy.go | 16 ++ .../opentelemetry.io_instrumentations.yaml | 7 + .../opentelemetry.io_instrumentations.yaml | 7 + pkg/instrumentation/annotation.go | 1 + pkg/instrumentation/podmutator.go | 10 +- pkg/instrumentation/podmutator_test.go | 100 +++++++ pkg/instrumentation/python.go | 82 ++++++ pkg/instrumentation/python_test.go | 251 ++++++++++++++++++ pkg/instrumentation/sdk.go | 6 + pkg/instrumentation/sdk_test.go | 84 ++++++ .../00-install-collector.yaml | 25 ++ .../00-install-instrumentation.yaml | 12 + .../e2e/instrumentation-python/01-assert.yaml | 32 +++ .../01-install-app.yaml | 20 ++ 16 files changed, 673 insertions(+), 2 deletions(-) create mode 100644 pkg/instrumentation/python.go create mode 100644 pkg/instrumentation/python_test.go create mode 100644 tests/e2e/instrumentation-python/00-install-collector.yaml create mode 100644 tests/e2e/instrumentation-python/00-install-instrumentation.yaml create mode 100644 tests/e2e/instrumentation-python/01-assert.yaml create mode 100644 tests/e2e/instrumentation-python/01-install-app.yaml diff --git a/README.md b/README.md index 0e9d9ab108..9e7d317a0a 100644 --- a/README.md +++ b/README.md @@ -155,7 +155,7 @@ EOF ### OpenTelemetry auto-instrumentation injection -The operator can inject and configure OpenTelemetry auto-instrumentation libraries. Currently Java and NodeJS are supported. +The operator can inject and configure OpenTelemetry auto-instrumentation libraries. Currently Java, NodeJS and Python are supported. To use auto-instrumentation, configure an `Instrumentation` resource with the configuration for the SDK and instrumentation. @@ -179,6 +179,8 @@ spec: image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-java:latest nodejs: image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-nodejs:latest + python: + image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-python:latest EOF ``` @@ -198,6 +200,11 @@ NodeJS: instrumentation.opentelemetry.io/inject-nodejs: "true" ``` +Python: +```bash +instrumentation.opentelemetry.io/inject-python: "true" +``` + The possible values for the annotation can be * `"true"` - inject and `Instrumentation` resource from the namespace. * `"my-instrumentation"` - name of `Instrumentation` CR instance. diff --git a/apis/instrumentation/v1alpha1/instrumentation_types.go b/apis/instrumentation/v1alpha1/instrumentation_types.go index c38ee1de4f..e7de372a95 100644 --- a/apis/instrumentation/v1alpha1/instrumentation_types.go +++ b/apis/instrumentation/v1alpha1/instrumentation_types.go @@ -47,6 +47,11 @@ type InstrumentationSpec struct { // +optional // +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors=true NodeJS NodeJSSpec `json:"nodejs,omitempty"` + + // Python defines configuration for python auto-instrumentation. + // +optional + // +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors=true + Python PythonSpec `json:"python,omitempty"` } // JavaSpec defines Java SDK and instrumentation configuration. @@ -65,6 +70,14 @@ type NodeJSSpec struct { Image string `json:"image,omitempty"` } +// PythonSpec defines Python SDK and instrumentation configuration. +type PythonSpec struct { + // Image is a container image with Python SDK and autoinstrumentation. + // +optional + // +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors=true + Image string `json:"image,omitempty"` +} + // Exporter defines OTLP exporter configuration. type Exporter struct { // Endpoint is address of the collector with OTLP endpoint. diff --git a/apis/instrumentation/v1alpha1/zz_generated.deepcopy.go b/apis/instrumentation/v1alpha1/zz_generated.deepcopy.go index 03c55c9eca..650159db37 100644 --- a/apis/instrumentation/v1alpha1/zz_generated.deepcopy.go +++ b/apis/instrumentation/v1alpha1/zz_generated.deepcopy.go @@ -116,6 +116,7 @@ func (in *InstrumentationSpec) DeepCopyInto(out *InstrumentationSpec) { out.Sampler = in.Sampler out.Java = in.Java out.NodeJS = in.NodeJS + out.Python = in.Python } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstrumentationSpec. @@ -173,6 +174,21 @@ func (in *NodeJSSpec) DeepCopy() *NodeJSSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PythonSpec) DeepCopyInto(out *PythonSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PythonSpec. +func (in *PythonSpec) DeepCopy() *PythonSpec { + if in == nil { + return nil + } + out := new(PythonSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Sampler) DeepCopyInto(out *Sampler) { *out = *in diff --git a/bundle/manifests/opentelemetry.io_instrumentations.yaml b/bundle/manifests/opentelemetry.io_instrumentations.yaml index 56417bd776..752cd09002 100644 --- a/bundle/manifests/opentelemetry.io_instrumentations.yaml +++ b/bundle/manifests/opentelemetry.io_instrumentations.yaml @@ -80,6 +80,13 @@ spec: - "no" type: string type: array + python: + description: Python defines configuration for python auto-instrumentation. + properties: + image: + description: Image is a container image with Python SDK and autoinstrumentation. + type: string + type: object resourceAttributes: additionalProperties: type: string diff --git a/config/crd/bases/opentelemetry.io_instrumentations.yaml b/config/crd/bases/opentelemetry.io_instrumentations.yaml index c3cbe5bf7c..9d54f81927 100644 --- a/config/crd/bases/opentelemetry.io_instrumentations.yaml +++ b/config/crd/bases/opentelemetry.io_instrumentations.yaml @@ -82,6 +82,13 @@ spec: - "no" type: string type: array + python: + description: Python defines configuration for python auto-instrumentation. + properties: + image: + description: Image is a container image with Python SDK and autoinstrumentation. + type: string + type: object resourceAttributes: additionalProperties: type: string diff --git a/pkg/instrumentation/annotation.go b/pkg/instrumentation/annotation.go index 6cc252a035..8ce17a7c6f 100644 --- a/pkg/instrumentation/annotation.go +++ b/pkg/instrumentation/annotation.go @@ -25,6 +25,7 @@ const ( // Possible values are "true", "false" or "" name. annotationInjectJava = "instrumentation.opentelemetry.io/inject-java" annotationInjectNodeJS = "instrumentation.opentelemetry.io/inject-nodejs" + annotationInjectPython = "instrumentation.opentelemetry.io/inject-python" ) // annotationValue returns the effective annotationInjectJava value, based on the annotations from the pod and namespace. diff --git a/pkg/instrumentation/podmutator.go b/pkg/instrumentation/podmutator.go index 151a546a73..91a1525d7f 100644 --- a/pkg/instrumentation/podmutator.go +++ b/pkg/instrumentation/podmutator.go @@ -41,6 +41,7 @@ type instPodMutator struct { type languageInstrumentations struct { Java *v1alpha1.Instrumentation NodeJS *v1alpha1.Instrumentation + Python *v1alpha1.Instrumentation } var _ webhookhandler.PodMutator = (*instPodMutator)(nil) @@ -76,7 +77,14 @@ func (pm *instPodMutator) Mutate(ctx context.Context, ns corev1.Namespace, pod c } insts.NodeJS = inst - if insts.Java == nil && insts.NodeJS == nil { + if inst, err = pm.getInstrumentationInstance(ctx, ns, pod, annotationInjectPython); err != nil { + // we still allow the pod to be created, but we log a message to the operator's logs + logger.Error(err, "failed to select an OpenTelemetry Instrumentation instance for this pod") + return pod, err + } + insts.Python = inst + + if insts.Java == nil && insts.NodeJS == nil && insts.Python == nil { logger.V(1).Info("annotation not present in deployment, skipping instrumentation injection") return pod, nil } diff --git a/pkg/instrumentation/podmutator_test.go b/pkg/instrumentation/podmutator_test.go index 247beec783..52587fc00b 100644 --- a/pkg/instrumentation/podmutator_test.go +++ b/pkg/instrumentation/podmutator_test.go @@ -268,6 +268,103 @@ func TestMutatePod(t *testing.T) { }, }, }, + { + name: "python injection, true", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "python", + }, + }, + inst: v1alpha1.Instrumentation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-inst", + Namespace: "python", + }, + Spec: v1alpha1.InstrumentationSpec{ + Python: v1alpha1.PythonSpec{ + Image: "otel/python:1", + }, + Exporter: v1alpha1.Exporter{ + Endpoint: "http://collector:12345", + }, + }, + }, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectPython: "true", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + }, + }, + }, + }, + expected: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectPython: "true", + }, + }, + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: "opentelemetry-auto-instrumentation", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: initContainerName, + Image: "otel/python:1", + Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation/"}, + VolumeMounts: []corev1.VolumeMount{{ + Name: volumeName, + MountPath: "/otel-auto-instrumentation", + }}, + }, + }, + Containers: []corev1.Container{ + { + Name: "app", + Env: []corev1.EnvVar{ + { + Name: "OTEL_SERVICE_NAME", + Value: "app", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://collector:12345", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=app,k8s.namespace.name=python", + }, + { + Name: "PYTHONPATH", + Value: fmt.Sprintf("%s:%s", pythonPathPrefix, pythonPathSuffix), + }, + { + Name: "OTEL_TRACES_EXPORTER", + Value: "otlp_proto_http", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "opentelemetry-auto-instrumentation", + MountPath: "/otel-auto-instrumentation", + }, + }, + }, + }, + }, + }, + }, { name: "missing annotation", ns: corev1.Namespace{ @@ -401,6 +498,9 @@ func TestMutatePod(t *testing.T) { t.Run(test.name, func(t *testing.T) { err := k8sClient.Create(context.Background(), &test.ns) require.NoError(t, err) + defer func() { + _ = k8sClient.Delete(context.Background(), &test.ns) + }() err = k8sClient.Create(context.Background(), &test.inst) require.NoError(t, err) diff --git a/pkg/instrumentation/python.go b/pkg/instrumentation/python.go new file mode 100644 index 0000000000..401c812094 --- /dev/null +++ b/pkg/instrumentation/python.go @@ -0,0 +1,82 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package instrumentation + +import ( + "fmt" + + "github.com/go-logr/logr" + corev1 "k8s.io/api/core/v1" + + "github.com/open-telemetry/opentelemetry-operator/apis/instrumentation/v1alpha1" +) + +const ( + envPythonPath = "PYTHONPATH" + envOtelTracesExporter = "OTEL_TRACES_EXPORTER" + pythonPathPrefix = "/otel-auto-instrumentation/opentelemetry/instrumentation/auto_instrumentation" + pythonPathSuffix = "/otel-auto-instrumentation" +) + +func injectPythonSDK(logger logr.Logger, pythonSpec v1alpha1.PythonSpec, pod corev1.Pod) corev1.Pod { + // caller checks if there is at least one container + container := &pod.Spec.Containers[0] + idx := getIndexOfEnv(container.Env, envPythonPath) + if idx == -1 { + container.Env = append(container.Env, corev1.EnvVar{ + Name: envPythonPath, + Value: fmt.Sprintf("%s:%s", pythonPathPrefix, pythonPathSuffix), + }) + } else if idx > -1 { + if container.Env[idx].ValueFrom != nil { + // TODO add to status object or submit it as an event + logger.Info("Skipping Python SDK injection, the container defines PYTHONPATH env var value via ValueFrom", "container", container.Name) + return pod + } + container.Env[idx].Value = fmt.Sprintf("%s:%s:%s", pythonPathPrefix, container.Env[idx].Value, pythonPathSuffix) + } + + // Set OTEL_TRACES_EXPORTER to HTTP exporter if not set by user because it is what our autoinstrumentation supports. + idx = getIndexOfEnv(container.Env, envOtelTracesExporter) + if idx == -1 { + container.Env = append(container.Env, corev1.EnvVar{ + Name: envOtelTracesExporter, + Value: "otlp_proto_http", + }) + } + + container.VolumeMounts = append(container.VolumeMounts, corev1.VolumeMount{ + Name: volumeName, + MountPath: "/otel-auto-instrumentation", + }) + + pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{ + Name: volumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }}) + + pod.Spec.InitContainers = append(pod.Spec.InitContainers, corev1.Container{ + Name: initContainerName, + Image: pythonSpec.Image, + Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation/"}, + VolumeMounts: []corev1.VolumeMount{{ + Name: volumeName, + MountPath: "/otel-auto-instrumentation", + }}, + }) + + return pod +} diff --git a/pkg/instrumentation/python_test.go b/pkg/instrumentation/python_test.go new file mode 100644 index 0000000000..dcff1c3d5e --- /dev/null +++ b/pkg/instrumentation/python_test.go @@ -0,0 +1,251 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package instrumentation + +import ( + "fmt" + "testing" + + "github.com/go-logr/logr" + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + + "github.com/open-telemetry/opentelemetry-operator/apis/instrumentation/v1alpha1" +) + +func TestInjectPythonSDK(t *testing.T) { + tests := []struct { + name string + v1alpha1.PythonSpec + pod corev1.Pod + expected corev1.Pod + }{ + { + name: "PYTHONPATH not defined", + PythonSpec: v1alpha1.PythonSpec{Image: "foo/bar:1"}, + pod: corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {}, + }, + }, + }, + expected: corev1.Pod{ + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: volumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: initContainerName, + Image: "foo/bar:1", + Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation/"}, + VolumeMounts: []corev1.VolumeMount{{ + Name: volumeName, + MountPath: "/otel-auto-instrumentation", + }}, + }, + }, + Containers: []corev1.Container{ + { + VolumeMounts: []corev1.VolumeMount{ + { + Name: volumeName, + MountPath: "/otel-auto-instrumentation", + }, + }, + Env: []corev1.EnvVar{ + { + Name: "PYTHONPATH", + Value: fmt.Sprintf("%s:%s", pythonPathPrefix, pythonPathSuffix), + }, + { + Name: "OTEL_TRACES_EXPORTER", + Value: "otlp_proto_http", + }, + }, + }, + }, + }, + }, + }, + { + name: "PYTHONPATH defined", + PythonSpec: v1alpha1.PythonSpec{Image: "foo/bar:1"}, + pod: corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Env: []corev1.EnvVar{ + { + Name: "PYTHONPATH", + Value: "/foo:/bar", + }, + }, + }, + }, + }, + }, + expected: corev1.Pod{ + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: volumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: initContainerName, + Image: "foo/bar:1", + Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation/"}, + VolumeMounts: []corev1.VolumeMount{{ + Name: volumeName, + MountPath: "/otel-auto-instrumentation", + }}, + }, + }, + Containers: []corev1.Container{ + { + VolumeMounts: []corev1.VolumeMount{ + { + Name: volumeName, + MountPath: "/otel-auto-instrumentation", + }, + }, + Env: []corev1.EnvVar{ + { + Name: "PYTHONPATH", + Value: fmt.Sprintf("%s:%s:%s", pythonPathPrefix, "/foo:/bar", pythonPathSuffix), + }, + { + Name: "OTEL_TRACES_EXPORTER", + Value: "otlp_proto_http", + }, + }, + }, + }, + }, + }, + }, + { + name: "OTEL_TRACES_EXPORTER defined", + PythonSpec: v1alpha1.PythonSpec{Image: "foo/bar:1"}, + pod: corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Env: []corev1.EnvVar{ + { + Name: "OTEL_TRACES_EXPORTER", + Value: "zipkin", + }, + }, + }, + }, + }, + }, + expected: corev1.Pod{ + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: volumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: initContainerName, + Image: "foo/bar:1", + Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation/"}, + VolumeMounts: []corev1.VolumeMount{{ + Name: volumeName, + MountPath: "/otel-auto-instrumentation", + }}, + }, + }, + Containers: []corev1.Container{ + { + VolumeMounts: []corev1.VolumeMount{ + { + Name: volumeName, + MountPath: "/otel-auto-instrumentation", + }, + }, + Env: []corev1.EnvVar{ + { + Name: "OTEL_TRACES_EXPORTER", + Value: "zipkin", + }, + { + Name: "PYTHONPATH", + Value: fmt.Sprintf("%s:%s", pythonPathPrefix, pythonPathSuffix), + }, + }, + }, + }, + }, + }, + }, + { + name: "PYTHONPATH defined as ValueFrom", + PythonSpec: v1alpha1.PythonSpec{Image: "foo/bar:1"}, + pod: corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Env: []corev1.EnvVar{ + { + Name: "PYTHONPATH", + ValueFrom: &corev1.EnvVarSource{}, + }, + }, + }, + }, + }, + }, + expected: corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Env: []corev1.EnvVar{ + { + Name: "PYTHONPATH", + ValueFrom: &corev1.EnvVarSource{}, + }, + }, + }, + }, + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pod := injectPythonSDK(logr.Discard(), test.PythonSpec, test.pod) + assert.Equal(t, test.expected, pod) + }) + } +} diff --git a/pkg/instrumentation/sdk.go b/pkg/instrumentation/sdk.go index 1b46fd3501..7a57ab571a 100644 --- a/pkg/instrumentation/sdk.go +++ b/pkg/instrumentation/sdk.go @@ -58,6 +58,12 @@ func inject(logger logr.Logger, insts languageInstrumentations, ns corev1.Namesp pod = injectCommonSDKConfig(otelinst, ns, pod) pod = injectNodeJSSDK(logger, otelinst.Spec.NodeJS, pod) } + if insts.Python != nil { + otelinst := *insts.Python + logger.V(1).Info("injecting instrumentation into pod", "otelinst-namespace", otelinst.Namespace, "otelinst-name", otelinst.Name) + pod = injectCommonSDKConfig(otelinst, ns, pod) + pod = injectPythonSDK(logger, otelinst.Spec.Python, pod) + } return pod } diff --git a/pkg/instrumentation/sdk_test.go b/pkg/instrumentation/sdk_test.go index 1b8cf98fce..a828052dba 100644 --- a/pkg/instrumentation/sdk_test.go +++ b/pkg/instrumentation/sdk_test.go @@ -15,6 +15,7 @@ package instrumentation import ( + "fmt" "testing" "github.com/go-logr/logr" @@ -352,3 +353,86 @@ func TestInjectNodeJS(t *testing.T) { }, }, pod) } + +func TestInjectPython(t *testing.T) { + inst := v1alpha1.Instrumentation{ + Spec: v1alpha1.InstrumentationSpec{ + Python: v1alpha1.PythonSpec{ + Image: "img:1", + }, + Exporter: v1alpha1.Exporter{ + Endpoint: "https://collector:4318", + }, + }, + } + insts := languageInstrumentations{ + Python: &inst, + } + pod := inject(logr.Discard(), insts, + corev1.Namespace{}, + corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + }, + }, + }, + }) + assert.Equal(t, corev1.Pod{ + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: volumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: initContainerName, + Image: "img:1", + Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation/"}, + VolumeMounts: []corev1.VolumeMount{{ + Name: volumeName, + MountPath: "/otel-auto-instrumentation", + }}, + }, + }, + Containers: []corev1.Container{ + { + Name: "app", + VolumeMounts: []corev1.VolumeMount{ + { + Name: volumeName, + MountPath: "/otel-auto-instrumentation", + }, + }, + Env: []corev1.EnvVar{ + { + Name: "OTEL_SERVICE_NAME", + Value: "app", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "https://collector:4318", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=app,k8s.namespace.name=", + }, + { + Name: "PYTHONPATH", + Value: fmt.Sprintf("%s:%s", pythonPathPrefix, pythonPathSuffix), + }, + { + Name: "OTEL_TRACES_EXPORTER", + Value: "otlp_proto_http", + }, + }, + }, + }, + }, + }, pod) +} diff --git a/tests/e2e/instrumentation-python/00-install-collector.yaml b/tests/e2e/instrumentation-python/00-install-collector.yaml new file mode 100644 index 0000000000..b823086361 --- /dev/null +++ b/tests/e2e/instrumentation-python/00-install-collector.yaml @@ -0,0 +1,25 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: sidecar +spec: + mode: sidecar + args: + metrics-level: detailed + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + + exporters: + logging: + + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [logging] diff --git a/tests/e2e/instrumentation-python/00-install-instrumentation.yaml b/tests/e2e/instrumentation-python/00-install-instrumentation.yaml new file mode 100644 index 0000000000..786a4ba9e3 --- /dev/null +++ b/tests/e2e/instrumentation-python/00-install-instrumentation.yaml @@ -0,0 +1,12 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: Instrumentation +metadata: + name: python +spec: + exporter: + endpoint: http://localhost:4318/v1/traces + propagators: + - jaeger + - b3 + python: + image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-python:latest diff --git a/tests/e2e/instrumentation-python/01-assert.yaml b/tests/e2e/instrumentation-python/01-assert.yaml new file mode 100644 index 0000000000..c4a4e54fe4 --- /dev/null +++ b/tests/e2e/instrumentation-python/01-assert.yaml @@ -0,0 +1,32 @@ +apiVersion: v1 +kind: Pod +metadata: + annotations: + sidecar.opentelemetry.io/inject: "true" + instrumentation.opentelemetry.io/inject-python: "true" + labels: + app: my-pod-with-sidecar +spec: + containers: + - name: myapp + env: + - name: OTEL_SERVICE_NAME + value: myapp + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: http://localhost:4318/v1/traces + - name: OTEL_RESOURCE_ATTRIBUTES + - name: OTEL_PROPAGATORS + value: jaeger,b3 + - name: PYTHONPATH + value: "/otel-auto-instrumentation/opentelemetry/instrumentation/auto_instrumentation:/otel-auto-instrumentation" + - name: OTEL_TRACES_EXPORTER + value: otlp_proto_http + volumeMounts: + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + - mountPath: /otel-auto-instrumentation + name: opentelemetry-auto-instrumentation + - name: otc-container + initContainers: + - name: opentelemetry-auto-instrumentation +status: + phase: Running diff --git a/tests/e2e/instrumentation-python/01-install-app.yaml b/tests/e2e/instrumentation-python/01-install-app.yaml new file mode 100644 index 0000000000..8f3ba8d3b0 --- /dev/null +++ b/tests/e2e/instrumentation-python/01-install-app.yaml @@ -0,0 +1,20 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-deployment-with-sidecar +spec: + selector: + matchLabels: + app: my-pod-with-sidecar + replicas: 1 + template: + metadata: + labels: + app: my-pod-with-sidecar + annotations: + sidecar.opentelemetry.io/inject: "true" + instrumentation.opentelemetry.io/inject-python: "true" + spec: + containers: + - name: myapp + image: ghcr.io/anuraaga/flask-hello-world:latest From 673280647eb8d0ed62a53bef448bb7f07b09dd64 Mon Sep 17 00:00:00 2001 From: Anuraag Agrawal Date: Wed, 17 Nov 2021 13:17:14 +0900 Subject: [PATCH 2/3] Better log --- pkg/instrumentation/sdk.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/instrumentation/sdk.go b/pkg/instrumentation/sdk.go index 5b63c589a0..430ef2146f 100644 --- a/pkg/instrumentation/sdk.go +++ b/pkg/instrumentation/sdk.go @@ -48,19 +48,19 @@ func inject(logger logr.Logger, insts languageInstrumentations, ns corev1.Namesp // in the future we can define an annotation to configure this if insts.Java != nil { otelinst := *insts.Java - logger.V(1).Info("injecting instrumentation into pod", "otelinst-namespace", otelinst.Namespace, "otelinst-name", otelinst.Name) + logger.V(1).Info("injecting java instrumentation into pod", "otelinst-namespace", otelinst.Namespace, "otelinst-name", otelinst.Name) pod = injectCommonSDKConfig(otelinst, ns, pod) pod = injectJavaagent(logger, otelinst.Spec.Java, pod) } if insts.NodeJS != nil { otelinst := *insts.NodeJS - logger.V(1).Info("injecting instrumentation into pod", "otelinst-namespace", otelinst.Namespace, "otelinst-name", otelinst.Name) + logger.V(1).Info("injecting nodejs instrumentation into pod", "otelinst-namespace", otelinst.Namespace, "otelinst-name", otelinst.Name) pod = injectCommonSDKConfig(otelinst, ns, pod) pod = injectNodeJSSDK(logger, otelinst.Spec.NodeJS, pod) } if insts.Python != nil { otelinst := *insts.Python - logger.V(1).Info("injecting instrumentation into pod", "otelinst-namespace", otelinst.Namespace, "otelinst-name", otelinst.Name) + logger.V(1).Info("injecting python instrumentation into pod", "otelinst-namespace", otelinst.Namespace, "otelinst-name", otelinst.Name) pod = injectCommonSDKConfig(otelinst, ns, pod) pod = injectPythonSDK(logger, otelinst.Spec.Python, pod) } From d6ceca9ea3851204d1e0f26bd764fb0aec50cbfc Mon Sep 17 00:00:00 2001 From: Anuraag Agrawal Date: Wed, 17 Nov 2021 13:24:12 +0900 Subject: [PATCH 3/3] Fix merge --- pkg/instrumentation/python_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/instrumentation/python_test.go b/pkg/instrumentation/python_test.go index dcff1c3d5e..c57fcd8613 100644 --- a/pkg/instrumentation/python_test.go +++ b/pkg/instrumentation/python_test.go @@ -22,7 +22,7 @@ import ( "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" - "github.com/open-telemetry/opentelemetry-operator/apis/instrumentation/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" ) func TestInjectPythonSDK(t *testing.T) {