From ba0bedefca269467167bcbec1cd82d8a029756ae Mon Sep 17 00:00:00 2001 From: Anuraag Agrawal Date: Fri, 5 Nov 2021 17:05:46 +0900 Subject: [PATCH 01/16] Add autoinstrumentation of NodeJS --- .../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 | 9 +- pkg/instrumentation/annotation_test.go | 22 +-- pkg/instrumentation/nodejs.go | 68 +++++++ pkg/instrumentation/nodejs_test.go | 181 ++++++++++++++++++ pkg/instrumentation/podmutator.go | 31 ++- pkg/instrumentation/podmutator_test.go | 151 ++++++++++++++- pkg/instrumentation/sdk.go | 9 +- pkg/instrumentation/sdk_test.go | 74 ++++++- 12 files changed, 553 insertions(+), 35 deletions(-) create mode 100644 pkg/instrumentation/nodejs.go create mode 100644 pkg/instrumentation/nodejs_test.go diff --git a/api/instrumentation/v1alpha1/instrumentation_types.go b/api/instrumentation/v1alpha1/instrumentation_types.go index 2e20111b2e..82c8bb6c74 100644 --- a/api/instrumentation/v1alpha1/instrumentation_types.go +++ b/api/instrumentation/v1alpha1/instrumentation_types.go @@ -27,6 +27,11 @@ type InstrumentationSpec struct { // +optional // +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors=true Java JavaSpec `json:"java,omitempty"` + + // NodeJS defines configuration for nodejs auto-instrumentation. + // +optional + // +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors=true + NodeJS NodeJSSpec `json:"nodejs,omitempty"` } // JavaSpec defines Java SDK and instrumentation configuration. @@ -37,6 +42,14 @@ type JavaSpec struct { Image string `json:"image,omitempty"` } +// NodeJSSpec defines NodeJS SDK and instrumentation configuration. +type NodeJSSpec struct { + // Image is a container image with NodeJS 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/api/instrumentation/v1alpha1/zz_generated.deepcopy.go b/api/instrumentation/v1alpha1/zz_generated.deepcopy.go index 23ecbabcf1..77fd44daa2 100644 --- a/api/instrumentation/v1alpha1/zz_generated.deepcopy.go +++ b/api/instrumentation/v1alpha1/zz_generated.deepcopy.go @@ -102,6 +102,7 @@ func (in *InstrumentationSpec) DeepCopyInto(out *InstrumentationSpec) { *out = *in out.Exporter = in.Exporter out.Java = in.Java + out.NodeJS = in.NodeJS } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstrumentationSpec. @@ -143,3 +144,18 @@ func (in *JavaSpec) DeepCopy() *JavaSpec { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NodeJSSpec) DeepCopyInto(out *NodeJSSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeJSSpec. +func (in *NodeJSSpec) DeepCopy() *NodeJSSpec { + if in == nil { + return nil + } + out := new(NodeJSSpec) + in.DeepCopyInto(out) + return out +} diff --git a/bundle/manifests/opentelemetry.io_instrumentations.yaml b/bundle/manifests/opentelemetry.io_instrumentations.yaml index 9cf9e406ed..fc71751741 100644 --- a/bundle/manifests/opentelemetry.io_instrumentations.yaml +++ b/bundle/manifests/opentelemetry.io_instrumentations.yaml @@ -57,6 +57,13 @@ spec: description: Image is a container image with javaagent JAR. type: string type: object + nodejs: + description: NodeJS defines configuration for nodejs auto-instrumentation. + properties: + image: + description: Image is a container image with NodeJS SDK and autoinstrumentation. + type: string + type: object type: object status: description: InstrumentationStatus defines status of the instrumentation. diff --git a/config/crd/bases/opentelemetry.io_instrumentations.yaml b/config/crd/bases/opentelemetry.io_instrumentations.yaml index ade599959b..aea7021987 100644 --- a/config/crd/bases/opentelemetry.io_instrumentations.yaml +++ b/config/crd/bases/opentelemetry.io_instrumentations.yaml @@ -59,6 +59,13 @@ spec: description: Image is a container image with javaagent JAR. type: string type: object + nodejs: + description: NodeJS defines configuration for nodejs auto-instrumentation. + properties: + image: + description: Image is a container image with NodeJS SDK and autoinstrumentation. + type: string + type: object type: object status: description: InstrumentationStatus defines status of the instrumentation. diff --git a/pkg/instrumentation/annotation.go b/pkg/instrumentation/annotation.go index fd063f8a3f..76884a5dcb 100644 --- a/pkg/instrumentation/annotation.go +++ b/pkg/instrumentation/annotation.go @@ -23,15 +23,16 @@ import ( const ( // annotationInjectJava indicates whether java auto-instrumentation should be injected or not. // Possible values are "true", "false" or "" name. - annotationInjectJava = "instrumentation.opentelemetry.io/inject-java" + annotationInject = "instrumentation.opentelemetry.io/inject" + annotationLanguage = "instrumentation.opentelemetry.io/language" ) // annotationValue returns the effective annotationInjectJava value, based on the annotations from the pod and namespace. -func annotationValue(ns metav1.ObjectMeta, pod metav1.ObjectMeta) string { +func annotationValue(ns metav1.ObjectMeta, pod metav1.ObjectMeta, annotation string) string { // is the pod annotated with instructions to inject sidecars? is the namespace annotated? // if any of those is true, a sidecar might be desired. - podAnnValue := pod.Annotations[annotationInjectJava] - nsAnnValue := ns.Annotations[annotationInjectJava] + podAnnValue := pod.Annotations[annotation] + nsAnnValue := ns.Annotations[annotation] // if the namespace value is empty, the pod annotation should be used, whatever it is if len(nsAnnValue) == 0 { diff --git a/pkg/instrumentation/annotation_test.go b/pkg/instrumentation/annotation_test.go index d8f2ac2b52..35835982ff 100644 --- a/pkg/instrumentation/annotation_test.go +++ b/pkg/instrumentation/annotation_test.go @@ -35,14 +35,14 @@ func TestEffectiveAnnotationValue(t *testing.T) { corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInjectJava: "true", + annotationInject: "true", }, }, }, corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInjectJava: "false", + annotationInject: "false", }, }, }, @@ -54,14 +54,14 @@ func TestEffectiveAnnotationValue(t *testing.T) { corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInjectJava: "true", + annotationInject: "true", }, }, }, corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInjectJava: "some-instance", + annotationInject: "some-instance", }, }, }, @@ -73,14 +73,14 @@ func TestEffectiveAnnotationValue(t *testing.T) { corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInjectJava: "some-instance-from-pod", + annotationInject: "some-instance-from-pod", }, }, }, corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInjectJava: "some-instance", + annotationInject: "some-instance", }, }, }, @@ -92,14 +92,14 @@ func TestEffectiveAnnotationValue(t *testing.T) { corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInjectJava: "false", + annotationInject: "false", }, }, }, corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInjectJava: "some-instance", + annotationInject: "some-instance", }, }, }, @@ -112,7 +112,7 @@ func TestEffectiveAnnotationValue(t *testing.T) { corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInjectJava: "some-instance", + annotationInject: "some-instance", }, }, }, @@ -124,7 +124,7 @@ func TestEffectiveAnnotationValue(t *testing.T) { corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInjectJava: "true", + annotationInject: "true", }, }, }, @@ -133,7 +133,7 @@ func TestEffectiveAnnotationValue(t *testing.T) { } { t.Run(tt.desc, func(t *testing.T) { // test - annValue := annotationValue(tt.ns.ObjectMeta, tt.pod.ObjectMeta) + annValue := annotationValue(tt.ns.ObjectMeta, tt.pod.ObjectMeta, annotationInject) // verify assert.Equal(t, tt.expected, annValue) diff --git a/pkg/instrumentation/nodejs.go b/pkg/instrumentation/nodejs.go new file mode 100644 index 0000000000..308fc73344 --- /dev/null +++ b/pkg/instrumentation/nodejs.go @@ -0,0 +1,68 @@ +// 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 ( + "github.com/go-logr/logr" + corev1 "k8s.io/api/core/v1" + + "github.com/open-telemetry/opentelemetry-operator/api/instrumentation/v1alpha1" +) + +const ( + envNodeOptions = "NODE_OPTIONS" + nodeRequireArgument = " --require /otel-auto-instrumentation/autoinstrumentation.js" +) + +func injectNodeJSSDK(logger logr.Logger, nodeJSSpec v1alpha1.NodeJSSpec, pod corev1.Pod) corev1.Pod { + // caller checks if there is at least one container + container := &pod.Spec.Containers[0] + idx := getIndexOfEnv(container.Env, envNodeOptions) + if idx == -1 { + container.Env = append(container.Env, corev1.EnvVar{ + Name: envNodeOptions, + Value: nodeRequireArgument, + }) + } else if idx > -1 { + if container.Env[idx].ValueFrom != nil { + // TODO add to status object or submit it as an event + logger.Info("Skipping NodeJS SDK injection, the container defines NODE_OPTIONS env var value via ValueFrom", "container", container.Name) + return pod + } + container.Env[idx].Value = container.Env[idx].Value + nodeRequireArgument + } + 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: nodeJSSpec.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/nodejs_test.go b/pkg/instrumentation/nodejs_test.go new file mode 100644 index 0000000000..86b6302985 --- /dev/null +++ b/pkg/instrumentation/nodejs_test.go @@ -0,0 +1,181 @@ +// 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 ( + "testing" + + "github.com/go-logr/logr" + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + + "github.com/open-telemetry/opentelemetry-operator/api/instrumentation/v1alpha1" +) + +func TestInjectNodeJSSDK(t *testing.T) { + tests := []struct { + name string + v1alpha1.NodeJSSpec + pod corev1.Pod + expected corev1.Pod + }{ + { + name: "NODE_OPTIONS not defined", + NodeJSSpec: v1alpha1.NodeJSSpec{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", "-R", "/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: "NODE_OPTIONS", + Value: javaJVMArgument, + }, + }, + }, + }, + }, + }, + }, + { + name: "NODE_OPTIONS defined", + NodeJSSpec: v1alpha1.NodeJSSpec{Image: "foo/bar:1"}, + pod: corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Env: []corev1.EnvVar{ + { + Name: "NODE_OPTIONS", + Value: "-Dbaz=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", "-R", "/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: "NODE_OPTIONS", + Value: "-Dbaz=bar" + javaJVMArgument, + }, + }, + }, + }, + }, + }, + }, + { + name: "NODE_OPTIONS defined as ValueFrom", + NodeJSSpec: v1alpha1.NodeJSSpec{Image: "foo/bar:1"}, + pod: corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Env: []corev1.EnvVar{ + { + Name: "NODE_OPTIONS", + ValueFrom: &corev1.EnvVarSource{}, + }, + }, + }, + }, + }, + }, + expected: corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Env: []corev1.EnvVar{ + { + Name: "NODE_OPTIONS", + ValueFrom: &corev1.EnvVarSource{}, + }, + }, + }, + }, + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pod := injectNodeJSSDK(logr.Discard(), test.NodeJSSpec, test.pod) + assert.Equal(t, test.expected, pod) + }) + } +} diff --git a/pkg/instrumentation/podmutator.go b/pkg/instrumentation/podmutator.go index cc358249bb..a3bc9a4f2e 100644 --- a/pkg/instrumentation/podmutator.go +++ b/pkg/instrumentation/podmutator.go @@ -17,6 +17,7 @@ package instrumentation import ( "context" "errors" + "fmt" "strings" "github.com/go-logr/logr" @@ -31,6 +32,8 @@ import ( var ( errMultipleInstancesPossible = errors.New("multiple OpenTelemetry Instrumentation instances available, cannot determine which one to select") errNoInstancesAvailable = errors.New("no OpenTelemetry Instrumentation instances available") + errNoLanguageSpecified = fmt.Errorf("%s must be set to the desired SDK language", annotationLanguage) + errUnsupportedLanguage = errors.New("SDK language not supported, supported languages are (java, nodejs)") ) type instPodMutator struct { @@ -51,20 +54,22 @@ func (pm *instPodMutator) Mutate(ctx context.Context, ns corev1.Namespace, pod c logger := pm.Logger.WithValues("namespace", pod.Namespace, "name", pod.Name) // if no annotations are found at all, just return the same pod - annValue := annotationValue(ns.ObjectMeta, pod.ObjectMeta) - if len(annValue) == 0 { + instValue := annotationValue(ns.ObjectMeta, pod.ObjectMeta, annotationInject) + if len(instValue) == 0 { logger.V(1).Info("annotation not present in deployment, skipping instrumentation injection") return pod, nil } // is the annotation value 'false'? if so, we need a pod without the instrumentation - if strings.EqualFold(annValue, "false") { + if strings.EqualFold(instValue, "false") { logger.V(1).Info("pod explicitly refuses instrumentation injection, attempting to remove instrumentation if it exists") return pod, nil } + langValue := annotationValue(ns.ObjectMeta, pod.ObjectMeta, annotationLanguage) + // which instance should it talk to? - otelinst, err := pm.getInstrumentationInstance(ctx, ns, annValue) + otelinst, err := pm.getInstrumentationInstance(ctx, ns, instValue, langValue) if err != nil { if err == errNoInstancesAvailable || err == errMultipleInstancesPossible { // we still allow the pod to be created, but we log a message to the operator's logs @@ -79,16 +84,24 @@ func (pm *instPodMutator) Mutate(ctx context.Context, ns corev1.Namespace, pod c // once it's been determined that instrumentation is desired, none exists yet, and we know which instance it should talk to, // we should inject the instrumentation. logger.V(1).Info("injecting instrumentation into pod", "otelinst-namespace", otelinst.Namespace, "otelinst-name", otelinst.Name) - return inject(pm.Logger, otelinst, pod), nil + return inject(pm.Logger, otelinst, pod, langValue), nil } -func (pm *instPodMutator) getInstrumentationInstance(ctx context.Context, ns corev1.Namespace, ann string) (v1alpha1.Instrumentation, error) { - if strings.EqualFold(ann, "true") { +func (pm *instPodMutator) getInstrumentationInstance(ctx context.Context, ns corev1.Namespace, instValue string, langValue string) (v1alpha1.Instrumentation, error) { + otelInst := v1alpha1.Instrumentation{} + + if len(langValue) == 0 { + return otelInst, errNoLanguageSpecified + } + if langValue != "java" && langValue != "nodejs" { + return otelInst, errUnsupportedLanguage + } + + if strings.EqualFold(instValue, "true") { return pm.selectInstrumentationInstanceFromNamespace(ctx, ns) } - otelInst := v1alpha1.Instrumentation{} - err := pm.Client.Get(ctx, types.NamespacedName{Name: ann, Namespace: ns.Name}, &otelInst) + err := pm.Client.Get(ctx, types.NamespacedName{Name: instValue, Namespace: ns.Name}, &otelInst) if err != nil { return otelInst, err } diff --git a/pkg/instrumentation/podmutator_test.go b/pkg/instrumentation/podmutator_test.go index e16a9ffc72..e80b94d49b 100644 --- a/pkg/instrumentation/podmutator_test.go +++ b/pkg/instrumentation/podmutator_test.go @@ -26,7 +26,6 @@ import ( "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes/scheme" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/envtest" @@ -36,7 +35,7 @@ import ( var k8sClient client.Client var testEnv *envtest.Environment -var testScheme *runtime.Scheme = scheme.Scheme +var testScheme = scheme.Scheme func TestMain(m *testing.M) { testEnv = &envtest.Environment{ @@ -107,7 +106,8 @@ func TestMutatePod(t *testing.T) { pod: corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInjectJava: "true", + annotationInject: "true", + annotationLanguage: "java", }, }, Spec: corev1.PodSpec{ @@ -121,7 +121,8 @@ func TestMutatePod(t *testing.T) { expected: corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInjectJava: "true", + annotationInject: "true", + annotationLanguage: "java", }, }, Spec: corev1.PodSpec{ @@ -172,6 +173,142 @@ func TestMutatePod(t *testing.T) { }, }, }, + { + name: "nodejs injection, true", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "nodejs", + }, + }, + inst: v1alpha1.Instrumentation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-inst", + Namespace: "nodejs", + }, + Spec: v1alpha1.InstrumentationSpec{ + Java: v1alpha1.JavaSpec{ + Image: "otel/nodejs:1", + }, + Exporter: v1alpha1.Exporter{ + Endpoint: "http://collector:12345", + }, + }, + }, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInject: "true", + annotationLanguage: "nodejs", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + }, + }, + }, + }, + expected: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInject: "true", + annotationLanguage: "nodejs", + }, + }, + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: "opentelemetry-auto-instrumentation", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: initContainerName, + Image: "otel/java: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: "NODE_OPTIONS", + Value: nodeRequireArgument, + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "opentelemetry-auto-instrumentation", + MountPath: "/otel-auto-instrumentation", + }, + }, + }, + }, + }, + }, + }, + { + name: "missing language", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "missing-language", + }, + }, + inst: v1alpha1.Instrumentation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-inst", + Namespace: "missing-language", + }, + Spec: v1alpha1.InstrumentationSpec{ + Java: v1alpha1.JavaSpec{ + Image: "otel/java:1", + }, + Exporter: v1alpha1.Exporter{ + Endpoint: "http://collector:12345", + }, + }, + }, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInject: "true", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + }, + }, + }, + }, + expected: corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + }, + }, + }, + }, + }, { name: "missing annotation", ns: corev1.Namespace{ @@ -236,7 +373,7 @@ func TestMutatePod(t *testing.T) { pod: corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInjectJava: "false", + annotationInject: "false", }, }, Spec: corev1.PodSpec{ @@ -250,7 +387,7 @@ func TestMutatePod(t *testing.T) { expected: corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInjectJava: "false", + annotationInject: "false", }, }, Spec: corev1.PodSpec{ @@ -286,7 +423,7 @@ func TestMutatePod(t *testing.T) { pod: corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInjectJava: "doesnotexists", + annotationInject: "doesnotexists", }, }, Spec: corev1.PodSpec{ diff --git a/pkg/instrumentation/sdk.go b/pkg/instrumentation/sdk.go index 013a676502..08a617738d 100644 --- a/pkg/instrumentation/sdk.go +++ b/pkg/instrumentation/sdk.go @@ -30,7 +30,7 @@ const ( ) // inject a new sidecar container to the given pod, based on the given OpenTelemetryCollector. -func inject(logger logr.Logger, otelinst v1alpha1.Instrumentation, pod corev1.Pod) corev1.Pod { +func inject(logger logr.Logger, otelinst v1alpha1.Instrumentation, pod corev1.Pod, language string) corev1.Pod { if len(pod.Spec.Containers) < 1 { return pod } @@ -38,7 +38,12 @@ func inject(logger logr.Logger, otelinst v1alpha1.Instrumentation, pod corev1.Po // inject only to the first container for now // in the future we can define an annotation to configure this pod = injectCommonSDKConfig(otelinst, pod) - pod = injectJavaagent(logger, otelinst.Spec.Java, pod) + if language == "java" { + pod = injectJavaagent(logger, otelinst.Spec.Java, pod) + } + if language == "nodejs" { + pod = injectNodeJSSDK(logger, otelinst.Spec.NodeJS, pod) + } return pod } diff --git a/pkg/instrumentation/sdk_test.go b/pkg/instrumentation/sdk_test.go index a71d381744..156f54b59d 100644 --- a/pkg/instrumentation/sdk_test.go +++ b/pkg/instrumentation/sdk_test.go @@ -125,7 +125,7 @@ func TestSDKInjection(t *testing.T) { } } -func TestInjection(t *testing.T) { +func TestInjectJava(t *testing.T) { inst := v1alpha1.Instrumentation{ Spec: v1alpha1.InstrumentationSpec{ Java: v1alpha1.JavaSpec{ @@ -144,7 +144,7 @@ func TestInjection(t *testing.T) { }, }, }, - }) + }, "java") assert.Equal(t, corev1.Pod{ Spec: corev1.PodSpec{ Volumes: []corev1.Volume{ @@ -194,3 +194,73 @@ func TestInjection(t *testing.T) { }, }, pod) } + +func TestInjectNodeJS(t *testing.T) { + inst := v1alpha1.Instrumentation{ + Spec: v1alpha1.InstrumentationSpec{ + NodeJS: v1alpha1.NodeJSSpec{ + Image: "img:1", + }, + Exporter: v1alpha1.Exporter{ + Endpoint: "https://collector:4318", + }, + }, + } + pod := inject(logr.Discard(), inst, corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + }, + }, + }, + }, "nodejs") + 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: "NODE_OPTIONS", + Value: nodeRequireArgument, + }, + }, + }, + }, + }, + }, pod) +} From 8a3770a8c22934a41be764669198a8a1d21b7452 Mon Sep 17 00:00:00 2001 From: Anuraag Agrawal Date: Fri, 5 Nov 2021 17:24:15 +0900 Subject: [PATCH 02/16] Drift --- pkg/instrumentation/nodejs_test.go | 8 ++++---- pkg/instrumentation/podmutator_test.go | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/instrumentation/nodejs_test.go b/pkg/instrumentation/nodejs_test.go index 86b6302985..8516036fd5 100644 --- a/pkg/instrumentation/nodejs_test.go +++ b/pkg/instrumentation/nodejs_test.go @@ -55,7 +55,7 @@ func TestInjectNodeJSSDK(t *testing.T) { { Name: initContainerName, Image: "foo/bar:1", - Command: []string{"cp", "-R", "/autoinstrumentation/*", "/otel-auto-instrumentation/"}, + Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation/"}, VolumeMounts: []corev1.VolumeMount{{ Name: volumeName, MountPath: "/otel-auto-instrumentation", @@ -73,7 +73,7 @@ func TestInjectNodeJSSDK(t *testing.T) { Env: []corev1.EnvVar{ { Name: "NODE_OPTIONS", - Value: javaJVMArgument, + Value: nodeRequireArgument, }, }, }, @@ -112,7 +112,7 @@ func TestInjectNodeJSSDK(t *testing.T) { { Name: initContainerName, Image: "foo/bar:1", - Command: []string{"cp", "-R", "/autoinstrumentation/*", "/otel-auto-instrumentation/"}, + Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation/"}, VolumeMounts: []corev1.VolumeMount{{ Name: volumeName, MountPath: "/otel-auto-instrumentation", @@ -130,7 +130,7 @@ func TestInjectNodeJSSDK(t *testing.T) { Env: []corev1.EnvVar{ { Name: "NODE_OPTIONS", - Value: "-Dbaz=bar" + javaJVMArgument, + Value: "-Dbaz=bar" + nodeRequireArgument, }, }, }, diff --git a/pkg/instrumentation/podmutator_test.go b/pkg/instrumentation/podmutator_test.go index e80b94d49b..e21a9fc743 100644 --- a/pkg/instrumentation/podmutator_test.go +++ b/pkg/instrumentation/podmutator_test.go @@ -186,7 +186,7 @@ func TestMutatePod(t *testing.T) { Namespace: "nodejs", }, Spec: v1alpha1.InstrumentationSpec{ - Java: v1alpha1.JavaSpec{ + NodeJS: v1alpha1.NodeJSSpec{ Image: "otel/nodejs:1", }, Exporter: v1alpha1.Exporter{ @@ -228,7 +228,7 @@ func TestMutatePod(t *testing.T) { InitContainers: []corev1.Container{ { Name: initContainerName, - Image: "otel/java:1", + Image: "otel/nodejs:1", Command: []string{"cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation/"}, VolumeMounts: []corev1.VolumeMount{{ Name: volumeName, From ae74544f056894732f79f61ada3210d28df56467 Mon Sep 17 00:00:00 2001 From: Anuraag Agrawal Date: Tue, 9 Nov 2021 17:45:31 +0900 Subject: [PATCH 03/16] Switch to single annotation --- pkg/instrumentation/annotation.go | 4 +- pkg/instrumentation/annotation_test.go | 22 ++++---- pkg/instrumentation/podmutator.go | 75 +++++++++++++------------- pkg/instrumentation/podmutator_test.go | 20 +++---- pkg/instrumentation/sdk.go | 13 +++-- pkg/instrumentation/sdk_test.go | 14 +++-- 6 files changed, 76 insertions(+), 72 deletions(-) diff --git a/pkg/instrumentation/annotation.go b/pkg/instrumentation/annotation.go index 76884a5dcb..6cc252a035 100644 --- a/pkg/instrumentation/annotation.go +++ b/pkg/instrumentation/annotation.go @@ -23,8 +23,8 @@ import ( const ( // annotationInjectJava indicates whether java auto-instrumentation should be injected or not. // Possible values are "true", "false" or "" name. - annotationInject = "instrumentation.opentelemetry.io/inject" - annotationLanguage = "instrumentation.opentelemetry.io/language" + annotationInjectJava = "instrumentation.opentelemetry.io/inject-java" + annotationInjectNodeJS = "instrumentation.opentelemetry.io/inject-nodejs" ) // annotationValue returns the effective annotationInjectJava value, based on the annotations from the pod and namespace. diff --git a/pkg/instrumentation/annotation_test.go b/pkg/instrumentation/annotation_test.go index 35835982ff..511a71adea 100644 --- a/pkg/instrumentation/annotation_test.go +++ b/pkg/instrumentation/annotation_test.go @@ -35,14 +35,14 @@ func TestEffectiveAnnotationValue(t *testing.T) { corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInject: "true", + annotationInjectJava: "true", }, }, }, corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInject: "false", + annotationInjectJava: "false", }, }, }, @@ -54,14 +54,14 @@ func TestEffectiveAnnotationValue(t *testing.T) { corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInject: "true", + annotationInjectJava: "true", }, }, }, corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInject: "some-instance", + annotationInjectJava: "some-instance", }, }, }, @@ -73,14 +73,14 @@ func TestEffectiveAnnotationValue(t *testing.T) { corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInject: "some-instance-from-pod", + annotationInjectJava: "some-instance-from-pod", }, }, }, corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInject: "some-instance", + annotationInjectJava: "some-instance", }, }, }, @@ -92,14 +92,14 @@ func TestEffectiveAnnotationValue(t *testing.T) { corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInject: "false", + annotationInjectJava: "false", }, }, }, corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInject: "some-instance", + annotationInjectJava: "some-instance", }, }, }, @@ -112,7 +112,7 @@ func TestEffectiveAnnotationValue(t *testing.T) { corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInject: "some-instance", + annotationInjectJava: "some-instance", }, }, }, @@ -124,7 +124,7 @@ func TestEffectiveAnnotationValue(t *testing.T) { corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInject: "true", + annotationInjectJava: "true", }, }, }, @@ -133,7 +133,7 @@ func TestEffectiveAnnotationValue(t *testing.T) { } { t.Run(tt.desc, func(t *testing.T) { // test - annValue := annotationValue(tt.ns.ObjectMeta, tt.pod.ObjectMeta, annotationInject) + annValue := annotationValue(tt.ns.ObjectMeta, tt.pod.ObjectMeta, annotationInjectJava) // verify assert.Equal(t, tt.expected, annValue) diff --git a/pkg/instrumentation/podmutator.go b/pkg/instrumentation/podmutator.go index b4968448b2..ec66403241 100644 --- a/pkg/instrumentation/podmutator.go +++ b/pkg/instrumentation/podmutator.go @@ -17,7 +17,6 @@ package instrumentation import ( "context" "errors" - "fmt" "strings" "github.com/go-logr/logr" @@ -32,8 +31,6 @@ import ( var ( errMultipleInstancesPossible = errors.New("multiple OpenTelemetry Instrumentation instances available, cannot determine which one to select") errNoInstancesAvailable = errors.New("no OpenTelemetry Instrumentation instances available") - errNoLanguageSpecified = fmt.Errorf("%s must be set to the desired SDK language", annotationLanguage) - errUnsupportedLanguage = errors.New("SDK language not supported, supported languages are (java, nodejs)") ) type instPodMutator struct { @@ -41,6 +38,11 @@ type instPodMutator struct { Client client.Client } +type languageInstrumentations struct { + Java *v1alpha1.Instrumentation + NodeJS *v1alpha1.Instrumentation +} + var _ webhookhandler.PodMutator = (*instPodMutator)(nil) func NewMutator(logger logr.Logger, client client.Client) *instPodMutator { @@ -53,74 +55,69 @@ func NewMutator(logger logr.Logger, client client.Client) *instPodMutator { func (pm *instPodMutator) Mutate(ctx context.Context, ns corev1.Namespace, pod corev1.Pod) (corev1.Pod, error) { logger := pm.Logger.WithValues("namespace", pod.Namespace, "name", pod.Name) - // if no annotations are found at all, just return the same pod - instValue := annotationValue(ns.ObjectMeta, pod.ObjectMeta, annotationInject) - if len(instValue) == 0 { - logger.V(1).Info("annotation not present in deployment, skipping instrumentation injection") + var inst *v1alpha1.Instrumentation + var err error + + insts := languageInstrumentations{} + + // We bail out if any annotation fails to process. + + if inst, err = pm.getInstrumentationInstance(ctx, ns, pod, annotationInjectJava); 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, nil } + insts.Java = inst - // is the annotation value 'false'? if so, we need a pod without the instrumentation - if strings.EqualFold(instValue, "false") { - logger.V(1).Info("pod explicitly refuses instrumentation injection, attempting to remove instrumentation if it exists") + if inst, err = pm.getInstrumentationInstance(ctx, ns, pod, annotationInjectNodeJS); 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, nil } + insts.NodeJS = inst - langValue := annotationValue(ns.ObjectMeta, pod.ObjectMeta, annotationLanguage) - - // which instance should it talk to? - otelinst, err := pm.getInstrumentationInstance(ctx, ns, instValue, langValue) - if err != nil { - if err == errNoInstancesAvailable || err == errMultipleInstancesPossible { - // 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, nil - } - - // something else happened, better fail here - return pod, err + if insts.Java == nil && insts.NodeJS == nil { + logger.V(1).Info("annotation not present in deployment, skipping instrumentation injection") + return pod, nil } // once it's been determined that instrumentation is desired, none exists yet, and we know which instance it should talk to, // we should inject the instrumentation. - logger.V(1).Info("injecting instrumentation into pod", "otelinst-namespace", otelinst.Namespace, "otelinst-name", otelinst.Name) - return inject(pm.Logger, otelinst, pod, langValue), nil + return inject(pm.Logger, insts, pod), nil } -func (pm *instPodMutator) getInstrumentationInstance(ctx context.Context, ns corev1.Namespace, instValue string, langValue string) (v1alpha1.Instrumentation, error) { - otelInst := v1alpha1.Instrumentation{} +func (pm *instPodMutator) getInstrumentationInstance(ctx context.Context, ns corev1.Namespace, pod corev1.Pod, instAnnotation string) (*v1alpha1.Instrumentation, error) { + instValue := annotationValue(ns.ObjectMeta, pod.ObjectMeta, instAnnotation) - if len(langValue) == 0 { - return otelInst, errNoLanguageSpecified - } - if langValue != "java" && langValue != "nodejs" { - return otelInst, errUnsupportedLanguage + if strings.EqualFold(instValue, "false") { + return nil, nil } if strings.EqualFold(instValue, "true") { return pm.selectInstrumentationInstanceFromNamespace(ctx, ns) } - err := pm.Client.Get(ctx, types.NamespacedName{Name: instValue, Namespace: ns.Name}, &otelInst) + otelInst := &v1alpha1.Instrumentation{} + err := pm.Client.Get(ctx, types.NamespacedName{Name: instValue, Namespace: ns.Name}, otelInst) if err != nil { - return otelInst, err + return nil, err } return otelInst, nil } -func (pm *instPodMutator) selectInstrumentationInstanceFromNamespace(ctx context.Context, ns corev1.Namespace) (v1alpha1.Instrumentation, error) { +func (pm *instPodMutator) selectInstrumentationInstanceFromNamespace(ctx context.Context, ns corev1.Namespace) (*v1alpha1.Instrumentation, error) { var otelInsts v1alpha1.InstrumentationList if err := pm.Client.List(ctx, &otelInsts, client.InNamespace(ns.Name)); err != nil { - return v1alpha1.Instrumentation{}, err + return nil, err } switch s := len(otelInsts.Items); { case s == 0: - return v1alpha1.Instrumentation{}, errNoInstancesAvailable + return nil, errNoInstancesAvailable case s > 1: - return v1alpha1.Instrumentation{}, errMultipleInstancesPossible + return nil, errMultipleInstancesPossible default: - return otelInsts.Items[0], nil + return &otelInsts.Items[0], nil } } diff --git a/pkg/instrumentation/podmutator_test.go b/pkg/instrumentation/podmutator_test.go index 6977447fb6..251ea38661 100644 --- a/pkg/instrumentation/podmutator_test.go +++ b/pkg/instrumentation/podmutator_test.go @@ -106,8 +106,7 @@ func TestMutatePod(t *testing.T) { pod: corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInject: "true", - annotationLanguage: "java", + annotationInjectJava: "true", }, }, Spec: corev1.PodSpec{ @@ -121,8 +120,7 @@ func TestMutatePod(t *testing.T) { expected: corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInject: "true", - annotationLanguage: "java", + annotationInjectJava: "true", }, }, Spec: corev1.PodSpec{ @@ -197,8 +195,7 @@ func TestMutatePod(t *testing.T) { pod: corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInject: "true", - annotationLanguage: "nodejs", + annotationInjectNodeJS: "true", }, }, Spec: corev1.PodSpec{ @@ -212,8 +209,7 @@ func TestMutatePod(t *testing.T) { expected: corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInject: "true", - annotationLanguage: "nodejs", + annotationInjectNodeJS: "true", }, }, Spec: corev1.PodSpec{ @@ -288,7 +284,7 @@ func TestMutatePod(t *testing.T) { pod: corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInject: "true", + annotationInjectJava: "true", }, }, Spec: corev1.PodSpec{ @@ -373,7 +369,7 @@ func TestMutatePod(t *testing.T) { pod: corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInject: "false", + annotationInjectJava: "false", }, }, Spec: corev1.PodSpec{ @@ -387,7 +383,7 @@ func TestMutatePod(t *testing.T) { expected: corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInject: "false", + annotationInjectJava: "false", }, }, Spec: corev1.PodSpec{ @@ -423,7 +419,7 @@ func TestMutatePod(t *testing.T) { pod: corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ - annotationInject: "doesnotexists", + annotationInjectJava: "doesnotexists", }, }, Spec: corev1.PodSpec{ diff --git a/pkg/instrumentation/sdk.go b/pkg/instrumentation/sdk.go index 271874bd3c..ea0831879f 100644 --- a/pkg/instrumentation/sdk.go +++ b/pkg/instrumentation/sdk.go @@ -30,18 +30,23 @@ const ( ) // inject a new sidecar container to the given pod, based on the given OpenTelemetryCollector. -func inject(logger logr.Logger, otelinst v1alpha1.Instrumentation, pod corev1.Pod, language string) corev1.Pod { +func inject(logger logr.Logger, insts languageInstrumentations, pod corev1.Pod) corev1.Pod { if len(pod.Spec.Containers) < 1 { return pod } // inject only to the first container for now // in the future we can define an annotation to configure this - pod = injectCommonSDKConfig(otelinst, pod) - if language == "java" { + if insts.Java != nil { + otelinst := *insts.Java + logger.V(1).Info("injecting instrumentation into pod", "otelinst-namespace", otelinst.Namespace, "otelinst-name", otelinst.Name) + pod = injectCommonSDKConfig(otelinst, pod) pod = injectJavaagent(logger, otelinst.Spec.Java, pod) } - if language == "nodejs" { + if insts.NodeJS != nil { + otelinst := *insts.NodeJS + logger.V(1).Info("injecting instrumentation into pod", "otelinst-namespace", otelinst.Namespace, "otelinst-name", otelinst.Name) + pod = injectCommonSDKConfig(otelinst, pod) pod = injectNodeJSSDK(logger, otelinst.Spec.NodeJS, pod) } return pod diff --git a/pkg/instrumentation/sdk_test.go b/pkg/instrumentation/sdk_test.go index 54f6b637a8..17d548a39a 100644 --- a/pkg/instrumentation/sdk_test.go +++ b/pkg/instrumentation/sdk_test.go @@ -136,7 +136,10 @@ func TestInjectJava(t *testing.T) { }, }, } - pod := inject(logr.Discard(), inst, corev1.Pod{ + insts := languageInstrumentations{ + Java: &inst, + } + pod := inject(logr.Discard(), insts, corev1.Pod{ Spec: corev1.PodSpec{ Containers: []corev1.Container{ { @@ -144,7 +147,7 @@ func TestInjectJava(t *testing.T) { }, }, }, - }, "java") + }) assert.Equal(t, corev1.Pod{ Spec: corev1.PodSpec{ Volumes: []corev1.Volume{ @@ -206,7 +209,10 @@ func TestInjectNodeJS(t *testing.T) { }, }, } - pod := inject(logr.Discard(), inst, corev1.Pod{ + insts := languageInstrumentations{ + NodeJS: &inst, + } + pod := inject(logr.Discard(), insts, corev1.Pod{ Spec: corev1.PodSpec{ Containers: []corev1.Container{ { @@ -214,7 +220,7 @@ func TestInjectNodeJS(t *testing.T) { }, }, }, - }, "nodejs") + }) assert.Equal(t, corev1.Pod{ Spec: corev1.PodSpec{ Volumes: []corev1.Volume{ From 3c5c95e2e038be576a2f02e6504df0f05465880d Mon Sep 17 00:00:00 2001 From: Anuraag Agrawal Date: Tue, 9 Nov 2021 17:46:27 +0900 Subject: [PATCH 04/16] Fix merge --- pkg/instrumentation/nodejs.go | 2 +- pkg/instrumentation/nodejs_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/instrumentation/nodejs.go b/pkg/instrumentation/nodejs.go index 308fc73344..86a2cc20cc 100644 --- a/pkg/instrumentation/nodejs.go +++ b/pkg/instrumentation/nodejs.go @@ -18,7 +18,7 @@ import ( "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" - "github.com/open-telemetry/opentelemetry-operator/api/instrumentation/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/apis/instrumentation/v1alpha1" ) const ( diff --git a/pkg/instrumentation/nodejs_test.go b/pkg/instrumentation/nodejs_test.go index 8516036fd5..37722ae32a 100644 --- a/pkg/instrumentation/nodejs_test.go +++ b/pkg/instrumentation/nodejs_test.go @@ -21,7 +21,7 @@ import ( "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" - "github.com/open-telemetry/opentelemetry-operator/api/instrumentation/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/apis/instrumentation/v1alpha1" ) func TestInjectNodeJSSDK(t *testing.T) { From 21d5433e75d74198905645e9b06169a0ac29b294 Mon Sep 17 00:00:00 2001 From: Anuraag Agrawal Date: Tue, 9 Nov 2021 18:04:30 +0900 Subject: [PATCH 05/16] Fix --- ...emetry-operator.clusterserviceversion.yaml | 2 +- pkg/instrumentation/podmutator.go | 6 +-- pkg/instrumentation/podmutator_test.go | 45 ------------------- 3 files changed, 4 insertions(+), 49 deletions(-) diff --git a/bundle/manifests/opentelemetry-operator.clusterserviceversion.yaml b/bundle/manifests/opentelemetry-operator.clusterserviceversion.yaml index 8155ccec6a..aba0818095 100644 --- a/bundle/manifests/opentelemetry-operator.clusterserviceversion.yaml +++ b/bundle/manifests/opentelemetry-operator.clusterserviceversion.yaml @@ -21,7 +21,7 @@ metadata: containerImage: quay.io/opentelemetry/opentelemetry-operator createdAt: "2020-12-16T13:37:00+00:00" description: Provides the OpenTelemetry components, including the Collector - operators.operatorframework.io/builder: operator-sdk-v1.13.0+git + operators.operatorframework.io/builder: operator-sdk-v1.14.0 operators.operatorframework.io/project_layout: go.kubebuilder.io/v2 repository: github.com/open-telemetry/opentelemetry-operator support: OpenTelemetry Community diff --git a/pkg/instrumentation/podmutator.go b/pkg/instrumentation/podmutator.go index ec66403241..9a26cb63c6 100644 --- a/pkg/instrumentation/podmutator.go +++ b/pkg/instrumentation/podmutator.go @@ -65,14 +65,14 @@ func (pm *instPodMutator) Mutate(ctx context.Context, ns corev1.Namespace, pod c if inst, err = pm.getInstrumentationInstance(ctx, ns, pod, annotationInjectJava); 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, nil + return pod, err } insts.Java = inst if inst, err = pm.getInstrumentationInstance(ctx, ns, pod, annotationInjectNodeJS); 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, nil + return pod, err } insts.NodeJS = inst @@ -89,7 +89,7 @@ func (pm *instPodMutator) Mutate(ctx context.Context, ns corev1.Namespace, pod c func (pm *instPodMutator) getInstrumentationInstance(ctx context.Context, ns corev1.Namespace, pod corev1.Pod, instAnnotation string) (*v1alpha1.Instrumentation, error) { instValue := annotationValue(ns.ObjectMeta, pod.ObjectMeta, instAnnotation) - if strings.EqualFold(instValue, "false") { + if len(instValue) == 0 || strings.EqualFold(instValue, "false") { return nil, nil } diff --git a/pkg/instrumentation/podmutator_test.go b/pkg/instrumentation/podmutator_test.go index 251ea38661..39b50e3097 100644 --- a/pkg/instrumentation/podmutator_test.go +++ b/pkg/instrumentation/podmutator_test.go @@ -260,51 +260,6 @@ func TestMutatePod(t *testing.T) { }, }, }, - { - name: "missing language", - ns: corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "missing-language", - }, - }, - inst: v1alpha1.Instrumentation{ - ObjectMeta: metav1.ObjectMeta{ - Name: "example-inst", - Namespace: "missing-language", - }, - Spec: v1alpha1.InstrumentationSpec{ - Java: v1alpha1.JavaSpec{ - Image: "otel/java:1", - }, - Exporter: v1alpha1.Exporter{ - Endpoint: "http://collector:12345", - }, - }, - }, - pod: corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{ - annotationInjectJava: "true", - }, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "app", - }, - }, - }, - }, - expected: corev1.Pod{ - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "app", - }, - }, - }, - }, - }, { name: "missing annotation", ns: corev1.Namespace{ From 9b7a81e95eb26358c544974e4a0df0eb08d4521e Mon Sep 17 00:00:00 2001 From: Anuraag Agrawal Date: Tue, 9 Nov 2021 18:08:37 +0900 Subject: [PATCH 06/16] Hurts --- .../manifests/opentelemetry-operator.clusterserviceversion.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundle/manifests/opentelemetry-operator.clusterserviceversion.yaml b/bundle/manifests/opentelemetry-operator.clusterserviceversion.yaml index aba0818095..8155ccec6a 100644 --- a/bundle/manifests/opentelemetry-operator.clusterserviceversion.yaml +++ b/bundle/manifests/opentelemetry-operator.clusterserviceversion.yaml @@ -21,7 +21,7 @@ metadata: containerImage: quay.io/opentelemetry/opentelemetry-operator createdAt: "2020-12-16T13:37:00+00:00" description: Provides the OpenTelemetry components, including the Collector - operators.operatorframework.io/builder: operator-sdk-v1.14.0 + operators.operatorframework.io/builder: operator-sdk-v1.13.0+git operators.operatorframework.io/project_layout: go.kubebuilder.io/v2 repository: github.com/open-telemetry/opentelemetry-operator support: OpenTelemetry Community From 7cc5182b76a91740fb06c3fc95cb81e3cfc21ca2 Mon Sep 17 00:00:00 2001 From: Anuraag Agrawal Date: Tue, 9 Nov 2021 18:19:39 +0900 Subject: [PATCH 07/16] Format --- pkg/instrumentation/sdk_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/instrumentation/sdk_test.go b/pkg/instrumentation/sdk_test.go index 98caaa7e11..3026ddbe49 100644 --- a/pkg/instrumentation/sdk_test.go +++ b/pkg/instrumentation/sdk_test.go @@ -253,14 +253,14 @@ func TestInjectNodeJS(t *testing.T) { pod := inject(logr.Discard(), insts, corev1.Namespace{}, corev1.Pod{ - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "app", + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + }, }, }, - }, - }) + }) assert.Equal(t, corev1.Pod{ Spec: corev1.PodSpec{ Volumes: []corev1.Volume{ From fb2358831f9ddd13281429286058029faf981578 Mon Sep 17 00:00:00 2001 From: Anuraag Agrawal Date: Tue, 9 Nov 2021 18:21:03 +0900 Subject: [PATCH 08/16] Revert autogen --- autoinstrumentation/ruby/Dockerfile | 12 ++++++++++++ autoinstrumentation/ruby/Gemfile | 4 ++++ ...opentelemetry-operator.clusterserviceversion.yaml | 8 ++++---- config/manager/kustomization.yaml | 6 ------ 4 files changed, 20 insertions(+), 10 deletions(-) create mode 100644 autoinstrumentation/ruby/Dockerfile create mode 100644 autoinstrumentation/ruby/Gemfile diff --git a/autoinstrumentation/ruby/Dockerfile b/autoinstrumentation/ruby/Dockerfile new file mode 100644 index 0000000000..db4e6641b4 --- /dev/null +++ b/autoinstrumentation/ruby/Dockerfile @@ -0,0 +1,12 @@ +FROM ruby:3-slim AS build + +WORKDIR /operator-build + +COPY . . + +RUN bundle install --path vendor/bundle + +FROM busybox + +COPY --from=build /operator-build /autoinstrumentation + diff --git a/autoinstrumentation/ruby/Gemfile b/autoinstrumentation/ruby/Gemfile new file mode 100644 index 0000000000..ab519d431a --- /dev/null +++ b/autoinstrumentation/ruby/Gemfile @@ -0,0 +1,4 @@ +gem 'opentelemetry-sdk' +gem 'opentelemetry-exporter-otlp' +gem 'opentelemetry-instrumentation-all' + diff --git a/bundle/manifests/opentelemetry-operator.clusterserviceversion.yaml b/bundle/manifests/opentelemetry-operator.clusterserviceversion.yaml index 1256af1d3b..137ef5439b 100644 --- a/bundle/manifests/opentelemetry-operator.clusterserviceversion.yaml +++ b/bundle/manifests/opentelemetry-operator.clusterserviceversion.yaml @@ -33,11 +33,11 @@ metadata: containerImage: quay.io/opentelemetry/opentelemetry-operator createdAt: "2020-12-16T13:37:00+00:00" description: Provides the OpenTelemetry components, including the Collector - operators.operatorframework.io/builder: operator-sdk-v1.14.0 + operators.operatorframework.io/builder: operator-sdk-v1.13.0+git operators.operatorframework.io/project_layout: go.kubebuilder.io/v2 repository: github.com/open-telemetry/opentelemetry-operator support: OpenTelemetry Community - name: opentelemetry-operator.v0.33.0-65-g9b7a81e + name: opentelemetry-operator.v0.38.0 namespace: placeholder spec: apiservicedefinitions: {} @@ -227,7 +227,7 @@ spec: - args: - --metrics-addr=127.0.0.1:8080 - --enable-leader-election - image: quay.io/aanuraag/opentelemetry-operator:v0.33.0-65-g9b7a81e + image: quay.io/opentelemetry/opentelemetry-operator:v0.38.0 name: manager ports: - containerPort: 9443 @@ -319,7 +319,7 @@ spec: maturity: alpha provider: name: OpenTelemetry Community - version: 0.33.0-65-g9b7a81e + version: 0.38.0 webhookdefinitions: - admissionReviewVersions: - v1 diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 6f3b5d4af3..5c5f0b84cb 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -1,8 +1,2 @@ resources: - manager.yaml -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -images: -- name: controller - newName: quay.io/aanuraag/opentelemetry-operator - newTag: v0.33.0-65-g9b7a81e From a302e31f6cb939e15af1d9620770fcb3def82639 Mon Sep 17 00:00:00 2001 From: Anuraag Agrawal Date: Tue, 9 Nov 2021 20:12:36 +0900 Subject: [PATCH 09/16] Drift --- pkg/instrumentation/podmutator_test.go | 4 ++++ pkg/instrumentation/sdk_test.go | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/pkg/instrumentation/podmutator_test.go b/pkg/instrumentation/podmutator_test.go index d0f8636b02..247beec783 100644 --- a/pkg/instrumentation/podmutator_test.go +++ b/pkg/instrumentation/podmutator_test.go @@ -248,6 +248,10 @@ func TestMutatePod(t *testing.T) { Name: "OTEL_EXPORTER_OTLP_ENDPOINT", Value: "http://collector:12345", }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=app,k8s.namespace.name=nodejs", + }, { Name: "NODE_OPTIONS", Value: nodeRequireArgument, diff --git a/pkg/instrumentation/sdk_test.go b/pkg/instrumentation/sdk_test.go index 3026ddbe49..3e37d18102 100644 --- a/pkg/instrumentation/sdk_test.go +++ b/pkg/instrumentation/sdk_test.go @@ -300,6 +300,10 @@ func TestInjectNodeJS(t *testing.T) { Name: "OTEL_EXPORTER_OTLP_ENDPOINT", Value: "https://collector:4318", }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=app,k8s.namespace.name=", + }, { Name: "NODE_OPTIONS", Value: nodeRequireArgument, From 293a8e084c7c0a3a720b3d33fb7aa95bb2188da1 Mon Sep 17 00:00:00 2001 From: Anuraag Agrawal Date: Wed, 10 Nov 2021 12:20:10 +0900 Subject: [PATCH 10/16] Revert autogen --- .../opentelemetry-operator.clusterserviceversion.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bundle/manifests/opentelemetry-operator.clusterserviceversion.yaml b/bundle/manifests/opentelemetry-operator.clusterserviceversion.yaml index 8bafc0dc33..137ef5439b 100644 --- a/bundle/manifests/opentelemetry-operator.clusterserviceversion.yaml +++ b/bundle/manifests/opentelemetry-operator.clusterserviceversion.yaml @@ -33,11 +33,11 @@ metadata: containerImage: quay.io/opentelemetry/opentelemetry-operator createdAt: "2020-12-16T13:37:00+00:00" description: Provides the OpenTelemetry components, including the Collector - operators.operatorframework.io/builder: operator-sdk-v1.14.0 + operators.operatorframework.io/builder: operator-sdk-v1.13.0+git operators.operatorframework.io/project_layout: go.kubebuilder.io/v2 repository: github.com/open-telemetry/opentelemetry-operator support: OpenTelemetry Community - name: opentelemetry-operator.v0.33.0-71-ga302e31 + name: opentelemetry-operator.v0.38.0 namespace: placeholder spec: apiservicedefinitions: {} @@ -227,7 +227,7 @@ spec: - args: - --metrics-addr=127.0.0.1:8080 - --enable-leader-election - image: quay.io/aanuraag/opentelemetry-operator:v0.33.0-71-ga302e31 + image: quay.io/opentelemetry/opentelemetry-operator:v0.38.0 name: manager ports: - containerPort: 9443 @@ -319,7 +319,7 @@ spec: maturity: alpha provider: name: OpenTelemetry Community - version: 0.33.0-71-ga302e31 + version: 0.38.0 webhookdefinitions: - admissionReviewVersions: - v1 From b02d1d9378fcb0f828f8fec2278d171ac3021c20 Mon Sep 17 00:00:00 2001 From: Anuraag Agrawal Date: Wed, 10 Nov 2021 15:37:53 +0900 Subject: [PATCH 11/16] autogen --- bundle/manifests/opentelemetry.io_instrumentations.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/bundle/manifests/opentelemetry.io_instrumentations.yaml b/bundle/manifests/opentelemetry.io_instrumentations.yaml index cc75190693..ce64147287 100644 --- a/bundle/manifests/opentelemetry.io_instrumentations.yaml +++ b/bundle/manifests/opentelemetry.io_instrumentations.yaml @@ -57,6 +57,13 @@ spec: description: Image is a container image with javaagent JAR. type: string type: object + nodejs: + description: NodeJS defines configuration for nodejs auto-instrumentation. + properties: + image: + description: Image is a container image with NodeJS SDK and autoinstrumentation. + type: string + type: object propagators: description: Propagators defines inter-process context propagation configuration. From 0b72fa3cce55a1b2b29611bfa90e4ac8b687f44a Mon Sep 17 00:00:00 2001 From: Anuraag Agrawal Date: Wed, 10 Nov 2021 15:42:27 +0900 Subject: [PATCH 12/16] Drift --- autoinstrumentation/ruby/Dockerfile | 12 ------------ autoinstrumentation/ruby/Gemfile | 4 ---- config/manager/kustomization.yaml | 2 +- 3 files changed, 1 insertion(+), 17 deletions(-) delete mode 100644 autoinstrumentation/ruby/Dockerfile delete mode 100644 autoinstrumentation/ruby/Gemfile diff --git a/autoinstrumentation/ruby/Dockerfile b/autoinstrumentation/ruby/Dockerfile deleted file mode 100644 index db4e6641b4..0000000000 --- a/autoinstrumentation/ruby/Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM ruby:3-slim AS build - -WORKDIR /operator-build - -COPY . . - -RUN bundle install --path vendor/bundle - -FROM busybox - -COPY --from=build /operator-build /autoinstrumentation - diff --git a/autoinstrumentation/ruby/Gemfile b/autoinstrumentation/ruby/Gemfile deleted file mode 100644 index ab519d431a..0000000000 --- a/autoinstrumentation/ruby/Gemfile +++ /dev/null @@ -1,4 +0,0 @@ -gem 'opentelemetry-sdk' -gem 'opentelemetry-exporter-otlp' -gem 'opentelemetry-instrumentation-all' - diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 5c5f0b84cb..7394a6d059 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -1,2 +1,2 @@ resources: -- manager.yaml + - manager.yaml From 50785ae88733c491f1be9890f41d9b1d1bc43b61 Mon Sep 17 00:00:00 2001 From: Anuraag Agrawal Date: Wed, 10 Nov 2021 16:01:42 +0900 Subject: [PATCH 13/16] Add doc --- README.md | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 66b71ed837..27850c6e3d 100644 --- a/README.md +++ b/README.md @@ -156,8 +156,11 @@ EOF ### OpenTelemetry auto-instrumentation injection -The operator can inject and configure OpenTelemetry auto-instrumentation libraries. At this moment, the operator can inject only OpenTelemetry [Java auto-instrumentation](https://github.com/open-telemetry/opentelemetry-java-instrumentation). +The operator can inject and configure OpenTelemetry auto-instrumentation libraries. +#### Java + +The operator can inject OpenTelemetry [Java auto-instrumentation](https://github.com/open-telemetry/opentelemetry-java-instrumentation). The injection of the Java agent can be enabled by adding an annotation to the namespace, so that all pods within that namespace will get the instrumentation, or by adding the annotation to individual PodSpec objects, available as part of Deployment, Statefulset, and other resources. ```bash @@ -193,6 +196,42 @@ EOF The above CR can be queried by `kubectl get otelinst`. +#### NodeJS + +The operator can inject OpenTelemetry [NodeJS auto-instrumentation](https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/metapackages/auto-instrumentations-node). +The injection of the NodeJS instrumentation can be enabled by adding an annotation to the namespace, so that all pods within that namespace will get the instrumentation, or by adding the annotation to individual PodSpec objects, available as part of Deployment, Statefulset, and other resources. + +```bash +instrumentation.opentelemetry.io/inject-nodejs: "true" +``` + +The value can be +* `"false"` - do not inject +* `"true"` - inject and `Instrumentation` resource from the namespace. +* `"nodejs-instrumentation"` - name of `Instrumentation` CR instance. + +In addition to the annotation, the following `CR` has to be created. The `Instrumentation` resource provides configuration for OpenTelemetry SDK and auto-instrumentation. + +```yaml +kubectl apply -f - < Date: Wed, 10 Nov 2021 16:03:48 +0900 Subject: [PATCH 14/16] e2e --- .../00-install-collector.yaml | 25 ++++++++++++++++ .../00-install-instrumentation.yaml | 12 ++++++++ .../e2e/instrumentation-nodejs/01-assert.yaml | 30 +++++++++++++++++++ .../01-install-app.yaml | 20 +++++++++++++ 4 files changed, 87 insertions(+) create mode 100644 tests/e2e/instrumentation-nodejs/00-install-collector.yaml create mode 100644 tests/e2e/instrumentation-nodejs/00-install-instrumentation.yaml create mode 100644 tests/e2e/instrumentation-nodejs/01-assert.yaml create mode 100644 tests/e2e/instrumentation-nodejs/01-install-app.yaml diff --git a/tests/e2e/instrumentation-nodejs/00-install-collector.yaml b/tests/e2e/instrumentation-nodejs/00-install-collector.yaml new file mode 100644 index 0000000000..b823086361 --- /dev/null +++ b/tests/e2e/instrumentation-nodejs/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-nodejs/00-install-instrumentation.yaml b/tests/e2e/instrumentation-nodejs/00-install-instrumentation.yaml new file mode 100644 index 0000000000..a17642f744 --- /dev/null +++ b/tests/e2e/instrumentation-nodejs/00-install-instrumentation.yaml @@ -0,0 +1,12 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: Instrumentation +metadata: + name: nodejs +spec: + exporter: + endpoint: http://localhost:4317 + propagators: + - jaeger + - b3 + nodejs: + image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-nodejs:latest diff --git a/tests/e2e/instrumentation-nodejs/01-assert.yaml b/tests/e2e/instrumentation-nodejs/01-assert.yaml new file mode 100644 index 0000000000..db5ff83555 --- /dev/null +++ b/tests/e2e/instrumentation-nodejs/01-assert.yaml @@ -0,0 +1,30 @@ +apiVersion: v1 +kind: Pod +metadata: + annotations: + sidecar.opentelemetry.io/inject: "true" + instrumentation.opentelemetry.io/inject-nodejs: "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:4317 + - name: OTEL_RESOURCE_ATTRIBUTES + - name: OTEL_PROPAGATORS + value: jaeger,b3 + - name: NODE_OPTIONS + value: " --require /otel-auto-instrumentation/autoinstrumentation.js" + 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-nodejs/01-install-app.yaml b/tests/e2e/instrumentation-nodejs/01-install-app.yaml new file mode 100644 index 0000000000..7514b59866 --- /dev/null +++ b/tests/e2e/instrumentation-nodejs/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-nodejs: "true" + spec: + containers: + - name: myapp + image: ghcr.io/anuraaga/express-hello-world:latest From d51b0aac92077985fd28d4f64cd893898741acc9 Mon Sep 17 00:00:00 2001 From: Anuraag Agrawal Date: Wed, 10 Nov 2021 19:54:46 +0900 Subject: [PATCH 15/16] Less doc --- README.md | 64 +++++++++++++++++++------------------------------------ 1 file changed, 22 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 94f1e6466f..b4ef6a2621 100644 --- a/README.md +++ b/README.md @@ -155,30 +155,16 @@ EOF ### OpenTelemetry auto-instrumentation injection -The operator can inject and configure OpenTelemetry auto-instrumentation libraries. +The operator can inject and configure OpenTelemetry auto-instrumentation libraries. Currently Java and NodeJS are supported. -#### Java - -The operator can inject OpenTelemetry [Java auto-instrumentation](https://github.com/open-telemetry/opentelemetry-java-instrumentation). -The injection of the Java agent can be enabled by adding an annotation to the namespace, so that all pods within that namespace will get the instrumentation, or by adding the annotation to individual PodSpec objects, available as part of Deployment, Statefulset, and other resources. - -```bash -instrumentation.opentelemetry.io/inject-java: "true" -``` - -The value can be -* `"false"` - do not inject -* `"true"` - inject and `Instrumentation` resource from the namespace. -* `"java-instrumentation"` - name of `Instrumentation` CR instance. - -In addition to the annotation, the following `CR` has to be created. The `Instrumentation` resource provides configuration for OpenTelemetry SDK and auto-instrumentation. +To use auto-instrumentation, configure an `Instrumentation` resource with the configuration for the SDK and instrumentation. ```yaml kubectl apply -f - < -EOF + nodejs: + image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-nodejs:latest + EOF ``` +The above CR can be queried by `kubectl get otelinst`. + 1. Container image with [OpenTelemetry Java auto-instrumentation](https://github.com/open-telemetry/opentelemetry-java-instrumentation). The image must contain the Java agent JAR `/javaagent.jar`, and the operator will copy it to a shared volume mounted to the application container. -The above CR can be queried by `kubectl get otelinst`. +#### Java + +The operator can inject OpenTelemetry [Java auto-instrumentation](https://github.com/open-telemetry/opentelemetry-java-instrumentation). +The injection of the Java agent can be enabled by adding an annotation to the namespace, so that all pods within that namespace will get the instrumentation, or by adding the annotation to individual PodSpec objects, available as part of Deployment, Statefulset, and other resources. + +```bash +instrumentation.opentelemetry.io/inject-java: "true" +``` + +The value can be +* `"false"` - do not inject +* `"true"` - inject and `Instrumentation` resource from the namespace. +* `"my-instrumentation"` - name of `Instrumentation` CR instance. #### NodeJS @@ -210,29 +212,7 @@ instrumentation.opentelemetry.io/inject-nodejs: "true" The value can be * `"false"` - do not inject * `"true"` - inject and `Instrumentation` resource from the namespace. -* `"nodejs-instrumentation"` - name of `Instrumentation` CR instance. - -In addition to the annotation, the following `CR` has to be created. The `Instrumentation` resource provides configuration for OpenTelemetry SDK and auto-instrumentation. - -```yaml -kubectl apply -f - < Date: Wed, 10 Nov 2021 21:13:20 +0900 Subject: [PATCH 16/16] Cleanup --- README.md | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index b4ef6a2621..0e9d9ab108 100644 --- a/README.md +++ b/README.md @@ -176,43 +176,32 @@ spec: type: parentbased_traceidratio argument: "0.25" java: - image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-java:latest # <1> + image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-java:latest nodejs: image: ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-nodejs:latest - EOF +EOF ``` The above CR can be queried by `kubectl get otelinst`. -1. Container image with [OpenTelemetry Java auto-instrumentation](https://github.com/open-telemetry/opentelemetry-java-instrumentation). The image must contain the Java agent JAR `/javaagent.jar`, and the operator will copy it to a shared volume mounted to the application container. - -#### Java - -The operator can inject OpenTelemetry [Java auto-instrumentation](https://github.com/open-telemetry/opentelemetry-java-instrumentation). -The injection of the Java agent can be enabled by adding an annotation to the namespace, so that all pods within that namespace will get the instrumentation, or by adding the annotation to individual PodSpec objects, available as part of Deployment, Statefulset, and other resources. +Then add an annotation to a pod to enable injection. The annotation can be added to a namespace, so that all pods within +that namespace wil get instrumentation, or by adding the annotation to individual PodSpec objects, available as part of +Deployment, Statefulset, and other resources. +Java: ```bash instrumentation.opentelemetry.io/inject-java: "true" ``` -The value can be -* `"false"` - do not inject -* `"true"` - inject and `Instrumentation` resource from the namespace. -* `"my-instrumentation"` - name of `Instrumentation` CR instance. - -#### NodeJS - -The operator can inject OpenTelemetry [NodeJS auto-instrumentation](https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/metapackages/auto-instrumentations-node). -The injection of the NodeJS instrumentation can be enabled by adding an annotation to the namespace, so that all pods within that namespace will get the instrumentation, or by adding the annotation to individual PodSpec objects, available as part of Deployment, Statefulset, and other resources. - +NodeJS: ```bash instrumentation.opentelemetry.io/inject-nodejs: "true" ``` -The value can be -* `"false"` - do not inject +The possible values for the annotation can be * `"true"` - inject and `Instrumentation` resource from the namespace. * `"my-instrumentation"` - name of `Instrumentation` CR instance. +* `"false"` - do not inject ## Compatibility matrix