diff --git a/.github/workflows/operator-integration-test.yml b/.github/workflows/operator-integration-test.yml index 463e9d0c3..6027f21fe 100644 --- a/.github/workflows/operator-integration-test.yml +++ b/.github/workflows/operator-integration-test.yml @@ -127,6 +127,24 @@ jobs: go run integration-tests/manifests/cmd/validate_instrumentation_vars.go default integration-tests/manifests/cmd/ns_instrumentation_env_variables.json kubectl delete instrumentation sample-instrumentation + - name: Test for default instrumentation resources for nodejs + run: | + cat integration-tests/nodejs/sample-deployment-nodejs.yaml + kubectl apply -f integration-tests/nodejs/sample-deployment-nodejs.yaml + sleep 5 + kubectl wait --for=condition=Available deployment/nginx -n default + kubectl get pods -A + kubectl describe pods -n default + go run integration-tests/manifests/cmd/validate_instrumentation_vars.go default integration-tests/nodejs/default_instrumentation_nodejs_env_variables.json + + - name: Test for defined instrumentation resources for nodejs + run: | + kubectl apply -f integration-tests/manifests/sample-instrumentation.yaml + kubectl delete pods --all -n default + sleep 5 + kubectl wait --for=condition=Ready pod --all -n default + go run integration-tests/manifests/cmd/validate_instrumentation_vars.go default integration-tests/manifests/cmd/ns_instrumentation_env_variables.json + kubectl delete instrumentation sample-instrumentation - name: Test for default instrumentation resources for all languages run: | @@ -195,6 +213,8 @@ jobs: sleep 5 go test -v -run TestDotNetOnlyDeployment ./integration-tests/manifests/annotations -timeout 30m sleep 5 + go test -v -run TestNodeJSOnlyDeployment ./integration-tests/manifests/annotations -timeout 30m + sleep 5 go test -v -run TestAnnotationsOnMultipleResources ./integration-tests/manifests/annotations -timeout 30m DaemonsetAnnotationsTest: @@ -245,6 +265,8 @@ jobs: sleep 5 go test -v -run TestDotNetOnlyDaemonSet ./integration-tests/manifests/annotations -timeout 30m sleep 5 + go test -v -run TestNodeJSOnlyDaemonSet ./integration-tests/manifests/annotations -timeout 30m + sleep 5 go test -v -run TestAutoAnnotationForManualAnnotationRemoval ./integration-tests/manifests/annotations -timeout 30m StatefulsetAnnotationsTest: @@ -293,6 +315,10 @@ jobs: sleep 5 go test -v -run TestPythonOnlyStatefulSet ./integration-tests/manifests/annotations -timeout 30m sleep 5 + go test -v -run TestDotNetOnlyStatefulSet ./integration-tests/manifests/annotations -timeout 30m + sleep 5 + go test -v -run TestNodeJSOnlyStatefulSet ./integration-tests/manifests/annotations -timeout 30m + sleep 5 go test -v -run TestOnlyNonAnnotatedAppsShouldBeRestarted ./integration-tests/manifests/annotations -timeout 30m @@ -342,4 +368,6 @@ jobs: sleep 5 go test -v -run TestDotNetOnlyNamespace ./integration-tests/manifests/annotations -timeout 30m sleep 5 + go test -v -run TestNodeJSOnlyNamespace ./integration-tests/manifests/annotations -timeout 30m + sleep 5 go test -v -run TestAlreadyAutoAnnotatedResourceShouldNotRestart ./integration-tests/manifests/annotations -timeout 30m diff --git a/Dockerfile b/Dockerfile index 57e479d38..ba9d1a49d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,6 +26,7 @@ ARG VERSION_DATE ARG AGENT_VERSION ARG AUTO_INSTRUMENTATION_JAVA_VERSION ARG AUTO_INSTRUMENTATION_PYTHON_VERSION +ARG AUTO_INSTRUMENTATION_NODEJS_VERSION ARG AUTO_INSTRUMENTATION_DOTNET_VERSION ARG DCMG_EXPORTER_VERSION ARG NEURON_MONITOR_VERSION diff --git a/Makefile b/Makefile index 6df879668..57677dd98 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,7 @@ VERSION_PKG ?= "github.com/aws/amazon-cloudwatch-agent-operator/internal/version AGENT_VERSION ?= "$(shell grep -v '\#' versions.txt | grep cloudwatch-agent | awk -F= '{print $$2}')" AUTO_INSTRUMENTATION_JAVA_VERSION ?= "$(shell grep -v '\#' versions.txt | grep aws-otel-java-instrumentation | awk -F= '{print $$2}')" AUTO_INSTRUMENTATION_PYTHON_VERSION ?= "$(shell grep -v '\#' versions.txt | grep aws-otel-python-instrumentation | awk -F= '{print $$2}')" +AUTO_INSTRUMENTATION_NODEJS_VERSION ?= "$(shell grep -v '\#' versions.txt | grep aws-otel-nodejs-instrumentation | awk -F= '{print $$2}')" AUTO_INSTRUMENTATION_DOTNET_VERSION ?= "$(shell grep -v '\#' versions.txt | grep aws-otel-dotnet-instrumentation | awk -F= '{print $$2}')" DCGM_EXPORTER_VERSION ?= "$(shell grep -v '\#' versions.txt | grep dcgm-exporter | awk -F= '{print $$2}')" NEURON_MONITOR_VERSION ?= "$(shell grep -v '\#' versions.txt | grep neuron-monitor | awk -F= '{print $$2}')" @@ -154,6 +155,7 @@ generate: controller-gen api-docs # buildx is used to ensure same results for arm based systems (m1/2 chips) .PHONY: container container: + docker buildx build --load --platform linux/${ARCH} -t ${IMG} --build-arg VERSION_PKG=${VERSION_PKG} --build-arg VERSION=${VERSION} --build-arg VERSION_DATE=${VERSION_DATE} --build-arg AGENT_VERSION=${AGENT_VERSION} --build-arg AUTO_INSTRUMENTATION_JAVA_VERSION=${AUTO_INSTRUMENTATION_JAVA_VERSION} --build-arg AUTO_INSTRUMENTATION_PYTHON_VERSION=${AUTO_INSTRUMENTATION_PYTHON_VERSION} --build-arg AUTO_INSTRUMENTATION_NODEJS_VERSION=${AUTO_INSTRUMENTATION_NODEJS_VERSION} --build-arg DCGM_EXPORTER_VERSION=${DCGM_EXPORTER_VERSION} --build-arg NEURON_MONITOR_VERSION=${NEURON_MONITOR_VERSION} . docker buildx build --load --platform linux/${ARCH} -t ${IMG} --build-arg VERSION_PKG=${VERSION_PKG} --build-arg VERSION=${VERSION} --build-arg VERSION_DATE=${VERSION_DATE} --build-arg AGENT_VERSION=${AGENT_VERSION} --build-arg AUTO_INSTRUMENTATION_JAVA_VERSION=${AUTO_INSTRUMENTATION_JAVA_VERSION} --build-arg AUTO_INSTRUMENTATION_PYTHON_VERSION=${AUTO_INSTRUMENTATION_PYTHON_VERSION} --build-arg AUTO_INSTRUMENTATION_DOTNET_VERSION=${AUTO_INSTRUMENTATION_DOTNET_VERSION} --build-arg DCGM_EXPORTER_VERSION=${DCGM_EXPORTER_VERSION} --build-arg NEURON_MONITOR_VERSION=${NEURON_MONITOR_VERSION} . # Push the container image, used only for local dev purposes diff --git a/integration-tests/manifests/annotations/validate_annotation_daemonset_test.go b/integration-tests/manifests/annotations/validate_annotation_daemonset_test.go index bacbb22c4..5aae96ee0 100644 --- a/integration-tests/manifests/annotations/validate_annotation_daemonset_test.go +++ b/integration-tests/manifests/annotations/validate_annotation_daemonset_test.go @@ -42,6 +42,12 @@ func TestAllLanguagesDaemonSet(t *testing.T) { Deployments: []string{""}, StatefulSets: []string{""}, }, + NodeJS: auto.AnnotationResources{ + Namespaces: []string{""}, + DaemonSets: []string{filepath.Join(uniqueNamespace, daemonSetName)}, + Deployments: []string{""}, + StatefulSets: []string{""}, + }, } jsonStr, err := json.Marshal(annotationConfig) if err != nil { @@ -51,7 +57,7 @@ func TestAllLanguagesDaemonSet(t *testing.T) { startTime := time.Now() updateTheOperator(t, clientSet, string(jsonStr)) - if err := checkResourceAnnotations(t, clientSet, "daemonset", uniqueNamespace, daemonSetName, sampleDaemonsetYamlRelPath, startTime, []string{injectJavaAnnotation, autoAnnotateJavaAnnotation, injectPythonAnnotation, autoAnnotatePythonAnnotation, injectDotNetAnnotation, autoAnnotateDotNetAnnotation}, false); err != nil { + if err := checkResourceAnnotations(t, clientSet, "daemonset", uniqueNamespace, daemonSetName, sampleDaemonsetYamlRelPath, startTime, []string{injectJavaAnnotation, autoAnnotateJavaAnnotation, injectPythonAnnotation, autoAnnotatePythonAnnotation, injectDotNetAnnotation, autoAnnotateDotNetAnnotation, injectNodeJSAnnotation, autoAnnotateNodeJSAnnotation}, false); err != nil { t.Fatalf("Failed annotation check: %s", err.Error()) } @@ -156,3 +162,27 @@ func TestDotNetOnlyDaemonSet(t *testing.T) { } } +func TestNodeJSOnlyDaemonSet(t *testing.T) { + clientSet := setupTest(t) + randomNumber, err := rand.Int(rand.Reader, big.NewInt(9000)) + if err != nil { + panic(err) + } + randomNumber.Add(randomNumber, big.NewInt(1000)) //adding a hash to namespace + uniqueNamespace := fmt.Sprintf("daemonset-namespace-nodejs-only-%d", randomNumber) + annotationConfig := auto.AnnotationConfig{ + NodeJS: auto.AnnotationResources{ + DaemonSets: []string{filepath.Join(uniqueNamespace, daemonSetName)}, + }, + } + jsonStr, err := json.Marshal(annotationConfig) + if err != nil { + t.Error("Error:", err) + } + startTime := time.Now() + updateTheOperator(t, clientSet, string(jsonStr)) + + if err := checkResourceAnnotations(t, clientSet, "daemonset", uniqueNamespace, daemonSetName, sampleDaemonsetYamlRelPath, startTime, []string{injectNodeJSAnnotation, autoAnnotateNodeJSAnnotation}, false); err != nil { + t.Fatalf("Failed annotation check: %s", err.Error()) + } +} diff --git a/integration-tests/manifests/annotations/validate_annotation_methods.go b/integration-tests/manifests/annotations/validate_annotation_methods.go index 4acb6eb5c..cdebf6afc 100644 --- a/integration-tests/manifests/annotations/validate_annotation_methods.go +++ b/integration-tests/manifests/annotations/validate_annotation_methods.go @@ -35,6 +35,9 @@ const ( injectDotNetAnnotation = "instrumentation.opentelemetry.io/inject-dotnet" autoAnnotateDotNetAnnotation = "cloudwatch.aws.amazon.com/auto-annotate-dotnet" + injectNodeJSAnnotation = "instrumentation.opentelemetry.io/inject-nodejs" + autoAnnotateNodeJSAnnotation = "cloudwatch.aws.amazon.com/auto-annotate-nodejs" + deploymentName = "sample-deployment" nginxDeploymentName = "nginx" statefulSetName = "sample-statefulset" @@ -175,8 +178,9 @@ func checkNameSpaceAnnotations(t *testing.T, clientSet *kubernetes.Clientset, ex fmt.Println("There was an error getting namespace, ", err) return false } + for _, annotation := range expectedAnnotations { - fmt.Printf("\n This is the annotation: %v and its status %v, namespace name: %v, \n", annotation, ns.Status, ns.Name) + fmt.Printf("\n This is the annotation: %v and its status %v, namespace name: %v, \n", ns.ObjectMeta.Annotations, ns.Status, ns.Name) if ns.ObjectMeta.Annotations[annotation] != "true" { time.Sleep(timeBetweenRetries) correct = false diff --git a/integration-tests/manifests/annotations/validate_annotations_deployment_test.go b/integration-tests/manifests/annotations/validate_annotations_deployment_test.go index 8c0a91cea..6c84bbbae 100644 --- a/integration-tests/manifests/annotations/validate_annotations_deployment_test.go +++ b/integration-tests/manifests/annotations/validate_annotations_deployment_test.go @@ -44,6 +44,12 @@ func TestAllLanguagesDeployment(t *testing.T) { Deployments: []string{filepath.Join(uniqueNamespace, deploymentName)}, StatefulSets: []string{""}, }, + NodeJS: auto.AnnotationResources{ + Namespaces: []string{""}, + DaemonSets: []string{""}, + Deployments: []string{filepath.Join(uniqueNamespace, deploymentName)}, + StatefulSets: []string{""}, + }, } jsonStr, err := json.Marshal(annotationConfig) assert.Nil(t, err) @@ -51,7 +57,7 @@ func TestAllLanguagesDeployment(t *testing.T) { startTime := time.Now() updateTheOperator(t, clientSet, string(jsonStr)) - if err := checkResourceAnnotations(t, clientSet, "deployment", uniqueNamespace, deploymentName, sampleDeploymentYamlNameRelPath, startTime, []string{injectJavaAnnotation, autoAnnotateJavaAnnotation, injectPythonAnnotation, autoAnnotatePythonAnnotation, injectDotNetAnnotation, autoAnnotateDotNetAnnotation}, false); err != nil { + if err := checkResourceAnnotations(t, clientSet, "deployment", uniqueNamespace, deploymentName, sampleDeploymentYamlNameRelPath, startTime, []string{injectJavaAnnotation, autoAnnotateJavaAnnotation, injectPythonAnnotation, autoAnnotatePythonAnnotation, injectDotNetAnnotation, autoAnnotateDotNetAnnotation, injectNodeJSAnnotation, autoAnnotateNodeJSAnnotation}, false); err != nil { t.Fatalf("Failed annotation check: %s", err.Error()) } @@ -167,3 +173,38 @@ func TestDotNetOnlyDeployment(t *testing.T) { } } + +func TestNodeJSOnlyDeployment(t *testing.T) { + + clientSet := setupTest(t) + randomNumber, err := rand.Int(rand.Reader, big.NewInt(9000)) + if err != nil { + panic(err) + } + randomNumber.Add(randomNumber, big.NewInt(1000)) //adding a hash to namespace + uniqueNamespace := fmt.Sprintf("deployment-namespace-nodejs-only-%d", randomNumber) + + annotationConfig := auto.AnnotationConfig{ + NodeJS: auto.AnnotationResources{ + Namespaces: []string{""}, + DaemonSets: []string{""}, + Deployments: []string{filepath.Join(uniqueNamespace, deploymentName)}, + StatefulSets: []string{""}, + }, + } + jsonStr, err := json.Marshal(annotationConfig) + if err != nil { + t.Error("Error:", err) + } + + startTime := time.Now() + updateTheOperator(t, clientSet, string(jsonStr)) + if err != nil { + t.Errorf("Failed to get deployment app: %s", err.Error()) + } + + if err := checkResourceAnnotations(t, clientSet, "deployment", uniqueNamespace, deploymentName, sampleDeploymentYamlNameRelPath, startTime, []string{injectNodeJSAnnotation, autoAnnotateNodeJSAnnotation}, false); err != nil { + t.Fatalf("Failed annotation check: %s", err.Error()) + } + +} diff --git a/integration-tests/manifests/annotations/validate_annotations_namespace_test.go b/integration-tests/manifests/annotations/validate_annotations_namespace_test.go index 4f8e97316..520e37066 100644 --- a/integration-tests/manifests/annotations/validate_annotations_namespace_test.go +++ b/integration-tests/manifests/annotations/validate_annotations_namespace_test.go @@ -50,6 +50,12 @@ func TestAllLanguagesNamespace(t *testing.T) { Deployments: []string{""}, StatefulSets: []string{""}, }, + NodeJS: auto.AnnotationResources{ + Namespaces: []string{uniqueNamespace}, + DaemonSets: []string{""}, + Deployments: []string{""}, + StatefulSets: []string{""}, + }, } jsonStr, err := json.Marshal(annotationConfig) if err != nil { @@ -58,7 +64,7 @@ func TestAllLanguagesNamespace(t *testing.T) { startTime := time.Now() updateTheOperator(t, clientSet, string(jsonStr)) - if !checkNameSpaceAnnotations(t, clientSet, []string{injectJavaAnnotation, autoAnnotateJavaAnnotation, injectPythonAnnotation, autoAnnotatePythonAnnotation, injectDotNetAnnotation, autoAnnotateDotNetAnnotation}, uniqueNamespace, startTime) { + if !checkNameSpaceAnnotations(t, clientSet, []string{injectJavaAnnotation, autoAnnotateJavaAnnotation, injectPythonAnnotation, autoAnnotatePythonAnnotation, injectDotNetAnnotation, autoAnnotateDotNetAnnotation, injectNodeJSAnnotation, autoAnnotateNodeJSAnnotation}, uniqueNamespace, startTime) { t.Error("Missing Languages annotations") } } @@ -184,6 +190,44 @@ func TestDotNetOnlyNamespace(t *testing.T) { } } +func TestNodeJSOnlyNamespace(t *testing.T) { + + clientSet := setupTest(t) + randomNumber, err := rand.Int(rand.Reader, big.NewInt(9000)) + if err != nil { + panic(err) + } + randomNumber.Add(randomNumber, big.NewInt(1000)) //adding a hash to namespace + uniqueNamespace := fmt.Sprintf("namespace-nodejs-only-%d", randomNumber) + if err := createNamespace(clientSet, uniqueNamespace); err != nil { + t.Fatalf("Failed to create/apply resoures on namespace: %v", err) + } + + defer func() { + if err := deleteNamespace(clientSet, uniqueNamespace); err != nil { + t.Fatalf("Failed to delete namespace: %v", err) + } + }() + + annotationConfig := auto.AnnotationConfig{ + DotNet: auto.AnnotationResources{ + Namespaces: []string{uniqueNamespace}, + }, + } + jsonStr, err := json.Marshal(annotationConfig) + if err != nil { + t.Error("Error:", err) + } + + startTime := time.Now() + + updateTheOperator(t, clientSet, string(jsonStr)) + + if !checkNameSpaceAnnotations(t, clientSet, []string{injectNodeJSAnnotation, autoAnnotateNodeJSAnnotation}, uniqueNamespace, startTime) { + t.Error("Missing nodejs annotations") + } +} + // Multiple resources on the same namespace should all get annotations func TestAnnotationsOnMultipleResources(t *testing.T) { diff --git a/integration-tests/manifests/annotations/validate_annotations_statefulset_test.go b/integration-tests/manifests/annotations/validate_annotations_statefulset_test.go index 3e25f34e9..4f8ccea04 100644 --- a/integration-tests/manifests/annotations/validate_annotations_statefulset_test.go +++ b/integration-tests/manifests/annotations/validate_annotations_statefulset_test.go @@ -42,6 +42,12 @@ func TestAllLanguagesStatefulSet(t *testing.T) { Deployments: []string{""}, StatefulSets: []string{filepath.Join(uniqueNamespace, statefulSetName)}, }, + NodeJS: auto.AnnotationResources{ + Namespaces: []string{""}, + DaemonSets: []string{""}, + Deployments: []string{""}, + StatefulSets: []string{filepath.Join(uniqueNamespace, statefulSetName)}, + }, } jsonStr, err := json.Marshal(annotationConfig) if err != nil { @@ -49,7 +55,7 @@ func TestAllLanguagesStatefulSet(t *testing.T) { } startTime := time.Now() updateTheOperator(t, clientSet, string(jsonStr)) - if err := checkResourceAnnotations(t, clientSet, "statefulset", uniqueNamespace, statefulSetName, sampleStatefulsetYamlNameRelPath, startTime, []string{injectJavaAnnotation, autoAnnotateJavaAnnotation, injectPythonAnnotation, autoAnnotatePythonAnnotation, injectDotNetAnnotation, autoAnnotateDotNetAnnotation}, false); err != nil { + if err := checkResourceAnnotations(t, clientSet, "statefulset", uniqueNamespace, statefulSetName, sampleStatefulsetYamlNameRelPath, startTime, []string{injectJavaAnnotation, autoAnnotateJavaAnnotation, injectPythonAnnotation, autoAnnotatePythonAnnotation, injectDotNetAnnotation, autoAnnotateDotNetAnnotation, injectNodeJSAnnotation, autoAnnotateNodeJSAnnotation}, false); err != nil { t.Fatalf("Failed annotation check: %s", err.Error()) } } @@ -150,7 +156,36 @@ func TestDotNetOnlyStatefulSet(t *testing.T) { startTime := time.Now() updateTheOperator(t, clientSet, string(jsonStr)) - if err := checkResourceAnnotations(t, clientSet, "statefulset", uniqueNamespace, statefulSetName, sampleStatefulsetYamlNameRelPath, startTime, []string{injectPythonAnnotation, autoAnnotatePythonAnnotation, injectDotNetAnnotation, autoAnnotateDotNetAnnotation}, false); err != nil { + if err := checkResourceAnnotations(t, clientSet, "statefulset", uniqueNamespace, statefulSetName, sampleStatefulsetYamlNameRelPath, startTime, []string{injectDotNetAnnotation, autoAnnotateDotNetAnnotation}, false); err != nil { + t.Fatalf("Failed annotation check: %s", err.Error()) + } +} +func TestNodeJSOnlyStatefulSet(t *testing.T) { + + clientSet := setupTest(t) + randomNumber, err := rand.Int(rand.Reader, big.NewInt(9000)) + if err != nil { + panic(err) + } + randomNumber.Add(randomNumber, big.NewInt(1000)) //adding a hash to namespace + uniqueNamespace := fmt.Sprintf("statefulset-namespace-nodejs-only-%d", randomNumber) + annotationConfig := auto.AnnotationConfig{ + NodeJS: auto.AnnotationResources{ + Namespaces: []string{""}, + DaemonSets: []string{""}, + Deployments: []string{""}, + StatefulSets: []string{filepath.Join(uniqueNamespace, statefulSetName)}, + }, + } + jsonStr, err := json.Marshal(annotationConfig) + if err != nil { + t.Error("Error:", err) + } + + startTime := time.Now() + updateTheOperator(t, clientSet, string(jsonStr)) + + if err := checkResourceAnnotations(t, clientSet, "statefulset", uniqueNamespace, statefulSetName, sampleStatefulsetYamlNameRelPath, startTime, []string{injectNodeJSAnnotation, autoAnnotateNodeJSAnnotation}, false); err != nil { t.Fatalf("Failed annotation check: %s", err.Error()) } } diff --git a/integration-tests/nodejs/default_instrumentation_nodejs_env_variables.json b/integration-tests/nodejs/default_instrumentation_nodejs_env_variables.json new file mode 100644 index 000000000..a075b8eed --- /dev/null +++ b/integration-tests/nodejs/default_instrumentation_nodejs_env_variables.json @@ -0,0 +1,11 @@ + +{ +"OTEL_AWS_APPLICATION_SIGNALS_ENABLED": "true", +"OTEL_TRACES_SAMPLER_ARG" : "endpoint=http://cloudwatch-agent.amazon-cloudwatch:2000", +"OTEL_TRACES_SAMPLER": "xray", +"OTEL_EXPORTER_OTLP_PROTOCOL": "http/protobuf", +"OTEL_EXPORTER_OTLP_TRACES_ENDPOINT" : "http://cloudwatch-agent.amazon-cloudwatch:4316/v1/traces", +"OTEL_AWS_APPLICATION_SIGNALS_EXPORTER_ENDPOINT": "http://cloudwatch-agent.amazon-cloudwatch:4316/v1/metrics", +"OTEL_METRICS_EXPORTER": "none", +"OTEL_LOGS_EXPORTER": "none" +} \ No newline at end of file diff --git a/integration-tests/nodejs/sample-deployment-nodejs.yaml b/integration-tests/nodejs/sample-deployment-nodejs.yaml new file mode 100644 index 000000000..a5d57aa32 --- /dev/null +++ b/integration-tests/nodejs/sample-deployment-nodejs.yaml @@ -0,0 +1,20 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx +spec: + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + annotations: + instrumentation.opentelemetry.io/inject-nodejs: "true" + spec: + containers: + - name: nginx + image: nginx:1.14.2 + restartPolicy: Always +status: {} \ No newline at end of file diff --git a/main.go b/main.go index cf607e7bd..4ee5e8d84 100644 --- a/main.go +++ b/main.go @@ -49,6 +49,8 @@ const ( autoInstrumentationJavaImageRepository = "public.ecr.aws/aws-observability/adot-autoinstrumentation-java" autoInstrumentationPythonImageRepository = "public.ecr.aws/aws-observability/adot-autoinstrumentation-python" autoInstrumentationDotNetImageRepository = "ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-dotnet" + + autoInstrumentationNodeJSImageRepository = "ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-nodejs" dcgmExporterImageRepository = "nvcr.io/nvidia/k8s/dcgm-exporter" neuronMonitorImageRepository = "public.ecr.aws/neuron" ) @@ -118,6 +120,7 @@ func main() { autoInstrumentationJava string autoInstrumentationPython string autoInstrumentationDotNet string + autoInstrumentationNodeJS string autoAnnotationConfigStr string autoInstrumentationConfigStr string webhookPort int @@ -132,6 +135,7 @@ func main() { stringFlagOrEnv(&agentImage, "agent-image", "RELATED_IMAGE_COLLECTOR", fmt.Sprintf("%s:%s", cloudwatchAgentImageRepository, v.AmazonCloudWatchAgent), "The default CloudWatch Agent image. This image is used when no image is specified in the CustomResource.") stringFlagOrEnv(&autoInstrumentationJava, "auto-instrumentation-java-image", "RELATED_IMAGE_AUTO_INSTRUMENTATION_JAVA", fmt.Sprintf("%s:%s", autoInstrumentationJavaImageRepository, v.AutoInstrumentationJava), "The default OpenTelemetry Java instrumentation image. This image is used when no image is specified in the CustomResource.") stringFlagOrEnv(&autoInstrumentationPython, "auto-instrumentation-python-image", "RELATED_IMAGE_AUTO_INSTRUMENTATION_PYTHON", fmt.Sprintf("%s:%s", autoInstrumentationPythonImageRepository, v.AutoInstrumentationPython), "The default OpenTelemetry Python instrumentation image. This image is used when no image is specified in the CustomResource.") + stringFlagOrEnv(&autoInstrumentationNodeJS, "auto-instrumentation-nodejs-image", "RELATED_IMAGE_AUTO_INSTRUMENTATION_NODEJS", fmt.Sprintf("%s:%s", autoInstrumentationNodeJSImageRepository, v.AutoInstrumentationNodeJS), "The default OpenTelemetry NodeJS instrumentation image. This image is used when no image is specified in the CustomResource.") stringFlagOrEnv(&autoInstrumentationDotNet, "auto-instrumentation-dotnet-image", "RELATED_IMAGE_AUTO_INSTRUMENTATION_DOTNET", fmt.Sprintf("%s:%s", autoInstrumentationDotNetImageRepository, v.AutoInstrumentationDotNet), "The default OpenTelemetry Dotnet instrumentation image. This image is used when no image is specified in the CustomResource.") stringFlagOrEnv(&autoAnnotationConfigStr, "auto-annotation-config", "AUTO_ANNOTATION_CONFIG", "", "The configuration for auto-annotation.") pflag.StringVar(&autoInstrumentationConfigStr, "auto-instrumentation-config", "", "The configuration for auto-instrumentation.") @@ -139,6 +143,10 @@ func main() { stringFlagOrEnv(&neuronMonitorImage, "neuron-monitor-image", "RELATED_IMAGE_NEURON_MONITOR", fmt.Sprintf("%s:%s", neuronMonitorImageRepository, v.NeuronMonitor), "The default Neuron monitor image. This image is used when no image is specified in the CustomResource.") pflag.Parse() + // set supported languages instrumentation images in environment variable to be used for default instrumentation + os.Setenv("AUTO_INSTRUMENTATION_JAVA", autoInstrumentationJava) + os.Setenv("AUTO_INSTRUMENTATION_PYTHON", autoInstrumentationPython) + os.Setenv("AUTO_INSTRUMENTATION_NODEJS", autoInstrumentationNodeJS) // set instrumentation cpu and memory limits in environment variables to be used for default instrumentation; default values received from https://github.com/open-telemetry/opentelemetry-operator/blob/main/apis/v1alpha1/instrumentation_webhook.go autoInstrumentationConfig := map[string]map[string]map[string]string{"java": {"limits": {"cpu": "500m", "memory": "64Mi"}, "requests": {"cpu": "50m", "memory": "64Mi"}}, "python": {"limits": {"cpu": "500m", "memory": "32Mi"}, "requests": {"cpu": "50m", "memory": "32Mi"}}, "dotnet": {"limits": {"cpu": "500m", "memory": "128Mi"}, "requests": {"cpu": "50m", "memory": "128Mi"}}} err := json.Unmarshal([]byte(autoInstrumentationConfigStr), &autoInstrumentationConfig) @@ -159,6 +167,7 @@ func main() { os.Setenv("AUTO_INSTRUMENTATION_JAVA", autoInstrumentationJava) os.Setenv("AUTO_INSTRUMENTATION_PYTHON", autoInstrumentationPython) os.Setenv("AUTO_INSTRUMENTATION_DOTNET", autoInstrumentationDotNet) + os.Setenv("AUTO_INSTRUMENTATION_DOTNET", autoInstrumentationNodeJS) logger := zap.New(zap.UseFlagOptions(&opts)) ctrl.SetLogger(logger) @@ -168,6 +177,7 @@ func main() { "cloudwatch-agent", agentImage, "auto-instrumentation-java", autoInstrumentationJava, "auto-instrumentation-python", autoInstrumentationPython, + "auto-instrumentation-nodejs", autoInstrumentationNodeJS, "auto-instrumentation-dotnet", autoInstrumentationDotNet, "dcgm-exporter", dcgmExporterImage, "neuron-monitor", neuronMonitorImage, @@ -183,6 +193,7 @@ func main() { config.WithCollectorImage(agentImage), config.WithAutoInstrumentationJavaImage(autoInstrumentationJava), config.WithAutoInstrumentationPythonImage(autoInstrumentationPython), + config.WithAutoInstrumentationNodeJSImage(autoInstrumentationNodeJS), config.WithAutoInstrumentationDotNetImage(autoInstrumentationDotNet), config.WithDcgmExporterImage(dcgmExporterImage), config.WithNeuronMonitorImage(neuronMonitorImage), @@ -280,6 +291,7 @@ func main() { instrumentation.NewTypeSet( instrumentation.TypeJava, instrumentation.TypePython, + instrumentation.TypeNodeJS, instrumentation.TypeDotNet, ), ) diff --git a/pkg/instrumentation/auto/config.go b/pkg/instrumentation/auto/config.go index 30ade1eef..eaf371a7b 100644 --- a/pkg/instrumentation/auto/config.go +++ b/pkg/instrumentation/auto/config.go @@ -10,6 +10,7 @@ import "github.com/aws/amazon-cloudwatch-agent-operator/pkg/instrumentation" type AnnotationConfig struct { Java AnnotationResources `json:"java"` Python AnnotationResources `json:"python"` + NodeJS AnnotationResources `json:"nodejs"` DotNet AnnotationResources `json:"dotnet"` } @@ -19,6 +20,8 @@ func (c AnnotationConfig) getResources(instType instrumentation.Type) Annotation return c.Java case instrumentation.TypePython: return c.Python + case instrumentation.TypeNodeJS: + return c.NodeJS case instrumentation.TypeDotNet: return c.DotNet default: diff --git a/pkg/instrumentation/auto/config_test.go b/pkg/instrumentation/auto/config_test.go index 1300ad9dc..68e5c8a7e 100644 --- a/pkg/instrumentation/auto/config_test.go +++ b/pkg/instrumentation/auto/config_test.go @@ -25,6 +25,12 @@ func TestConfig(t *testing.T) { DaemonSets: []string{"ds2"}, StatefulSets: []string{"ss2"}, }, + NodeJS: AnnotationResources{ + Namespaces: []string{"n3"}, + Deployments: []string{"d3"}, + DaemonSets: []string{"ds3"}, + StatefulSets: []string{"ss3"}, + }, DotNet: AnnotationResources{ Namespaces: []string{"n3"}, Deployments: []string{"d3"}, @@ -32,12 +38,16 @@ func TestConfig(t *testing.T) { StatefulSets: []string{"ss3"}, }, } + assert.Equal(t, cfg.Java, cfg.getResources(instrumentation.TypeJava)) assert.Equal(t, []string{"n1"}, getNamespaces(cfg.Java)) assert.Equal(t, []string{"d1"}, getDeployments(cfg.Java)) assert.Equal(t, cfg.Python, cfg.getResources(instrumentation.TypePython)) assert.Equal(t, []string{"ds2"}, getDaemonSets(cfg.Python)) assert.Equal(t, []string{"ss2"}, getStatefulSets(cfg.Python)) + assert.Equal(t, cfg.NodeJS, cfg.getResources(instrumentation.TypeNodeJS)) + assert.Equal(t, []string{"ds3"}, getDaemonSets(cfg.NodeJS)) + assert.Equal(t, []string{"ss3"}, getStatefulSets(cfg.NodeJS)) assert.Equal(t, cfg.DotNet, cfg.getResources(instrumentation.TypeDotNet)) assert.Equal(t, []string{"ds3"}, getDaemonSets(cfg.DotNet)) assert.Equal(t, []string{"ss3"}, getStatefulSets(cfg.DotNet)) diff --git a/pkg/instrumentation/defaultinstrumentation.go b/pkg/instrumentation/defaultinstrumentation.go index e9946148c..fcc7f2a1c 100644 --- a/pkg/instrumentation/defaultinstrumentation.go +++ b/pkg/instrumentation/defaultinstrumentation.go @@ -18,10 +18,10 @@ import ( ) const ( - defaultAPIVersion = "cloudwatch.aws.amazon.com/v1alpha1" - defaultInstrumenation = "java-instrumentation" - defaultNamespace = "default" - defaultKind = "Instrumentation" + defaultAPIVersion = "cloudwatch.aws.amazon.com/v1alpha1" + defaultInstrumentation = "java-instrumentation" + defaultNamespace = "default" + defaultKind = "Instrumentation" http = "http" https = "https" @@ -58,6 +58,10 @@ func getDefaultInstrumentation(agentConfig *adapters.CwaConfig, isWindowsPod boo if !ok { return nil, errors.New("unable to determine python instrumentation image") } + nodeJSInstrumentationImage, ok := os.LookupEnv("AUTO_INSTRUMENTATION_NODEJS") + if !ok { + return nil, errors.New("unable to determine nodejs instrumentation image") + } dotNetInstrumentationImage, ok := os.LookupEnv("AUTO_INSTRUMENTATION_DOTNET") if !ok { return nil, errors.New("unable to determine dotnet instrumentation image") @@ -86,7 +90,7 @@ func getDefaultInstrumentation(agentConfig *adapters.CwaConfig, isWindowsPod boo Kind: defaultKind, }, ObjectMeta: metav1.ObjectMeta{ - Name: defaultInstrumenation, + Name: defaultInstrumentation, Namespace: defaultNamespace, }, Spec: v1alpha1.InstrumentationSpec{ @@ -157,6 +161,19 @@ func getDefaultInstrumentation(agentConfig *adapters.CwaConfig, isWindowsPod boo Requests: getInstrumentationConfigForResource(dotNet, request), }, }, + NodeJS: v1alpha1.NodeJS{ + Image: nodeJSInstrumentationImage, + Env: []corev1.EnvVar{ + {Name: "OTEL_AWS_APPLICATION_SIGNALS_ENABLED", Value: "true"}, + {Name: "OTEL_TRACES_SAMPLER_ARG", Value: fmt.Sprintf("endpoint=%s://%s:2000", exporterPrefix, cloudwatchAgentServiceEndpoint)}, + {Name: "OTEL_TRACES_SAMPLER", Value: "xray"}, + {Name: "OTEL_EXPORTER_OTLP_PROTOCOL", Value: "http/protobuf"}, + {Name: "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", Value: fmt.Sprintf("%s://%s:4316/v1/traces", exporterPrefix, cloudwatchAgentServiceEndpoint)}, + {Name: "OTEL_AWS_APPLICATION_SIGNALS_EXPORTER_ENDPOINT", Value: fmt.Sprintf("%s://%s:4316/v1/metrics", exporterPrefix, cloudwatchAgentServiceEndpoint)}, + {Name: "OTEL_METRICS_EXPORTER", Value: "none"}, + {Name: "OTEL_LOGS_EXPORTER", Value: "none"}, + }, + }, }, }, nil } diff --git a/pkg/instrumentation/defaultinstrumentation_test.go b/pkg/instrumentation/defaultinstrumentation_test.go index ebe5c3f3c..20034f4a8 100644 --- a/pkg/instrumentation/defaultinstrumentation_test.go +++ b/pkg/instrumentation/defaultinstrumentation_test.go @@ -4,10 +4,12 @@ package instrumentation import ( + "fmt" "os" - "reflect" "testing" + "github.com/google/go-cmp/cmp" + "k8s.io/apimachinery/pkg/api/resource" corev1 "k8s.io/api/core/v1" @@ -20,6 +22,7 @@ import ( func Test_getDefaultInstrumentationLinux(t *testing.T) { os.Setenv("AUTO_INSTRUMENTATION_JAVA", defaultJavaInstrumentationImage) os.Setenv("AUTO_INSTRUMENTATION_PYTHON", defaultPythonInstrumentationImage) + os.Setenv("AUTO_INSTRUMENTATION_NODEJS", defaultNodeJSInstrumentationImage) os.Setenv("AUTO_INSTRUMENTATION_DOTNET", defaultDotNetInstrumentationImage) os.Setenv("AUTO_INSTRUMENTATION_JAVA_CPU_LIMIT", "500m") os.Setenv("AUTO_INSTRUMENTATION_JAVA_MEM_LIMIT", "64Mi") @@ -41,7 +44,7 @@ func Test_getDefaultInstrumentationLinux(t *testing.T) { Kind: defaultKind, }, ObjectMeta: metav1.ObjectMeta{ - Name: defaultInstrumenation, + Name: defaultInstrumentation, Namespace: defaultNamespace, }, Spec: v1alpha1.InstrumentationSpec{ @@ -130,6 +133,19 @@ func Test_getDefaultInstrumentationLinux(t *testing.T) { }, }, }, + NodeJS: v1alpha1.NodeJS{ + Image: defaultNodeJSInstrumentationImage, + Env: []corev1.EnvVar{ + {Name: "OTEL_AWS_APPLICATION_SIGNALS_ENABLED", Value: "true"}, + {Name: "OTEL_TRACES_SAMPLER_ARG", Value: "endpoint=http://cloudwatch-agent.amazon-cloudwatch:2000"}, + {Name: "OTEL_TRACES_SAMPLER", Value: "xray"}, + {Name: "OTEL_EXPORTER_OTLP_PROTOCOL", Value: "http/protobuf"}, + {Name: "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", Value: "http://cloudwatch-agent.amazon-cloudwatch:4316/v1/traces"}, + {Name: "OTEL_AWS_APPLICATION_SIGNALS_EXPORTER_ENDPOINT", Value: "http://cloudwatch-agent.amazon-cloudwatch:4316/v1/metrics"}, + {Name: "OTEL_METRICS_EXPORTER", Value: "none"}, + {Name: "OTEL_LOGS_EXPORTER", Value: "none"}, + }, + }, }, } httpsInst := &v1alpha1.Instrumentation{ @@ -139,7 +155,7 @@ func Test_getDefaultInstrumentationLinux(t *testing.T) { Kind: defaultKind, }, ObjectMeta: metav1.ObjectMeta{ - Name: defaultInstrumenation, + Name: defaultInstrumentation, Namespace: defaultNamespace, }, Spec: v1alpha1.InstrumentationSpec{ @@ -228,6 +244,19 @@ func Test_getDefaultInstrumentationLinux(t *testing.T) { }, }, }, + NodeJS: v1alpha1.NodeJS{ + Image: defaultNodeJSInstrumentationImage, + Env: []corev1.EnvVar{ + {Name: "OTEL_AWS_APPLICATION_SIGNALS_ENABLED", Value: "true"}, + {Name: "OTEL_TRACES_SAMPLER_ARG", Value: "endpoint=https://cloudwatch-agent.amazon-cloudwatch:2000"}, + {Name: "OTEL_TRACES_SAMPLER", Value: "xray"}, + {Name: "OTEL_EXPORTER_OTLP_PROTOCOL", Value: "http/protobuf"}, + {Name: "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", Value: "https://cloudwatch-agent.amazon-cloudwatch:4316/v1/traces"}, + {Name: "OTEL_AWS_APPLICATION_SIGNALS_EXPORTER_ENDPOINT", Value: "https://cloudwatch-agent.amazon-cloudwatch:4316/v1/metrics"}, + {Name: "OTEL_METRICS_EXPORTER", Value: "none"}, + {Name: "OTEL_LOGS_EXPORTER", Value: "none"}, + }, + }, }, } @@ -281,17 +310,19 @@ func Test_getDefaultInstrumentationLinux(t *testing.T) { t.Errorf("getDefaultInstrumentation() error = %v, wantErr %v", err, tt.wantErr) return } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("getDefaultInstrumentation() got = %v, want %v", got, tt.want) + if diff := cmp.Diff(tt.want, got); diff != "" { + t.Errorf("getDefaultInstrumentation() mismatch (-want +got):\n%s", diff) } }) } + } func Test_getDefaultInstrumentationWindows(t *testing.T) { os.Setenv("AUTO_INSTRUMENTATION_JAVA", defaultJavaInstrumentationImage) os.Setenv("AUTO_INSTRUMENTATION_PYTHON", defaultPythonInstrumentationImage) os.Setenv("AUTO_INSTRUMENTATION_DOTNET", defaultDotNetInstrumentationImage) + os.Setenv("AUTO_INSTRUMENTATION_NODEJS", defaultNodeJSInstrumentationImage) os.Setenv("AUTO_INSTRUMENTATION_JAVA_CPU_LIMIT", "500m") os.Setenv("AUTO_INSTRUMENTATION_JAVA_MEM_LIMIT", "64Mi") os.Setenv("AUTO_INSTRUMENTATION_JAVA_CPU_REQUEST", "50m") @@ -312,7 +343,7 @@ func Test_getDefaultInstrumentationWindows(t *testing.T) { Kind: defaultKind, }, ObjectMeta: metav1.ObjectMeta{ - Name: defaultInstrumenation, + Name: defaultInstrumentation, Namespace: defaultNamespace, }, Spec: v1alpha1.InstrumentationSpec{ @@ -401,6 +432,19 @@ func Test_getDefaultInstrumentationWindows(t *testing.T) { }, }, }, + NodeJS: v1alpha1.NodeJS{ + Image: defaultNodeJSInstrumentationImage, + Env: []corev1.EnvVar{ + {Name: "OTEL_AWS_APPLICATION_SIGNALS_ENABLED", Value: "true"}, + {Name: "OTEL_TRACES_SAMPLER_ARG", Value: "endpoint=http://cloudwatch-agent-windows-headless.amazon-cloudwatch.svc.cluster.local:2000"}, + {Name: "OTEL_TRACES_SAMPLER", Value: "xray"}, + {Name: "OTEL_EXPORTER_OTLP_PROTOCOL", Value: "http/protobuf"}, + {Name: "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", Value: "http://cloudwatch-agent-windows-headless.amazon-cloudwatch.svc.cluster.local:4316/v1/traces"}, + {Name: "OTEL_AWS_APPLICATION_SIGNALS_EXPORTER_ENDPOINT", Value: "http://cloudwatch-agent-windows-headless.amazon-cloudwatch.svc.cluster.local:4316/v1/metrics"}, + {Name: "OTEL_METRICS_EXPORTER", Value: "none"}, + {Name: "OTEL_LOGS_EXPORTER", Value: "none"}, + }, + }, }, } httpsInst := &v1alpha1.Instrumentation{ @@ -410,7 +454,7 @@ func Test_getDefaultInstrumentationWindows(t *testing.T) { Kind: defaultKind, }, ObjectMeta: metav1.ObjectMeta{ - Name: defaultInstrumenation, + Name: defaultInstrumentation, Namespace: defaultNamespace, }, Spec: v1alpha1.InstrumentationSpec{ @@ -499,6 +543,19 @@ func Test_getDefaultInstrumentationWindows(t *testing.T) { }, }, }, + NodeJS: v1alpha1.NodeJS{ + Image: defaultNodeJSInstrumentationImage, + Env: []corev1.EnvVar{ + {Name: "OTEL_AWS_APPLICATION_SIGNALS_ENABLED", Value: "true"}, + {Name: "OTEL_TRACES_SAMPLER_ARG", Value: "endpoint=https://cloudwatch-agent-windows-headless.amazon-cloudwatch.svc.cluster.local:2000"}, + {Name: "OTEL_TRACES_SAMPLER", Value: "xray"}, + {Name: "OTEL_EXPORTER_OTLP_PROTOCOL", Value: "http/protobuf"}, + {Name: "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", Value: "https://cloudwatch-agent-windows-headless.amazon-cloudwatch.svc.cluster.local:4316/v1/traces"}, + {Name: "OTEL_AWS_APPLICATION_SIGNALS_EXPORTER_ENDPOINT", Value: "https://cloudwatch-agent-windows-headless.amazon-cloudwatch.svc.cluster.local:4316/v1/metrics"}, + {Name: "OTEL_METRICS_EXPORTER", Value: "none"}, + {Name: "OTEL_LOGS_EXPORTER", Value: "none"}, + }, + }, }, } @@ -548,12 +605,14 @@ func Test_getDefaultInstrumentationWindows(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := getDefaultInstrumentation(tt.args.agentConfig, true) + fmt.Println(got) + fmt.Println("___------__---__") if (err != nil) != tt.wantErr { t.Errorf("getDefaultInstrumentation() error = %v, wantErr %v", err, tt.wantErr) return } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("getDefaultInstrumentation() got = %v, want %v", got, tt.want) + if diff := cmp.Diff(tt.want, got); diff != "" { + t.Errorf("getDefaultInstrumentation() mismatch (-want +got):\n%s", diff) } }) } diff --git a/pkg/instrumentation/podmutator_test.go b/pkg/instrumentation/podmutator_test.go index f00c78540..06f7f2361 100644 --- a/pkg/instrumentation/podmutator_test.go +++ b/pkg/instrumentation/podmutator_test.go @@ -9,6 +9,8 @@ import ( "os" "testing" + "github.com/aws/amazon-cloudwatch-agent-operator/internal/manifests/collector/adapters" + "github.com/go-logr/logr" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -19,19 +21,22 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client/fake" "github.com/aws/amazon-cloudwatch-agent-operator/apis/v1alpha1" - "github.com/aws/amazon-cloudwatch-agent-operator/internal/manifests/collector/adapters" "github.com/aws/amazon-cloudwatch-agent-operator/pkg/featuregate" ) const ( defaultJavaInstrumentationImage = "test.registry/adot-autoinstrumentation-java:test-tag" defaultPythonInstrumentationImage = "test.registry/adot-autoinstrumentation-python:test-tag" + defaultNodeJSInstrumentationImage = "test.registry/adot-autoinstrumentation-nodejs:test-tag" defaultDotNetInstrumentationImage = "test.registry/adot-autoinstrumentation-dotnet:test-tag" ) func TestGetInstrumentationInstanceFromNameSpaceDefault(t *testing.T) { - defaultInst, _ := getDefaultInstrumentation(&adapters.CwaConfig{}, false) + os.Setenv("AUTO_INSTRUMENTATION_JAVA", defaultJavaInstrumentationImage) + os.Setenv("AUTO_INSTRUMENTATION_PYTHON", defaultPythonInstrumentationImage) + os.Setenv("AUTO_INSTRUMENTATION_NODEJS", defaultNodeJSInstrumentationImage) + defaultInst, _ := getDefaultInstrumentation(&adapters.CwaConfig{}, false) namespace := corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: "default-namespace", @@ -49,7 +54,6 @@ func TestGetInstrumentationInstanceFromNameSpaceDefault(t *testing.T) { assert.Nil(t, err) assert.Equal(t, defaultInst, instrumentation) - } func TestMutatePod(t *testing.T) { diff --git a/versions.txt b/versions.txt index 89f400f1e..e88a78b5e 100644 --- a/versions.txt +++ b/versions.txt @@ -7,6 +7,7 @@ operator=1.4.1 # Represents the current release of ADOT language instrumentation. aws-otel-java-instrumentation=v1.32.2 aws-otel-python-instrumentation=v0.2.0 +aws-otel-nodejs-instrumentation=0.51.0 # This needs to be updated with the latest release of ADOT SDK aws-otel-dotnet-instrumentation=1.6.0