diff --git a/.chloggen/node-import.yaml b/.chloggen/node-import.yaml new file mode 100644 index 0000000000..8ec124c59b --- /dev/null +++ b/.chloggen/node-import.yaml @@ -0,0 +1,18 @@ +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. collector, target allocator, auto-instrumentation, opamp, github action) +component: auto-instrumentation + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Add Node.js support for `--import` flag + +# One or more tracking issues related to the change +issues: [3414] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: | + The new UseImport option overrides the default injected --require flag with an --import flag. + Node.js ^18.19.0 || ^20.6.0 || >=22 is required for the flag to be supported. diff --git a/apis/v1alpha1/instrumentation_types.go b/apis/v1alpha1/instrumentation_types.go index 5caf3fb0ea..ffe580e6b5 100644 --- a/apis/v1alpha1/instrumentation_types.go +++ b/apis/v1alpha1/instrumentation_types.go @@ -212,6 +212,11 @@ type NodeJS struct { // Resources describes the compute resource requirements. // +optional Resources corev1.ResourceRequirements `json:"resourceRequirements,omitempty"` + + // UseImport overrides the default injected --require flag with an --import flag. + // Node.js ^18.19.0 || ^20.6.0 || >=22 is required for the flag to be supported. + // +optional + UseImport bool `json:"useImport,omitempty"` } // Python defines Python SDK and instrumentation configuration. diff --git a/autoinstrumentation/nodejs/Dockerfile b/autoinstrumentation/nodejs/Dockerfile index 70106ecd6b..703dac21d2 100644 --- a/autoinstrumentation/nodejs/Dockerfile +++ b/autoinstrumentation/nodejs/Dockerfile @@ -9,6 +9,8 @@ # - Grant the necessary access to `/autoinstrumentation` directory. `chmod -R go+r /autoinstrumentation` # - For auto-instrumentation by container injection, the Linux command cp is # used and must be available in the image. +# - By default a file named autoinstrumentation.js is loaded using `require`, but the UseImport configuration option +# overrides this default behaviour and loads it autoinstrumentation.mjs using `import`. FROM node:20 AS build WORKDIR /operator-build diff --git a/autoinstrumentation/nodejs/package.json b/autoinstrumentation/nodejs/package.json index 9611d2af29..74047250f2 100644 --- a/autoinstrumentation/nodejs/package.json +++ b/autoinstrumentation/nodejs/package.json @@ -4,12 +4,11 @@ "private": true, "scripts": { "clean": "rimraf build/*", - "postinstall": "copyfiles -f 'build/src/**' build/workspace/ && copyfiles 'node_modules/**' package.json build/workspace/ && npm -C build/workspace prune --omit=dev --no-package-lock" + "postinstall": "copyfiles 'node_modules/**' package.json build/workspace/ && npm -C build/workspace prune --omit=dev --no-package-lock" }, "devDependencies": { "copyfiles": "^2.4.1", - "rimraf": "^6.0.1", - "typescript": "^5.6.3" + "rimraf": "^6.0.1" }, "dependencies": { "@opentelemetry/api": "1.9.0", diff --git a/autoinstrumentation/nodejs/tsconfig.json b/autoinstrumentation/nodejs/tsconfig.json deleted file mode 100644 index 38012118db..0000000000 --- a/autoinstrumentation/nodejs/tsconfig.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "compilerOptions": { - "rootDir": ".", - "outDir": "build", - - "allowUnreachableCode": false, - "allowUnusedLabels": false, - "composite": true, - "declaration": true, - "declarationMap": true, - "forceConsistentCasingInFileNames": true, - "incremental": true, - "inlineSources": true, - "module": "commonjs", - "newLine": "LF", - "noEmitOnError": true, - "noFallthroughCasesInSwitch": true, - "noImplicitOverride": true, - "noImplicitReturns": true, - "noUnusedLocals": true, - "pretty": true, - "skipLibCheck": true, - "sourceMap": true, - "strict": true, - "strictNullChecks": true, - "target": "es2017" - }, - "include": [ - "src/**/*.ts", - ], - "exclude": [ - "node_modules" - ] -} \ No newline at end of file diff --git a/bundle/community/manifests/opentelemetry-operator.clusterserviceversion.yaml b/bundle/community/manifests/opentelemetry-operator.clusterserviceversion.yaml index fec2635c30..48ef486d11 100644 --- a/bundle/community/manifests/opentelemetry-operator.clusterserviceversion.yaml +++ b/bundle/community/manifests/opentelemetry-operator.clusterserviceversion.yaml @@ -99,7 +99,7 @@ metadata: categories: Logging & Tracing,Monitoring certified: "false" containerImage: ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator - createdAt: "2025-07-05T13:19:47Z" + createdAt: "2025-07-08T15:59:44Z" description: Provides the OpenTelemetry components, including the Collector operators.operatorframework.io/builder: operator-sdk-v1.29.0 operators.operatorframework.io/project_layout: go.kubebuilder.io/v3 diff --git a/bundle/community/manifests/opentelemetry.io_instrumentations.yaml b/bundle/community/manifests/opentelemetry.io_instrumentations.yaml index 6b71ba088b..191a192cbc 100644 --- a/bundle/community/manifests/opentelemetry.io_instrumentations.yaml +++ b/bundle/community/manifests/opentelemetry.io_instrumentations.yaml @@ -1498,6 +1498,8 @@ spec: x-kubernetes-int-or-string: true type: object type: object + useImport: + type: boolean volumeClaimTemplate: properties: metadata: diff --git a/bundle/openshift/manifests/opentelemetry-operator.clusterserviceversion.yaml b/bundle/openshift/manifests/opentelemetry-operator.clusterserviceversion.yaml index 5cc60031fc..96c1e9ba7d 100644 --- a/bundle/openshift/manifests/opentelemetry-operator.clusterserviceversion.yaml +++ b/bundle/openshift/manifests/opentelemetry-operator.clusterserviceversion.yaml @@ -99,7 +99,7 @@ metadata: categories: Logging & Tracing,Monitoring certified: "false" containerImage: ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator - createdAt: "2025-07-05T13:19:48Z" + createdAt: "2025-07-08T15:59:44Z" description: Provides the OpenTelemetry components, including the Collector operators.operatorframework.io/builder: operator-sdk-v1.29.0 operators.operatorframework.io/project_layout: go.kubebuilder.io/v3 diff --git a/bundle/openshift/manifests/opentelemetry.io_instrumentations.yaml b/bundle/openshift/manifests/opentelemetry.io_instrumentations.yaml index 6b71ba088b..191a192cbc 100644 --- a/bundle/openshift/manifests/opentelemetry.io_instrumentations.yaml +++ b/bundle/openshift/manifests/opentelemetry.io_instrumentations.yaml @@ -1498,6 +1498,8 @@ spec: x-kubernetes-int-or-string: true type: object type: object + useImport: + type: boolean volumeClaimTemplate: properties: metadata: diff --git a/config/crd/bases/opentelemetry.io_instrumentations.yaml b/config/crd/bases/opentelemetry.io_instrumentations.yaml index b06e657a27..ce6983f694 100644 --- a/config/crd/bases/opentelemetry.io_instrumentations.yaml +++ b/config/crd/bases/opentelemetry.io_instrumentations.yaml @@ -1496,6 +1496,8 @@ spec: x-kubernetes-int-or-string: true type: object type: object + useImport: + type: boolean volumeClaimTemplate: properties: metadata: diff --git a/docs/api/instrumentations.md b/docs/api/instrumentations.md index 87ac838c16..13891c16d0 100644 --- a/docs/api/instrumentations.md +++ b/docs/api/instrumentations.md @@ -5670,6 +5670,14 @@ If the former var had been defined, then the other vars would be ignored.
Resources describes the compute resource requirements.
false + + useImport + boolean + + UseImport overrides the default injected --require flag with an --import flag. +Node.js ^18.19.0 || ^20.6.0 || >=22 is required for the flag to be supported.
+ + false volumeClaimTemplate object diff --git a/internal/instrumentation/nodejs.go b/internal/instrumentation/nodejs.go index fc712f386e..e8c2ec354f 100644 --- a/internal/instrumentation/nodejs.go +++ b/internal/instrumentation/nodejs.go @@ -12,6 +12,7 @@ import ( const ( envNodeOptions = "NODE_OPTIONS" nodeRequireArgument = " --require /otel-auto-instrumentation-nodejs/autoinstrumentation.js" + nodeImportArgument = " --import /otel-auto-instrumentation-nodejs/autoinstrumentation.js" nodejsInitContainerName = initContainerName + "-nodejs" nodejsVolumeName = volumeName + "-nodejs" nodejsInstrMountPath = "/otel-auto-instrumentation-nodejs" @@ -31,14 +32,20 @@ func injectNodeJSSDK(nodeJSSpec v1alpha1.NodeJS, pod corev1.Pod, index int, inst // inject NodeJS instrumentation spec env vars. container.Env = appendIfNotSet(container.Env, nodeJSSpec.Env...) + var nodeArgument string + if nodeJSSpec.UseImport { + nodeArgument = nodeImportArgument + } else { + nodeArgument = nodeRequireArgument + } idx := getIndexOfEnv(container.Env, envNodeOptions) if idx == -1 { container.Env = append(container.Env, corev1.EnvVar{ Name: envNodeOptions, - Value: nodeRequireArgument, + Value: nodeArgument, }) } else if idx > -1 { - container.Env[idx].Value = container.Env[idx].Value + nodeRequireArgument + container.Env[idx].Value = container.Env[idx].Value + nodeArgument } container.VolumeMounts = append(container.VolumeMounts, corev1.VolumeMount{ diff --git a/internal/instrumentation/nodejs_test.go b/internal/instrumentation/nodejs_test.go index 9c921ad029..934c405f73 100644 --- a/internal/instrumentation/nodejs_test.go +++ b/internal/instrumentation/nodejs_test.go @@ -135,6 +135,120 @@ func TestInjectNodeJSSDK(t *testing.T) { }, err: nil, }, + { + name: "NODE_OPTIONS not defined and UseImport true", + NodeJS: v1alpha1.NodeJS{Image: "foo/bar:1", UseImport: true}, + pod: corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {}, + }, + }, + }, + expected: corev1.Pod{ + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: "opentelemetry-auto-instrumentation-nodejs", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: "opentelemetry-auto-instrumentation-nodejs", + Image: "foo/bar:1", + Command: []string{"cp", "-r", "/autoinstrumentation/.", "/otel-auto-instrumentation-nodejs"}, + VolumeMounts: []corev1.VolumeMount{{ + Name: "opentelemetry-auto-instrumentation-nodejs", + MountPath: "/otel-auto-instrumentation-nodejs", + }}, + }, + }, + Containers: []corev1.Container{ + { + VolumeMounts: []corev1.VolumeMount{ + { + Name: "opentelemetry-auto-instrumentation-nodejs", + MountPath: "/otel-auto-instrumentation-nodejs", + }, + }, + Env: []corev1.EnvVar{ + { + Name: "NODE_OPTIONS", + Value: " --import /otel-auto-instrumentation-nodejs/autoinstrumentation.js", + }, + }, + }, + }, + }, + }, + err: nil, + }, + { + name: "NODE_OPTIONS defined and UseImport true", + NodeJS: v1alpha1.NodeJS{Image: "foo/bar:1", Resources: testResourceRequirements, UseImport: true}, + 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: "opentelemetry-auto-instrumentation-nodejs", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: "opentelemetry-auto-instrumentation-nodejs", + Image: "foo/bar:1", + Command: []string{"cp", "-r", "/autoinstrumentation/.", "/otel-auto-instrumentation-nodejs"}, + VolumeMounts: []corev1.VolumeMount{{ + Name: "opentelemetry-auto-instrumentation-nodejs", + MountPath: "/otel-auto-instrumentation-nodejs", + }}, + Resources: testResourceRequirements, + }, + }, + Containers: []corev1.Container{ + { + VolumeMounts: []corev1.VolumeMount{ + { + Name: "opentelemetry-auto-instrumentation-nodejs", + MountPath: "/otel-auto-instrumentation-nodejs", + }, + }, + Env: []corev1.EnvVar{ + { + Name: "NODE_OPTIONS", + Value: "-Dbaz=bar" + " --import /otel-auto-instrumentation-nodejs/autoinstrumentation.js", + }, + }, + }, + }, + }, + }, + err: nil, + }, { name: "NODE_OPTIONS defined as ValueFrom", NodeJS: v1alpha1.NodeJS{Image: "foo/bar:1"}, diff --git a/internal/instrumentation/podmutator_test.go b/internal/instrumentation/podmutator_test.go index e674608a2d..620d9b6376 100644 --- a/internal/instrumentation/podmutator_test.go +++ b/internal/instrumentation/podmutator_test.go @@ -869,6 +869,193 @@ func TestMutatePod(t *testing.T) { EnableNodeJSAutoInstrumentation: true, }, }, + { + name: "nodejs injection use import, true", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "nodejs-import", + }, + }, + inst: v1alpha1.Instrumentation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-inst", + Namespace: "nodejs-import", + }, + Spec: v1alpha1.InstrumentationSpec{ + NodeJS: v1alpha1.NodeJS{ + Image: "otel/nodejs:1", + Env: []corev1.EnvVar{ + { + Name: "OTEL_NODEJS_DEBUG", + Value: "true", + }, + }, + UseImport: true, + }, + Env: []corev1.EnvVar{ + { + Name: "OTEL_TRACES_EXPORTER", + Value: "otlp", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://localhost:4317", + }, + { + Name: "OTEL_EXPORTER_OTLP_TIMEOUT", + Value: "20", + }, + { + Name: "OTEL_TRACES_SAMPLER", + Value: "parentbased_traceidratio", + }, + { + Name: "OTEL_TRACES_SAMPLER_ARG", + Value: "0.85", + }, + { + Name: "SPLUNK_TRACE_RESPONSE_HEADER_ENABLED", + Value: "true", + }, + }, + Exporter: v1alpha1.Exporter{ + Endpoint: "http://collector:12345", + }, + }, + }, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectNodeJS: "true", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + }, + }, + }, + }, + expected: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectNodeJS: "true", + }, + }, + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: nodejsVolumeName, + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{ + SizeLimit: &defaultVolumeLimitSize, + }, + }, + }, + }, + InitContainers: []corev1.Container{ + { + Name: nodejsInitContainerName, + Image: "otel/nodejs:1", + Command: []string{"cp", "-r", "/autoinstrumentation/.", nodejsInstrMountPath}, + VolumeMounts: []corev1.VolumeMount{{ + Name: nodejsVolumeName, + MountPath: nodejsInstrMountPath, + }}, + }, + }, + Containers: []corev1.Container{ + { + Name: "app", + Env: []corev1.EnvVar{ + { + Name: "OTEL_NODE_IP", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "status.hostIP", + }, + }, + }, + { + Name: "OTEL_POD_IP", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "status.podIP", + }, + }, + }, + { + Name: "OTEL_NODEJS_DEBUG", + Value: "true", + }, + { + Name: "NODE_OPTIONS", + Value: nodeImportArgument, + }, + { + Name: "OTEL_TRACES_EXPORTER", + Value: "otlp", + }, + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "http://localhost:4317", + }, + { + Name: "OTEL_EXPORTER_OTLP_TIMEOUT", + Value: "20", + }, + { + Name: "OTEL_TRACES_SAMPLER", + Value: "parentbased_traceidratio", + }, + { + Name: "OTEL_TRACES_SAMPLER_ARG", + Value: "0.85", + }, + { + Name: "SPLUNK_TRACE_RESPONSE_HEADER_ENABLED", + Value: "true", + }, + { + Name: "OTEL_SERVICE_NAME", + Value: "app", + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES_NODE_NAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + { + Name: "OTEL_RESOURCE_ATTRIBUTES", + Value: "k8s.container.name=app,k8s.namespace.name=nodejs-import,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME),service.instance.id=nodejs-import.$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME).app,service.namespace=nodejs-import", + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: nodejsVolumeName, + MountPath: nodejsInstrMountPath, + }, + }, + }, + }, + }, + }, + config: config.Config{ + EnableNodeJSAutoInstrumentation: true, + }, + }, { name: "nodejs injection multiple containers, true", ns: corev1.Namespace{ diff --git a/tests/e2e-instrumentation/instrumentation-nodejs-import/00-install-collector.yaml b/tests/e2e-instrumentation/instrumentation-nodejs-import/00-install-collector.yaml new file mode 100644 index 0000000000..e32ff2b422 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-nodejs-import/00-install-collector.yaml @@ -0,0 +1,27 @@ +apiVersion: opentelemetry.io/v1beta1 +kind: OpenTelemetryCollector +metadata: + name: sidecar +spec: + config: + receivers: + otlp: + protocols: + grpc: {} + http: {} + + processors: {} + + exporters: + debug: + verbosity: detailed + + service: + pipelines: + traces: + receivers: [otlp] + exporters: [debug] + metrics: + receivers: [otlp] + exporters: [debug] + mode: sidecar diff --git a/tests/e2e-instrumentation/instrumentation-nodejs-import/00-install-instrumentation.yaml b/tests/e2e-instrumentation/instrumentation-nodejs-import/00-install-instrumentation.yaml new file mode 100644 index 0000000000..daa2bc1637 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-nodejs-import/00-install-instrumentation.yaml @@ -0,0 +1,28 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: Instrumentation +metadata: + name: nodejs +spec: + env: + - name: OTEL_TRACES_EXPORTER + value: otlp + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: http://localhost:4318 + - name: OTEL_EXPORTER_OTLP_TIMEOUT + value: "20000" + - name: OTEL_TRACES_SAMPLER + value: always_on + - name: SPLUNK_TRACE_RESPONSE_HEADER_ENABLED + value: "true" + - name: OTEL_METRICS_EXPORTER + value: otlp + exporter: + endpoint: http://localhost:4318 + propagators: + - jaeger + - b3 + nodejs: + env: + - name: OTEL_NODEJS_DEBUG + value: "true" + useImport: true diff --git a/tests/e2e-instrumentation/instrumentation-nodejs-import/01-assert.yaml b/tests/e2e-instrumentation/instrumentation-nodejs-import/01-assert.yaml new file mode 100644 index 0000000000..0b48de1338 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-nodejs-import/01-assert.yaml @@ -0,0 +1,78 @@ +apiVersion: v1 +kind: Pod +metadata: + annotations: + instrumentation.opentelemetry.io/inject-nodejs: "true" + sidecar.opentelemetry.io/inject: "true" + labels: + app: my-nodejs +spec: + containers: + - env: + - name: OTEL_NODE_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: OTEL_POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: NODE_PATH + value: /usr/local/lib/node_modules + - name: OTEL_NODEJS_DEBUG + value: "true" + - name: NODE_OPTIONS + value: ' --import /otel-auto-instrumentation-nodejs/autoinstrumentation.js' + - name: OTEL_TRACES_EXPORTER + value: otlp + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: http://localhost:4318 + - name: OTEL_EXPORTER_OTLP_TIMEOUT + value: "20000" + - name: OTEL_TRACES_SAMPLER + value: always_on + - name: SPLUNK_TRACE_RESPONSE_HEADER_ENABLED + value: "true" + - name: OTEL_METRICS_EXPORTER + value: otlp + - name: OTEL_SERVICE_NAME + value: my-nodejs + - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName + - name: OTEL_PROPAGATORS + value: jaeger,b3 + - name: OTEL_RESOURCE_ATTRIBUTES + name: myapp + ports: + - containerPort: 3000 + protocol: TCP + volumeMounts: + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + readOnly: true + - mountPath: /otel-auto-instrumentation-nodejs + name: opentelemetry-auto-instrumentation-nodejs + - args: + - --config=env:OTEL_CONFIG + name: otc-container + initContainers: + - name: opentelemetry-auto-instrumentation-nodejs +status: + containerStatuses: + - name: myapp + ready: true + started: true + - name: otc-container + ready: true + started: true + initContainerStatuses: + - name: opentelemetry-auto-instrumentation-nodejs + ready: true + phase: Running diff --git a/tests/e2e-instrumentation/instrumentation-nodejs-import/01-install-app.yaml b/tests/e2e-instrumentation/instrumentation-nodejs-import/01-install-app.yaml new file mode 100644 index 0000000000..7efff21d2b --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-nodejs-import/01-install-app.yaml @@ -0,0 +1,42 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-nodejs +spec: + selector: + matchLabels: + app: my-nodejs + replicas: 1 + template: + metadata: + labels: + app: my-nodejs + annotations: + sidecar.opentelemetry.io/inject: "true" + instrumentation.opentelemetry.io/inject-nodejs: "true" + spec: + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + fsGroup: 3000 + containers: + - name: myapp + ports: + - containerPort: 3000 + image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-nodejs:main + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + env: + - name: NODE_PATH + value: /usr/local/lib/node_modules + readinessProbe: + httpGet: + path: /rolldice + port: 3000 + scheme: HTTP + initialDelaySeconds: 5 + periodSeconds: 15 + timeoutSeconds: 2 + failureThreshold: 3 \ No newline at end of file diff --git a/tests/e2e-instrumentation/instrumentation-nodejs-import/chainsaw-test.yaml b/tests/e2e-instrumentation/instrumentation-nodejs-import/chainsaw-test.yaml new file mode 100755 index 0000000000..f6c57b64b2 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-nodejs-import/chainsaw-test.yaml @@ -0,0 +1,120 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/kyverno/chainsaw/main/.schemas/json/test-chainsaw-v1alpha1.json +apiVersion: chainsaw.kyverno.io/v1alpha1 +kind: Test +metadata: + creationTimestamp: null + name: instrumentation-nodejs-import +spec: + steps: + - name: step-00 + try: + # In OpenShift, when a namespace is created, all necessary SCC annotations are automatically added. However, if a namespace is created using a resource file with only selected SCCs, the other auto-added SCCs are not included. Therefore, the UID-range and supplemental groups SCC annotations must be set after the namespace is created. + - command: + entrypoint: kubectl + args: + - annotate + - namespace + - ${NAMESPACE} + - openshift.io/sa.scc.uid-range=1000/1000 + - --overwrite + - command: + entrypoint: kubectl + args: + - annotate + - namespace + - ${NAMESPACE} + - openshift.io/sa.scc.supplemental-groups=3000/3000 + - --overwrite + - apply: + file: 00-install-collector.yaml + - apply: + file: 00-install-instrumentation.yaml + - name: step-01 + try: + - apply: + file: 01-install-app.yaml + - assert: + file: 01-assert.yaml + catch: + - podLogs: + selector: app=my-nodejs + - name: Make a request to the app + try: + - command: + entrypoint: kubectl + args: + - get + - pod + - -n + - ${NAMESPACE} + - -l + - app=my-nodejs + - -o + - jsonpath={.items[0].metadata.name} + outputs: + - name: podName + value: ($stdout) + - proxy: + apiVersion: v1 + kind: Pod + name: ($podName) + namespace: ${NAMESPACE} + port: "3000" + path: /rolldice + - name: Wait for telemetry data + try: + - script: + env: + - name: LABEL_SELECTOR + value: "app=my-nodejs" + - name: CONTAINER_NAME + value: "otc-container" + - name: RETRY_TIMEOUT + value: "120" + - name: RETRY_SLEEP + value: "5" + - name: SEARCH_STRINGS_ENV + value: | + Name: http.server.duration + Description: Measures the duration of inbound HTTP requests + timeout: 2m + content: ../../test-e2e-apps/scripts/check_pod_logs.sh + - name: Check the instrumented app has sent the telemetry data successfully + try: + - command: + entrypoint: kubectl + args: + - get + - pod + - -n + - ${NAMESPACE} + - -l + - app=my-nodejs + - -o + - jsonpath={.items[0].metadata.name} + outputs: + - name: podName + value: ($stdout) + - script: + env: + - name: podName + value: ($podName) + content: | + #!/bin/bash + # set -ex + kubectl get --raw /api/v1/namespaces/$NAMESPACE/pods/${podName}:8888/proxy/metrics + outputs: + - name: metrics + value: (x_metrics_decode($stdout)) + check: + ($error == null): true + - assert: + resource: + ($metrics[?as_string(metric."__name__") == 'otelcol_exporter_sent_spans_total'].value | [0] > `1`): true + ($metrics[?as_string(metric."__name__") == 'otelcol_receiver_accepted_spans_total'].value | [0] > `1`): true + ($metrics[?as_string(metric."__name__") == 'otelcol_exporter_sent_metric_points_total'].value | [0] > `1`): true + ($metrics[?as_string(metric."__name__") == 'otelcol_receiver_accepted_metric_points_total'].value | [0] > `1`): true + catch: + - podLogs: + selector: app=my-nodejs + container: otc-container \ No newline at end of file