diff --git a/README.md b/README.md index 355d96bc94..af18445cee 100644 --- a/README.md +++ b/README.md @@ -210,6 +210,11 @@ Python: instrumentation.opentelemetry.io/inject-python: "true" ``` +OpenTelemetry SDK environment variables only: +```bash +instrumentation.opentelemetry.io/inject-sdk: "true" +``` + The possible values for the annotation can be * `"true"` - inject and `Instrumentation` resource from the namespace. * `"my-instrumentation"` - name of `Instrumentation` CR instance in the current namespace. @@ -273,9 +278,20 @@ spec: image: your-customized-auto-instrumentation-image:python ``` -The Dockerfiles for auto-instrumentation can be found in [autoinstrumentation directory](./autoinstrumentation). +The Dockerfiles for auto-instrumentation can be found in [autoinstrumentation directory](./autoinstrumentation). Follow the instructions in the Dockerfiles on how to build a custom container image. +#### Inject OpenTelemetry SDK environment variables only + +You can configure the OpenTelemetry SDK for applications which can't +currently be autoinstrumented by using `inject-sdk` in place of (e.g.) +`inject-python` or `inject-java`. This will inject environment variables +like `OTEL_RESOURCE_ATTRIBUTES` but will not actually provide the SDK. + +```bash +instrumentation.opentelemetry.io/inject-sdk: "true" +``` + ## Compatibility matrix ### OpenTelemetry Operator vs. OpenTelemetry Collector diff --git a/pkg/instrumentation/annotation.go b/pkg/instrumentation/annotation.go index 3f1519b2cb..81c047e4af 100644 --- a/pkg/instrumentation/annotation.go +++ b/pkg/instrumentation/annotation.go @@ -26,6 +26,7 @@ const ( annotationInjectJava = "instrumentation.opentelemetry.io/inject-java" annotationInjectNodeJS = "instrumentation.opentelemetry.io/inject-nodejs" annotationInjectPython = "instrumentation.opentelemetry.io/inject-python" + annotationInjectSdk = "instrumentation.opentelemetry.io/inject-sdk" annotationInjectContainerName = "instrumentation.opentelemetry.io/container-names" ) diff --git a/pkg/instrumentation/podmutator.go b/pkg/instrumentation/podmutator.go index cc49a8eab1..244940e25e 100644 --- a/pkg/instrumentation/podmutator.go +++ b/pkg/instrumentation/podmutator.go @@ -43,6 +43,7 @@ type languageInstrumentations struct { Java *v1alpha1.Instrumentation NodeJS *v1alpha1.Instrumentation Python *v1alpha1.Instrumentation + Sdk *v1alpha1.Instrumentation } var _ webhookhandler.PodMutator = (*instPodMutator)(nil) @@ -95,7 +96,14 @@ func (pm *instPodMutator) Mutate(ctx context.Context, ns corev1.Namespace, pod c } insts.Python = inst - if insts.Java == nil && insts.NodeJS == nil && insts.Python == nil { + if inst, err = pm.getInstrumentationInstance(ctx, ns, pod, annotationInjectSdk); 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.Sdk = inst + + if insts.Java == nil && insts.NodeJS == nil && insts.Python == nil && insts.Sdk == nil { logger.V(1).Info("annotation not present in deployment, skipping instrumentation injection") return pod, nil } diff --git a/pkg/instrumentation/sdk.go b/pkg/instrumentation/sdk.go index cbaaa7ac91..209e355bc5 100644 --- a/pkg/instrumentation/sdk.go +++ b/pkg/instrumentation/sdk.go @@ -83,6 +83,12 @@ func (i *sdkInjector) inject(ctx context.Context, insts languageInstrumentations pod = i.injectCommonEnvVar(otelinst, pod, index) pod = i.injectCommonSDKConfig(ctx, otelinst, ns, pod, index) } + if insts.Sdk != nil { + otelinst := *insts.Sdk + i.logger.V(1).Info("injecting sdk-only instrumentation into pod", "otelinst-namespace", otelinst.Namespace, "otelinst-name", otelinst.Name) + pod = i.injectCommonEnvVar(otelinst, pod, index) + pod = i.injectCommonSDKConfig(ctx, otelinst, ns, pod, index) + } return pod } diff --git a/pkg/instrumentation/sdk_test.go b/pkg/instrumentation/sdk_test.go index 304d0d93ad..5997b88a5e 100644 --- a/pkg/instrumentation/sdk_test.go +++ b/pkg/instrumentation/sdk_test.go @@ -670,3 +670,70 @@ func TestInjectPython(t *testing.T) { }, }, pod) } + +func TestInjectSdkOnly(t *testing.T) { + inst := v1alpha1.Instrumentation{ + Spec: v1alpha1.InstrumentationSpec{ + Exporter: v1alpha1.Exporter{ + Endpoint: "https://collector:4318", + }, + }, + } + insts := languageInstrumentations{ + Sdk: &inst, + } + + inj := sdkInjector{ + logger: logr.Discard(), + } + pod := inj.inject(context.Background(), insts, + corev1.Namespace{}, + corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + }, + }, + }, + }, "") + assert.Equal(t, corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + Env: []corev1.EnvVar{ + { + Name: "OTEL_SERVICE_NAME", + Value: "app", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "https://collector:4318", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=app,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME)", + }, + }, + }, + }, + }, + }, pod) +} diff --git a/tests/e2e/instrumentation-sdk/00-install-collector.yaml b/tests/e2e/instrumentation-sdk/00-install-collector.yaml new file mode 100644 index 0000000000..f8e1e98e07 --- /dev/null +++ b/tests/e2e/instrumentation-sdk/00-install-collector.yaml @@ -0,0 +1,23 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: OpenTelemetryCollector +metadata: + name: sidecar +spec: + mode: sidecar + config: | + receivers: + otlp: + protocols: + grpc: + http: + processors: + + exporters: + logging: + + service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [logging] diff --git a/tests/e2e/instrumentation-sdk/00-install-instrumentation.yaml b/tests/e2e/instrumentation-sdk/00-install-instrumentation.yaml new file mode 100644 index 0000000000..e6fe8125d3 --- /dev/null +++ b/tests/e2e/instrumentation-sdk/00-install-instrumentation.yaml @@ -0,0 +1,24 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: Instrumentation +metadata: + name: sdk-only +spec: + env: + - name: SPLUNK_TRACE_RESPONSE_HEADER_ENABLED + value: "true" + exporter: + endpoint: http://localhost:4317 + propagators: + - jaeger + - b3 + sampler: + type: parentbased_traceidratio + argument: "0.25" + nodejs: + env: + - name: OTEL_NODEJS_DEBUG + value: "true" + python: + env: + - name: OTEL_ENV_VAR + value: "true" diff --git a/tests/e2e/instrumentation-sdk/01-assert.yaml b/tests/e2e/instrumentation-sdk/01-assert.yaml new file mode 100644 index 0000000000..931beb4608 --- /dev/null +++ b/tests/e2e/instrumentation-sdk/01-assert.yaml @@ -0,0 +1,27 @@ +apiVersion: v1 +kind: Pod +metadata: + annotations: + sidecar.opentelemetry.io/inject: "true" + instrumentation.opentelemetry.io/inject-sdk: "true" + labels: + app: my-pod-with-sidecar +spec: + containers: + - name: myapp + env: + - name: SPLUNK_TRACE_RESPONSE_HEADER_ENABLED + value: "true" + - name: OTEL_SERVICE_NAME + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: http://localhost:4317 + - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME + - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME + - name: OTEL_PROPAGATORS + value: jaeger,b3 + - name: OTEL_TRACES_SAMPLER + - name: OTEL_TRACES_SAMPLER_ARG + - name: OTEL_RESOURCE_ATTRIBUTES + - name: otc-container +status: + phase: Running diff --git a/tests/e2e/instrumentation-sdk/01-install-app.yaml b/tests/e2e/instrumentation-sdk/01-install-app.yaml new file mode 100644 index 0000000000..a57db32a58 --- /dev/null +++ b/tests/e2e/instrumentation-sdk/01-install-app.yaml @@ -0,0 +1,24 @@ +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-sdk: "true" + spec: + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + fsGroup: 2000 + containers: + - name: myapp + image: ghcr.io/anuraaga/express-hello-world:latest