Skip to content

Commit

Permalink
Adding support for NodeJS auto instrumentation and integ tests (#220)
Browse files Browse the repository at this point in the history
  • Loading branch information
Paramadon authored Sep 3, 2024
1 parent 15bfd18 commit 95a68d1
Show file tree
Hide file tree
Showing 18 changed files with 365 additions and 27 deletions.
48 changes: 41 additions & 7 deletions .github/workflows/operator-integration-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,27 @@ 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: |
kubectl delete pods --all -n default
sleep 5
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=Available deployment/nginx -n default
sleep 5
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: |
Expand All @@ -142,7 +163,8 @@ jobs:
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
kubectl wait --for=condition=Available deployment/nginx -n default
sleep 5
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
Expand Down Expand Up @@ -183,18 +205,21 @@ jobs:
- name: Test Annotations
run: |
kubectl apply -f integration-tests/manifests/sample-deployment.yaml
kubectl get pods -A
kubectl describe pods -n default
sleep 5
sleep 10
go test -v -run TestAllLanguagesDeployment ./integration-tests/manifests/annotations -timeout 30m
kubectl get pods -A
kubectl describe pods -n default
sleep 5
go test -v -run TestJavaOnlyDeployment ./integration-tests/manifests/annotations -timeout 30m
sleep 5
go test -v -run TestPythonOnlyDeployment ./integration-tests/manifests/annotations -timeout 30m
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:
Expand Down Expand Up @@ -233,18 +258,21 @@ jobs:
- name: Test Annotations
run: |
kubectl apply -f integration-tests/manifests/sample-daemonset.yaml
sleep 5
kubectl get pods -A
kubectl describe pods -n default
go test -v -run TestAllLanguagesDaemonSet ./integration-tests/manifests/annotations -timeout 30m
kubectl get pods -A
kubectl describe pods -n default
sleep 5
go test -v -run TestJavaOnlyDaemonSet ./integration-tests/manifests/annotations -timeout 30m
sleep 5
go test -v -run TestPythonOnlyDaemonSet ./integration-tests/manifests/annotations -timeout 30m
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:
Expand Down Expand Up @@ -283,16 +311,20 @@ jobs:
- name: Test Annotations
run: |
kubectl apply -f integration-tests/manifests/sample-statefulset.yaml
sleep 5
kubectl get pods -A
kubectl describe pods -n default
go test -v -run TestAllLanguagesStatefulSet ./integration-tests/manifests/annotations -timeout 30m
kubectl get pods -A
kubectl describe pods -n default
sleep 5
go test -v -run TestJavaOnlyStatefulSet ./integration-tests/manifests/annotations -timeout 30m
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
Expand Down Expand Up @@ -342,4 +374,6 @@ jobs:
sleep 5
go test -v -run TestDotNetOnlyNamespace ./integration-tests/manifests/annotations -timeout 30m
sleep 5
go test -v -run TestAlreadyAutoAnnotatedResourceShouldNotRestart ./integration-tests/manifests/annotations -timeout 30m
go test -v -run TestNodeJSOnlyNamespace ./integration-tests/manifests/annotations -timeout 30m
sleep 5
go test -v -run TestAlreadyAutoAnnotatedResourceShouldNotRestart ./integration-tests/manifests/annotations -timeout 30m
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,12 @@ ARG AGENT_VERSION
ARG AUTO_INSTRUMENTATION_JAVA_VERSION
ARG AUTO_INSTRUMENTATION_PYTHON_VERSION
ARG AUTO_INSTRUMENTATION_DOTNET_VERSION
ARG AUTO_INSTRUMENTATION_NODEJS_VERSION
ARG DCMG_EXPORTER_VERSION
ARG NEURON_MONITOR_VERSION

# Build
RUN CGO_ENABLED=0 GOOS=linux GO111MODULE=on go build -ldflags="-X ${VERSION_PKG}.version=${VERSION} -X ${VERSION_PKG}.buildDate=${VERSION_DATE} -X ${VERSION_PKG}.agent=${AGENT_VERSION} -X ${VERSION_PKG}.autoInstrumentationJava=${AUTO_INSTRUMENTATION_JAVA_VERSION} -X ${VERSION_PKG}.autoInstrumentationPython=${AUTO_INSTRUMENTATION_PYTHON_VERSION} -X ${VERSION_PKG}.autoInstrumentationDotNet=${AUTO_INSTRUMENTATION_DOTNET_VERSION} -X ${VERSION_PKG}.dcgmExporter=${DCMG_EXPORTER_VERSION} -X ${VERSION_PKG}.neuronMonitor=${NEURON_MONITOR_VERSION}" -a -o manager main.go
RUN CGO_ENABLED=0 GOOS=linux GO111MODULE=on go build -ldflags="-X ${VERSION_PKG}.version=${VERSION} -X ${VERSION_PKG}.buildDate=${VERSION_DATE} -X ${VERSION_PKG}.agent=${AGENT_VERSION} -X ${VERSION_PKG}.autoInstrumentationJava=${AUTO_INSTRUMENTATION_JAVA_VERSION} -X ${VERSION_PKG}.autoInstrumentationPython=${AUTO_INSTRUMENTATION_PYTHON_VERSION} -X ${VERSION_PKG}.autoInstrumentationDotNet=${AUTO_INSTRUMENTATION_DOTNET_VERSION} -X ${VERSION_PKG}.autoInstrumentationNodeJS=${AUTO_INSTRUMENTATION_NODEJS_VERSION} -X ${VERSION_PKG}.dcgmExporter=${DCMG_EXPORTER_VERSION} -X ${VERSION_PKG}.neuronMonitor=${NEURON_MONITOR_VERSION}" -a -o manager main.go

# Use distroless as minimal base image to package the manager binary
# Refer to https://github.com/GoogleContainerTools/distroless for more details
Expand Down
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ AGENT_VERSION ?= "$(shell grep -v '\#' versions.txt | grep cloudwatch-agent | aw
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_DOTNET_VERSION ?= "$(shell grep -v '\#' versions.txt | grep aws-otel-dotnet-instrumentation | awk -F= '{print $$2}')"
AUTO_INSTRUMENTATION_NODEJS_VERSION ?= "$(shell grep -v '\#' versions.txt | grep aws-otel-nodejs-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}')"

Expand Down Expand Up @@ -154,7 +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_DOTNET_VERSION=${AUTO_INSTRUMENTATION_DOTNET_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 AUTO_INSTRUMENTATION_NODEJS_VERSION=${AUTO_INSTRUMENTATION_NODEJS_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
.PHONY: container-push
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Supported Languages:
- Java
- Python
- .NET
- NodeJS

This repo is based off of the [OpenTelemetry Operator](https://github.com/open-telemetry/opentelemetry-operator)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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())
}

Expand Down Expand Up @@ -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())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -261,6 +265,25 @@ func checkIfAnnotationExists(clientset *kubernetes.Clientset, pods *v1.PodList,
}

fmt.Println("Annotations not found in all pods or some pods are not in Running phase. Retrying...")
cmd := exec.Command("kubectl", "rollout", "restart", "deployment", amazonControllerManager, "-n", amazonCloudwatchNamespace)

// Run the command and capture the output
output, err := cmd.CombinedOutput()
if err != nil {
fmt.Printf("Error restarting deployment: %v\n", err)
fmt.Printf("Output: %s\n", output)
} else {
fmt.Printf("Successfully deleted deployment: %s\n", output)
}
waitCmd := exec.Command("kubectl", "wait", "--for=condition=Available", "deployment/"+amazonControllerManager, "-n", amazonCloudwatchNamespace, "--timeout=300s")

waitOutput, err := waitCmd.CombinedOutput()
if err != nil {
fmt.Printf("Error waiting for deployment: %v\n", err)
fmt.Printf("Output: %s\n", waitOutput)
} else {
fmt.Printf("Deployment is now available: %s\n", waitOutput)
}
time.Sleep(timeBetweenRetries)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,20 @@ 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)

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())
}

Expand Down Expand Up @@ -89,7 +95,7 @@ func TestJavaOnlyDeployment(t *testing.T) {
updateTheOperator(t, clientSet, string(jsonStr))

if err := checkResourceAnnotations(t, clientSet, "deployment", uniqueNamespace, deploymentName, sampleDeploymentYamlNameRelPath, startTime, []string{injectJavaAnnotation, autoAnnotateJavaAnnotation}, false); err != nil {
t.Fatalf("Failed annotation check: %s", err.Error())
t.Fatalf("Failed annotation check: %s", err.Error())
}
}

Expand Down Expand Up @@ -167,3 +173,40 @@ 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)
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())
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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")
}
}
Expand Down Expand Up @@ -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{
NodeJS: 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) {

Expand Down
Loading

0 comments on commit 95a68d1

Please sign in to comment.